Repository: sid88in/serverless-appsync-plugin
Pull request #625 (2023-12-30 17:58:14)

feat: Add introspection, resolverCountLImit and queryDepthLImit config


Patch:
diff --git a/src/resources/Api.ts b/src/resources/Api.ts
index 95e4192..4a451b0 100644
--- a/src/resources/Api.ts
+++ b/src/resources/Api.ts
@@ -111,6 +111,24 @@ export class Api {
       });
     }
 
+    if (this.config.introspection !== undefined) {
+      merge(endpointResource.Properties, {
+        IntrospectionConfig: this.config.introspection ? 'ENABLED' : 'DISABLED',
+      });
+    }
+
+    if (this.config.queryDepthLimit !== undefined) {
+      merge(endpointResource.Properties, {
+        QueryDepthLimit: this.config.queryDepthLimit,
+      });
+    }
+
+    if (this.config.resolverCountLimit !== undefined) {
+      merge(endpointResource.Properties, {
+        ResolverCountLimit: this.config.resolverCountLimit,
+      });
+    }
+
     const resources = {
       [logicalId]: endpointResource,
     };
diff --git a/src/types/index.ts b/src/types/index.ts
index c8cfae9..8105630 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -40,6 +40,9 @@ export type AppSyncConfig = {
   tags?: Record<string, string>;
   visibility?: 'GLOBAL' | 'PRIVATE';
   esbuild?: BuildOptions | false;
+  introspection?: boolean;
+  queryDepthLimit?: number;
+  resolverCountLimit?: number;
 };
 
 export type BaseResolverConfig = {
diff --git a/src/types/plugin.ts b/src/types/plugin.ts
index f7e245d..c6f7016 100644
--- a/src/types/plugin.ts
+++ b/src/types/plugin.ts
@@ -36,6 +36,9 @@ export type AppSyncConfig = {
   tags?: Record<string, string>;
   visibility?: 'GLOBAL' | 'PRIVATE';
   esbuild?: BuildOptions | false;
+  introspection?: boolean;
+  queryDepthLimit?: number;
+  resolverCountLimit?: number;
 };
 
 export type BaseResolverConfig = {
diff --git a/src/validation.ts b/src/validation.ts
index bc2887e..447787e 100644
--- a/src/validation.ts
+++ b/src/validation.ts
@@ -683,6 +683,9 @@ export const appSyncSchema = {
       enum: ['GLOBAL', 'PRIVATE'],
       errorMessage: 'must be "GLOBAL" or "PRIVATE"',
     },
+    introspection: { type: 'boolean' },
+    queryDepthLimit: { type: 'integer', minimum: 1, maximum: 75 },
+    resolverCountLimit: { type: 'integer', minimum: 1, maximum: 1000 },
     substitutions: { $ref: '#/definitions/substitutions' },
     waf: {
       type: 'object',


Test patch:
diff --git a/src/__tests__/api.test.ts b/src/__tests__/api.test.ts
index c8389f1..56ea77f 100644
--- a/src/__tests__/api.test.ts
+++ b/src/__tests__/api.test.ts
@@ -58,6 +58,38 @@ describe('Api', () => {
       `);
     });
 
+    it('should compile the Api Resource with config', () => {
+      const api = new Api(
+        given.appSyncConfig({
+          introspection: false,
+          queryDepthLimit: 10,
+          resolverCountLimit: 20,
+        }),
+        plugin,
+      );
+      expect(api.compileEndpoint()).toMatchInlineSnapshot(`
+        Object {
+          "GraphQlApi": Object {
+            "Properties": Object {
+              "AuthenticationType": "API_KEY",
+              "IntrospectionConfig": "DISABLED",
+              "Name": "MyApi",
+              "QueryDepthLimit": 10,
+              "ResolverCountLimit": 20,
+              "Tags": Array [
+                Object {
+                  "Key": "stage",
+                  "Value": "Dev",
+                },
+              ],
+              "XrayEnabled": false,
+            },
+            "Type": "AWS::AppSync::GraphQLApi",
+          },
+        }
+      `);
+    });
+
     it('should compile the Api Resource with logs enabled', () => {
       const api = new Api(
         given.appSyncConfig({
diff --git a/src/__tests__/validation/__snapshots__/base.test.ts.snap b/src/__tests__/validation/__snapshots__/base.test.ts.snap
index 79de313..5ee850e 100644
--- a/src/__tests__/validation/__snapshots__/base.test.ts.snap
+++ b/src/__tests__/validation/__snapshots__/base.test.ts.snap
@@ -57,5 +57,13 @@ exports[`Valdiation should validate  1`] = `
 /unknownPorp: invalid (unknown) property
 /xrayEnabled: must be boolean
 /visibility: must be \\"GLOBAL\\" or \\"PRIVATE\\"
+/introspection: must be boolean
+/queryDepthLimit: must be integer
+/resolverCountLimit: must be integer
 /esbuild: must be an esbuild config object or false"
 `;
+
+exports[`Valdiation should validate  2`] = `
+"/queryDepthLimit: must be <= 75
+/resolverCountLimit: must be <= 1000"
+`;
diff --git a/src/__tests__/validation/base.test.ts b/src/__tests__/validation/base.test.ts
index 4f899d2..e4d1f78 100644
--- a/src/__tests__/validation/base.test.ts
+++ b/src/__tests__/validation/base.test.ts
@@ -8,6 +8,9 @@ describe('Valdiation', () => {
       validateConfig({
         ...basicConfig,
         visibility: 'GLOBAL',
+        introspection: true,
+        queryDepthLimit: 10,
+        resolverCountLimit: 10,
         xrayEnabled: true,
         tags: {
           foo: 'bar',
@@ -23,11 +26,22 @@ describe('Valdiation', () => {
     expect(function () {
       validateConfig({
         visibility: 'FOO',
+        introspection: 10,
+        queryDepthLimit: 'foo',
+        resolverCountLimit: 'bar',
         xrayEnabled: 'BAR',
         unknownPorp: 'foo',
         esbuild: 'bad',
       });
     }).toThrowErrorMatchingSnapshot();
+
+    expect(function () {
+      validateConfig({
+        ...basicConfig,
+        queryDepthLimit: 76,
+        resolverCountLimit: 1001,
+      });
+    }).toThrowErrorMatchingSnapshot();
   });
 
   describe('Log', () => {