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

Issue:
Jakarta forced to use APIGatewayV2 for REGIONAL TLS 1.2
<!--- Please keep this note for the community --->

### Community Note

* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request
* Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
* If you are interested in working on this issue or have submitted a pull request, please leave a comment

<!--- Thank you for keeping this note for the community --->

## Bug Report

**Error Description**

Hi,

Continuing this issue (https://github.com/amplify-education/serverless-domain-manager/issues/536), it seems that Jakarta does not support APIGatewayV2 yet.

The fix (https://github.com/amplify-education/serverless-domain-manager/pull/537) only applies to EDGE and TLS 1.0. However I would like to deploy a REGIONAL TLS 1.2 custom domain.

I have manually verified that this combination can be created through the AWS Console. The current identification mechanism to switch to APIGatewayV1 might not cover all valid cases. If Jakarta is known to not support APIGatewayV2 should we just toggle to APIGatewayV1 based on the `ap-southeast-3` region?

**Command Run**
`sls deploy`


**Console Output**
```sh
Error: V2 - Unable to fetch information about '{domain}':
Bad Request
    at APIGatewayV2Wrapper.<anonymous> ({path}/node_modules/serverless-domain-manager/dist/src/aws/api-gateway-v2-wrapper.js:71:27)
    at Generator.throw (<anonymous>)
    at rejected ({path}/node_modules/serverless-domain-manager/dist/src/aws/api-gateway-v2-wrapper.js:6:65)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)

Exited with code exit status 1
```

**Domain Manager Configuration**
Replace this with your own `serverless.yml` file (anonymized, of course) to help us better resolve your issue.
```yaml
custom:
  customDomain:
    enabled: true
    domainName: {domain}
    certificateArn: {arn}
    createRoute53Record: false
    createRoute53IPv6Record: false
    endpointType: regional
    securityPolicy: tls_1_2
    apiType: rest
    autoDomain: true
```

**Versions**
- Domain Manager version(s): 6.4.1
- Node/npm version: 16.13.1/8.1.2
- Serverless Version: 3.26.0
- Lambda Code: Go

**Possible Solution**
If Jakarta is known to not support APIGatewayV2 should we just toggle to APIGatewayV1 based on the `ap-southeast-3` region?

**Additional context/Screenshots**
Add any other context about the problem here. If applicable, add screenshots to help explain.


Pull request:
Release 6.4.2
<!--- Information about referencing Github Issues: https://help.github.com/articles/basic-writing-and-formatting-syntax/#referencing-issues-and-pull-requests --->
Fixes #556 

**Description of Issue Fixed**

Fix the condition for using the right API Gateway version based on the API type

<!--- 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 97f22ac..895bfb3 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).
 
+## [6.4.2] - 2023-02-13
+
+### Fixed
+- Changed the logic to use `APIGateway` for the `rest` API type and `ApiGatewayV2` for the `http` and `websocket`.
+
 ## [6.4.1] - 2023-02-10
 
 ### Fixed
diff --git a/package-lock.json b/package-lock.json
index dc07902..0dc98f0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "serverless-domain-manager",
-  "version": "6.4.1",
+  "version": "6.4.2",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
       "name": "serverless-domain-manager",
-      "version": "6.4.1",
+      "version": "6.4.2",
       "license": "MIT",
       "dependencies": {
         "aws-sdk": "^2.1313.0"
@@ -1938,9 +1938,9 @@
       }
     },
     "node_modules/ci-info": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.7.1.tgz",
-      "integrity": "sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==",
+      "version": "3.8.0",
+      "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
+      "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==",
       "dev": true,
       "funding": [
         {
@@ -4597,9 +4597,9 @@
       }
     },
     "node_modules/minipass": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.2.tgz",
-      "integrity": "sha512-4Hbzei7ZyBp+1aw0874YWpKOubZd/jc53/XU+gkYry1QV+VvrbO8icLM5CUtm4F0hyXn85DXYKEMIS26gitD3A==",
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.3.tgz",
+      "integrity": "sha512-OW2r4sQ0sI+z5ckEt5c1Tri4xTgZwYDxpE54eqWlQloQRoWtXjqt9udJ5Z4dSv7wK+nfFI7FRXyCpBSft+gpFw==",
       "dev": true,
       "engines": {
         "node": ">=8"
@@ -4637,15 +4637,15 @@
       "dev": true
     },
     "node_modules/mkdirp": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
