Repository: amplify-education/serverless-domain-manager (ts)

Issue:
Incorrect hosted zone chosen when attempting to create a custom domain name
### Error Description
We appear to have hit a bug in how serverless automatically selects the appropriate hosted zone for some requested domain names. Specifically, when two domains exist, say serverlessdevapi.mydomain.com as well as api.mydomain.com, and our configuration calls for creating of a custom name of serverlessdevapi.mydomain.com, serverless selects the api.mydomain.com incorrectly when attempting to create the A record. 
We were able to temporarily workaround the issue in this instance by explicitly specifying hostedZoneId as the ID of mydomain.com to force that zone to be used.

Note: This peripherally looks the same as https://github.com/amplify-education/serverless-domain-manager/issues/182, which was resolved a couple of years back.

### Command Run
`OUR_ENV=dev DOMAIN_NAME=serverlessdevapi.mydomain.com serverless create_domain --stage dev`

### Console Output
```
11:34:24  > serverless create_domain --stage dev
11:34:24  

Environment: darwin, node 16.19.1, framework 3.37.0 (local) 3.36.0v (global), plugin 7.2.0, SDK 4.5.1
Docs:        docs.serverless.com
Support:     forum.serverless.com
Bugs:        github.com/serverless/serverless/issues
11:34:32  
11:34:32  Error:
11:34:32  Error: Unable to create domain 'serverlessdevapi.mydomain.com':
11:34:32  Failed to UPSERT A Alias for 'serverlessdevapi.mydomain.com':
11:34:32  [RRSet with DNS name serverlessdevapi.mydomain.com. is not permitted in zone api.mydomain.com., RRSet with DNS name serverlessdevapi.mydomain.com. is not permitted in zone api.mydomain.com.]
11:34:32      at ServerlessCustomDomain.<anonymous> (/tmp/workspace/sample-lambda/lambda/node_modules/serverless-domain-manager/dist/src/index.js:257:23)
11:34:32      at Generator.throw (<anonymous>)
11:34:32      at rejected (/tmp/workspace/sample-lambda/lambda/node_modules/serverless-domain-manager/dist/src/index.js:6:65)
11:34:32      at processTicksAndRejections (internal/process/task_queues.js:96:5)
```


### Domain Manager Configuration
```
custom:
  customDomain:
    basePath: ""
    enabled: true
    createRoute53Record: true
    domainName: serverlessdevapi.mydomain.com
    stage: dev
    certificateName: '*.mydomain.com'
```
### Versions
```
Domain Manager version(s): 7.1.2
Node/npm version: Node 16
Serverless Version: 3.37.0
Lambda Code JavaScript
```
https://github.com/amplify-education/serverless-domain-manager/blame/1dcb0828039563d5f195f336bf7a3e929110591f/src/aws/route53-wrapper.ts#L67

Pull request description:
AT-10914: Fix hosted zone filtering
https://amplify-education.atlassian.net/browse/AT-10914

<!--- Information about referencing Github Issues: https://help.github.com/articles/basic-writing-and-formatting-syntax/#referencing-issues-and-pull-requests --->
Fixes #600 

**Description of Issue Fixed**
Fixed hosted zone filtering

**Changes proposed in this pull request**:

* Fixed hosted zone filtering
* Updated tests

<!--- Please remember to allow edits from maintainers: https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/allowing-changes-to-a-pull-request-branch-created-from-a-fork --->


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b980cf..239a1ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [7.3.1] - 2023-12-01
+
+### Fixed 
+- Fixed hosted zone filtering.
+
 ## [7.3.0] - 2023-11-30
 
 ### Fixed 
diff --git a/package.json b/package.json
index ba6726e..557e01c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "serverless-domain-manager",
-  "version": "7.3.0",
+  "version": "7.3.1",
   "engines": {
     "node": ">=14"
   },
