Skip to content

Commit 767d53c

Browse files
committed
feat: Data client generation using SSM/S3 to provide the MIS to the runtime
1 parent f08abe4 commit 767d53c

File tree

10 files changed

+374
-147
lines changed

10 files changed

+374
-147
lines changed

package-lock.json

Lines changed: 281 additions & 133 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/backend-data/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
"@aws-amplify/backend-output-storage": "^1.1.3",
3232
"@aws-amplify/backend-output-schemas": "^1.4.0",
3333
"@aws-amplify/data-construct": "^1.10.1",
34-
"@aws-amplify/plugin-types": "^1.3.1",
35-
"@aws-amplify/data-schema-types": "^1.2.0"
34+
"@aws-amplify/data-schema-types": "^1.2.0",
35+
"@aws-amplify/graphql-generator": "^0.5.0",
36+
"@aws-amplify/plugin-types": "^1.3.1"
3637
}
3738
}

packages/backend-data/src/app_sync_policy_generator.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ export class AppSyncPolicyGenerator {
1414
/**
1515
* Initialize with the GraphqlAPI that the policies will be scoped to
1616
*/
17-
constructor(private readonly graphqlApi: IGraphqlApi) {
17+
constructor(
18+
private readonly graphqlApi: IGraphqlApi,
19+
private readonly modelIntrospectionSchemaArn?: string
20+
) {
1821
this.stack = Stack.of(graphqlApi);
1922
}
2023
/**
@@ -29,13 +32,25 @@ export class AppSyncPolicyGenerator {
2932
.map((action) => actionToTypeMap[action])
3033
// convert Type to resourceName
3134
.map((type) => [this.graphqlApi.arn, 'types', type, '*'].join('/'));
32-
return new Policy(this.stack, `${this.policyPrefix}${this.policyCount++}`, {
33-
statements: [
35+
36+
const statements = [
37+
new PolicyStatement({
38+
actions: ['appsync:GraphQL'],
39+
resources,
40+
}),
41+
];
42+
43+
if (this.modelIntrospectionSchemaArn) {
44+
statements.push(
3445
new PolicyStatement({
35-
actions: ['appsync:GraphQL'],
36-
resources,
37-
}),
38-
],
46+
actions: ['s3:GetObject'],
47+
resources: [this.modelIntrospectionSchemaArn],
48+
})
49+
);
50+
}
51+
52+
return new Policy(this.stack, `${this.policyPrefix}${this.policyCount++}`, {
53+
statements,
3954
});
4055
}
4156
}

packages/backend-data/src/factory.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
TranslationBehavior,
1717
} from '@aws-amplify/data-construct';
1818
import { GraphqlOutput } from '@aws-amplify/backend-output-schemas';
19+
import { generateModelsSync } from '@aws-amplify/graphql-generator';
1920
import * as path from 'path';
2021
import { AmplifyDataError, DataProps } from './types.js';
2122
import {
@@ -45,6 +46,10 @@ import {
4546
FunctionSchemaAccess,
4647
JsResolver,
4748
} from '@aws-amplify/data-schema-types';
49+
import { Bucket } from 'aws-cdk-lib/aws-s3';
50+
import { BucketDeployment, Source } from 'aws-cdk-lib/aws-s3-deployment';
51+
52+
const modelIntrospectionSchemaKey = 'modelIntrospectionSchema.json';
4853

4954
/**
5055
* Singleton factory for AmplifyGraphqlApi constructs that can be used in Amplify project files.
@@ -55,6 +60,8 @@ export class DataFactory implements ConstructFactory<AmplifyData> {
5560
// publicly accessible for testing purpose only.
5661
static factoryCount = 0;
5762

63+
readonly provides = 'DataResources';
64+
5865
private generator: ConstructContainerEntryGenerator;
5966

6067
/**
@@ -231,14 +238,21 @@ class DataGenerator implements ConstructContainerEntryGenerator {
231238
...schemasLambdaFunctions,
232239
});
233240
let amplifyApi = undefined;
241+
let modelIntrospectionSchema: string | undefined = undefined;
234242

235243
const isSandboxDeployment =
236244
scope.node.tryGetContext(CDKContextKey.DEPLOYMENT_TYPE) === 'sandbox';
237245

238246
try {
247+
const combinedSchema = combineCDKSchemas(amplifyGraphqlDefinitions);
248+
modelIntrospectionSchema = generateModelsSync({
249+
schema: combinedSchema.schema,
250+
target: 'introspection',
251+
})['model-introspection.json'];
252+
239253
amplifyApi = new AmplifyData(scope, this.name, {
240254
apiName: this.name,
241-
definition: combineCDKSchemas(amplifyGraphqlDefinitions),
255+
definition: combinedSchema,
242256
authorizationModes,
243257
outputStorageStrategy: this.outputStorageStrategy,
244258
functionNameMap,
@@ -263,6 +277,21 @@ class DataGenerator implements ConstructContainerEntryGenerator {
263277
);
264278
}
265279

280+
// TODO Any risk that this throws?
281+
const modelIntrospectionSchemaBucket = new Bucket(
282+
scope,
283+
'modelIntrospectionSchemaBucket',
284+
{ enforceSSL: true }
285+
);
286+
new BucketDeployment(scope, 'modelIntrospectionSchemaBucketDeployment', {
287+
// See https://github.com/aws-amplify/amplify-category-api/pull/1939
288+
memoryLimit: 1536,
289+
destinationBucket: modelIntrospectionSchemaBucket,
290+
sources: [
291+
Source.data(modelIntrospectionSchemaKey, modelIntrospectionSchema),
292+
],
293+
});
294+
266295
Tags.of(amplifyApi).add(TagName.FRIENDLY_NAME, this.name);
267296

268297
/**;
@@ -279,10 +308,15 @@ class DataGenerator implements ConstructContainerEntryGenerator {
279308
ssmEnvironmentEntriesGenerator.generateSsmEnvironmentEntries({
280309
[`${this.name}_GRAPHQL_ENDPOINT`]:
281310
amplifyApi.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl,
311+
[`${this.name}_MODEL_INTROSPECTION_SCHEMA_BUCKET_NAME`]:
312+
modelIntrospectionSchemaBucket.bucketName,
313+
[`${this.name}_MODEL_INTROSPECTION_SCHEMA_KEY`]:
314+
modelIntrospectionSchemaKey,
282315
});
283316

284317
const policyGenerator = new AppSyncPolicyGenerator(
285-
amplifyApi.resources.graphqlApi
318+
amplifyApi.resources.graphqlApi,
319+
`${modelIntrospectionSchemaBucket.bucketArn}/${modelIntrospectionSchemaKey}`
286320
);
287321

288322
schemasFunctionSchemaAccess.forEach((accessDefinition) => {

packages/backend-function/API.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export type FunctionProps = {
3838
schedule?: FunctionSchedule | FunctionSchedule[];
3939
layers?: Record<string, string>;
4040
bundling?: FunctionBundlingOptions;
41+
generateDataClientConfig?: boolean;
4142
};
4243

4344
// @public (undocumented)

packages/backend-function/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
"types": "./lib/index.d.ts",
1111
"import": "./lib/index.js",
1212
"require": "./lib/index.js"
13+
},
14+
"./internal": {
15+
"types": "./lib/internal/index.d.ts",
16+
"import": "./lib/internal/index.js",
17+
"require": "./lib/internal/index.js"
1318
}
1419
},
1520
"main": "lib/index.js",
@@ -27,6 +32,7 @@
2732
"devDependencies": {
2833
"@aws-amplify/backend-platform-test-stubs": "^0.3.6",
2934
"@aws-amplify/platform-core": "^1.1.0",
35+
"@aws-sdk/client-s3": "^3.624.0",
3036
"@aws-sdk/client-ssm": "^3.624.0",
3137
"aws-sdk": "^2.1550.0",
3238
"uuid": "^9.0.1"

packages/backend-function/src/factory.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { EOL } from 'os';
3737
import * as path from 'path';
3838
import { FunctionEnvironmentTranslator } from './function_env_translator.js';
3939
import { FunctionEnvironmentTypeGenerator } from './function_env_type_generator.js';
40+
import { FunctionClientConfigGenerator } from './function_client_config_generator.js';
4041
import { FunctionLayerArnParser } from './layer_parser.js';
4142
import { convertFunctionSchedulesToRuleSchedules } from './schedule_parser.js';
4243

@@ -145,6 +146,13 @@ export type FunctionProps = {
145146
* Options for bundling the function code.
146147
*/
147148
bundling?: FunctionBundlingOptions;
149+
150+
/*
151+
* Whether to generate a data client config for the function.
152+
*
153+
* Defaults to false.
154+
*/
155+
generateDataClientConfig?: boolean;
148156
};
149157

150158
export type FunctionBundlingOptions = {
@@ -177,6 +185,8 @@ class FunctionFactory implements ConstructFactory<AmplifyFunction> {
177185
outputStorageStrategy,
178186
resourceNameValidator,
179187
}: ConstructFactoryGetInstanceProps): AmplifyFunction => {
188+
// TODO this should be conditional on some signal,
189+
// for example that any access to data was granted for this function.
180190
if (!this.generator) {
181191
this.generator = new FunctionGenerator(
182192
this.hydrateDefaults(resourceNameValidator),
@@ -203,6 +213,7 @@ class FunctionFactory implements ConstructFactory<AmplifyFunction> {
203213
schedule: this.resolveSchedule(),
204214
bundling: this.resolveBundling(),
205215
layers,
216+
generateDataClientConfig: this.props.generateDataClientConfig ?? false,
206217
};
207218
};
208219

@@ -436,6 +447,12 @@ class AmplifyFunction
436447
// This will be overwritten with the typed file at the end of synthesis
437448
functionEnvironmentTypeGenerator.generateProcessEnvShim();
438449

450+
const functionClientConfigGenerator = new FunctionClientConfigGenerator(id);
451+
452+
if (props.generateDataClientConfig) {
453+
functionClientConfigGenerator.generateClientConfig();
454+
}
455+
439456
let functionLambda: NodejsFunction;
440457
try {
441458
functionLambda = new NodejsFunction(scope, `${id}-lambda`, {

packages/backend/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
},
1414
"./types/platform": {
1515
"types": "./lib/types/platform.d.ts"
16+
},
17+
"./internal": {
18+
"types": "./lib/internal/index.d.ts",
19+
"import": "./lib/internal/index.js",
20+
"require": "./lib/internal/index.js"
1621
}
1722
},
1823
"main": "lib/index.js",

packages/client-config/API.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ export type CustomClientConfig = {
627627
export const DEFAULT_CLIENT_CONFIG_VERSION: ClientConfigVersion;
628628

629629
// @public
630-
export const generateClientConfig: <T extends "1" | "1.1" | "1.2" | "1.3" | "0">(backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{
630+
export const generateClientConfig: <T extends "1.3" | "1.2" | "1.1" | "1" | "0">(backendIdentifier: DeployedBackendIdentifier, version: T, awsClientProvider?: AWSClientProvider<{
631631
getS3Client: S3Client;
632632
getAmplifyClient: AmplifyClient;
633633
getCloudFormationClient: CloudFormationClient;

packages/platform-core/API.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,12 @@ export const packageJsonSchema: z.ZodObject<{
166166
type: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"module">, z.ZodLiteral<"commonjs">]>>;
167167
}, "strip", z.ZodTypeAny, {
168168
name?: string | undefined;
169-
type?: "module" | "commonjs" | undefined;
170169
version?: string | undefined;
170+
type?: "module" | "commonjs" | undefined;
171171
}, {
172172
name?: string | undefined;
173-
type?: "module" | "commonjs" | undefined;
174173
version?: string | undefined;
174+
type?: "module" | "commonjs" | undefined;
175175
}>;
176176

177177
// @public

0 commit comments

Comments
 (0)