-      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "version": "0.5.6",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
       "dev": true,
+      "dependencies": {
+        "minimist": "^1.2.6"
+      },
       "bin": {
         "mkdirp": "bin/cmd.js"
-      },
-      "engines": {
-        "node": ">=10"
       }
     },
     "node_modules/mocha": {
@@ -5129,9 +5129,9 @@
       }
     },
     "node_modules/open": {
-      "version": "8.4.0",
-      "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz",
-      "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==",
+      "version": "8.4.1",
+      "resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz",
+      "integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==",
       "dev": true,
       "dependencies": {
         "define-lazy-prop": "^2.0.0",
@@ -6582,6 +6582,18 @@
         "node": ">=6"
       }
     },
+    "node_modules/tar/node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "dev": true,
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/tar/node_modules/yallist": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
@@ -6974,18 +6986,6 @@
         "node": "*"
       }
     },
-    "node_modules/tslint/node_modules/mkdirp": {
-      "version": "0.5.6",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
-      "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
-      "dev": true,
-      "dependencies": {
-        "minimist": "^1.2.6"
-      },
-      "bin": {
-        "mkdirp": "bin/cmd.js"
-      }
-    },
     "node_modules/tslint/node_modules/semver": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
diff --git a/package.json b/package.json
index 4546916..b7d2c8f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "serverless-domain-manager",
-  "version": "6.4.1",
+  "version": "6.4.2",
   "engines": {
     "node": ">=14"
   },
diff --git a/src/aws/cloud-formation-wrapper.ts b/src/aws/cloud-formation-wrapper.ts
index cb12711..0376c8a 100644
--- a/src/aws/cloud-formation-wrapper.ts
+++ b/src/aws/cloud-formation-wrapper.ts
@@ -152,7 +152,7 @@ class CloudFormationWrapper {
         );
 
         // filter stacks by given stackName and check by nested stack RootId
