Skip to content

Commit

Permalink
Add device management methods
Browse files Browse the repository at this point in the history
  • Loading branch information
jm-mailosaur committed May 12, 2022
1 parent da05442 commit e9fadb3
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 0 deletions.
5 changes: 5 additions & 0 deletions lib/mailosaur.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ declare class MailosaurClient {
* Account usage operations
*/
usage: operations.Usage;

/**
* Device management operations
*/
devices: operations.Devices;
}
1 change: 1 addition & 0 deletions lib/mailosaur.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class MailosaurClient {
this.messages = new operations.Messages(this);
this.servers = new operations.Servers(this);
this.usage = new operations.Usage(this);
this.devices = new operations.Devices(this);
this.models = models;
}

Expand Down
8 changes: 8 additions & 0 deletions lib/models/device.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class Device {
constructor(data = {}) {
this.id = data.id;
this.name = data.name;
}
}

module.exports = Device;
8 changes: 8 additions & 0 deletions lib/models/deviceCreateOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class DeviceCreateOptions {
constructor(data = {}) {
this.name = data.name;
this.sharedSecret = data.sharedSecret;
}
}

module.exports = DeviceCreateOptions;
9 changes: 9 additions & 0 deletions lib/models/deviceListResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const Device = require('./device');

class DeviceListResult {
constructor(data = {}) {
this.items = (data.items || []).map((i) => (new Device(i)));
}
}

module.exports = DeviceListResult;
52 changes: 52 additions & 0 deletions lib/models/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -595,3 +595,55 @@ export interface MailosaurError {
httpStatusCode?: number;
httpResponseBody?: string;
}

/**
* Mailosaur virtual security device.
*/
export interface Device {
/**
* Unique identifier for the device.
*/
id?: string;
/**
* The name of the device.
*/
name?: string;
}

/**
* Options used to create a new Mailosaur virtual security device.
*/
export interface DeviceCreateOptions {
/**
* A name used to identify the device.
*/
name?: string;
/**
* The base32-encoded shared secret for this device.
*/
sharedSecret?: string;
}

/**
* The result of the device listing operation.
*/
export interface DeviceListResult {
/**
* The individual devices forming the result.
*/
items?: Device[];
}

/**
* Mailosaur virtual security device OTP result.
*/
export interface OtpResult {
/**
* The current one-time password.
*/
code?: string;
/**
* The expiry date/time of the current one-time password.
*/
expires?: Date;
}
4 changes: 4 additions & 0 deletions lib/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ exports.UsageAccountLimit = require('./usageAccountLimit');
exports.UsageAccountLimits = require('./usageAccountLimits');
exports.UsageTransaction = require('./usageTransaction');
exports.UsageTransactionListResult = require('./usageTransactionListResult');
exports.Device = require('./device');
exports.DeviceCreateOptions = require('./deviceCreateOptions');
exports.DeviceListResult = require('./deviceListResult');
exports.OtpResult = require('./otpResult');
8 changes: 8 additions & 0 deletions lib/models/otpResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class OtpResult {
constructor(data = {}) {
this.code = data.code;
this.expires = new Date(data.expires);
}
}

module.exports = OtpResult;
80 changes: 80 additions & 0 deletions lib/operations/devices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const DeviceListResult = require('../models/deviceListResult');
const Device = require('../models/device');
const OtpResult = require('../models/otpResult');