diff --git a/src/aws/acm-wrapper.ts b/src/aws/acm-wrapper.ts
index c7b8400..eddb860 100644
--- a/src/aws/acm-wrapper.ts
+++ b/src/aws/acm-wrapper.ts
@@ -9,6 +9,7 @@ import {
 import Globals from "../globals";
 import DomainConfig = require("../models/domain-config");
 import {getAWSPagedResults} from "../utils";
+import Logging from "../logging";
 
 const certStatuses = [
     CertificateStatus.PENDING_VALIDATION,
@@ -47,8 +48,9 @@ class ACMWrapper {
                 certificateArn = this.getCertArnByCertName(certificates, certificateName);
             } else {
                 certificateName = domain.givenDomainName;
-                certificateArn = this.getCertArnByDomainName(certificates, certificateName);
+                certificateArn = ACMWrapper.getCertArnByDomainName(certificates, certificateName);
             }
+            Logging.logInfo(`Found a certificate ARN: '${certificateArn}'`);
         } catch (err) {
             throw Error(`Could not search certificates in Certificate Manager.\n${err.message}`);
         }
@@ -66,7 +68,7 @@ class ACMWrapper {
         return null;
     }
 
-    private getCertArnByDomainName(certificates, domainName): string {
+    private static getCertArnByDomainName(certificates, domainName): string {
         // The more specific name will be the longest
         let nameLength = 0;
         let certificateArn;
diff --git a/src/aws/route53-wrapper.ts b/src/aws/route53-wrapper.ts
index 944fd18..f93f817 100644
--- a/src/aws/route53-wrapper.ts
+++ b/src/aws/route53-wrapper.ts
@@ -56,17 +56,20 @@ class Route53Wrapper {
                 "NextMarker",
                 new ListHostedZonesCommand({})
             );
+            Logging.logInfo(`Founded hosted zones list: ${hostedZones.map(zone => zone.Name)}.`);
         } catch (err) {
             throw new Error(`Unable to list hosted zones in Route53.\n${err.message}`);
         }
 
+        // removing the first part of the domain name, api.test.com => test.com
+        const domainNameHost = domain.givenDomainName.substring(domain.givenDomainName.indexOf(".") + 1);
         const targetHostedZone = hostedZones
             .filter((hostedZone) => {
                 return !isPrivateDefined || isHostedZonePrivate === hostedZone.Config.PrivateZone;
             })
             .filter((hostedZone) => {
                 const hostedZoneName = hostedZone.Name.replace(/\.$/, "");
-                return domain.givenDomainName.endsWith(hostedZoneName);
+                return domainNameHost.endsWith(hostedZoneName);
             })
             .sort((zone1, zone2) => zone2.Name.length - zone1.Name.length)
             .shift();
diff --git a/src/index.ts b/src/index.ts
index b9784ac..9b83003 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -261,8 +261,6 @@ class ServerlessCustomDomain {
                     await this.s3Wrapper.assertTlsCertObjectExists(domain);
                 }
                 if (!domain.certificateArn) {
-                    const searchName = domain.certificateName || domain.givenDomainName;
-                    Logging.logInfo(`Searching for a certificate with the '${searchName}' domain`);
                     domain.certificateArn = await acm.getCertArn(domain);
                 }
                 domain.domainInfo = await apiGateway.createCustomDomain(domain);


Test patch:
diff --git a/test/integration-tests/debug/pr-example/serverless.yml b/test/integration-tests/debug/pr-example/serverless.yml
index e61a5a1..0ee2f9f 100644
--- a/test/integration-tests/debug/pr-example/serverless.yml
+++ b/test/integration-tests/debug/pr-example/serverless.yml
@@ -22,8 +22,8 @@ custom:
     autoDomain: true
     basePath: ""
     domainName: ${env:PLUGIN_IDENTIFIER}-http-${env:RANDOM_STRING}.${env:TEST_DOMAIN}
-    stage: "dev"
     createRoute53Record: true
+    endpointType: REGIONAL
 
 package:
   patterns:
diff --git a/test/unit-tests/aws/route53-wrapper.test.ts b/test/unit-tests/aws/route53-wrapper.test.ts
index 2b23844..0f9f3c4 100644
--- a/test/unit-tests/aws/route53-wrapper.test.ts
+++ b/test/unit-tests/aws/route53-wrapper.test.ts
@@ -165,19 +165,24 @@ describe("Route53 wrapper checks", () => {
                 {
                     CallerReference: "",
                     Config: {PrivateZone: false},
-                    Id: testId,
-                    Name: "test_domain",
+                    Id: "no_valid",
+                    Name: "api.test_domain",
                 }, {
                     CallerReference: "",
                     Config: {PrivateZone: true},
                     Id: "dummy_host_id",
                     Name: "test_domain",
-                }
+                }, {
+                    CallerReference: "",
+                    Config: {PrivateZone: false},
+                    Id: testId,
+                    Name: "test_domain",
+                },
             ]
         });
 
         const dc = new DomainConfig(getDomainConfig({
-            domainName: "test_domain"
+            domainName: "devapi.test_domain"
         }));
 
         const actualId = await new Route53Wrapper().getRoute53HostedZoneId(dc, false);