-        const regex = new RegExp(`\/${stackName}\/`);
+        const regex = new RegExp("/" + stackName + "/");
         const filteredStackNames = stacks
             .reduce((acc, stack) => {
                 if (!stack.RootId) {
diff --git a/src/index.ts b/src/index.ts
index bf88ad3..fb49114 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -139,8 +139,6 @@ class ServerlessCustomDomain {
      */
     public validateDomainConfigs() {
         this.domains.forEach((domain) => {
-
-            // Show warning if allowPathMatching is set to true
             if (domain.allowPathMatching) {
                 Globals.logWarning(`"allowPathMatching" is set for ${domain.givenDomainName}.
                     This should only be used when migrating a path to a different API type. e.g. REST to HTTP.`);
@@ -149,14 +147,15 @@ class ServerlessCustomDomain {
             if (domain.apiType === Globals.apiTypes.rest) {
                 // Currently no validation for REST API types
 
-            } else if (domain.apiType === Globals.apiTypes.http) { // Validation for http apis
-                // HTTP Apis do not support edge domains
+            } else if (domain.apiType === Globals.apiTypes.http) {
+                // HTTP APIs do not support edge domains
                 if (domain.endpointType === Globals.endpointTypes.edge) {
+                    // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-vs-rest.html
                     throw Error("'EDGE' endpointType is not compatible with HTTP APIs");
                 }
 
-            } else if (domain.apiType === Globals.apiTypes.websocket) { // Validation for WebSocket apis
-                // Websocket Apis do not support edge domains
+            } else if (domain.apiType === Globals.apiTypes.websocket) {
+                // Websocket APIs do not support edge domains
                 if (domain.endpointType === Globals.endpointTypes.edge) {
                     throw Error("'EDGE' endpointType is not compatible with WebSocket APIs");
                 }
@@ -179,13 +178,17 @@ class ServerlessCustomDomain {
     }
 
     public getApiGateway(domain: DomainConfig): APIGatewayBase {
-        const isEdge = domain.endpointType === Globals.endpointTypes.edge;
-        const isTLS10 = domain.securityPolicy === Globals.tlsVersions.tls_1_0;
-        // For EDGE domain name or TLS 1.0 use APIGateway
-        if (isEdge || isTLS10) {
+        // 1. https://stackoverflow.com/questions/72339224/aws-v1-vs-v2-api-for-listing-apis-on-aws-api-gateway-return-different-data-for-t
+        // 2. https://aws.amazon.com/blogs/compute/announcing-http-apis-for-amazon-api-gateway/
+        // There are currently two API Gateway namespaces for managing API Gateway deployments.
+        // The API V1 namespace represents REST APIs and API V2 represents WebSocket APIs and the new HTTP APIs.
+        // You can create an HTTP API by using the AWS Management Console, CLI, APIs, CloudFormation, SDKs, or the Serverless Application Model (SAM).
+
+        // use apiGatewayV1 for the `rest` API type
+        if (domain.apiType === Globals.apiTypes.rest) {
             return this.apiGatewayV1Wrapper;
         }
-        // For Regional domain use ApiGatewayV2
+        // ApiGatewayV2 for the `http` and `websocket` types
         return this.apiGatewayV2Wrapper;
     }
 
diff --git a/src/utils.ts b/src/utils.ts
index 1e958c8..71bf7da 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -14,14 +14,14 @@ async function sleep(seconds) {
 
 async function throttledCall(service: Service, funcName: string, params: object): Promise<any> {
     const maxTimePassed = 5 * 60;
+    const minWait = 3;
+    const maxWait = 60;
+    const runLoop = true;
 
     let timePassed = 0;
     let previousInterval = 0;
 
-    const minWait = 3;
-    const maxWait = 60;
-
-    while (true) {
+    while (runLoop) {
         try {
             return await service[funcName](params).promise();
         } catch (ex) {


Test patch:
diff --git a/test/unit-tests/index.test.ts b/test/unit-tests/index.test.ts
index d3b33e7..8edb242 100644
--- a/test/unit-tests/index.test.ts
+++ b/test/unit-tests/index.test.ts
@@ -297,7 +297,7 @@ describe("Custom Domain Plugin", () => {
         });
 
         it("Creates basepath mapping for regional tls 1.2 REST api", async () => {
-            AWS.mock("ApiGatewayV2", "createApiMapping", (params, callback) => {
+            AWS.mock("APIGateway", "createBasePathMapping", (params, callback) => {
                 callback(null, params);
             });
             const plugin = constructPlugin({
@@ -312,15 +312,15 @@ describe("Custom Domain Plugin", () => {
             dc.apiId = "test_rest_api_id";
 
             const apiGateway = plugin.getApiGateway(dc);
-            const spy = chai.spy.on(apiGateway.apiGateway, "createApiMapping");
+            const spy = chai.spy.on(apiGateway.apiGateway, "createBasePathMapping");
 
             await apiGateway.createBasePathMapping(dc);
 
             expect(spy).to.have.been.called.with({
-                ApiId: "test_rest_api_id",
-                ApiMappingKey: "test_basepath",
-                DomainName: "test_domain",
-                Stage: "test",
+                domainName: "test_domain",
+                restApiId: "test_rest_api_id",
+                basePath: "test_basepath",
+                stage: "test",
             });
         });
 
@@ -482,7 +482,7 @@ describe("Custom Domain Plugin", () => {
                 domainName: "test_domain",
             });
 
-            plugin.domains[0].endpointType = Globals.endpointTypes.regional;
+            plugin.domains[0].apiType = Globals.apiTypes.http;
             const spyV2 = chai.spy.on(plugin.apiGatewayV2Wrapper.apiGateway, "deleteApiMapping");
             await plugin.removeBasePathMappings();
             expect(spyV2).to.have.been.called.with({
@@ -2218,8 +2218,8 @@ describe("Custom Domain Plugin", () => {
         });
 
         it('should fail when the mutual TLS certificate is not stored in S3', async () => {
-            AWS.mock("ApiGatewayV2", "getDomainName", (params, callback) => {
-                callback(null, {DomainName: "test_domain", DomainNameConfigurations: [{HostedZoneId: "test_id"}]});
+            AWS.mock("APIGateway", "getDomainName", (params, callback) => {
+                callback(null, {domainName: "test_domain", regionalHostedZoneId: "test_id"});
             });
             AWS.mock("S3", "headObject", (params, callback) => {
                 // @ts-ignore