Repository: sid88in/serverless-appsync-plugin
Pull request #580 (2023-03-14 18:23:19)

docs: add functions config 
Couple of small corrections to the docs.


Patch:
diff --git a/README.md b/README.md
index f6bbba5..62096a0 100644
--- a/README.md
+++ b/README.md
@@ -52,7 +52,7 @@ appSync:
 - [Authentication](doc/authentication.md)
 - [API keys](doc/API-keys.md)
 - [Custom Domain](doc/custom-domain.md)
-- [VTL Template Substitutions](doc/substitutions.md)
+- [Variable Substitutions](doc/substitutions.md)
 - [Caching](doc/caching.md)
 - [Web Application Firewall (WAF)](doc/WAF.md)
 
@@ -65,7 +65,7 @@ This plugin adds some useful CLI commands. See [CLI commands documentation](doc/
 This plugin exports some handy variables that you can use in your yml files to reference some values generated by CloudFormation:
 
 - `${appsync:id}`: The id of the AppSync API
-- `${appsync:url}`: The ARN of the AppSync API
+- `${appsync:url}`: The URL of the AppSync API
 - `${appsync:arn}`: The ARN of the AppSync API
 - `${appsync:apiKey.[NAME]}`: An API key
 
diff --git a/doc/pipeline-functions.md b/doc/pipeline-functions.md
index abaacb8..02f3750 100644
--- a/doc/pipeline-functions.md
+++ b/doc/pipeline-functions.md
@@ -22,7 +22,7 @@ appSync:
 - `request`: The path to the VTL request mapping template file, relative to `serverless.yml`.
 - `response`: The path to the VTL response mapping template file, relative to `serverless.yml`.
 - `maxBatchSize`: The maximum [batch size](https://aws.amazon.com/blogs/mobile/introducing-configurable-batching-size-for-aws-appsync-lambda-resolvers/) to use (only available for AWS Lambda DataSources)
-- `substitutions`: See [VTL template substitutions](substitutions.md)
+- `substitutions`: See [Variable Substitutions](substitutions.md)
 - `sync`: [See SyncConfig](syncConfig.md)
 
 ## JavaScript vs VTL vs Direct Lambda
diff --git a/doc/resolvers.md b/doc/resolvers.md
index 0651196..bc9d20a 100644
--- a/doc/resolvers.md
+++ b/doc/resolvers.md
@@ -24,11 +24,12 @@ appSync:
 - `field`: The Field in the schema this resolver is attached to. Optional if specified in the configuration key.
 - `kind`: The kind of resolver. Can be `UNIT` or `PIPELINE` ([see below](#PIPELINE-resolvers)). Defaults to `PIPELINE`
 - `dataSource`: The name of the [dataSource](dataSources.md) this resolver uses.
+- `functions`: For pipeline resolvers ([see below](#PIPELINE-resolvers)) the array of functions to run in sequence
 - `maxBatchSize`: The maximum [batch size](https://aws.amazon.com/blogs/mobile/introducing-configurable-batching-size-for-aws-appsync-lambda-resolvers/) to use (only available for AWS Lambda DataSources)
 - `code`: The path of the JavaScript resolver handler file, relative to `serverless.yml`. If not specified, a [minimalistic default](#javascript-vs-vtl) is used.
 - `request`: The path to the VTL request mapping template file, relative to `serverless.yml`.
 - `response`: The path to the VTL response mapping template file, relative to `serverless.yml`.
-- `substitutions`: See [VTL template substitutions](substitutions.md)
+- `substitutions`: See [Variable Substitutions](substitutions.md)
 - `caching`: [See below](#Caching)
 - `sync`: [See SyncConfig](syncConfig.md)
 
@@ -64,7 +65,6 @@ appSync:
 
   resolvers:
     Query.user:
-      dataSource: my-table
       functions:
         - function1
         - function2
diff --git a/doc/substitutions.md b/doc/substitutions.md
index 5d2a857..794953b 100644
--- a/doc/substitutions.md
+++ b/doc/substitutions.md
@@ -1,10 +1,16 @@
 # Substitutions
 
-Substitutions is a feature that allows you to replace some variables in your VTL mapping templates with dynamic values.
+`Substitutions` is a feature that allows you to replace some variables in your VTL mapping templates or JS resolvers with dynamic values.
 
 They are usually useful to replace parts of the template with resource names or ARNs coming from your IaC, such as a DynamoDB table names, for example; or the stage name, region name, etc.
 
-## Quick start
+## Usage
+
+Substitutions are defined as key-value pairs under the `appSync.substitutions`, `appSync.resolvers.[resolverName].substitutions` or `appSync.pipelineFunctions.[functionName].substitutions` attributes.
+
+Global substitutions are available globally to all mapping templates. Resolver and Pipeline function substitutions are only available where they are defined. Resolver and Pipeline function substitutions take precedence over global substitutions (values will be overwritten).
+
+Once defined, you can then use them within the mapping templates as if they were VTL variables. For JS resolvers you'll need to add the variable name as a string padded by `#`. At deployment time, variables will be substituted with their corresponding value.
 
 ```yaml
 appSync:
@@ -21,43 +27,30 @@ appSync:
         someVariable: someValue
 ```
 
-```vtl
-#set($postsdata = [])
-#foreach($item in ${ctx.args.posts})
-    $util.qr($postsdata.add($util.dynamodb.toMapValues($item)))
-#end
-{
-    "version" : "2018-05-29",
-    "operation" : "BatchPutItem",
-    "tables" : {
-        "${postsTable}": $utils.toJson($postsdata)
-    }
-}
-```
-
-## Usage
-
-Substitutions are defined as key-value pairs under the `appSync.substitutions`, `appSync.resolvers.[resolverName].substitutions` or `appSync.pipelineFunctions.[functionName].substitutions` attributes.
-
-Global substitutions are available globally to all mapping templates. Resolver and Pipeline function substitutions are only available where they are defined. Resolver and Pipeline function substitutions take precedence over global substitutions (values will be overwritten).
-
-Once defined, you can then use them within the mapping templates as if they were VTL variables. At deployment time, variables will be substituted with their corresponding value.
-
-```yaml
-appSync:
-  name: my-api
-  substitutions:
-    postsTable: !Ref Posts
-```
-
-In the mapping template:
-
-```vtl
-{
-    "version" : "2018-05-29",
-    "operation" : "BatchPutItem",
-    "tables" : {
-        "${postsTable}": [...]
-    }
-}
-```
+<details open>
+  <summary>VTL mapping template</summary>
+
+  ```vtl
+  {
+      "version" : "2018-05-29",
+      "operation" : "BatchPutItem",
+      "tables" : {
+          "${postsTable}": [...]
+      }
+  }
+  ```
+</details>
+
+<details open>
+  <summary>JS Resolvers</summary>
+  
+  ```js
+  const tableName = '#postsTable#';
+  return {
+    operation: "BatchGetItem",
+    tables: {
+      [tableName]: { keys },
+    },
+  };
+  ```
+</details>
diff --git a/doc/upgrading-from-v1.md b/doc/upgrading-from-v1.md
index 375aafb..129d17b 100644
--- a/doc/upgrading-from-v1.md
+++ b/doc/upgrading-from-v1.md
@@ -6,10 +6,19 @@ This page will guide you through the process of migrating existing stacks to the
 
 The v1 is still available on the [v1](https://github.com/sid88in/serverless-appsync-plugin/tree/v1) branch
 
+⚠️ Using the AppSync API keys? Read about the [API keys rotation issue](#api-keys-rotation-issue)
+
 ## Breaking changes and dropped features
 
 There are a few breaking changes that you need to be aware of.
 
+### API keys rotation issue
+
+Due to a [backwards-compatibility issue](https://github.com/sid88in/serverless-appsync-plugin/issues/584) your API keys will get rotated
+(changed) upon the first deployment with the `v2` version of this plugin. If left unresolved, this will cause your clients that use the keys
+to break, until you are able to update them with the newly generated API keys. 
+Make sure to read about [mitigating the API key rotation issue](#mitigate-the-api-key-rotation-issue)
+
 ### Support for Serverless Framework v3 only
 
 `v2` only supports the new [Serverless Framework v3](https://www.serverless.com/blog/serverless-framework-v3-is-live). You will need to upgrade to [SF v3 first](https://www.serverless.com/framework/docs/guides/upgrading-v3).
@@ -111,7 +120,7 @@ configValidationMode: error
 
 🙋‍♂️ If you find information that is inaccurate or incomplete in this guide, please [open a PR](https://github.com/sid88in/serverless-appsync-plugin/compare) to fix it 🙏.
 
-**Path of the appSync configuration**
+#### Path of the appSync configuration
 
 The first significant change is that you must now define your API under the `appSync` attribute directly at the root of your `serverless.yml` file, as opposed to placing it under `custom` in `v1`.
 
@@ -132,7 +141,7 @@ appSync:
   name: my-api
 ```
 
-**Renamed attributes**
+#### Renamed attributes
 
 Some attributes have been renamed for clarity. Here are the most important ones.
 
@@ -141,7 +150,7 @@ Some attributes have been renamed for clarity. Here are the most important ones.
 - rename `logConfig` to `logging`
 - rename `wafConfig` to `waf`
 
-**DataSources**
+#### DataSources
 
 [DataSources](dataSources.md) are now defined as a key-value pair object. In `v1`, you passed them as an array. Replace the array with a key-value pair object where the key is what you used to have under `name`.
 
@@ -165,7 +174,7 @@ dataSources:
       tableName: my-table
 ```
 
-**Resolvers**
+#### Resolvers
 
 [Resolvers](resolvers.md) are now defined as a key-value pair object. In `v1` you passed them as an array. Replace the array with a key-value pair object. You can use any value as the key, or use the [`Type.field` shortcut](resolvers.md).
 
@@ -188,7 +197,7 @@ resolvers:
     dataSource: myDataSource
 ```
 
-**Pipeline functions**
+#### Pipeline functions
 
 [Pipeline functions](pipeline-functions.md) have moved from `functionConfigurations` to `pipelineFunctions`. Just like Resolvers and datasources, `pipelineFunction` expects a key-value pair object. Use the name of the function (`name`) as the key.
 
@@ -208,7 +217,7 @@ pipelineFunctions:
     dataSource: myDataSource
 ```
 
-**Authentication**
+#### Authentication
 
 In `v1` you would define the principal [authentication](authentication.md) provider directly under the `appSync` attribute. In `v2` it has moved under `authentication`.
 
@@ -255,7 +264,7 @@ additionalAuthentications:
       userPoolId: # user pool ID
 ```
 
-**Schema**
+#### Schema
 
 If you split your schema into several files, you must use [Object extensions](https://spec.graphql.org/October2021/#sec-Object-Extensions) on the types that have already been defined. This will often be the case for the `Query`, `Mutation` and `Subscription` types.
 
@@ -297,7 +306,7 @@ extend type Query {
 }
 ```
 
-**ElasticSearch**
+#### ElasticSearch
 
 As of September 2021, Amazon Elasticsearch Service is Amazon OpenSearch Service. DataSources of type `AMAZON_ELASTICSEARCH` should now use `AMAZON_OPENSEARCH_SERVICE` instead.
 
@@ -320,3 +329,28 @@ dataSources:
     config:
       endpoint: https://abcdefgh.us-east-1.es.amazonaws.com
 ```
+
+#### Mitigate the API key rotation issue
+
+Due to a [backwards-compatibility issue](https://github.com/sid88in/serverless-appsync-plugin/issues/584) your API keys will get rotated
+(changed) upon the first deployment with the `v2` version of this plugin. If left unresolved, this will cause your clients that use the keys
+to break, until you are able to update them with the newly generated API keys. 
+
+If some down time isn't acceptable, there's two ways to mitigate this:
+
+##### Option A: Add a temporary key
+
+1. Add a new temporary API key throught the settings in AWS AppSync Console
+2. Update all your clients to use this new temporary key
+3. Deploy your API using the `v2` version of this plugin. The old API keys will get replaced by new ones, but the temporary key will get 
+   retained since it's not part of the CloudFormattion stack.
+5. After deployment, take the newly generated keys, and update the clients again.
+6. Manually delete the temporary key from the AWS AppSync Console
+
+##### Option B: Add an entry to `Resources` key in the `serverless.yml`
+
+1. Find the CloudFormattion resource name of the existing API key(s). You can find the name in the *Resources* tab of your stack in the CloudFormattion AWS console.
+2. Add an entry to `Resources.resources` part of the `serverless.yml` file, making sure to keep all the configuration (especially the logical ID), exactly the same.
+3. Deploy your API using the `v2` version of this plugin. The old API keys will now get retained because they were manually referenced in the `Resources` part.
+4. Migrate your clients to the new API keys created by the `v2` plugin
+5. Delete the key from the `Resources` 
diff --git a/src/validation.ts b/src/validation.ts
index abbf8fb..6c10beb 100644
--- a/src/validation.ts
+++ b/src/validation.ts
@@ -133,7 +133,7 @@ export const appSyncSchema = {
         iatTTL: { type: 'number' },
         authTTL: { type: 'number' },
       },
-      required: ['issuer', 'clientId'],
+      required: ['issuer'],
     },
     iamAuth: {
       type: 'object',


Test patch:
diff --git a/src/__tests__/validation/__snapshots__/auth.test.ts.snap b/src/__tests__/validation/__snapshots__/auth.test.ts.snap
index 5912f9c..520b0ea 100644
--- a/src/__tests__/validation/__snapshots__/auth.test.ts.snap
+++ b/src/__tests__/validation/__snapshots__/auth.test.ts.snap
@@ -32,10 +32,7 @@ exports[`Valdiation Invalid should validate a Lambda with invalid functionName a
 
 exports[`Valdiation Invalid should validate a Lambda with missing config 1`] = `"/authentication: must have required property 'config'"`;
 
-exports[`Valdiation Invalid should validate a OIDC with empty config 1`] = `
-"/authentication/config: must have required property 'issuer'
-/authentication/config: must have required property 'clientId'"
-`;
+exports[`Valdiation Invalid should validate a OIDC with empty config 1`] = `"/authentication/config: must have required property 'issuer'"`;
 
 exports[`Valdiation Invalid should validate a OIDC with invalid config 1`] = `
 "/authentication/config/issuer: must be string
diff --git a/src/__tests__/validation/auth.test.ts b/src/__tests__/validation/auth.test.ts
index 1360951..6cc4cdf 100644
--- a/src/__tests__/validation/auth.test.ts
+++ b/src/__tests__/validation/auth.test.ts
@@ -61,6 +61,20 @@ describe('Valdiation', () => {
           },
         } as AppSyncConfigInput,
       },
+      {
+        name: 'OIDC without a clientId',
+        config: {
+          ...basicConfig,
+          authentication: {
+            type: 'OPENID_CONNECT',
+            config: {
+              issuer: 'https://auth.example.com',
+              iatTTL: 3600,
+              authTTL: 3600,
+            },
+          },
+        } as AppSyncConfigInput,
+      },
       {
         name: 'IAM',
         config: {