2019-12-05 08:55:17 +00:00
|
|
|
/**
|
|
|
|
* @license
|
2020-01-15 19:41:47 +00:00
|
|
|
* Copyright 2019-2020 Balena Ltd.
|
2019-12-05 08:55:17 +00:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2019-12-04 18:04:57 +00:00
|
|
|
import * as _ from 'lodash';
|
2020-01-15 19:41:47 +00:00
|
|
|
import * as path from 'path';
|
2019-12-05 08:55:17 +00:00
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
import { NockMock, ScopeOpts } from './nock-mock';
|
2019-12-05 08:55:17 +00:00
|
|
|
|
2020-01-24 01:21:51 +00:00
|
|
|
export const apiResponsePath = path.normalize(
|
2020-01-15 19:41:47 +00:00
|
|
|
path.join(__dirname, 'test-data', 'api-response'),
|
|
|
|
);
|
|
|
|
|
|
|
|
const jHeader = { 'Content-Type': 'application/json' };
|
2019-12-05 08:55:17 +00:00
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
export class BalenaAPIMock extends NockMock {
|
2019-12-05 08:55:17 +00:00
|
|
|
constructor() {
|
2020-06-15 22:53:04 +00:00
|
|
|
super(/api\.balena-cloud\.com/);
|
2020-01-15 19:41:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public expectGetApplication(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v5\/application($|[(?])/, opts).replyWithFile(
|
|
|
|
200,
|
|
|
|
path.join(apiResponsePath, 'application-GET-v5-expanded-app-type.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-06-11 11:32:20 +00:00
|
|
|
public expectDownloadConfig(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost('/download-config', opts).reply(
|
|
|
|
200,
|
|
|
|
JSON.parse(`{
|
|
|
|
"applicationId":1301645,
|
|
|
|
"deviceType":"raspberrypi3",
|
|
|
|
"userId":43699,
|
|
|
|
"appUpdatePollInterval":600000,
|
|
|
|
"listenPort":48484,
|
|
|
|
"vpnPort":443,
|
|
|
|
"apiEndpoint":"https://api.balena-cloud.com",
|
|
|
|
"vpnEndpoint":"vpn.balena-cloud.com",
|
|
|
|
"registryEndpoint":"registry2.balena-cloud.com",
|
|
|
|
"deltaEndpoint":"https://delta.balena-cloud.com",
|
|
|
|
"mixpanelToken":"",
|
|
|
|
"apiKey":"nothingtoseehere"
|
|
|
|
}`),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public expectApplicationProvisioning(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost(/^\/api-key\/application\/[0-9]+\/provisioning$/, opts).reply(
|
|
|
|
200,
|
|
|
|
'dummykey',
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetMyApplication(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v5\/my_application($|[(?])/, opts).reply(
|
|
|
|
200,
|
|
|
|
JSON.parse(`{"d": [{
|
|
|
|
"user": [{ "username": "bob", "__metadata": {} }],
|
|
|
|
"id": 1301645,
|
|
|
|
"__metadata": { "uri": "/resin/my_application(@id)?@id=1301645" }}]}
|
|
|
|
`),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public expectGetAuth(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/auth\/v1\//, opts).reply(200, {
|
|
|
|
token: 'test',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public expectGetRelease(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v5\/release($|[(?])/, opts).replyWithFile(
|
|
|
|
200,
|
|
|
|
path.join(apiResponsePath, 'release-GET-v5.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
|
|
|
}
|
2019-12-05 08:55:17 +00:00
|
|
|
|
2020-05-21 13:51:10 +00:00
|
|
|
public expectPatchRelease({
|
|
|
|
replyBody = 'OK',
|
|
|
|
statusCode = 200,
|
|
|
|
inspectRequest = this.inspectNoOp,
|
|
|
|
optional = false,
|
|
|
|
persist = false,
|
|
|
|
}) {
|
|
|
|
this.optPatch(/^\/v5\/release($|[(?])/, { optional, persist }).reply(
|
|
|
|
statusCode,
|
|
|
|
this.getInspectedReplyBodyFunction(inspectRequest, replyBody),
|
|
|
|
);
|
2020-01-15 19:41:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public expectPostRelease(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost(/^\/v5\/release($|[(?])/, opts).replyWithFile(
|
|
|
|
200,
|
|
|
|
path.join(apiResponsePath, 'release-POST-v5.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
|
|
|
}
|
2019-12-05 08:55:17 +00:00
|
|
|
|
2020-05-21 13:51:10 +00:00
|
|
|
public expectPatchImage({
|
|
|
|
replyBody = 'OK',
|
|
|
|
statusCode = 200,
|
|
|
|
inspectRequest = this.inspectNoOp,
|
|
|
|
optional = false,
|
|
|
|
persist = false,
|
|
|
|
}) {
|
|
|
|
this.optPatch(/^\/v5\/image($|[(?])/, { optional, persist }).reply(
|
|
|
|
statusCode,
|
|
|
|
this.getInspectedReplyBodyFunction(inspectRequest, replyBody),
|
|
|
|
);
|
2020-01-15 19:41:47 +00:00
|
|
|
}
|
2019-12-05 08:55:17 +00:00
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectPostImage(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost(/^\/v5\/image($|[(?])/, opts).replyWithFile(
|
|
|
|
201,
|
|
|
|
path.join(apiResponsePath, 'image-POST-v5.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
2019-12-05 08:55:17 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectPostImageLabel(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost(/^\/v5\/image_label($|[(?])/, opts).replyWithFile(
|
|
|
|
201,
|
|
|
|
path.join(apiResponsePath, 'image-label-POST-v5.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
2019-12-05 08:55:17 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectPostImageIsPartOfRelease(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost(
|
|
|
|
/^\/v5\/image__is_part_of__release($|[(?])/,
|
|
|
|
opts,
|
|
|
|
).replyWithFile(
|
|
|
|
200,
|
|
|
|
path.join(apiResponsePath, 'image-is-part-of-release-POST-v5.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
2019-12-05 17:09:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetDevice(opts: {
|
|
|
|
fullUUID: string;
|
|
|
|
inaccessibleApp?: boolean;
|
|
|
|
optional?: boolean;
|
|
|
|
persist?: boolean;
|
|
|
|
}) {
|
2019-12-04 18:04:57 +00:00
|
|
|
const id = 7654321;
|
2020-01-15 19:41:47 +00:00
|
|
|
this.optGet(/^\/v\d+\/device($|\?)/, opts).reply(200, {
|
2019-12-04 18:04:57 +00:00
|
|
|
d: [
|
|
|
|
{
|
|
|
|
id,
|
2020-01-15 19:41:47 +00:00
|
|
|
uuid: opts.fullUUID,
|
|
|
|
belongs_to__application: opts.inaccessibleApp
|
2019-12-04 18:04:57 +00:00
|
|
|
? []
|
|
|
|
: [{ app_name: 'test' }],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
2019-12-05 17:09:02 +00:00
|
|
|
}
|
|
|
|
|
2020-06-15 22:53:01 +00:00
|
|
|
public expectGetDeviceStatus(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(
|
|
|
|
/^\/v\d+\/device\?.+&\$select=overall_status$/,
|
|
|
|
opts,
|
|
|
|
).replyWithFile(
|
|
|
|
200,
|
|
|
|
path.join(apiResponsePath, 'device-status.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetAppEnvVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v\d+\/application_environment_variable($|\?)/, opts).reply(
|
|
|
|
200,
|
|
|
|
{
|
2019-12-05 17:09:02 +00:00
|
|
|
d: [
|
|
|
|
{
|
|
|
|
id: 120101,
|
|
|
|
name: 'var1',
|
|
|
|
value: 'var1-val',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 120102,
|
|
|
|
name: 'var2',
|
|
|
|
value: '22',
|
|
|
|
},
|
|
|
|
],
|
2020-01-15 19:41:47 +00:00
|
|
|
},
|
|
|
|
);
|
2019-12-05 15:34:35 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetAppConfigVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v\d+\/application_config_variable($|\?)/, opts).reply(200, {
|
2019-12-04 18:04:57 +00:00
|
|
|
d: [
|
|
|
|
{
|
|
|
|
id: 120300,
|
|
|
|
name: 'RESIN_SUPERVISOR_NATIVE_LOGGER',
|
|
|
|
value: 'false',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetAppServiceVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v\d+\/service_environment_variable($|\?)/, opts).reply(
|
|
|
|
function(uri, _requestBody) {
|
2019-12-04 18:04:57 +00:00
|
|
|
const match = uri.match(/service_name%20eq%20%27(.+?)%27/);
|
|
|
|
const serviceName = (match && match[1]) || undefined;
|
|
|
|
let varArray: any[];
|
|
|
|
if (serviceName) {
|
|
|
|
const varObj = appServiceVarsByService[serviceName];
|
|
|
|
varArray = varObj ? [varObj] : [];
|
|
|
|
} else {
|
|
|
|
varArray = _.map(appServiceVarsByService, value => value);
|
|
|
|
}
|
|
|
|
return [200, { d: varArray }];
|
2020-01-15 19:41:47 +00:00
|
|
|
},
|
|
|
|
);
|
2019-12-04 18:04:57 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetDeviceEnvVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v\d+\/device_environment_variable($|\?)/, opts).reply(200, {
|
2019-12-05 17:09:02 +00:00
|
|
|
d: [
|
|
|
|
{
|
|
|
|
id: 120203,
|
|
|
|
name: 'var3',
|
|
|
|
value: 'var3-val',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 120204,
|
|
|
|
name: 'var4',
|
|
|
|
value: '44',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetDeviceConfigVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v\d+\/device_config_variable($|\?)/, opts).reply(200, {
|
2019-12-05 17:09:02 +00:00
|
|
|
d: [
|
|
|
|
{
|
|
|
|
id: 120400,
|
|
|
|
name: 'RESIN_SUPERVISOR_POLL_INTERVAL',
|
|
|
|
value: '900900',
|
|
|
|
},
|
|
|
|
],
|
2019-12-05 15:34:35 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetDeviceServiceVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(
|
|
|
|
/^\/v\d+\/device_service_environment_variable($|\?)/,
|
|
|
|
opts,
|
|
|
|
).reply(function(uri, _requestBody) {
|
|
|
|
const match = uri.match(/service_name%20eq%20%27(.+?)%27/);
|
|
|
|
const serviceName = (match && match[1]) || undefined;
|
|
|
|
let varArray: any[];
|
|
|
|
if (serviceName) {
|
|
|
|
const varObj = deviceServiceVarsByService[serviceName];
|
|
|
|
varArray = varObj ? [varObj] : [];
|
|
|
|
} else {
|
|
|
|
varArray = _.map(deviceServiceVarsByService, value => value);
|
|
|
|
}
|
|
|
|
return [200, { d: varArray }];
|
|
|
|
});
|
2019-12-04 18:04:57 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetDeviceTypes(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet('/device-types/v1', opts).replyWithFile(
|
|
|
|
200,
|
|
|
|
path.join(apiResponsePath, 'device-types-GET-v1.json'),
|
|
|
|
jHeader,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public expectGetConfigVars(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet('/config/vars', opts).reply(200, {
|
2019-12-05 08:55:17 +00:00
|
|
|
reservedNames: [],
|
|
|
|
reservedNamespaces: [],
|
|
|
|
invalidRegex: '/^d|W/',
|
|
|
|
whiteListedNames: [],
|
|
|
|
whiteListedNamespaces: [],
|
|
|
|
blackListedNames: [],
|
|
|
|
configVarSchema: [],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetService(opts: {
|
|
|
|
optional?: boolean;
|
|
|
|
persist?: boolean;
|
|
|
|
serviceId?: number;
|
|
|
|
serviceName: string;
|
|
|
|
}) {
|
|
|
|
const serviceId = opts.serviceId || 243768;
|
|
|
|
this.optGet(/^\/v\d+\/service($|\?)/, opts).reply(200, {
|
|
|
|
d: [{ id: serviceId, service_name: opts.serviceName }],
|
2019-12-04 18:04:57 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectPostService404(opts: ScopeOpts = {}) {
|
|
|
|
this.optPost(/^\/v\d+\/service$/, opts).reply(
|
|
|
|
404,
|
|
|
|
'Unique key constraint violated',
|
2019-12-04 18:04:57 +00:00
|
|
|
);
|
2019-12-05 08:55:17 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetUser(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/v5\/user/, opts).reply(200, {
|
|
|
|
d: [
|
|
|
|
{
|
|
|
|
id: 99999,
|
|
|
|
actor: 1234567,
|
|
|
|
username: 'gh_user',
|
|
|
|
created_at: '2018-08-19T13:55:04.485Z',
|
|
|
|
__metadata: {
|
|
|
|
uri: '/resin/user(@id)?@id=43699',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
});
|
2019-12-05 08:55:17 +00:00
|
|
|
}
|
2019-12-11 12:18:58 +00:00
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
// User details are cached in the SDK
|
|
|
|
// so often we don't know if we can expect the whoami request
|
|
|
|
public expectGetWhoAmI(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet('/user/v1/whoami', opts).reply(200, {
|
|
|
|
id: 99999,
|
|
|
|
username: 'gh_user',
|
|
|
|
email: 'testuser@test.com',
|
2019-12-11 12:18:58 +00:00
|
|
|
});
|
2020-01-15 19:41:47 +00:00
|
|
|
}
|
2019-12-11 12:18:58 +00:00
|
|
|
|
2020-01-15 19:41:47 +00:00
|
|
|
public expectGetMixpanel(opts: ScopeOpts = {}) {
|
|
|
|
this.optGet(/^\/mixpanel\/track/, opts).reply(200, {});
|
2019-12-11 12:18:58 +00:00
|
|
|
}
|
2019-12-05 08:55:17 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 18:04:57 +00:00
|
|
|
const appServiceVarsByService: { [key: string]: any } = {
|
|
|
|
service1: {
|
|
|
|
id: 120110,
|
|
|
|
name: 'svar1',
|
|
|
|
value: 'svar1-value',
|
|
|
|
service: [
|
|
|
|
{
|
|
|
|
id: 210110,
|
|
|
|
service_name: 'service1',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
service2: {
|
|
|
|
id: 120111,
|
|
|
|
name: 'svar2',
|
|
|
|
value: 'svar2-value',
|
|
|
|
service: [
|
|
|
|
{
|
|
|
|
id: 210111,
|
|
|
|
service_name: 'service2',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const deviceServiceVarsByService: { [key: string]: any } = {
|
|
|
|
service1: {
|
|
|
|
id: 120120,
|
|
|
|
name: 'svar3',
|
|
|
|
value: 'svar3-value',
|
|
|
|
service: [
|
|
|
|
{
|
|
|
|
id: 210110,
|
|
|
|
service_name: 'service1',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
service2: {
|
|
|
|
id: 120121,
|
|
|
|
name: 'svar4',
|
|
|
|
value: 'svar4-value',
|
|
|
|
service: [
|
|
|
|
{
|
|
|
|
id: 210111,
|
|
|
|
service_name: 'service2',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|