class Devices {
constructor(client) {
this.client = client;
}

list() {
const self = this;
const url = `api/devices`;

return new Promise((resolve, reject) => {
self.client.request.get(url, (err, response, body) => {
if (err || response.statusCode !== 200) {
return reject(err || self.client.httpError(response));
}
resolve(new DeviceListResult(body));
});
});
}

create(options) {
const self = this;
const url = `api/devices`;

return new Promise((resolve, reject) => {
self.client.request.post(url, { body: options }, (err, response, body) => {
if (err || response.statusCode !== 200) {
return reject(err || self.client.httpError(response));
}
resolve(new Device(body));
});
});
}

otp(query) {
const self = this;

if (!query || query.indexOf('-') > -1) {
const url = `api/devices/${query}/otp`;

return new Promise((resolve, reject) => {
self.client.request.get(url, (err, response, body) => {
if (err || response.statusCode !== 200) {
return reject(err || self.client.httpError(response));
}
resolve(new OtpResult(body));
});
});
}

return new Promise((resolve, reject) => {
const url = `api/devices/otp`;
self.client.request.post(url, { body: { sharedSecret: query } }, (err, response, body) => {
if (err || response.statusCode !== 200) {
return reject(err || self.client.httpError(response));
}
resolve(new OtpResult(body));
});
});
}

del(deviceId) {
const self = this;
const url = `api/devices/${deviceId}`;

return new Promise((resolve, reject) => {
self.client.request.del(url, (err, response) => {
if (err || response.statusCode !== 204) {
return reject(err || self.client.httpError(response));
}
resolve();
});
});
}
}

module.exports = Devices;
38 changes: 38 additions & 0 deletions lib/operations/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,41 @@ export interface Usage {
transactions(
): Promise<models.UsageTransactionListResult>;
}

export interface Devices {
/**
* Returns a list of your virtual security devices.
*/
list(
): Promise<models.DeviceListResult>;

/**
* Creates a new virtual security device.
*/
create(
/**
* Options used to create a new Mailosaur virtual security device.
*/
options: models.DeviceCreateOptions
): Promise<models.Device>;

/**
* Retrieves the current one-time password for a saved device, or given base32-encoded shared secret.
*/
otp(
/**
* Either the unique identifier of the device, or a base32-encoded shared secret.
*/
query: string
): Promise<models.OtpResult>;

/**
* Permanently delete a virtual security device. This operation cannot be undone.
*/
del(
/**
* The unique identifier of the device.
*/
deviceId: string
): Promise<void>;
}
1 change: 1 addition & 0 deletions lib/operations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ exports.Files = require('./files');
exports.Messages = require('./messages');
exports.Servers = require('./servers');
exports.Usage = require('./usage');
exports.Devices = require('./devices');
75 changes: 75 additions & 0 deletions test/devices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const { assert } = require('chai');
const MailosaurClient = require('../lib/mailosaur');

const outputError = (done) => (err) => {
// eslint-disable-next-line no-console
console.log(err.errorType, err.httpStatusCode, err.httpResponseBody);
done(err);
};

describe('devices', () => {
let client;
const apiKey = process.env.MAILOSAUR_API_KEY;
const baseUrl = process.env.MAILOSAUR_BASE_URL || 'https://mailosaur.com/';

before(() => {
if (!apiKey) {
throw new Error('Missing necessary environment variables - refer to README.md');
}

client = new MailosaurClient(apiKey, baseUrl);
});

describe('CRUD', () => {
const deviceName = 'My test';
const sharedSecret = 'ONSWG4TFOQYTEMY=';
let createdDevice;

it('should create a new device', (done) => {
client.devices.create({
name: deviceName,
sharedSecret
}).then((device) => {
createdDevice = device;
assert.isNotEmpty(createdDevice.id);
assert.equal(createdDevice.name, deviceName);
done();
}).catch(outputError(done));
});

it('retrieve an otp via device ID', (done) => {
client.devices.otp(createdDevice.id)
.then((otpResult) => {
assert.isString(otpResult.code);
assert.equal(otpResult.code.length, 6);
done();
})
.catch(outputError(done));
});

it('should delete an existing device', (done) => {
client.devices.list().then((result) => {
assert.equal(result.items.length, 1);
}).then(() => (
client.devices.del(createdDevice.id)
)).then(() => (
client.devices.list()
))
.then((result) => {
assert.equal(result.items.length, 0);
done();
})
.catch(outputError(done));
});

it('retrieve an otp via shared secret', (done) => {
client.devices.otp(sharedSecret)
.then((otpResult) => {
assert.isString(otpResult.code);
assert.equal(otpResult.code.length, 6);
done();
})
.catch(outputError(done));
});
});
});

0 comments on commit e9fadb3

Please sign in to comment.