diff --git a/index.js b/index.js index 60ebdca7..0c2036a2 100644 --- a/index.js +++ b/index.js @@ -3,6 +3,21 @@ const AWS = require('aws-sdk'); const chalk = require('chalk'); +/** + * Private function used to sort hosted zones + * @param hostedZoneFromAWS the result of listHostedZones api call + * @returns same structure as parameter with the property "HostedZone" sorted in reverse order + */ +function sortHostedZone(hostedZoneFromAWS) { + const sortedHostedZones = hostedZoneFromAWS.HostedZones.sort((hostedZone1, hostedZone2) => { + if (hostedZone1.Name < hostedZone2.Name) { return -1; } + if (hostedZone1.Name > hostedZone2.Name) { return 1; } + return 0; + }); + + return Object.assign({}, hostedZoneFromAWS, { HostedZone: sortedHostedZones.reverse() }); +} + class ServerlessCustomDomain { constructor(serverless) { @@ -253,23 +268,25 @@ class ServerlessCustomDomain { getHostedZoneId() { const hostedZonePromise = this.route53.listHostedZones({}).promise(); - return hostedZonePromise.then((data) => { - // Gets the hostzone that contains the root of the custom domain name - let hostedZoneId = data.HostedZones.find((hostedZone) => { - let hZoneName = hostedZone.Name; - // Takes out the . at the end if there is one - hZoneName = hZoneName.endsWith('.') ? hZoneName.slice(0, -1) : hZoneName; - return (this.targetHostedZone === hZoneName); - }); - if (hostedZoneId) { - hostedZoneId = hostedZoneId.Id; - // Extracts the hostzone Id - const startPos = hostedZoneId.indexOf('e/') + 2; - const endPos = hostedZoneId.length; - return hostedZoneId.substring(startPos, endPos); - } - throw new Error('Could not find hosted zone.'); - }) + return hostedZonePromise.then(sortHostedZone) + .then((data) => { + // Gets the hostzone that contains the root of the custom domain name + let hostedZoneId = data.HostedZones.find((hostedZone) => { + let hZoneName = hostedZone.Name; + // Takes out the . at the end if there is one + hZoneName = hZoneName.endsWith('.') ? hZoneName.slice(0, -1) : hZoneName; + return (this.targetHostedZone || '').endsWith(hZoneName); + }); + + if (hostedZoneId) { + hostedZoneId = hostedZoneId.Id; + // Extracts the hostzone Id + const startPos = hostedZoneId.indexOf('e/') + 2; + const endPos = hostedZoneId.length; + return hostedZoneId.substring(startPos, endPos); + } + throw new Error('Could not find hosted zone.'); + }) .catch((err) => { throw new Error(`${err} Unable to retrieve Route53 hosted zone id.`); }); diff --git a/package.json b/package.json index 2e07203f..31e6b6a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-domain-manager", - "version": "1.1.11", + "version": "1.1.12", "engines": { "node": ">=4.0" }, diff --git a/test/index.test.js b/test/index.test.js index 933541b2..ed29bf90 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -361,6 +361,77 @@ describe('Custom Domain Plugin', () => { expect(result).to.equal('test_id_2'); }); + it('Sub domain name - only root hosted zones', async () => { + AWS.mock('Route53', 'listHostedZones', (params, callback) => { + callback(null, { HostedZones: [ + { Name: 'aaa.com.', Id: '/hostedzone/test_id_0' }, + { Name: 'bbb.fr.', Id: '/hostedzone/test_id_1' }, + { Name: 'ccc.com.', Id: '/hostedzone/test_id_3' }], + }); + }); + + const plugin = constructPlugin(null, null, null); + plugin.route53 = new aws.Route53(); + plugin.setGivenDomainName('bar.foo.bbb.fr'); + + const result = await plugin.getHostedZoneId(); + expect(result).to.equal('test_id_1'); + }); + + + it('Sub domain name - natural order', async () => { + AWS.mock('Route53', 'listHostedZones', (params, callback) => { + callback(null, { HostedZones: [ + { Name: 'aaa.com.', Id: '/hostedzone/test_id_0' }, + { Name: 'bbb.fr.', Id: '/hostedzone/test_id_1' }, + { Name: 'foo.bbb.fr.', Id: '/hostedzone/test_id_3' }, + { Name: 'ccc.com.', Id: '/hostedzone/test_id_4' }], + }); + }); + + const plugin = constructPlugin(null, null, null); + plugin.route53 = new aws.Route53(); + plugin.setGivenDomainName('bar.foo.bbb.fr'); + + const result = await plugin.getHostedZoneId(); + expect(result).to.equal('test_id_3'); + }); + + it('Sub domain name - reverse order', async () => { + AWS.mock('Route53', 'listHostedZones', (params, callback) => { + callback(null, { HostedZones: [ + { Name: 'foo.bbb.fr.', Id: '/hostedzone/test_id_3' }, + { Name: 'bbb.fr.', Id: '/hostedzone/test_id_1' }, + { Name: 'ccc.com.', Id: '/hostedzone/test_id_4' }, + { Name: 'aaa.com.', Id: '/hostedzone/test_id_0' }], + }); + }); + + const plugin = constructPlugin(null, null, null); + plugin.route53 = new aws.Route53(); + plugin.setGivenDomainName('bar.foo.bbb.fr'); + + const result = await plugin.getHostedZoneId(); + expect(result).to.equal('test_id_3'); + }); + + it('Sub domain name - random order', async () => { + AWS.mock('Route53', 'listHostedZones', (params, callback) => { + callback(null, { HostedZones: [ + { Name: 'bbb.fr.', Id: '/hostedzone/test_id_1' }, + { Name: 'aaa.com.', Id: '/hostedzone/test_id_0' }, + { Name: 'foo.bbb.fr.', Id: '/hostedzone/test_id_3' }], + }); + }); + + const plugin = constructPlugin(null, null, null); + plugin.route53 = new aws.Route53(); + plugin.setGivenDomainName('bar.foo.bbb.fr'); + + const result = await plugin.getHostedZoneId(); + expect(result).to.equal('test_id_3'); + }); + afterEach(() => { AWS.restore(); });