Skip to content

Commit

Permalink
Add LocalStack validation pipeline (#11)
Browse files Browse the repository at this point in the history
# Motivation

A release of this library has at least once broken the LocalStack CI
pipeline, with a spooky action at a distance. This is because importing
the JS libraries into running AppSync resolvers was broken, and so the
AppSync JavaScript resolver tests failed.

# Changes

* Add a CI pipeline step that runs LocalStack, configuring the appsync
resolver JS library version to use the commit specified in the current
branch.

Closes #6
  • Loading branch information
simonrw authored Feb 3, 2024
1 parent cbe194c commit bc1d805
Show file tree
Hide file tree
Showing 12 changed files with 4,588 additions and 0 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/npm-publish-github-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- main

env:
LOCALSTACK_API_KEY: ${{ secrets.LOCALSTACK_API_KEY }}
TEST_IMAGE_NAME: public.ecr.aws/lambda/nodejs:16

jobs:
Expand Down Expand Up @@ -39,3 +40,45 @@ jobs:

- name: Integration test with LocalStack invoke method
run: bash ./test_in_docker.sh

localstack-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Start LocalStack
uses: LocalStack/[email protected]
with:
image-tag: 'latest'
env:
LOCALSTACK_API_KEY: ${{ secrets.LOCALSTACK_API_KEY }}
APPSYNC_JS_LIBS_VERSION: ${{ github.sha }}
DEBUG: "1"
- uses: actions/setup-node@v4
- name: Install cdk project
working-directory: cdk
run: npm install
- name: CDK bootstrap
working-directory: cdk
run: npm run cdklocal -- bootstrap
- name: CDK deploy
working-directory: cdk
run: npm run cdklocal -- deploy --require-approval never --outputs-file outputs.json
- name: Fetch output variables for test step
working-directory: cdk
run: |
jq -r '"TEST_URL=\(.CdkStack.GraphQLURL)"' outputs.json >> $GITHUB_ENV
jq -r '"TEST_API_KEY=\(.CdkStack.ApiKey)"' outputs.json >> $GITHUB_ENV
- name: Invoke the URL
run: |
set -x
curl --connect-timeout 30 --retry 10 --retry-delay 6 -X POST -H "x-api-key:$TEST_API_KEY" -H "Accept:application/json" -H "Content-Type:application/json" $TEST_URL -d '{"query": "query { foo }"}'
- name: Get the LocalStack logs
run: |
localstack logs | gzip > logs.txt.gz
- name: Upload the logs as an artifact
uses: actions/upload-artifact@v3
with:
name: ls-logs-${{ github.run_number }}
path: |
logs.txt.gz
retention-days: 7
11 changes: 11 additions & 0 deletions cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
*.js
!lib/function.js
!lib/pipeline.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
/outputs.json
6 changes: 6 additions & 0 deletions cdk/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
21 changes: 21 additions & 0 deletions cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkStack } from '../lib/cdk-stack';

const app = new cdk.App();
new CdkStack(app, 'CdkStack', {
/* If you don't specify 'env', this stack will be environment-agnostic.
* Account/Region-dependent features and context lookups will not work,
* but a single synthesized template can be deployed anywhere. */

/* Uncomment the next line to specialize this stack for the AWS Account
* and Region that are implied by the current CLI configuration. */
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },

/* Uncomment the next line if you know exactly what Account and Region you
* want to deploy the stack to. */
// env: { account: '123456789012', region: 'us-east-1' },

/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
});
65 changes: 65 additions & 0 deletions cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"app": "npx ts-node --prefer-ts-exts bin/cdk.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true
}
}
42 changes: 42 additions & 0 deletions cdk/lib/cdk-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as appsync from 'aws-cdk-lib/aws-appsync';
import path = require('path');

export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const api = new appsync.GraphqlApi(this, "Api", {
name: "myapi",
definition: appsync.Definition.fromFile(path.join(__dirname, "schema.graphql")),
});

const noneDataSource = api.addNoneDataSource("NoneDataSource");

const fn = new appsync.AppsyncFunction(this, "Fn", {
api,
name: "fn",
dataSource: noneDataSource,
runtime: appsync.FunctionRuntime.JS_1_0_0,
code: appsync.Code.fromAsset(path.join(__dirname, "function.js")),
});

new appsync.Resolver(this, "FooResolver", {
api,
typeName: "Query",
fieldName: "foo",
runtime: appsync.FunctionRuntime.JS_1_0_0,
code: appsync.Code.fromAsset(path.join(__dirname, "pipeline.js")),
pipelineConfig: [fn],
});

new cdk.CfnOutput(this, "GraphQLURL", {
value: api.graphqlUrl,
});

new cdk.CfnOutput(this, "ApiKey", {
value: api.apiKey || "",
});
}
}
15 changes: 15 additions & 0 deletions cdk/lib/function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { util } from '@aws-appsync/utils';
import { select } from '@aws-appsync/utils/rds';
import { get } from '@aws-appsync/utils/dynamodb';

export function request(ctx) {
return {
payload: {
value: "my-string",
},
};
}

export function response(ctx) {
return ctx.result;
}
9 changes: 9 additions & 0 deletions cdk/lib/pipeline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { util } from '@aws-appsync/utils';

export function request(ctx) {
return ctx;
}

export function response(ctx) {
return ctx.prev.result.value;
}
7 changes: 7 additions & 0 deletions cdk/lib/schema.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
schema {
query: Query
}

type Query {
foo: String!
}
Loading

0 comments on commit bc1d805

Please sign in to comment.