Repository: pwrdrvr/microapps-core (language: python)

Issue:
Demo - Add the /api/ route back to CF
- The `set version` call in the Release app has periods in the path
- This causes the request to get routed to S3 instead of to API Gateway
- Add an option to add an `/api/` behavior that will route to API Gateway, on by default (can be turned off if no periods in paths for API Gateway is guaranteed)


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/main-build.yml b/.github/workflows/main-build.yml
index 68a7a4f4..ee3a0eaf 100644
--- a/.github/workflows/main-build.yml
+++ b/.github/workflows/main-build.yml
@@ -80,7 +80,7 @@ jobs:
           github.ref }} | cut -f1)"
 
       - name: Upload CDK Construct Artifact
-        if: ${{ steps.git_remote.outputs.latest_commit == github.sha }}
+        # if: ${{ steps.git_remote.outputs.latest_commit == github.sha }}
         uses: actions/upload-artifact@v2
         with:
           name: cdk-construct-dist
@@ -148,7 +148,7 @@ jobs:
   #
   release-assets:
     # Release only on main branch
-    name: Release Assets
+    name: Release Assets - Dry Run
     needs: [ build-cdk-construct, build-publish-tool ]
     runs-on: ubuntu-latest
     permissions:
diff --git a/packages/microapps-cdk/API.md b/packages/microapps-cdk/API.md
index 72196042..a19160cd 100644
--- a/packages/microapps-cdk/API.md
+++ b/packages/microapps-cdk/API.md
@@ -338,30 +338,54 @@ const addRoutesOptions: AddRoutesOptions = { ... }
 
 - *Type:* [`@aws-cdk/aws-cloudfront.IOrigin`](#@aws-cdk/aws-cloudfront.IOrigin)
 
+API Gateway CloudFront Origin for API calls.
+
 ---
 
 ##### `apigwyOriginRequestPolicy`<sup>Required</sup> <a name="@pwrdrvr/microapps-cdk.AddRoutesOptions.apigwyOriginRequestPolicy"></a>
 
 - *Type:* [`@aws-cdk/aws-cloudfront.IOriginRequestPolicy`](#@aws-cdk/aws-cloudfront.IOriginRequestPolicy)
 
+Origin Request policy for API Gateway Origin.
+
 ---
 
 ##### `bucketAppsOrigin`<sup>Required</sup> <a name="@pwrdrvr/microapps-cdk.AddRoutesOptions.bucketAppsOrigin"></a>
 
 - *Type:* [`@aws-cdk/aws-cloudfront-origins.S3Origin`](#@aws-cdk/aws-cloudfront-origins.S3Origin)
 
+S3 Bucket CloudFront Origin for static assets.
+
 ---
 
 ##### `distro`<sup>Required</sup> <a name="@pwrdrvr/microapps-cdk.AddRoutesOptions.distro"></a>
 
 - *Type:* [`@aws-cdk/aws-cloudfront.Distribution`](#@aws-cdk/aws-cloudfront.Distribution)
 
+CloudFront Distribution to add the Behaviors (Routes) to.
+
+---
+
+##### `createAPIPathRoute`<sup>Optional</sup> <a name="@pwrdrvr/microapps-cdk.AddRoutesOptions.createAPIPathRoute"></a>
+
+- *Type:* `boolean`
+- *Default:* true
+
+Create an extra Behavior (Route) for /api/ that allows API routes to have a period in them.
+
+When false API routes with a period in the path will get routed to S3.
+
+When true API routes that contain /api/ in the path will get routed to API Gateway
+even if they have a period in the path.
+
 ---
 
 ##### `rootPathPrefix`<sup>Optional</sup> <a name="@pwrdrvr/microapps-cdk.AddRoutesOptions.rootPathPrefix"></a>
 
 - *Type:* `string`
 
+Path prefix on the root of the CloudFront distribution.
+
 ---
 
 ### CreateAPIOriginPolicyOptions <a name="@pwrdrvr/microapps-cdk.CreateAPIOriginPolicyOptions"></a>
@@ -537,6 +561,20 @@ ACM Certificate that covers `domainNameEdge` name.
 
 ---
 
+##### `createAPIPathRoute`<sup>Optional</sup> <a name="@pwrdrvr/microapps-cdk.MicroAppsCFProps.createAPIPathRoute"></a>
+
+- *Type:* `boolean`
+- *Default:* true
+
+Create an extra Behavior (Route) for /api/ that allows API routes to have a period in them.
+
+When false API routes with a period in the path will get routed to S3.
+
+When true API routes that contain /api/ in the path will get routed to API Gateway
+even if they have a period in the path.
+
+---
+
 ##### `domainNameEdge`<sup>Optional</sup> <a name="@pwrdrvr/microapps-cdk.MicroAppsCFProps.domainNameEdge"></a>
 
 - *Type:* `string`
@@ -637,6 +675,20 @@ Certificate in deployed region for the API Gateway.
 
 ---
 
+##### `createAPIPathRoute`<sup>Optional</sup> <a name="@pwrdrvr/microapps-cdk.MicroAppsProps.createAPIPathRoute"></a>
+
+- *Type:* `boolean`
+- *Default:* true
+
+Create an extra Behavior (Route) for /api/ that allows API routes to have a period in them.
+
+When false API routes with a period in the path will get routed to S3.
+
+When true API routes that contain /api/ in the path will get routed to API Gateway
+even if they have a period in the path.
+
+---
+
 ##### `domainNameEdge`<sup>Optional</sup> <a name="@pwrdrvr/microapps-cdk.MicroAppsProps.domainNameEdge"></a>
 
 - *Type:* `string`
diff --git a/packages/microapps-cdk/src/MicroApps.ts b/packages/microapps-cdk/src/MicroApps.ts
index 5fb9692c..4f684a27 100644
--- a/packages/microapps-cdk/src/MicroApps.ts
+++ b/packages/microapps-cdk/src/MicroApps.ts
@@ -154,6 +154,19 @@ export interface MicroAppsProps {
    * @example dev/
    */
   readonly rootPathPrefix?: string;
+
+  /**
+   * Create an extra Behavior (Route) for /api/ that allows
+   * API routes to have a period in them.
+   *
+   * When false API routes with a period in the path will get routed to S3.
+   *
+   * When true API routes that contain /api/ in the path will get routed to API Gateway
+   * even if they have a period in the path.
+   *
+   * @default true
+   */
+  readonly createAPIPathRoute?: boolean;
 }
 
 export interface IMicroApps {
@@ -216,6 +229,7 @@ export class MicroApps extends cdk.Construct implements IMicroApps {
       s3PolicyBypassPrincipalARNs,
       s3StrictBucketPolicy,
       rootPathPrefix,
+      createAPIPathRoute = true,
     } = props;
 
     this._s3 = new MicroAppsS3(this, 's3', {
@@ -250,6 +264,7 @@ export class MicroApps extends cdk.Construct implements IMicroApps {
       bucketAppsOrigin: this._s3.bucketAppsOrigin,
       bucketLogs: this._s3.bucketLogs,
       rootPathPrefix,
+      createAPIPathRoute,
     });
     this._svcs = new MicroAppsSvcs(this, 'svcs', {
       httpApi: this.apigwy.httpApi,
diff --git a/packages/microapps-cdk/src/MicroAppsCF.ts b/packages/microapps-cdk/src/MicroAppsCF.ts
index ff643551..93270f0d 100644
--- a/packages/microapps-cdk/src/MicroAppsCF.ts
+++ b/packages/microapps-cdk/src/MicroAppsCF.ts
@@ -86,6 +86,19 @@ export interface MicroAppsCFProps {
    * @example dev/
    */
   readonly rootPathPrefix?: string;
+
+  /**
+   * Create an extra Behavior (Route) for /api/ that allows
+   * API routes to have a period in them.
+   *
+   * When false API routes with a period in the path will get routed to S3.
+   *
+   * When true API routes that contain /api/ in the path will get routed to API Gateway
+   * even if they have a period in the path.
+   *
+   * @default true
+   */
+  readonly createAPIPathRoute?: boolean;
 }
 
 export interface CreateAPIOriginPolicyOptions {
@@ -101,11 +114,45 @@ export interface CreateAPIOriginPolicyOptions {
 }
 
 export interface AddRoutesOptions {
+  /**
+   * API Gateway CloudFront Origin for API calls
+   */
   readonly apiGwyOrigin: cf.IOrigin;
+
+  /**
+   * S3 Bucket CloudFront Origin for static assets
+   */
   readonly bucketAppsOrigin: cforigins.S3Origin;
+
+  /**
+   * CloudFront Distribution to add the Behaviors (Routes) to
+   */
   readonly distro: cf.Distribution;
+
+  /**
+   * Origin Request policy for API Gateway Origin
+   */
   readonly apigwyOriginRequestPolicy: cf.IOriginRequestPolicy;
+
+  /**
+   * Path prefix on the root of the CloudFront distribution
+   *
+   * @example dev/
+   */
   readonly rootPathPrefix?: string;
+
+  /**
+   * Create an extra Behavior (Route) for /api/ that allows
+   * API routes to have a period in them.
+   *
+   * When false API routes with a period in the path will get routed to S3.
+   *
+   * When true API routes that contain /api/ in the path will get routed to API Gateway
+   * even if they have a period in the path.
+   *
+   * @default true
+   */
+  readonly createAPIPathRoute?: boolean;
 }
 
 export class MicroAppsCF extends cdk.Construct implements IMicroAppsCF {
@@ -169,6 +216,7 @@ export class MicroAppsCF extends cdk.Construct implements IMicroAppsCF {
       distro,
       apigwyOriginRequestPolicy,
       rootPathPrefix = '',
+      createAPIPathRoute = true,
     } = props;
 
     //
@@ -190,6 +238,19 @@ export class MicroAppsCF extends cdk.Construct implements IMicroAppsCF {
       viewerProtocolPolicy: cf.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
     };
 
+    //
+    // If a route specifically has `/api/` in it, send it to API Gateway
+    // This is needed to catch routes that have periods in the API path data,
+    // such as: /release/0.0.0/api/update/default/release/0.0.0
+    //
+    if (createAPIPathRoute) {
+      distro.addBehavior(
+        posixPath.join(rootPathPrefix, '/*/*/api/*'),
+        apiGwyOrigin,
+        apiGwyBehaviorOptions,
+      );
+    }
+
     //
     // All static assets are assumed to have a dot in them
     //
@@ -244,6 +305,7 @@ export class MicroAppsCF extends cdk.Construct implements IMicroAppsCF {
       bucketLogs,
       bucketAppsOrigin,
       rootPathPrefix,
+      createAPIPathRoute = true,
     } = props;
 
     const apigwyOriginRequestPolicy = MicroAppsCF.createAPIOriginPolicy(this, {
@@ -300,6 +362,7 @@ export class MicroAppsCF extends cdk.Construct implements IMicroAppsCF {
       distro: this._cloudFrontDistro,
       apigwyOriginRequestPolicy: apigwyOriginRequestPolicy,
       rootPathPrefix,
+      createAPIPathRoute,
     });
 
     //


Test patch:
diff --git a/packages/microapps-cdk/test/__snapshots__/MicroAppsCF.test.ts.snap b/packages/microapps-cdk/test/__snapshots__/MicroAppsCF.test.ts.snap
index 744afe1c..d7fd0f82 100644
--- a/packages/microapps-cdk/test/__snapshots__/MicroAppsCF.test.ts.snap
+++ b/packages/microapps-cdk/test/__snapshots__/MicroAppsCF.test.ts.snap
@@ -72,6 +72,25 @@ Object {
       "Properties": Object {
         "DistributionConfig": Object {
           "CacheBehaviors": Array [
+            Object {
+              "AllowedMethods": Array [
+                "GET",
+                "HEAD",
+                "OPTIONS",
+                "PUT",
+                "PATCH",
+                "POST",
+                "DELETE",
+              ],
+              "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
+              "Compress": true,
+              "OriginRequestPolicyId": Object {
+                "Ref": "constructapigwyoriginpolicystack77776C6F",
+              },
+              "PathPattern": "/*/*/api/*",
+              "TargetOriginId": "stackconstructcftOrigin1D000029C",
+              "ViewerProtocolPolicy": "redirect-to-https",
+            },
             Object {
               "AllowedMethods": Array [
                 "GET",
@@ -285,6 +304,23 @@ Object {
             "some.test.pwrdrvr.com",
           ],
           "CacheBehaviors": Array [
+            Object {
+              "AllowedMethods": Array [
+                "GET",
+                "HEAD",
+                "OPTIONS",
+                "PUT",
+                "PATCH",
+                "POST",
+                "DELETE",
+              ],
+              "CachePolicyId": "4135ea2d-6df8-44a3-9df3-4b5a84be39ad",
+              "Compress": true,
+              "OriginRequestPolicyId": "216adef6-5c7f-47e4-b989-5492eafa07d3",
+              "PathPattern": "/*/*/api/*",
+              "TargetOriginId": "stackconstructcftOrigin1D000029C",
+              "ViewerProtocolPolicy": "redirect-to-https",
+            },
             Object {
               "AllowedMethods": Array [
                 "GET",