diff --git a/src/request.js b/src/request.js index c374fc3..a7786fe 100644 --- a/src/request.js +++ b/src/request.js @@ -47,17 +47,24 @@ class Request { _requestOptions(opts) { let parsedUrl = url.parse(opts.url); + const hasBody = opts.body !== undefined && opts.body !== null; - return { + const request = { host: parsedUrl.host, url: opts.url, method: opts.method ? opts.method.toUpperCase() : 'GET', path: parsedUrl.path, - headers: { - 'Content-Type': 'application/json' - }, - body: opts.body ? JSON.stringify(opts.body) : '' + service: this.config.service || 'VinylDNS', + region: this.config.region || 'us-east-1', + headers: {}, + body: hasBody ? JSON.stringify(opts.body) : undefined }; + + if (hasBody) { + request.headers['Content-Type'] = 'application/json'; + } + + return request; } _request(opts) { @@ -67,7 +74,9 @@ class Request { }); // axios expects a 'data'; aws4.sign expects a 'body' - signedReq.data = opts.body; + if (opts.body !== undefined) { + signedReq.data = opts.body; + } return new Promise((fulfill, reject) => { axios(signedReq) diff --git a/src/urls.js b/src/urls.js index 01699c1..63d91ac 100644 --- a/src/urls.js +++ b/src/urls.js @@ -39,6 +39,26 @@ class Urls { return `${this.zonesBase()}/name/${name}`; } + zoneDetails(id) { + return `${this.zone(id)}/details`; + } + + zoneBackendIds() { + return `${this.zonesBase()}/backendids`; + } + + zoneChangesFailure(query) { + return `${this.apiUrl}/metrics/health/zonechangesfailure${this.queryString(query)}`; + } + + zonesDeletedChanges(query) { + return `${this.zonesBase()}/deleted/changes${this.queryString(query)}`; + } + + zoneAclRules(id) { + return `${this.zone(id)}/acl/rules`; + } + syncZone(id) { return `${this.zone(id)}/sync`; } @@ -55,6 +75,18 @@ class Urls { return `${this.recordSetsBase(details.zoneId)}/${details.id || details.recordSetId}`; } + recordSetCount(zoneId) { + return `${this.zone(zoneId)}/recordsetcount`; + } + + recordSetChangeHistory(query) { + return `${this.apiUrl}/recordsetchange/history${this.queryString(query)}`; + } + + recordSetChangesFailure(zoneId, query) { + return `${this.apiUrl}/metrics/health/zones/${zoneId}/recordsetchangesfailure${this.queryString(query)}`; + } + recordSetChanges(zoneId, query) { return `${this.zone(zoneId)}/recordsetchanges${this.queryString(query)}`; } @@ -71,6 +103,18 @@ class Urls { return `${this.batchChanges()}/${id}`; } + batchChangeApprove(id) { + return `${this.batchChange(id)}/approve`; + } + + batchChangeReject(id) { + return `${this.batchChange(id)}/reject`; + } + + batchChangeCancel(id) { + return `${this.batchChange(id)}/cancel`; + } + groupsBase() { return `${this.apiUrl}/groups`; } @@ -87,6 +131,14 @@ class Urls { return `${this.group(id)}/activity${this.queryString(query)}`; } + groupChange(id) { + return `${this.groupsBase()}/change/${id}`; + } + + groupValidDomains() { + return `${this.groupsBase()}/valid/domains`; + } + getGroupAdmins(id) { return `${this.group(id)}/admins`; } @@ -95,6 +147,72 @@ class Urls { return `${this.group(id)}/members${this.queryString(query)}`; } + user(id) { + return `${this.apiUrl}/users/${id}`; + } + + userLock(id) { + return `${this.user(id)}/lock`; + } + + userUnlock(id) { + return `${this.user(id)}/unlock`; + } + + ping() { + return `${this.apiUrl}/ping`; + } + + health() { + return `${this.apiUrl}/health`; + } + + color() { + return `${this.apiUrl}/color`; + } + + metricsPrometheus(names) { + if (!names || names.length === 0) { + return `${this.apiUrl}/metrics/prometheus`; + } + + const query = names.map(name => `name=${name}`).join('&'); + return `${this.apiUrl}/metrics/prometheus?${query}`; + } + + status() { + return `${this.apiUrl}/status`; + } + + statusUpdate(processingDisabled) { + return `${this.apiUrl}/status?processingDisabled=${processingDisabled}`; + } + + recordSetsGlobal(query) { + if (!query) { + return `${this.apiUrl}/recordsets`; + } + + let params = []; + if (query.recordTypeFilter) { + const recordTypes = Array.isArray(query.recordTypeFilter) + ? query.recordTypeFilter + : [query.recordTypeFilter]; + recordTypes.forEach(recordType => { + params.push(`recordTypeFilter[]=${recordType}`); + }); + } + + Object.keys(query).forEach(key => { + if (key === 'recordTypeFilter') { + return; + } + params.push(`${key}=${query[key]}`); + }); + + return `${this.apiUrl}/recordsets${params.length ? '?' + params.join('&') : ''}`; + } + queryString(obj) { if (obj) { return '?' + Object.keys(obj).map(key => `${key}=${obj[key]}`).join('&'); diff --git a/src/vinyldns.js b/src/vinyldns.js index 90d41f0..97a4a66 100644 --- a/src/vinyldns.js +++ b/src/vinyldns.js @@ -58,6 +58,44 @@ class VinylDNS { return this.request.getOrDelete(this.urls.zoneByName(name), 'get'); } + /** + * Fetch zone details. + * @param {string} id - The zone ID. + */ + getZoneDetails(id) { + return this.request.getOrDelete(this.urls.zoneDetails(id), 'get'); + } + + /** + * Fetch zone backend IDs. + */ + getZoneBackendIds() { + return this.request.getOrDelete(this.urls.zoneBackendIds(), 'get'); + } + + /** + * Fetch failed zone changes. + * @param {object} queryOpts - The query parameters. + * @param {string} queryOpts.nameFilter - Filter by zone name. + * @param {string} queryOpts.startFrom - The start key of the page. + * @param {number} queryOpts.maxItems - The page limit. + */ + getZoneChangesFailure(queryOpts) { + return this.request.getOrDelete(this.urls.zoneChangesFailure(queryOpts), 'get'); + } + + /** + * Fetch deleted zone changes. + * @param {object} queryOpts - The query parameters. + * @param {string} queryOpts.nameFilter - Filter by zone name. + * @param {string} queryOpts.startFrom - The start key of the page. + * @param {number} queryOpts.maxItems - The page limit. + * @param {boolean} queryOpts.ignoreAccess - Ignore access restrictions. + */ + getZonesDeletedChanges(queryOpts) { + return this.request.getOrDelete(this.urls.zonesDeletedChanges(queryOpts), 'get'); + } + /** * Fetch zone changes. * @param {string} id - The zone ID. @@ -93,6 +131,24 @@ class VinylDNS { return this.request.createOrUpdate(zone, this.urls.zone(zone.id), 'put'); } + /** + * Add ACL rule to zone. + * @param {string} zoneId - The zone ID. + * @param {object} aclRule - The ACL rule to add. + */ + addZoneAclRule(zoneId, aclRule) { + return this.request.createOrUpdate(aclRule, this.urls.zoneAclRules(zoneId), 'put'); + } + + /** + * Delete ACL rule from zone. + * @param {string} zoneId - The zone ID. + * @param {object} aclRule - The ACL rule to delete. + */ + deleteZoneAclRule(zoneId, aclRule) { + return this.request.createOrUpdate(aclRule, this.urls.zoneAclRules(zoneId), 'delete'); + } + /** * Delete zone. * @param {string} id - The zone ID. @@ -140,6 +196,65 @@ class VinylDNS { return this.request.createOrUpdate(recordSet, this.urls.recordSet(recordSet), 'put'); } + /** + * Request record set ownership transfer. + * @param {object} recordSet - The record set to update. + * @param {string} requestedOwnerGroupId - The requested owner group ID. + */ + requestRecordSetOwnership(recordSet, requestedOwnerGroupId) { + return this._recordSetOwnershipTransfer(recordSet, 'Requested', requestedOwnerGroupId, false); + } + + /** + * Approve record set ownership transfer. + * @param {object} recordSet - The record set to update. + * @param {string} requestedOwnerGroupId - The requested owner group ID. + */ + approveRecordSetOwnership(recordSet, requestedOwnerGroupId) { + return this._recordSetOwnershipTransfer(recordSet, 'ManuallyApproved', requestedOwnerGroupId, true); + } + + /** + * Reject record set ownership transfer. + * @param {object} recordSet - The record set to update. + * @param {string} requestedOwnerGroupId - The requested owner group ID. + */ + rejectRecordSetOwnership(recordSet, requestedOwnerGroupId) { + return this._recordSetOwnershipTransfer(recordSet, 'ManuallyRejected', requestedOwnerGroupId, false); + } + + /** + * Cancel record set ownership transfer. + * @param {object} recordSet - The record set to update. + * @param {string} requestedOwnerGroupId - The requested owner group ID. + */ + cancelRecordSetOwnership(recordSet, requestedOwnerGroupId) { + return this._recordSetOwnershipTransfer(recordSet, 'Cancelled', requestedOwnerGroupId, false); + } + + _recordSetOwnershipTransfer(recordSet, status, requestedOwnerGroupId, updateOwnerGroup) { + const updated = Object.assign({}, recordSet, { + recordSetGroupChange: { + ownershipTransferStatus: status, + requestedOwnerGroupId: requestedOwnerGroupId + } + }); + + if (updateOwnerGroup) { + updated.ownerGroupId = requestedOwnerGroupId; + } + + return this.updateRecordSet(updated); + } + + /** + * Fetch record set count for a zone. + * @param {string} zoneId - The zone ID. + */ + getRecordSetCount(zoneId) { + return this.request.getOrDelete(this.urls.recordSetCount(zoneId), 'get'); + } + /** * Delete record set. * @param {object} details - The record set details. @@ -161,6 +276,19 @@ class VinylDNS { return this.request.getOrDelete(this.urls.recordSetChange(details), 'get'); } + /** + * Fetch record set change history. + * @param {object} queryOpts - The query parameters. + * @param {string} queryOpts.zoneId - The zone ID. + * @param {string} queryOpts.fqdn - The record set FQDN. + * @param {string} queryOpts.recordType - The record set type. + * @param {string} queryOpts.startFrom - The start key of the page. + * @param {number} queryOpts.maxItems - The page limit. + */ + getRecordSetChangeHistory(queryOpts) { + return this.request.getOrDelete(this.urls.recordSetChangeHistory(queryOpts), 'get'); + } + /** * Get record set change. * @param {string} zoneId - The zone ID. @@ -172,6 +300,25 @@ class VinylDNS { return this.request.getOrDelete(this.urls.recordSetChanges(zoneId, queryOpts), 'get'); } + /** + * Fetch failed record set changes for a zone. + * @param {string} zoneId - The zone ID. + * @param {object} queryOpts - The query parameters. + * @param {string} queryOpts.startFrom - The start key of the page. + * @param {number} queryOpts.maxItems - The page limit. + */ + getRecordSetChangesFailure(zoneId, queryOpts) { + return this.request.getOrDelete(this.urls.recordSetChangesFailure(zoneId, queryOpts), 'get'); + } + + /** + * Fetch record sets globally across zones. + * @param {object} queryOpts - The query parameters. + */ + getRecordSetsGlobal(queryOpts) { + return this.request.getOrDelete(this.urls.recordSetsGlobal(queryOpts), 'get'); + } + /** * Get batch changes. * @param {number} queryOpts.startFrom - In order to advance through pages of results, the startFrom is set to the nextId that is returned on the previous response. @@ -197,6 +344,33 @@ class VinylDNS { return this.request.createOrUpdate(batchChange, this.urls.batchChanges(), 'post'); } + /** + * Approve a batch change. + * @param {string} id - The batch change ID. + * @param {object} review - Optional review payload. + */ + approveBatchChange(id, review) { + return this.request.createOrUpdate(review || {}, this.urls.batchChangeApprove(id), 'post'); + } + + /** + * Reject a batch change. + * @param {string} id - The batch change ID. + * @param {object} review - Optional review payload. + */ + rejectBatchChange(id, review) { + return this.request.createOrUpdate(review || {}, this.urls.batchChangeReject(id), 'post'); + } + + /** + * Cancel a batch change. + * @param {string} id - The batch change ID. + * @param {object} review - Optional review payload. + */ + cancelBatchChange(id, review) { + return this.request.createOrUpdate(review || {}, this.urls.batchChangeCancel(id), 'post'); + } + /** * Get groups. * @param {number} queryOpts.startFrom - In order to advance through pages of results, the startFrom is set to the nextId that is returned on the previous response. @@ -225,6 +399,21 @@ class VinylDNS { return this.request.getOrDelete(this.urls.getGroupActivity(id, queryOpts), 'get'); } + /** + * Get group change by ID. + * @param {string} id - The group change ID. + */ + getGroupChange(id) { + return this.request.getOrDelete(this.urls.groupChange(id), 'get'); + } + + /** + * Get group valid domains. + */ + getGroupValidDomains() { + return this.request.getOrDelete(this.urls.groupValidDomains(), 'get'); + } + /** * Get group admins. * @param {string} id - The group ID. @@ -243,6 +432,30 @@ class VinylDNS { return this.request.getOrDelete(this.urls.getGroupMembers(id, queryOpts), 'get'); } + /** + * Get user by ID. + * @param {string} id - The user ID. + */ + getUser(id) { + return this.request.getOrDelete(this.urls.user(id), 'get'); + } + + /** + * Lock user. + * @param {string} id - The user ID. + */ + lockUser(id) { + return this.request.createOrUpdate({}, this.urls.userLock(id), 'put'); + } + + /** + * Unlock user. + * @param {string} id - The user ID. + */ + unlockUser(id) { + return this.request.createOrUpdate({}, this.urls.userUnlock(id), 'put'); + } + /** * Create group. * @param {object} group - The VinylDNS group to create. See the [VinylDNS group docs]{@link https://www.vinyldns.io/api/group-model.html} to learn more. @@ -266,6 +479,50 @@ class VinylDNS { deleteGroup(id) { return this.request.getOrDelete(this.urls.group(id), 'delete'); } + + /** + * Ping VinylDNS. + */ + ping() { + return this.request.getOrDelete(this.urls.ping(), 'get'); + } + + /** + * Health check. + */ + health() { + return this.request.getOrDelete(this.urls.health(), 'get'); + } + + /** + * Color status. + */ + color() { + return this.request.getOrDelete(this.urls.color(), 'get'); + } + + /** + * Prometheus metrics. + * @param {array} names - Optional metric names. + */ + metricsPrometheus(names) { + return this.request.getOrDelete(this.urls.metricsPrometheus(names), 'get'); + } + + /** + * Get system status. + */ + getStatus() { + return this.request.getOrDelete(this.urls.status(), 'get'); + } + + /** + * Update system status. + * @param {boolean} processingDisabled - Processing disabled flag. + */ + updateStatus(processingDisabled) { + return this.request.createOrUpdate({}, this.urls.statusUpdate(processingDisabled), 'post'); + } } module.exports = VinylDNS; diff --git a/test/fixtures/responses.js b/test/fixtures/responses.js index bc43605..6709a2c 100644 --- a/test/fixtures/responses.js +++ b/test/fixtures/responses.js @@ -652,4 +652,267 @@ module.exports = { } ] } + , + ping: 'PONG', + health: 'OK', + color: 'blue', + metricsPrometheus: 'some_metric 1', + status: { + processingDisabled: false, + color: 'blue', + keyName: 'vinyldns.', + version: '0.21.3' + }, + zoneDetails: { + zone: { + name: 'system-test.', + email: 'test@example.com', + status: 'Active', + adminGroupId: '6baa85ad-267f-44ff-b535-818b7d7a2467', + adminGroupName: 'admins' + } + }, + zoneBackendIds: { + backendIds: ['b1', 'b2'] + }, + zoneChangesFailure: { + failedZoneChanges: [ + { + status: 'Failed', + zone: { + status: 'Active', + updated: '2016-12-30T15:37:57Z', + name: 'system-test-history.', + adminGroupId: '67b4da23-6832-4600-8450-9fa0664caeeb', + created: '2016-12-30T15:37:56Z', + account: '67b4da23-6832-4600-8450-9fa0664caeeb', + email: 'i.changed.this.10.times@history-test.com', + shared: true, + acl: { + rules: [] + }, + id: '9f353bc7-cb8d-491c-b074-34afafc97c5f' + }, + created: '2016-12-30T15:37:57Z', + changeType: 'Update', + userId: 'history-id', + id: '6d4deccb-4632-475e-9ebc-3f6bace5fe68' + } + ] + }, + zonesDeletedChanges: { + zonesDeletedInfo: [ + { + zoneChange: { + status: 'Pending', + zone: { + status: 'Deleted', + name: 'deleted-zone.', + adminGroupId: 'admin-group', + created: '2016-12-28T18:45:53Z', + account: 'test_group', + email: 'test@test.com', + shared: false, + acl: { + rules: [] + }, + id: 'deleted-zone-id' + }, + created: '2016-12-28T18:45:53Z', + changeType: 'Delete', + userId: 'vinyl', + id: 'deleted-change-id' + }, + adminGroupName: 'admins', + userName: 'user', + accessLevel: 'Read' + } + ] + }, + recordSetCount: { + count: 5 + }, + recordSetChangeHistory: { + recordSetChanges: [ + { + status: 'Complete', + zone: { + status: 'Active', + updated: '2016-12-30T15:37:57Z', + name: 'system-test-history.', + adminGroupId: '67b4da23-6832-4600-8450-9fa0664caeeb', + created: '2016-12-30T15:37:56Z', + account: '67b4da23-6832-4600-8450-9fa0664caeeb', + email: 'i.changed.this.10.times@history-test.com', + shared: true, + acl: { + rules: [] + }, + id: '9f353bc7-cb8d-491c-b074-34afafc97c5f' + }, + created: '2016-12-30T15:37:58Z', + recordSet: { + status: 'Active', + updated: '2016-12-30T15:37:58Z', + name: 'test-create-cname-ok', + created: '2016-12-30T15:37:57Z', + account: 'history-id', + zoneId: '9f353bc7-cb8d-491c-b074-34afafc97c5f', + records: [{ + cname: 'changed-cname.' + }], + ttl: 200, + type: 'CNAME', + id: 'f62235df-5372-443c-9ba4-bdd3fca452f4' + }, + changeType: 'Delete', + userId: 'history-id', + updates: { + status: 'Active', + updated: '2016-12-30T15:37:58Z', + name: 'test-create-cname-ok', + created: '2016-12-30T15:37:57Z', + account: 'history-id', + zoneId: '9f353bc7-cb8d-491c-b074-34afafc97c5f', + records: [{ + cname: 'changed-cname.' + }], + ttl: 200, + type: 'CNAME', + id: 'f62235df-5372-443c-9ba4-bdd3fca452f4' + }, + id: '68fd6dbe-0da8-4280-bcf3-37f54528dc41' + } + ] + }, + recordSetChangesFailure: { + failedRecordSetChanges: [ + { + status: 'Failed', + zone: { + status: 'Active', + updated: '2016-12-30T15:37:57Z', + name: 'system-test-history.', + adminGroupId: '67b4da23-6832-4600-8450-9fa0664caeeb', + created: '2016-12-30T15:37:56Z', + account: '67b4da23-6832-4600-8450-9fa0664caeeb', + email: 'i.changed.this.10.times@history-test.com', + shared: true, + acl: { + rules: [] + }, + id: '9f353bc7-cb8d-491c-b074-34afafc97c5f' + }, + created: '2016-12-30T15:37:58Z', + recordSet: { + status: 'Active', + updated: '2016-12-30T15:37:58Z', + name: 'test-create-cname-ok', + created: '2016-12-30T15:37:57Z', + account: 'history-id', + zoneId: '9f353bc7-cb8d-491c-b074-34afafc97c5f', + records: [{ + cname: 'changed-cname.' + }], + ttl: 200, + type: 'CNAME', + id: 'f62235df-5372-443c-9ba4-bdd3fca452f4' + }, + changeType: 'Delete', + userId: 'history-id', + id: '68fd6dbe-0da8-4280-bcf3-37f54528dc41' + } + ] + }, + recordSetsGlobal: { + recordSets: [{ + type: 'A', + zoneId: '2467dc05-68eb-4498-a9d5-78d24bb0893c', + name: 'global-record-set', + ttl: 38400, + status: 'Active', + created: '2017-02-23T15:12:41Z', + updated: '2017-02-23T15:12:41Z', + records: [{ + address: '6.6.6.1' + }], + id: 'global-record-set-id', + account: '9b22b686-54bc-47fb-a8f8-cdc48e6d04ae', + accessLevel: 'Delete' + }] + }, + batchChangeApprove: { + id: '02bd95f4-a32c-443b-82eb-54dbaa55b31a', + status: 'Pending', + approvalStatus: 'Approved' + }, + batchChangeReject: { + id: '02bd95f4-a32c-443b-82eb-54dbaa55b31a', + status: 'Pending', + approvalStatus: 'Rejected' + }, + batchChangeCancel: { + id: '02bd95f4-a32c-443b-82eb-54dbaa55b31a', + status: 'Pending', + approvalStatus: 'Cancelled' + }, + groupChange: { + id: 'change-id', + userId: 'user', + userName: 'user', + changeType: 'Update', + created: '2017-03-02T18:49:58Z', + groupChangeMessage: 'updated', + newGroup: { + status: 'Active', + name: 'group-change', + created: '2017-03-02T18:49:58Z', + id: 'group-change-id', + admins: [{ + id: 'ok' + }], + members: [{ + id: 'dummy199' + }], + email: 'test@test.com' + }, + oldGroup: { + status: 'Active', + name: 'group-change', + created: '2017-03-02T18:49:58Z', + id: 'group-change-id', + admins: [{ + id: 'ok' + }], + members: [{ + id: 'dummy198' + }], + email: 'test@test.com' + } + }, + groupValidDomains: ['example.com', 'test.com'], + user: { + id: 'user-id', + userName: 'user', + groupId: [{ + id: 'group-1' + }], + lockStatus: 'Unlocked' + }, + userLock: { + id: 'user-id', + userName: 'user', + groupId: [{ + id: 'group-1' + }], + lockStatus: 'Locked' + }, + userUnlock: { + id: 'user-id', + userName: 'user', + groupId: [{ + id: 'group-1' + }], + lockStatus: 'Unlocked' + } }; diff --git a/test/vinyldns_test.js b/test/vinyldns_test.js index bb48135..5914b4c 100644 --- a/test/vinyldns_test.js +++ b/test/vinyldns_test.js @@ -63,6 +63,74 @@ describe('VinylDNS', () => { assert.equal(vinyl.config.apiUrl, 'http://my-vinyldns.com'); }); + describe('health and status methods', () => { + it('fetches ping', (done) => { + mockGet('/ping', fixtures.ping); + + vinyl.ping() + .then(result => { + assert.equal(result, 'PONG'); + + done(); + }); + }); + + it('fetches health', (done) => { + mockGet('/health', fixtures.health); + + vinyl.health() + .then(result => { + assert.equal(result, 'OK'); + + done(); + }); + }); + + it('fetches color', (done) => { + mockGet('/color', fixtures.color); + + vinyl.color() + .then(result => { + assert.equal(result, 'blue'); + + done(); + }); + }); + + it('fetches prometheus metrics', (done) => { + mockGet('/metrics/prometheus?name=foo&name=bar', fixtures.metricsPrometheus); + + vinyl.metricsPrometheus(['foo', 'bar']) + .then(result => { + assert.equal(result, 'some_metric 1'); + + done(); + }); + }); + + it('fetches status', (done) => { + mockGet('/status', fixtures.status); + + vinyl.getStatus() + .then(result => { + assert.equal(result.processingDisabled, false); + + done(); + }); + }); + + it('updates status', (done) => { + mockPost('/status?processingDisabled=true', {}, fixtures.status); + + vinyl.updateStatus(true) + .then(result => { + assert.equal(result.processingDisabled, false); + + done(); + }); + }); + }); + describe('zones methods', () => { describe('getZones', () => { it('fetches zones', (done) => { @@ -160,6 +228,65 @@ describe('VinylDNS', () => { }); }); + describe('getZoneDetails', () => { + it('fetches zone details', (done) => { + mockGet('/zones/123/details', fixtures.zoneDetails); + + vinyl.getZoneDetails('123') + .then(result => { + assert.equal(result.zone.adminGroupName, 'admins'); + + done(); + }); + }); + }); + + describe('getZoneBackendIds', () => { + it('fetches zone backend ids', (done) => { + mockGet('/zones/backendids', fixtures.zoneBackendIds); + + vinyl.getZoneBackendIds() + .then(result => { + assert.equal(result.backendIds.length, 2); + + done(); + }); + }); + }); + + describe('getZoneChangesFailure', () => { + it('fetches failed zone changes', (done) => { + mockGet('/metrics/health/zonechangesfailure?startFrom=1&maxItems=100', fixtures.zoneChangesFailure); + + vinyl.getZoneChangesFailure({ + startFrom: 1, + maxItems: 100 + }) + .then(result => { + assert.equal(result.failedZoneChanges[0].status, 'Failed'); + + done(); + }); + }); + }); + + describe('getZonesDeletedChanges', () => { + it('fetches deleted zone changes', (done) => { + mockGet('/zones/deleted/changes?startFrom=1&maxItems=100&ignoreAccess=true', fixtures.zonesDeletedChanges); + + vinyl.getZonesDeletedChanges({ + startFrom: 1, + maxItems: 100, + ignoreAccess: true + }) + .then(result => { + assert.equal(result.zonesDeletedInfo[0].accessLevel, 'Read'); + + done(); + }); + }); + }); + describe('syncZone', () => { it('syncs the zone with the ID it is passed', () => { mockPost('/zones/123/sync', '', fixtures.syncZone); @@ -294,6 +421,44 @@ describe('VinylDNS', () => { }); }); + describe('addZoneAclRule', () => { + it('adds a zone acl rule', (done) => { + let rule = { + accessLevel: 'Read', + recordTypes: ['A'], + groupId: 'group-id' + }; + + mockPut('/zones/123/acl/rules', rule, fixtures.updateZone); + + vinyl.addZoneAclRule('123', rule) + .then(result => { + assert.equal(result.zone.name, 'dummy.'); + + done(); + }); + }); + }); + + describe('deleteZoneAclRule', () => { + it('deletes a zone acl rule', (done) => { + let rule = { + accessLevel: 'Read', + recordTypes: ['A'], + groupId: 'group-id' + }; + + mockDelete('/zones/123/acl/rules', fixtures.updateZone); + + vinyl.deleteZoneAclRule('123', rule) + .then(result => { + assert.equal(result.zone.name, 'dummy.'); + + done(); + }); + }); + }); + describe('deleteZone', () => { it('deletes the zone with the ID it is passed', (done) => { mockDelete('/zones/123', fixtures.deleteZone); @@ -494,6 +659,19 @@ describe('VinylDNS', () => { }); }); + describe('getRecordSetCount', () => { + it('fetches record set count', (done) => { + mockGet('/zones/123/recordsetcount', fixtures.recordSetCount); + + vinyl.getRecordSetCount('123') + .then(result => { + assert.equal(result.count, 5); + + done(); + }); + }); + }); + describe('deleteRecordSet', () => { it('deletes the record with the details it is passed', (done) => { mockDelete('/zones/123/recordsets/456', fixtures.deleteRecordSet); @@ -568,6 +746,75 @@ describe('VinylDNS', () => { }); }); + describe('getRecordSetChangeHistory', () => { + it('fetches record set change history', (done) => { + mockGet('/recordsetchange/history?zoneId=123&fqdn=rs.ok.&recordType=A&startFrom=1&maxItems=100', + fixtures.recordSetChangeHistory); + + vinyl.getRecordSetChangeHistory({ + zoneId: '123', + fqdn: 'rs.ok.', + recordType: 'A', + startFrom: 1, + maxItems: 100 + }) + .then(result => { + assert.equal(result.recordSetChanges[0].status, 'Complete'); + + done(); + }); + }); + }); + + describe('getRecordSetChangesFailure', () => { + it('fetches failed record set changes', (done) => { + mockGet('/metrics/health/zones/123/recordsetchangesfailure?startFrom=1&maxItems=100', + fixtures.recordSetChangesFailure); + + vinyl.getRecordSetChangesFailure('123', { + startFrom: 1, + maxItems: 100 + }) + .then(result => { + assert.equal(result.failedRecordSetChanges[0].status, 'Failed'); + + done(); + }); + }); + }); + + describe('getRecordSetsGlobal', () => { + it('fetches record sets globally', (done) => { + mockGet('/recordsets?startFrom=1&maxItems=100', fixtures.recordSetsGlobal); + + vinyl.getRecordSetsGlobal({ + startFrom: 1, + maxItems: 100 + }) + .then(result => { + assert.equal(result.recordSets[0].name, 'global-record-set'); + + done(); + }); + }); + + it('fetches record sets globally with record type filter', (done) => { + mockGet('/recordsets?recordTypeFilter[]=A&recordTypeFilter[]=CNAME&startFrom=1&maxItems=100', + fixtures.recordSetsGlobal); + + vinyl.getRecordSetsGlobal({ + recordTypeFilter: ['A', 'CNAME'], + startFrom: 1, + maxItems: 100 + }) + .then(result => { + assert.equal(result.recordSets[0].name, 'global-record-set'); + + done(); + }); + }); + }); + describe('getRecordSetChange', () => { it('fetches the record set change with the change ID, record set ID, and zone ID details it is passed', (done) => { mockGet('/zones/123/recordsets/456/changes/789', fixtures.getRecordSetChange); @@ -602,6 +849,125 @@ describe('VinylDNS', () => { }); }); }); + + describe('ownership transfer', () => { + it('requests ownership transfer', (done) => { + let recordSet = { + name: 'foo', + type: 'A', + ttl: 300, + records: [{ + address: '10.10.10.10' + }], + zoneId: '123', + id: '456' + }; + + let payload = Object.assign({}, recordSet, { + recordSetGroupChange: { + ownershipTransferStatus: 'Requested', + requestedOwnerGroupId: 'group-id' + } + }); + + mockPut('/zones/123/recordsets/456', payload, fixtures.updateRecordSet); + + vinyl.requestRecordSetOwnership(recordSet, 'group-id') + .then(result => { + assert.equal(result.recordSet.name, 'foo'); + + done(); + }); + }); + + it('approves ownership transfer', (done) => { + let recordSet = { + name: 'foo', + type: 'A', + ttl: 300, + records: [{ + address: '10.10.10.10' + }], + zoneId: '123', + id: '456' + }; + + let payload = Object.assign({}, recordSet, { + ownerGroupId: 'group-id', + recordSetGroupChange: { + ownershipTransferStatus: 'ManuallyApproved', + requestedOwnerGroupId: 'group-id' + } + }); + + mockPut('/zones/123/recordsets/456', payload, fixtures.updateRecordSet); + + vinyl.approveRecordSetOwnership(recordSet, 'group-id') + .then(result => { + assert.equal(result.recordSet.name, 'foo'); + + done(); + }); + }); + + it('rejects ownership transfer', (done) => { + let recordSet = { + name: 'foo', + type: 'A', + ttl: 300, + records: [{ + address: '10.10.10.10' + }], + zoneId: '123', + id: '456' + }; + + let payload = Object.assign({}, recordSet, { + recordSetGroupChange: { + ownershipTransferStatus: 'ManuallyRejected', + requestedOwnerGroupId: 'group-id' + } + }); + + mockPut('/zones/123/recordsets/456', payload, fixtures.updateRecordSet); + + vinyl.rejectRecordSetOwnership(recordSet, 'group-id') + .then(result => { + assert.equal(result.recordSet.name, 'foo'); + + done(); + }); + }); + + it('cancels ownership transfer', (done) => { + let recordSet = { + name: 'foo', + type: 'A', + ttl: 300, + records: [{ + address: '10.10.10.10' + }], + zoneId: '123', + id: '456' + }; + + let payload = Object.assign({}, recordSet, { + recordSetGroupChange: { + ownershipTransferStatus: 'Cancelled', + requestedOwnerGroupId: 'group-id' + } + }); + + mockPut('/zones/123/recordsets/456', payload, fixtures.updateRecordSet); + + vinyl.cancelRecordSetOwnership(recordSet, 'group-id') + .then(result => { + assert.equal(result.recordSet.name, 'foo'); + + done(); + }); + }); + }); }); describe('batch changes methods', () => { @@ -741,6 +1107,45 @@ describe('VinylDNS', () => { }); }); }); + + describe('approveBatchChange', () => { + it('approves the batch change', (done) => { + mockPost('/zones/batchrecordchanges/123/approve', {}, fixtures.batchChangeApprove); + + vinyl.approveBatchChange('123') + .then(result => { + assert.equal(result.approvalStatus, 'Approved'); + + done(); + }); + }); + }); + + describe('rejectBatchChange', () => { + it('rejects the batch change', (done) => { + mockPost('/zones/batchrecordchanges/123/reject', {}, fixtures.batchChangeReject); + + vinyl.rejectBatchChange('123') + .then(result => { + assert.equal(result.approvalStatus, 'Rejected'); + + done(); + }); + }); + }); + + describe('cancelBatchChange', () => { + it('cancels the batch change', (done) => { + mockPost('/zones/batchrecordchanges/123/cancel', {}, fixtures.batchChangeCancel); + + vinyl.cancelBatchChange('123') + .then(result => { + assert.equal(result.approvalStatus, 'Cancelled'); + + done(); + }); + }); + }); }); describe('groups methods', () => { @@ -854,6 +1259,32 @@ describe('VinylDNS', () => { }); }); + describe('getGroupChange', () => { + it('fetches group change', (done) => { + mockGet('/groups/change/123', fixtures.groupChange); + + vinyl.getGroupChange('123') + .then(result => { + assert.equal(result.groupChangeMessage, 'updated'); + + done(); + }); + }); + }); + + describe('getGroupValidDomains', () => { + it('fetches group valid domains', (done) => { + mockGet('/groups/valid/domains', fixtures.groupValidDomains); + + vinyl.getGroupValidDomains() + .then(result => { + assert.equal(result.length, 2); + + done(); + }); + }); + }); + describe('getGroupAdmins', () => { it('fetches the admins of the group ID it is passed', (done) => { mockGet('/groups/123/admins', fixtures.getGroupAdmins); @@ -1053,4 +1484,45 @@ describe('VinylDNS', () => { }); }); }); + + describe('users methods', () => { + describe('getUser', () => { + it('fetches the user', (done) => { + mockGet('/users/123', fixtures.user); + + vinyl.getUser('123') + .then(result => { + assert.equal(result.userName, 'user'); + + done(); + }); + }); + }); + + describe('lockUser', () => { + it('locks the user', (done) => { + mockPut('/users/123/lock', {}, fixtures.userLock); + + vinyl.lockUser('123') + .then(result => { + assert.equal(result.lockStatus, 'Locked'); + + done(); + }); + }); + }); + + describe('unlockUser', () => { + it('unlocks the user', (done) => { + mockPut('/users/123/unlock', {}, fixtures.userUnlock); + + vinyl.unlockUser('123') + .then(result => { + assert.equal(result.lockStatus, 'Unlocked'); + + done(); + }); + }); + }); + }); });