Skip to content

Commit

Permalink
feat: include all envs in mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
dpilch committed Feb 10, 2025
1 parent 0da6a3c commit 671ee13
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 115 deletions.
3 changes: 1 addition & 2 deletions packages/backend-data/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ export type DataProps = {
authorizationModes?: AuthorizationModes;
functions?: Record<string, ConstructFactory<AmplifyFunction>>;
logging?: DataLoggingOptions;
importedAmplifyDynamoDBTableMap?: Record<string, string>;
importedModels?: string[];
importedAmplifyDynamoDBTableMap?: Record<string, Record<string, string> | undefined>;
};

// @public
Expand Down
16 changes: 1 addition & 15 deletions packages/backend-data/src/convert_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,32 +249,18 @@ const customSqlStatementsFromStrategies = (
/**
* Extracts the imported models from non-imported models in a single string schema.
* @param schema String GraphQL schema
* @param importedModels Models that should be extracted.
* @param importedAmplifyDynamoDBTableMap Table names for the models that should be extracted.
* @returns a schema split into imported models and non-imported models
*/
export const extractImportedModels = (
schema: string,
importedModels: string[] | undefined,
importedAmplifyDynamoDBTableMap: Record<string, string> | undefined
): {
importedSchemas: { schema: string; importedTableName: string }[];
nonImportedSchema: string | undefined;
} => {
if (
importedAmplifyDynamoDBTableMap &&
Object.keys(importedAmplifyDynamoDBTableMap).length
) {
Object.keys(importedAmplifyDynamoDBTableMap).forEach((modelName) => {
if (!importedModels?.includes(modelName)) {
throw new Error(
`Imported table defined in importedAmplifyDynamoDBTableMap not found in importedModels list: ${modelName}`
);
}
});
}
const importedModels = Object.keys(importedAmplifyDynamoDBTableMap ?? {});
if (importedModels?.length) {
// TODO: maybe provide exported function from construct
const parsedSchema = parse(schema);
const [importedDefinitionNodes, nonImportedDefinitionNodes] = partition(
parsedSchema.definitions,
Expand Down
155 changes: 77 additions & 78 deletions packages/backend-data/src/factory.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { beforeEach, describe, it, mock } from 'node:test';
import assert from 'node:assert';
import { DataFactory, defineData } from './factory.js';
import { App, Stack } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import { Match, Template } from 'aws-cdk-lib/assertions';
import {
AmplifyFunction,
AuthResources,
Expand Down Expand Up @@ -56,6 +56,7 @@ const testSchema = /* GraphQL */ `

const createStackAndSetContext = (settings: {
isSandboxMode: boolean;
amplifyEnvironmentName?: string;
}): Stack => {
const app = new App();
app.node.setContext('amplify-backend-name', 'testEnvName');
Expand All @@ -64,6 +65,10 @@ const createStackAndSetContext = (settings: {
'amplify-backend-type',
settings.isSandboxMode ? 'sandbox' : 'branch'
);
app.node.setContext(
'amplifyEnvironmentName',
settings.amplifyEnvironmentName
);
const stack = new Stack(app);
return stack;
};
Expand Down Expand Up @@ -113,9 +118,11 @@ const createConstructContainerWithUserPoolAuthRegistered = (

const createInstancePropsBySetupCDKApp = (settings: {
isSandboxMode: boolean;
amplifyEnvironmentName?: string;
}): ConstructFactoryGetInstanceProps => {
const stack: Stack = createStackAndSetContext({
isSandboxMode: settings.isSandboxMode,
amplifyEnvironmentName: settings.amplifyEnvironmentName,
});
const constructContainer: ConstructContainer =
createConstructContainerWithUserPoolAuthRegistered(stack);
Expand Down Expand Up @@ -964,12 +971,14 @@ void describe('Table Import', () => {
const dataFactory = defineData({
schema,
importedAmplifyDynamoDBTableMap: {
ImportedModel: 'ImportedModel-1234-dev',
dev: {
ImportedModel: 'ImportedModel-1234-dev',
},
},
importedModels: ['ImportedModel'],
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
isSandboxMode: false,
amplifyEnvironmentName: 'dev',
});
const instance = dataFactory.getInstance(getInstanceProps);
const blogStack = Template.fromStack(
Expand All @@ -994,12 +1003,14 @@ void describe('Table Import', () => {
const dataFactory = defineData({
schema,
importedAmplifyDynamoDBTableMap: {
ImportedModel: 'ImportedModel-1234-dev',
dev: {
ImportedModel: 'ImportedModel-1234-dev',
},
},
importedModels: ['ImportedModel'],
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
isSandboxMode: false,
amplifyEnvironmentName: 'dev',
});
const instance = dataFactory.getInstance(getInstanceProps);
const importedModelStack = Template.fromStack(
Expand All @@ -1011,136 +1022,124 @@ void describe('Table Import', () => {
});
});

void it('fails when importedModels is not supplied', () => {
void it('fails when imported model is missing from the schema', () => {
const schema = /* GraphQL */ `
type Blog @model {
title: String
content: String
authors: [String]
}
type ImportedModel @model {
description: String
}
`;
const dataFactory = defineData({
schema,
importedAmplifyDynamoDBTableMap: {
ImportedModel: 'ImportedModel-1234-dev',
dev: {
ImportedModel: 'ImportedModel-1234-dev',
},
},
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
isSandboxMode: false,
amplifyEnvironmentName: 'dev',
});
assert.throws(() => dataFactory.getInstance(getInstanceProps), {
message:
'importedAmplifyDynamoDBTableMap is defined but importedModels is not defined.',
message: 'Imported model not found in schema: ImportedModel',
});
});

void it('fails when importedAmplifyDynamoDBTableMap is not supplied', () => {
void it('ignores other branches', () => {
const schema = /* GraphQL */ `
type Blog @model {
title: String
content: String
authors: [String]
}
type ImportedModel @model {
description: String
}
`;
const dataFactory = defineData({
schema,
importedModels: ['ImportedModel'],
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
});
assert.throws(() => dataFactory.getInstance(getInstanceProps), {
message:
'importedModels is defined but importedAmplifyDynamoDBTableMap is not defined.',
});
});

void it('fails when imported model is missing from the schema', () => {
const schema = /* GraphQL */ `
type Blog @model {
title: String
content: String
authors: [String]
}
`;
const dataFactory = defineData({
schema,
importedAmplifyDynamoDBTableMap: {
ImportedModel: 'ImportedModel-1234-dev',
dev: {
ImportedModel: 'ImportedModel-1234-dev',
},
prod: {
ImportedModel: 'ImportedModel-1234-prod',
},
},
importedModels: ['ImportedModel'],
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
isSandboxMode: false,
amplifyEnvironmentName: 'dev',
});
assert.throws(() => dataFactory.getInstance(getInstanceProps), {
message: 'Imported model not found in schema: ImportedModel',
const instance = dataFactory.getInstance(getInstanceProps);
const importedModelStack = Template.fromStack(
Stack.of(instance.resources.nestedStacks['ImportedModel'])
);
importedModelStack.hasResourceProperties(CUSTOM_IMPORTED_DDB_CFN_TYPE, {
isImported: true,
tableName: 'ImportedModel-1234-dev',
});
importedModelStack.hasResourceProperties(CUSTOM_IMPORTED_DDB_CFN_TYPE, {
tableName: Match.not('ImportedModel-1234-prod'),
});
});

void it('throws when model missing in imported models list', () => {
void it('uses sandbox key for sandbox mode', () => {
const schema = /* GraphQL */ `
type Blog @model {
title: String
content: String
authors: [String]
}
type ImportedModel @model {
description: String
}
type Foo @model {
description: String
}
`;
const dataFactory = defineData({
schema,
importedAmplifyDynamoDBTableMap: {
ImportedModel: 'ImportedModel-1234-dev',
Foo: 'Foo-1234-dev',
dev: {
ImportedModel: 'ImportedModel-1234-dev',
},
sandbox: {
ImportedModel: 'ImportedModel-1234-sandbox',
},
},
importedModels: ['Foo'],
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
});
assert.throws(() => dataFactory.getInstance(getInstanceProps), {
message:
'Imported table defined in importedAmplifyDynamoDBTableMap not found in importedModels list: ImportedModel',
const instance = dataFactory.getInstance(getInstanceProps);
const importedModelStack = Template.fromStack(
Stack.of(instance.resources.nestedStacks['ImportedModel'])
);
importedModelStack.hasResourceProperties(CUSTOM_IMPORTED_DDB_CFN_TYPE, {
isImported: true,
tableName: 'ImportedModel-1234-sandbox',
});
importedModelStack.hasResourceProperties(CUSTOM_IMPORTED_DDB_CFN_TYPE, {
tableName: Match.not('ImportedModel-1234-dev'),
});
});

void it('throws when model missing in imported models map', () => {
void it('ignores undefined branches', () => {
const schema = /* GraphQL */ `
type Blog @model {
title: String
content: String
authors: [String]
}
type ImportedModel @model {
description: String
}
`;
const dataFactory = defineData({
schema,
importedAmplifyDynamoDBTableMap: {},
importedModels: ['ImportedModel'],
importedAmplifyDynamoDBTableMap: {
dev: {
ImportedModel: 'ImportedModel-1234-dev',
},
prod: undefined,
},
});
const getInstanceProps = createInstancePropsBySetupCDKApp({
isSandboxMode: true,
isSandboxMode: false,
amplifyEnvironmentName: 'dev',
});
assert.throws(() => dataFactory.getInstance(getInstanceProps), {
message: 'No table found for imported model ImportedModel.',
const instance = dataFactory.getInstance(getInstanceProps);
const importedModelStack = Template.fromStack(
Stack.of(instance.resources.nestedStacks['ImportedModel'])
);
importedModelStack.hasResourceProperties(CUSTOM_IMPORTED_DDB_CFN_TYPE, {
isImported: true,
tableName: 'ImportedModel-1234-dev',
});
});
});
Expand Down
21 changes: 7 additions & 14 deletions packages/backend-data/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,6 @@ class DataGenerator implements ConstructContainerEntryGenerator {
private readonly outputStorageStrategy: BackendOutputStorageStrategy<GraphqlOutput>
) {
this.name = props.name ?? defaultName;
const { importedModels, importedAmplifyDynamoDBTableMap } = props;
if (importedAmplifyDynamoDBTableMap && !importedModels) {
throw new Error(
'importedAmplifyDynamoDBTableMap is defined but importedModels is not defined.'
);
}
if (!importedAmplifyDynamoDBTableMap && importedModels) {
throw new Error(
'importedModels is defined but importedAmplifyDynamoDBTableMap is not defined.'
);
}
// TODO: add importedModels validation
}

generateContainerEntry = ({
Expand All @@ -163,6 +151,12 @@ class DataGenerator implements ConstructContainerEntryGenerator {
? this.props.schema.schemas
: [this.props.schema];

// get the branch name and use the imported table map for that key
// use the sandbox key when the branch name is not available (e.g. in the sandbox deployment)
const amplifyBranchName =
scope.node.tryGetContext('amplifyEnvironmentName') ?? 'sandbox';
const importedTableMap = (this.props.importedAmplifyDynamoDBTableMap ??
{})[amplifyBranchName];
const splitSchemas: {
schema: string | DerivedModelSchema;
importedTableName?: string;
Expand All @@ -171,8 +165,7 @@ class DataGenerator implements ConstructContainerEntryGenerator {
if (!isDataSchema(schema)) {
const { importedSchemas, nonImportedSchema } = extractImportedModels(
schema,
this.props.importedModels,
this.props.importedAmplifyDynamoDBTableMap
importedTableMap
);
if (importedSchemas.length > 0) {
return [
Expand Down
10 changes: 4 additions & 6 deletions packages/backend-data/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,10 @@ export type DataProps = {
/**
* Mapping of model name to existing DynamoDB table that should be used as the data source.
*/
importedAmplifyDynamoDBTableMap?: Record<string, string>;

/**
* List of models that should be imported.
*/
importedModels?: string[];
importedAmplifyDynamoDBTableMap?: Record<
string,
Record<string, string> | undefined
>;
};

export type AmplifyDataError =
Expand Down

0 comments on commit 671ee13

Please sign in to comment.