Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add api id and amplify environment name to stash #2273

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
6 changes: 6 additions & 0 deletions .changeset/strong-toes-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@aws-amplify/backend-data': minor
'@aws-amplify/backend': minor
---

Add GraphQL API ID and Amplify environment name to custom JS resolver stash
50 changes: 49 additions & 1 deletion packages/backend-data/src/convert_js_resolvers.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Template } from 'aws-cdk-lib/assertions';
import { Match, Template } from 'aws-cdk-lib/assertions';
import assert from 'node:assert';
import { beforeEach, describe, it } from 'node:test';
import { App, Duration, Stack } from 'aws-cdk-lib';
Expand Down Expand Up @@ -158,4 +158,52 @@ void describe('convertJsResolverDefinition', () => {

template.resourceCountIs('AWS::AppSync::Resolver', 1);
});

void it('adds api id and environment name to stash', () => {
const absolutePath = resolve(
fileURLToPath(import.meta.url),
'../../lib/assets',
'js_resolver_handler.js'
);

const schema = a.schema({
customQuery: a
.query()
.authorization((allow) => allow.publicApiKey())
.returns(a.string())
.handler(
a.handler.custom({
entry: absolutePath,
})
),
});
const { jsFunctions } = schema.transform();
convertJsResolverDefinition(stack, amplifyApi, jsFunctions);

const template = Template.fromStack(stack);
template.hasResourceProperties('AWS::AppSync::Resolver', {
Runtime: {
Name: 'APPSYNC_JS',
RuntimeVersion: '1.0.0',
},
Kind: 'PIPELINE',
TypeName: 'Query',
FieldName: 'customQuery',
Code: {
'Fn::Join': [
'',
[
"\n /**\n * Pipeline resolver request handler\n */\n export const request = (ctx) => {\n ctx.stash.awsAppsyncApiId = '",
{
'Fn::GetAtt': [
Match.stringLikeRegexp('amplifyDataGraphQLAPI.*'),
'ApiId',
],
},
"';\n ctx.stash.awsAmplifyEnvironmentName = 'NONE';\n return {};\n };\n /**\n * Pipeline resolver response handler\n */\n export const response = (ctx) => {\n return ctx.prev.result;\n };\n ",
],
],
},
});
});
});
52 changes: 27 additions & 25 deletions packages/backend-data/src/convert_js_resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,12 @@ import { Construct } from 'constructs';
import { AmplifyData } from '@aws-amplify/data-construct';
import { CfnFunctionConfiguration, CfnResolver } from 'aws-cdk-lib/aws-appsync';
import { JsResolver } from '@aws-amplify/data-schema-types';
import { resolve } from 'path';
import { fileURLToPath } from 'node:url';
import { Asset } from 'aws-cdk-lib/aws-s3-assets';
import { resolveEntryPath } from './resolve_entry_path.js';

const APPSYNC_PIPELINE_RESOLVER = 'PIPELINE';
const APPSYNC_JS_RUNTIME_NAME = 'APPSYNC_JS';
const APPSYNC_JS_RUNTIME_VERSION = '1.0.0';
const JS_PIPELINE_RESOLVER_HANDLER = './assets/js_resolver_handler.js';
dpilch marked this conversation as resolved.
Show resolved Hide resolved

/**
*
* This returns the top-level passthrough resolver request/response handler (see: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html#anatomy-of-a-pipeline-resolver-js)
* It's required for defining a pipeline resolver. The only purpose it serves is returning the output of the last function in the pipeline back to the client.
*
* Customer-provided handlers are added as a Functions list in `pipelineConfig.functions`
*/
const defaultJsResolverAsset = (scope: Construct): Asset => {
const resolvedTemplatePath = resolve(
fileURLToPath(import.meta.url),
'../../lib',
JS_PIPELINE_RESOLVER_HANDLER
);

return new Asset(scope, 'default_js_resolver_handler_asset', {
path: resolveEntryPath(resolvedTemplatePath),
});
};

/**
* Converts JS Resolver definition emitted by data-schema into AppSync pipeline
Expand All @@ -44,8 +22,6 @@ export const convertJsResolverDefinition = (
return;
}

const jsResolverTemplateAsset = defaultJsResolverAsset(scope);

for (const resolver of jsResolvers) {
const functions: string[] = resolver.handlers.map((handler, idx) => {
const fnName = `Fn_${resolver.typeName}_${resolver.fieldName}_${idx + 1}`;
Expand All @@ -71,12 +47,38 @@ export const convertJsResolverDefinition = (

const resolverName = `Resolver_${resolver.typeName}_${resolver.fieldName}`;

const amplifyEnvironmentName =
scope.node.tryGetContext('amplifyEnvironmentName') ?? 'NONE';
new CfnResolver(scope, resolverName, {
apiId: amplifyApi.apiId,
fieldName: resolver.fieldName,
typeName: resolver.typeName,
kind: APPSYNC_PIPELINE_RESOLVER,
codeS3Location: jsResolverTemplateAsset.s3ObjectUrl,
/**
* The top-level passthrough resolver request/response handler (see: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-overview-js.html#anatomy-of-a-pipeline-resolver-js)
* It's required for defining a pipeline resolver. Adds the GraphQL API ID and Amplify environment name to the context stash.
* Returns the output of the last function in the pipeline back to the client.
*
* Customer-provided handlers are added as a Functions list in `pipelineConfig.functions`
*
* Uses synth-time inline code to avoid circular dependency when adding the API ID as an environment variable.
*/
code: `
/**
* Pipeline resolver request handler
*/
export const request = (ctx) => {
ctx.stash.awsAppsyncApiId = '${amplifyApi.apiId}';
ctx.stash.awsAmplifyEnvironmentName = '${amplifyEnvironmentName}';
return {};
};
/**
* Pipeline resolver response handler
*/
export const response = (ctx) => {
return ctx.prev.result;
};
`,
dpilch marked this conversation as resolved.
Show resolved Hide resolved
runtime: {
name: APPSYNC_JS_RUNTIME_NAME,
runtimeVersion: APPSYNC_JS_RUNTIME_VERSION,
Expand Down