UNPKG

particle-api-js

Version:
1,932 lines (1,797 loc) 83.1 kB
'use strict'; const should = require('should'); // monkeypatch the world~!1 const Particle = require('../src/Particle'); const Defaults = require('../src/Defaults'); const Client = require('../src/Client'); const EventStream = require('../src/EventStream'); const FakeAgent = require('./FakeAgent'); const { sinon, expect } = require('./test-setup'); let api; const props = { url: 'http://www.zombo.com/', password: 'test-password', currentPassword: 'test-password', data: { sentient: true }, isPrivate: true, username: 'test-user', argument: 'noThanks', shouldUpdate: 'duh', name: 'specialName', productId: '9001', deviceId: '1337', device: 'my-device', key: 'c1a55e5', event: 'main', token: 'Y', signal: '1', auth: 'X', accountInfo : { first_name: 'John', last_name: 'Scully', business_account: true, company_name: 'Apple Inc.' }, files: { 'app.ino': new Buffer('void(){}\nsetup(){}\n') }, binaryId: '123456', targetVersion: '0.4.7', headers: { test: 'header' }, rejectUnauthorized: true, noDefaults: true, hook: { method: 'PUT', auth: { username: 'u', password: 'p' }, headers: { one: '1', two: '2' }, query: { q: 'p' }, json: { j: 'd' }, form: { f: 'd' }, body: '{{data}}', responseTemplate: 'template', responseEvent: 'res-event', errorResponseEvent: 'res-err-event' }, hookId: 'hook-1234567890', integrationId: 'integration-1234567890', clientId: 'client-123', type: 'web', redirect_uri: 'https://example.com', scope: 'Owner', deviceName: 'test-device', page: 5, perPage: 50, sortAttr: 'deviceId', sortDir: 'asc', deny: false, deviceIds: ['abc', 'xyz'], development: false, notes: 'A fancy Photon', desiredFirmwareVersion: 42, flash: false, version: '42', title: 'prod', description: 'ready for production', file: new Buffer('ELF...'), country: 'RO', iccid: '1234567890', iccids: ['1234567890', '9876543210'], serialNumber: 'PH-123456', settings: { url: 'http://example.com' }, otp: '123456', mfaToken: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', networkId: '65535', groups: ['foo', 'bar'], dateRange: '2020-05-15T18:29:45.000Z,2020-05-19T18:29:45.000Z', rectBl: '56.185412,-4.049868', rectTr: '56.571537,-5.385920', logicFunctionId: 'ea838324-a4a7-4fa6-b278-95bdaed7114b', logicRunId: 'd0580956-71dc-4f11-89e2-987dcf82a86f', logicFunction: { enabled: true, name: 'function-1', description: 'hello world', source: { type: 'JavaScript', code: 'console.log("hello from function-1");' }, logic_triggers: [ { type: 'Event', enabled: true, product_id: 9001, event_name: 'main', }, { type: 'Scheduled', enabled: true, cron: '0 0 1 * *', start_at: '2021-05-15T18:29:45.000Z', end_at: '2021-05-19T18:29:45.000Z', } ] }, logic: { source: { type: 'JavaScript', code: 'export default function main() { console.log("hi"); }' }, event: { event_name: 'main', device_id: '1234', event_data: JSON.stringify({ data: true }), product_id: 9001, user_id: '5678' } }, ledgerName: 'myledger', ledger: { scope: 'Owner', name: 'myledger', description: 'my ledger', direction: 'CloudOnly' }, scopeValue: 'abc1234', instance: { property: 'yes' }, setMode: 'Replace' }; const product = 'ze-product-v1'; const propsWithProduct = Object.assign({ product }, props); const org = 'test-org'; const propsWithOrg = Object.assign({ org }, props); class Common { static expectCredentials({ form }){ form.username.should.equal(props.username); form.password.should.equal(props.password); } static expectDeviceUrlAndToken(results){ results.uri.should.containEql(props.deviceId); results.auth.should.equal(props.auth); } } describe('ParticleAPI', () => { beforeEach(() => { api = new Particle(); }); describe('operations', () => { beforeEach(() => { api.agent = new FakeAgent(); }); describe('constructor', () => { it('sets maps defaults to instance properties', () => { Object.keys(Defaults).forEach((setting) => { expect(api[setting]).to.eql(Defaults[setting]); }); }); describe('without defaultAuth', () => { it('does NOT call .setDefaultAuth(defaultAuth) unless provided value is truthy', () => { sinon.stub(api, 'setDefaultAuth'); expect(api.setDefaultAuth).to.have.property('callCount', 0); }); }); describe('with defaultAuth', () => { it('calls .setDefaultAuth(defaultAuth) when provided defaultAuth value is truthy', () => { const fakeAuthToken = 'foo'; sinon.stub(Particle.prototype, 'setDefaultAuth'); api = new Particle({ auth: fakeAuthToken }); expect(api.setDefaultAuth).to.have.property('callCount', 1); expect(api.setDefaultAuth.firstCall.args).to.have.lengthOf(1); expect(api.setDefaultAuth.firstCall.args[0]).to.eql(fakeAuthToken); }); }); }); describe('trackingIdentity', () => { const context = { tool: { name: 'cli', version:'1.2.3' } }; it('full', () => { return api.trackingIdentity({ auth: 'X', full: true, context }) .then((req) => { expect(req).to.have.property('uri', '/v1/user/identify'); expect(req).to.have.property('method', 'get'); expect(req).to.have.property('query', undefined); expect(req).to.have.property('context').that.is.eql(context); expect(req).to.have.property('auth', 'X'); }); }); it('id only', () => { return api.trackingIdentity().then(req => { expect(req).to.have.property('uri').eql('/v1/user/identify'); expect(req).to.have.property('method').eql('get'); expect(req).to.have.property('context').eql({}); expect(req).to.have.property('query').eql({ tracking: 1 }); }); }); }); describe('.login', () => { it('sends credentials', () => { return api.login(props).then(Common.expectCredentials); }); }); describe('.loginAsClientOwner', () => { it('sends client ID and secret', () => { const clientApi = new Particle({ clientId: 'foo', clientSecret: 'bar' }); clientApi.agent = new FakeAgent(); return clientApi.loginAsClientOwner({}).then(req => { expect(req.form).to.have.property('client_id').eql('foo'); expect(req.form).to.have.property('client_secret').eql('bar'); expect(req.form).to.have.property('grant_type').eql('client_credentials'); }); }); }); describe('.sendOtp', () => { it('sends request to oauth token endpoint to verify the user login', () => { return api.sendOtp(props).then((results) => { expect(results).to.have.property('method').eql('post'); expect(results).to.have.property('uri').eql('/oauth/token'); expect(results.form).to.have.property('otp').eql(props.otp); expect(results.form).to.have.property('mfa_token').eql(props.mfaToken); }); }); }); describe('.enableMfa', () => { it('sends request to begin mfa enrollment', () => { return api.enableMfa(props).then((results) => { results.should.eql({ uri: '/v1/user/mfa-enable', method: 'get', auth: props.auth, headers: props.headers, query: undefined, context: {} }); }); }); }); describe('.confirmMfa', () => { it('sends request to confirm mfa enrollment', () => { return api.confirmMfa(props).then((results) => { results.should.eql({ uri: '/v1/user/mfa-enable', method: 'post', auth: props.auth, headers: props.headers, data: { otp: props.otp, mfa_token: props.mfaToken }, context: {} }); }); }); it('allows invalidating tokens', () => { return api.confirmMfa(Object.assign({ invalidateTokens: true }, props)).then((results) => { results.should.eql({ uri: '/v1/user/mfa-enable', method: 'post', auth: props.auth, headers: props.headers, data: { otp: props.otp, mfa_token: props.mfaToken, invalidate_tokens: true }, context: {} }); }); }); }); describe('.disableMfa', () => { it('sends request to disable mfa', () => { return api.disableMfa(props).then((results) => { results.should.eql({ uri: '/v1/user/mfa-disable', method: 'put', auth: props.auth, headers: props.headers, query: undefined, data: { current_password: props.password, }, context: {}, }); }); }); }); describe('.createCustomer', () => { it('sends client ID and secret', () => { const clientApi = new Particle({ clientId: 'foo', clientSecret: 'bar' }); clientApi.agent = new FakeAgent(); return clientApi.loginAsClientOwner({}).then(req => { expect(req.form).to.have.property('client_id').eql('foo'); expect(req.form).to.have.property('client_secret').eql('bar'); expect(req.form).to.have.property('grant_type').eql('client_credentials'); }); }); }); describe('.createUser', () => { it('sends credentials', () => { return api.createUser(props).then((results) => { results.should.eql({ uri: '/v1/users', method: 'post', auth: undefined, headers: props.headers, data: { username: props.username, password: props.password, account_info: props.accountInfo, utm: undefined }, context: {} }); }); }); it('allows sending utm parameters', () => { props.utm = { utm_source: 'web' }; return api.createUser(props).then((results) => { results.should.eql({ uri: '/v1/users', method: 'post', auth: undefined, headers: props.headers, data: { username: props.username, password: props.password, account_info: props.accountInfo, utm: { utm_source: 'web' } }, context: {} }); }); }); }); describe('.resetPassword', () => { it('sends request', () => { return api.resetPassword(props).then(({ data }) => { data.username.should.equal(props.username); }); }); }); describe('.deleteAccessToken', () => { it('sends request', () => { return api.deleteAccessToken(props).then((results) => { results.should.match({ method: 'delete', uri: `/v1/access_tokens/${props.token}`, }); }); }); }); describe('.deleteCurrentAccessToken', () => { it('sends request', () => { return api.deleteCurrentAccessToken(props).then((results) => { results.should.match({ method: 'delete', uri: '/v1/access_tokens/current', auth: props.auth, }); }); }); }); describe('.deleteActiveAccessTokens', () => { it('sends request', () => { return api.deleteActiveAccessTokens(props).then((results) => { results.should.match({ method: 'delete', uri: '/v1/access_tokens', auth: props.auth, }); }); }); }); describe('.listDevices', () => { describe('user scope', () => { it('generates request', () => { return api.listDevices(props).then((results) => { results.should.match({ method: 'get', uri: '/v1/devices', auth: props.auth, query: undefined }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.listDevices(propsWithProduct).then((results) => { results.should.match({ method: 'get', uri: `/v1/products/${product}/devices`, auth: props.auth, query: { deviceName: props.deviceName, page: props.page, per_page: props.perPage, groups: props.groups.join(','), sortAttr: props.sortAttr, sortDir: props.sortDir } }); }); }); }); }); describe('.getDevice', () => { describe('user scope', () => { it('generates request', () => { return api.getDevice(props).then((results) => { results.should.match({ method: 'get', uri: `/v1/devices/${props.deviceId}`, auth: props.auth }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.getDevice(propsWithProduct).then((results) => { results.should.match({ method: 'get', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth }); }); }); }); }); describe('.claimDevice', () => { it('sends credentials', () => { return api.claimDevice(props).then((results) => { results.data.should.be.instanceOf(Object); results.data.id.should.equal(props.deviceId); results.auth.should.equal(props.auth); }); }); }); describe('.addDeviceToProduct', () => { it('sends request to add a single device by id', () => { const prodProps = Object.assign({}, propsWithProduct); delete prodProps.file; return api.addDeviceToProduct(prodProps).then((results) => { results.should.match({ method: 'post', uri: `/v1/products/${product}/devices`, auth: props.auth, data: { id: props.deviceId } }); }); }); it('sends request to add multiple devices using a file', () => { return api.addDeviceToProduct(propsWithProduct).then((results) => { results.should.match({ method: 'post', uri: `/v1/products/${product}/devices`, auth: props.auth, files: { file: props.file, } }); }); }); }); describe('.removeDevice', () => { describe('user scope', () => { it('generates request', () => { return api.removeDevice(props).then((results) => { results.should.match({ method: 'delete', uri: `/v1/devices/${props.deviceId}`, auth: props.auth }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.removeDevice(propsWithProduct).then((results) => { results.should.match({ method: 'delete', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { deny: props.deny } }); }); }); }); }); describe('.removeDeviceOwner', () => { it('generates request', () => { return api.removeDeviceOwner(propsWithProduct).then((results) => { results.should.match({ method: 'delete', uri: `/v1/products/${product}/devices/${props.deviceId}/owner`, auth: props.auth }); }); }); }); describe('.renameDevice', () => { describe('user scope', () => { it('generates request', () => { return api.renameDevice(props).then((results) => { results.should.match({ method: 'put', uri: `/v1/devices/${props.deviceId}`, auth: props.auth, data: { name: props.name } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.renameDevice(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { name: props.name } }); }); }); }); }); describe('.setDeviceNotes', () => { describe('user scope', () => { it('generates request', () => { return api.setDeviceNotes(props).then((results) => { results.should.match({ method: 'put', uri: `/v1/devices/${props.deviceId}`, auth: props.auth, data: { notes: props.notes } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.setDeviceNotes(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { notes: props.notes } }); }); }); }); }); describe('.markAsDevelopmentDevice', () => { describe('without development parameter', () => { it('generates request', () => { const params = Object.assign({}, propsWithProduct, { development: undefined }); return api.markAsDevelopmentDevice(params).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { development: true } }); }); }); }); describe('with development parameter', () => { it('generates request', () => { return api.markAsDevelopmentDevice(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { development: false } }); }); }); }); }); describe('.lockDeviceProductFirmware', () => { it('generates request', () => { return api.lockDeviceProductFirmware(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { desired_firmware_version: props.desiredFirmwareVersion, flash: props.flash } }); }); }); }); describe('.unlockDeviceProductFirmware', () => { it('generates request', () => { return api.unlockDeviceProductFirmware(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { desired_firmware_version: null } }); }); }); }); describe('.updateDevice', () => { describe('user scope', () => { it('generates request', () => { return api.updateDevice(props).then((results) => { results.should.match({ method: 'put', uri: `/v1/devices/${props.deviceId}`, auth: props.auth, data: { name: props.name, notes: props.notes } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.updateDevice(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth, data: { name: props.name, notes: props.notes, development: props.development, desired_firmware_version: props.desiredFirmwareVersion, flash: props.flash } }); }); }); }); }); describe('.provisionDevice', () => { it('generates request', () => { return api.provisionDevice(props).then((results) => { results.should.match({ method: 'post', uri: '/v1/devices', auth: props.auth, data: { product_id: props.productId } }); }); }); }); describe('.getClaimCode', () => { describe('user scope', () => { it('generates request', () => { return api.getClaimCode(props).then((results) => { results.should.match({ method: 'post', uri: '/v1/device_claims', auth: props.auth, data: { iccid: props.iccid } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.getClaimCode(propsWithProduct).then((results) => { results.should.match({ method: 'post', uri: `/v1/products/${product}/device_claims`, auth: props.auth, data: { iccid: props.iccid } }); }); }); }); }); describe('.getVariable', () => { describe('user scope', () => { it('generates request', () => { return api.getVariable(props).then((results) => { results.should.match({ method: 'get', uri: `/v1/devices/${props.deviceId}/${props.name}`, auth: props.auth }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.getVariable(propsWithProduct).then((results) => { results.should.match({ method: 'get', uri: `/v1/products/${product}/devices/${props.deviceId}/${props.name}`, auth: props.auth }); }); }); }); }); describe('.signalDevice', () => { it('generates request', () => { return api.signalDevice(props).then(Common.expectDeviceUrlAndToken); }); it('sends proper data', () => { return api.signalDevice(props).then(({ data }) => { data.should.be.instanceOf(Object); data.signal.should.equal(props.signal); }); }); }); describe('.flashTinker', () => { it('generates request', () => { return api.flashTinker(props).then(Common.expectDeviceUrlAndToken); }); it('sends proper data', () => { return api.flashTinker(props).then(({ data }) => { data.should.be.instanceOf(Object); data.app.should.equal('tinker'); }); }); }); describe('.flashDevice', () => { describe('user scope', () => { it('generates request', () => { return api.flashDevice(props).then(Common.expectDeviceUrlAndToken); }); it('sends proper data', () => { return api.flashDevice(props).then(({ files, form }) => { form.should.be.instanceOf(Object); files.should.be.instanceOf(Object); files.should.have.property('app.ino').and.be.ok; form.build_target_version.should.equal(props.targetVersion); }); }); }); describe('product scope', () => { it('generates request', () => { return api.flashDevice(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/devices/${props.deviceId}`, auth: props.auth }); }); }); }); }); describe('.compileCode', () => { it('generates request', () => { return api.compileCode(props).then((results) => { results.auth.should.equal(props.auth); }); }); it('sends proper data', () => { return api.compileCode(props).then(({ files, form }) => { form.should.be.instanceOf(Object); files.should.be.instanceOf(Object); files.should.have.property('app.ino').and.be.ok; form.build_target_version.should.equal(props.targetVersion); }); }); }); describe('.downloadFirmwareBinary', () => { it('generates request', () => { sinon.stub(api, 'request').callsFake(x => Promise.resolve(x)); const req = api.downloadFirmwareBinary(propsWithProduct); api.request.callCount.should.equal(1); return req.then((results) => { results.should.match({ uri: `/v1/binaries/${props.binaryId}`, method: 'get', auth: props.auth, isBuffer: true }); }); }); }); describe('.sendPublicKey', () => { it('generates request', () => { return api.sendPublicKey(props).then(Common.expectDeviceUrlAndToken); }); it('sends proper data', () => { return api.sendPublicKey(props).then(({ data }) => { data.should.be.instanceOf(Object); data.deviceID.should.equal(props.deviceId); data.publicKey.should.equal(props.key); data.filename.should.equal('particle-api'); data.order.should.containEql('manual'); data.algorithm.should.equal('rsa'); }); }); it('works with buffer', () => { return api.sendPublicKey({ deviceId: '1337', key: new Buffer(props.key), }).then(({ data }) => { data.publicKey.should.equal(props.key); }); }); }); describe('.callFunction', () => { describe('user scope', () => { it('generates request', () => { return api.callFunction(props).then((results) => { results.should.match({ method: 'post', uri: `/v1/devices/${props.deviceId}/${props.name}`, auth: props.auth, data: { args: props.argument, } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.callFunction(propsWithProduct).then((results) => { results.should.match({ method: 'post', uri: `/v1/products/${product}/devices/${props.deviceId}/${props.name}`, auth: props.auth, data: { args: props.argument, } }); }); }); }); }); describe('.getEventStream', () => { before(() => { sinon.stub(EventStream.prototype, 'connect').callsFake(function connect(){ return Promise.resolve({ uri: this.uri }); }); }); after(() => { sinon.restore(); }); it('requests public events', () => { return api.getEventStream({ }).then(({ uri }) => { uri.should.endWith('events'); }); }); it('requests all user events', () => { return api.getEventStream({ deviceId: 'mine' }).then(({ uri }) => { uri.should.endWith('devices/events'); }); }); it('requests all named events', () => { return api.getEventStream({ name: 'test' }).then(({ uri }) => { uri.should.endWith('v1/events/test'); }); }); it('requests all device events', () => { return api.getEventStream({ deviceId: '1337' }).then(({ uri }) => { uri.should.endWith('1337/events'); }); }); it('requests user\'s named events', () => { return api.getEventStream({ deviceId: 'mine', name: 'test', }).then(({ uri }) => { uri.should.endWith('devices/events/test'); }); }); it('requests device\'s named events', () => { return api.getEventStream(props).then(({ uri }) => { uri.should.endWith(`${ props.deviceId }/events/${ props.name }`); }); }); it('requests org\'s events', () => { return api.getEventStream({ org: 'test-org' }).then(({ uri }) => { uri.should.endWith('v1/orgs/test-org/events'); }); }); it('requests org\'s device events', () => { return api.getEventStream({ org: 'test-org', deviceId: props.deviceId }).then(({ uri }) => { uri.should.endWith(`v1/orgs/test-org/devices/${props.deviceId}/events`); }); }); it('requests org\'s device named events', () => { return api.getEventStream({ org: 'test-org', deviceId: props.deviceId, name: 'test' }).then(({ uri }) => { uri.should.endWith(`v1/orgs/test-org/devices/${props.deviceId}/events/test`); }); }); it('requests product\'s events', () => { return api.getEventStream({ org: 'test-org', product: 'test-product' }).then(({ uri }) => { uri.should.endWith('v1/orgs/test-org/products/test-product/events'); }); }); it('requests product\'s named events', () => { return api.getEventStream({ org: 'test-org', product: 'test-product', name: 'test' }).then(({ uri }) => { uri.should.endWith('v1/orgs/test-org/products/test-product/events/test'); }); }); it('requests a product\'s event without an org provided', () => { return api.getEventStream({ product: 'test-product' }) .then(({ uri }) => { uri.should.endWith('v1/products/test-product/events'); }); }); it('requests a product\'s named event without an org provided', () => { return api.getEventStream({ product: 'test-product', name: 'foo' }) .then(({ uri }) => { uri.should.endWith('v1/products/test-product/events/foo'); }); }); it('requests product\'s device events', () => { return api.getEventStream({ product: 'test-product', deviceId: props.deviceId }).then(({ uri }) => { uri.should.endWith(`v1/products/test-product/devices/${props.deviceId}/events`); }); }); it('requests product\'s device named events', () => { return api.getEventStream({ product: 'test-product', deviceId: props.deviceId, name: 'foo' }).then(({ uri }) => { uri.should.endWith(`v1/products/test-product/devices/${props.deviceId}/events/foo`); }); }); it('calls _getActiveAuthToken(auth)', () => { const fakeToken = 'abc123'; sinon.stub(api, '_getActiveAuthToken').returns(fakeToken); api.getEventStream({}); expect(api._getActiveAuthToken).to.have.property('callCount', 1); }); }); describe('.publishEvent', () => { describe('user scope', () => { it('generates request', () => { return api.publishEvent(props).then((results) => { results.should.match({ method: 'post', uri: '/v1/devices/events', auth: props.auth, data: { name: props.name, data: props.data, private: props.isPrivate } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.publishEvent(propsWithProduct).then((results) => { results.should.match({ method: 'post', uri: `/v1/products/${product}/events`, auth: props.auth, data: { name: props.name, data: props.data, private: props.isPrivate } }); }); }); }); }); describe('.createWebhook', () => { describe('user scope', () => { it('creates for a single device', () => { return api.createWebhook(props).then((results) => { results.should.match({ uri: '/v1/webhooks', method: 'post', auth: props.auth, headers: props.headers, data: { event: props.event, url: props.url, deviceId: props.device, rejectUnauthorized: props.rejectUnauthorized, noDefaults: props.noDefaults, requestType: props.hook.method, auth: props.hook.auth, headers: props.hook.headers, query: props.hook.query, json: props.hook.json, form: props.hook.form, body: props.hook.body, responseTemplate: props.hook.responseTemplate, responseTopic: props.hook.responseEvent, errorResponseTopic: props.hook.errorResponseEvent, }, context: {} }); }); }); it('creates for user\'s devices', () => { const params = Object.assign({}, props); delete params.device; return api.createWebhook(params).then((results) => { results.should.match({ uri: '/v1/webhooks', method: 'post', auth: props.auth, headers: props.headers, data: { event: props.event, url: props.url, deviceId: undefined, rejectUnauthorized: props.rejectUnauthorized, noDefaults: props.noDefaults, requestType: props.hook.method, auth: props.hook.auth, headers: props.hook.headers, query: props.hook.query, json: props.hook.json, form: props.hook.form, body: props.hook.body, responseTemplate: props.hook.responseTemplate, responseTopic: props.hook.responseEvent, errorResponseTopic: props.hook.errorResponseEvent, }, context: {} }); }); }); it('creates using defaults', () => { const params = Object.assign({}, props); delete params.device; delete params.rejectUnauthorized; delete params.noDefaults; delete params.hook; delete params.headers; delete params.context; return api.createWebhook(params).then((results) => { results.should.match({ uri: '/v1/webhooks', method: 'post', auth: props.auth, headers: undefined, data: { event: props.event, url: props.url, deviceId: undefined, rejectUnauthorized: undefined, noDefaults: undefined, requestType: 'POST' }, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.createWebhook(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/webhooks`, method: 'post', auth: props.auth, headers: props.headers, data: { event: props.event, url: props.url, deviceId: props.device, rejectUnauthorized: props.rejectUnauthorized, noDefaults: props.noDefaults, requestType: props.hook.method, auth: props.hook.auth, headers: props.hook.headers, query: props.hook.query, json: props.hook.json, form: props.hook.form, body: props.hook.body, responseTemplate: props.hook.responseTemplate, responseTopic: props.hook.responseEvent, errorResponseTopic: props.hook.errorResponseEvent, }, context: {} }); }); }); }); }); describe('.deleteWebhook', () => { describe('user scope', () => { it('generates request', () => { return api.deleteWebhook(props).then((results) => { results.should.match({ method: 'delete', uri: `/v1/webhooks/${props.hookId}`, auth: props.auth, }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.deleteWebhook(propsWithProduct).then((results) => { results.should.match({ method: 'delete', uri: `/v1/products/${product}/webhooks/${props.hookId}`, auth: props.auth, }); }); }); }); }); describe('.listWebhooks', () => { describe('user scope', () => { it('generates request', () => { return api.listWebhooks(props).then((results) => { results.should.match({ method: 'get', uri: '/v1/webhooks', auth: props.auth, }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.listWebhooks(propsWithProduct).then((results) => { results.should.match({ method: 'get', uri: `/v1/products/${product}/webhooks`, auth: props.auth, }); }); }); }); }); describe('.createIntegration', () => { describe('user scope', () => { it('generates request', () => { return api.createIntegration(props).then((results) => { results.should.match({ method: 'post', uri: '/v1/integrations', auth: props.auth, data: { event: props.event, deviceid: props.deviceId, url: props.settings.url, } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.createIntegration(propsWithProduct).then((results) => { results.should.match({ method: 'post', uri: `/v1/products/${product}/integrations`, auth: props.auth, data: { event: props.event, deviceid: props.deviceId, url: props.settings.url, } }); }); }); }); }); describe('.editIntegration', () => { describe('user scope', () => { it('generates request', () => { return api.editIntegration(props).then((results) => { results.should.match({ method: 'put', uri: `/v1/integrations/${props.integrationId}`, auth: props.auth, data: { event: props.event, deviceid: props.deviceId, url: props.settings.url, } }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.editIntegration(propsWithProduct).then((results) => { results.should.match({ method: 'put', uri: `/v1/products/${product}/integrations/${props.integrationId}`, auth: props.auth, data: { event: props.event, deviceid: props.deviceId, url: props.settings.url, } }); }); }); }); }); describe('.deleteIntegration', () => { describe('user scope', () => { it('generates request', () => { return api.deleteIntegration(props).then((results) => { results.should.match({ method: 'delete', uri: `/v1/integrations/${props.integrationId}`, auth: props.auth, }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.deleteIntegration(propsWithProduct).then((results) => { results.should.match({ method: 'delete', uri: `/v1/products/${product}/integrations/${props.integrationId}`, auth: props.auth, }); }); }); }); }); describe('.listIntegrations', () => { describe('user scope', () => { it('generates request', () => { return api.listIntegrations(props).then((results) => { results.should.match({ method: 'get', uri: '/v1/integrations', auth: props.auth, }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.listIntegrations(propsWithProduct).then((results) => { results.should.match({ method: 'get', uri: `/v1/products/${product}/integrations`, auth: props.auth, }); }); }); }); }); describe('.listBuildTargets', () => { it('generates request', () => { return api.listBuildTargets(props).then(({ auth, query }) => { auth.should.equal(props.auth); should.not.exist(query); }); }); it('passes featured flag', () => { const params = { auth: props.auth, onlyFeatured: true }; return api.listBuildTargets(params).then(({ auth, query }) => { auth.should.equal(props.auth); query.should.eql({ featured: true }); }); }); }); describe('.validatePromoCode', () => { it('generates request', () => { return api.validatePromoCode({ auth: 'X', promoCode: '123ABCD' }).then((results) => { results.uri.should.be.instanceOf(String); results.auth.should.equal('X'); }); }); }); describe('.setUserInfo', () => { it('generates request', () => { return api.setUserInfo({ auth: 'X', accountInfo: { first_name: 'John', last_name: 'Scully' } }) .then((results) => { results.should.eql({ uri: '/v1/user', method: 'put', auth: 'X', headers: undefined, query: undefined, data: { account_info: { first_name: 'John', last_name: 'Scully' } }, context: {} }); }); }); }); describe('.changeUsername', () => { it('generates request', () => { return api.changeUsername({ auth: 'X', currentPassword: 'blabla', username: 'john@skul.ly' }) .then((results) => { results.should.eql({ uri: '/v1/user', method: 'put', auth: 'X', headers: undefined, query: undefined, data: { current_password: 'blabla', username: 'john@skul.ly' }, context: {} }); }); }); it('allows invalidating tokens', () => { return api.changeUsername({ auth: 'X', currentPassword: 'blabla', username: 'john@skul.ly', invalidateTokens: true }) .then((results) => { results.should.eql({ uri: '/v1/user', method: 'put', auth: 'X', headers: undefined, query: undefined, data: { current_password: 'blabla', username: 'john@skul.ly', invalidate_tokens: true }, context: {} }); }); }); }); describe('.changeUserPassword', () => { it('generates request', () => { return api.changeUserPassword({ auth: 'X', currentPassword: 'blabla', password: 'blabla2' }) .then((results) => { results.should.eql({ uri: '/v1/user', method: 'put', auth: 'X', headers: undefined, query: undefined, data: { current_password: 'blabla', password: 'blabla2' }, context: {} }); }); }); it('allows invalidating tokens', () => { return api.changeUserPassword({ auth: 'X', currentPassword: 'blabla', password: 'blabla2', invalidateTokens: true }) .then((results) => { results.should.eql({ method: 'put', uri: '/v1/user', auth: 'X', headers: undefined, query: undefined, data: { current_password: 'blabla', password: 'blabla2', invalidate_tokens: true }, context: {} }); }); }); }); describe('.listSIMs', () => { describe('user scope', () => { it('generates request', () => { return api.listSIMs(props).then((results) => { results.should.match({ uri: '/v1/sims', method: 'get', auth: props.auth, headers: props.headers, query: undefined, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.listSIMs(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims`, method: 'get', auth: props.auth, headers: props.headers, query: { iccid: props.iccid, deviceId: props.deviceId, deviceName: props.deviceName, page: props.page, per_page: props.perPage }, context: {} }); }); }); }); }); describe('.activateSIM', () => { describe('user scope', () => { it('generates request', () => { return api.activateSIM(props).then((results) => { results.should.match({ uri: `/v1/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { country: props.country, action: 'activate' }, context: {} }); }); }); }); describe('product scope', () => { describe('single SIM', () => { it('generates request', () => { const propsSingleSIM = Object.assign({}, propsWithProduct); delete propsSingleSIM.iccids; return api.activateSIM(propsSingleSIM).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims`, method: 'post', auth: props.auth, headers: props.headers, data: { sims: [props.iccid], country: props.country, }, context: {} }); }); }); }); describe('multiple SIMs', () => { it('generates request', () => { return api.activateSIM(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims`, method: 'post', auth: props.auth, headers: props.headers, data: { sims: props.iccids, country: props.country, }, context: {} }); }); }); }); }); }); describe('.deactivateSIM', () => { describe('user scope', () => { it('generates request', () => { return api.deactivateSIM(props).then((results) => { results.should.match({ uri: `/v1/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { action: 'deactivate' }, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.deactivateSIM(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { action: 'deactivate' }, context: {} }); }); }); }); }); describe('.reactivateSIM', () => { describe('user scope', () => { it('generates request', () => { return api.reactivateSIM(props).then((results) => { results.should.match({ uri: `/v1/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { mb_limit: props.mbLimit, action: 'reactivate' }, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.reactivateSIM(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { mb_limit: props.mbLimit, action: 'reactivate' }, context: {} }); }); }); }); }); describe('.removeSIM', () => { describe('user scope', () => { it('generates request', () => { return api.removeSIM(props).then((results) => { results.should.match({ uri: `/v1/sims/${props.iccid}`, method: 'delete', auth: props.auth, headers: props.headers, data: undefined, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.removeSIM(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims/${props.iccid}`, method: 'delete', auth: props.auth, headers: props.headers, data: undefined, context: {} }); }); }); }); }); describe('.updateSIM', () => { describe('user scope', () => { it('generates request', () => { return api.updateSIM(props).then((results) => { results.should.match({ uri: `/v1/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { mb_limit: props.mbLimit }, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.updateSIM(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims/${props.iccid}`, method: 'put', auth: props.auth, headers: props.headers, data: { mb_limit: props.mbLimit }, context: {} }); }); }); }); }); describe('.getSIMDataUsage', () => { describe('user scope', () => { it('generates request', () => { return api.getSIMDataUsage(props).then((results) => { results.should.match({ uri: `/v1/sims/${props.iccid}/data_usage`, method: 'get', auth: props.auth, headers: props.headers, query: undefined, context: {} }); }); }); }); describe('product scope', () => { it('generates request', () => { return api.getSIMDataUsage(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims/${props.iccid}/data_usage`, method: 'get', auth: props.auth, headers: props.headers, query: undefined, context: {} }); }); }); }); }); describe('.getFleetDataUsage', () => { it('generates request', () => { return api.getFleetDataUsage(propsWithProduct).then((results) => { results.should.match({ uri: `/v1/products/${product}/sims/data_usage`, method: 'get', auth: props.auth, headers: props.headers, query: undefined, context: {} }); }); }); }); describe('.checkSIM', () => { it('generates request', () => { return api.checkSIM({ auth: 'X', iccid: '1234567890123456789' }).then((results) => { results.should.match({ uri: '/v1/sims/1234567890123456789', method: 'head', auth: 'X', headers: undefined, query: undefined, context: {} }); }); }); }); describe('.listLibraries', () => { it('generates request', () => { return api.listLibraries(props).then((results) => { results.should.match({ uri: '/v1/libraries', method: 'get', auth: props.auth, headers: props.headers, context: {} }); }); }); it('forwards query parameters', () => { return api.listLibraries({ auth: 'X', page: 3, filter: 'abc', sort: 'name', architectures: ['spark-core', 'particle-photon'], category: 'Other' }).then((results) => { results.query.should.eql({ page: 3, limit: undefined, filter: 'abc', excludeScopes: undefined, sort: 'name', scope: undefined, architectures: 'spark-core,particle-photon', category: 'Other' }); }); }); }); describe('.getLibrary', () => { it('generates request', () => { return api.getLibrary({ name: 'mylib', auth: 'X' }).then((results) => { results.should.match({ uri: '/v1/libraries/mylib', auth: 'X' }); }); }); it('forwards query parameters', () => { return api.getLibrary({ name: 'mylib', auth: 'X', version: '1.2.0' }).then((results) => { results.query.should.eql({ version: '1.2.0' }); }); }); }); describe('.getLibraryVersions', () => { it('generates request', () => { return api.getLibraryVersions({ name: 'mylib', auth: 'X' }).then((results) => { results.should.match({ uri: '/v1/libraries/mylib/versions', auth: 'X' }); }); }); it('forwards query parameters', () => { return api.getLibraryVersions({ name: 'mylib', auth: 'X', page: 3 }).then((results) => { results.query.should.eql({ page: 3, limit: undefined }); }); }); }); describe('.contributeLibrary', () => { it('generates request', () => { const archive = new Buffer('tarball'); return api.contributeLibrary({ archive: archive, auth: 'X' }).then((results) => { results.should.match({ uri: '/v1/libraries', method: 'post', auth: 'X' }); }); }); }); describe('.publishLibrary', () => { it('generates request', () => { const name = 'noname'; return api.publishLibrary({ name, auth: 'X' }).then((results) => { results.should.match({ uri: '/v1/libraries/noname', method: 'patch', auth: 'X', data: { visibility: 'public' } }); }); }); }); describe('.deleteLibrary', () => { it('generates request', () => { return api.deleteLibrary({ name: 'mylib', auth: 'X', force: 'xyz' }).the