Skip to content

Commit

Permalink
Merge branch 'main' into poc/e2e-test
Browse files Browse the repository at this point in the history
  • Loading branch information
0618 committed Oct 31, 2023
2 parents 185be00 + 4e48e4b commit a59cf5a
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 115 deletions.
7 changes: 7 additions & 0 deletions .changeset/proud-dolls-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@aws-amplify/integration-tests': patch
'create-amplify': patch
'@aws-amplify/backend': patch
---

chore: add new defineBackend to better align with other backend factories
5 changes: 5 additions & 0 deletions .changeset/thick-goats-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@aws-amplify/backend-deployer': patch
---

do not require confirmation at pipeline deploy
1 change: 1 addition & 0 deletions .github/workflows/health_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 60
needs:
- do_include_e2e
- build
Expand Down
12 changes: 9 additions & 3 deletions packages/backend-deployer/src/cdk_deployer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void describe('invokeCDKCommand', () => {
void it('handles options for branch deployments', async () => {
await invoker.deploy(uniqueBackendIdentifier);
assert.strictEqual(execaMock.mock.callCount(), 1);
assert.equal(execaMock.mock.calls[0].arguments[1]?.length, 12);
assert.equal(execaMock.mock.calls[0].arguments[1]?.length, 14);
assert.deepStrictEqual(execaMock.mock.calls[0].arguments[1], [
'cdk',
'deploy',
Expand All @@ -66,6 +66,8 @@ void describe('invokeCDKCommand', () => {
'backend-id=123',
'--context',
'branch-name=testBranch',
'--require-approval',
'never',
]);
});

Expand Down Expand Up @@ -161,7 +163,7 @@ void describe('invokeCDKCommand', () => {
'es2022',
'amplify/backend.ts',
]);
assert.equal(execaMock.mock.calls[1].arguments[1]?.length, 14);
assert.equal(execaMock.mock.calls[1].arguments[1]?.length, 16);
assert.deepStrictEqual(execaMock.mock.calls[1].arguments[1], [
'cdk',
'deploy',
Expand All @@ -175,6 +177,8 @@ void describe('invokeCDKCommand', () => {
'backend-id=123',
'--context',
'branch-name=testBranch',
'--require-approval',
'never',
'--context',
`${CDKContextKey.DEPLOYMENT_TYPE}=BRANCH`,
]);
Expand Down Expand Up @@ -226,7 +230,7 @@ void describe('invokeCDKCommand', () => {
validateAppSources: true,
});
assert.strictEqual(execaMock.mock.callCount(), 1);
assert.equal(execaMock.mock.calls[0].arguments[1]?.length, 14);
assert.equal(execaMock.mock.calls[0].arguments[1]?.length, 16);
assert.deepStrictEqual(execaMock.mock.calls[0].arguments[1], [
'cdk',
'deploy',
Expand All @@ -240,6 +244,8 @@ void describe('invokeCDKCommand', () => {
'backend-id=123',
'--context',
'branch-name=testBranch',
'--require-approval',
'never',
'--context',
'deployment-type=BRANCH',
]);
Expand Down
4 changes: 3 additions & 1 deletion packages/backend-deployer/src/cdk_deployer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ export class CDKDeployer implements BackendDeployer {
if (deploymentType !== BackendDeploymentType.SANDBOX) {
cdkCommandArgs.push(
'--context',
`branch-name=${uniqueBackendIdentifier.disambiguator}`
`branch-name=${uniqueBackendIdentifier.disambiguator}`,
'--require-approval',
'never'
);
}
}
Expand Down
8 changes: 5 additions & 3 deletions packages/backend/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,20 @@ import { Stack } from 'aws-cdk-lib';
export { a }

// @public
export class Backend<T extends Record<string, ConstructFactory<Construct>>> {
constructor(constructFactories: T, stack?: Stack);
export type Backend<T extends Record<string, ConstructFactory<Construct>>> = {
getOrCreateStack: (name: string) => Stack;
readonly resources: {
[K in keyof T]: ReturnType<T[K]['getInstance']>;
};
}
};

export { ClientSchema }

export { defineAuth }

// @public
export const defineBackend: <T extends Record<string, ConstructFactory<Construct>>>(constructFactories: T) => Backend<T>;

export { defineData }

export { defineStorage }
Expand Down
101 changes: 6 additions & 95 deletions packages/backend/src/backend.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,14 @@
import { Construct } from 'constructs';
import { ConstructFactory } from '@aws-amplify/plugin-types';
import { Construct } from 'constructs';
import { Stack } from 'aws-cdk-lib';
import {
NestedStackResolver,
StackResolver,
} from './engine/nested_stack_resolver.js';
import { SingletonConstructContainer } from './engine/singleton_construct_container.js';
import { ToggleableImportPathVerifier } from './engine/toggleable_import_path_verifier.js';
import {
AttributionMetadataStorage,
StackMetadataBackendOutputStorageStrategy,
} from '@aws-amplify/backend-output-storage';
import { createDefaultStack } from './default_stack_factory.js';
import { getUniqueBackendIdentifier } from './backend_identifier.js';
import {
BackendDeploymentType,
SandboxBackendIdentifier,
} from '@aws-amplify/platform-core';
import { stackOutputKey } from '@aws-amplify/backend-output-schemas';
import { fileURLToPath } from 'url';

// Be very careful editing this value. It is the value used in the BI metrics to attribute stacks as Amplify root stacks
const rootStackTypeIdentifier = 'root';

/**
* Class that collects and instantiates all the Amplify backend constructs
* Top level type for instance returned from `defineBackend`. Contains property `resources` for overriding
* Amplify generated resources and `getOrCreateStack()` for adding custom resources.
*/
export class Backend<T extends Record<string, ConstructFactory<Construct>>> {
private readonly stackResolver: StackResolver;
/**
* These are the resolved CDK constructs that are created by the inputs to the constructor
* Used for overriding properties of underlying CDK constructs or to reference in custom CDK code
*/
export type Backend<T extends Record<string, ConstructFactory<Construct>>> = {
getOrCreateStack: (name: string) => Stack;
readonly resources: {
[K in keyof T]: ReturnType<T[K]['getInstance']>;
};
/**
* Initialize an Amplify backend with the given construct factories and in the given CDK App.
* If no CDK App is specified a new one is created
*/
constructor(constructFactories: T, stack: Stack = createDefaultStack()) {
new AttributionMetadataStorage().storeAttributionMetadata(
stack,
rootStackTypeIdentifier,
fileURLToPath(new URL('../package.json', import.meta.url))
);
this.stackResolver = new NestedStackResolver(stack);

const constructContainer = new SingletonConstructContainer(
this.stackResolver
);

const outputStorageStrategy = new StackMetadataBackendOutputStorageStrategy(
stack
);

const uniqueBackendIdentifier = getUniqueBackendIdentifier(stack);
outputStorageStrategy.addBackendOutputEntry(stackOutputKey, {
version: '1',
payload: {
deploymentType:
uniqueBackendIdentifier instanceof SandboxBackendIdentifier
? BackendDeploymentType.SANDBOX
: BackendDeploymentType.BRANCH,
},
});

const importPathVerifier = new ToggleableImportPathVerifier();

// register providers but don't actually execute anything yet
Object.values(constructFactories).forEach((factory) => {
if (typeof factory.provides === 'string') {
constructContainer.registerConstructFactory(factory.provides, factory);
}
});

// now invoke all the factories and collect the constructs into this.resources
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.resources = {} as any;
Object.entries(constructFactories).forEach(
([resourceName, constructFactory]) => {
// The type inference on this.resources is not happy about this assignment because it doesn't know the exact type of .getInstance()
// However, the assignment is okay because we are iterating over the entries of constructFactories and assigning the resource name to the corresponding instance
this.resources[resourceName as keyof T] = constructFactory.getInstance(
{
constructContainer,
outputStorageStrategy,
importPathVerifier,
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) as any;
}
);
}

/**
* Returns a CDK stack within the Amplify project that can be used for creating custom resources
*/
getOrCreateStack = (name: string): Stack => {
return this.stackResolver.getStackFor(name);
};
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { beforeEach, describe, it } from 'node:test';
import { ConstructFactory } from '@aws-amplify/plugin-types';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import { Backend } from './backend.js';
import { BackendFactory } from './backend_factory.js';
import { App, Stack } from 'aws-cdk-lib';
import { Template } from 'aws-cdk-lib/assertions';
import assert from 'node:assert';
Expand Down Expand Up @@ -41,7 +41,7 @@ void describe('Backend', () => {
},
};

new Backend(
new BackendFactory(
{
testConstructFactory,
},
Expand Down Expand Up @@ -76,7 +76,7 @@ void describe('Backend', () => {
},
};

new Backend(
new BackendFactory(
{
testConstructFactory,
},
Expand Down Expand Up @@ -116,7 +116,7 @@ void describe('Backend', () => {
},
};

const backend = new Backend(
const backend = new BackendFactory(
{
testConstructFactory,
},
Expand All @@ -126,7 +126,7 @@ void describe('Backend', () => {
});

void it('stores attribution metadata in root stack', () => {
new Backend({}, rootStack);
new BackendFactory({}, rootStack);
const rootStackTemplate = Template.fromStack(rootStack);
assert.equal(
JSON.parse(rootStackTemplate.toJSON().Description).stackType,
Expand All @@ -136,7 +136,7 @@ void describe('Backend', () => {

void describe('getOrCreateStack', () => {
void it('returns nested stack', () => {
const backend = new Backend({}, rootStack);
const backend = new BackendFactory({}, rootStack);
const testStack = backend.getOrCreateStack('testStack');
assert.strictEqual(rootStack.node.findChild('testStack'), testStack);
});
Expand Down
117 changes: 117 additions & 0 deletions packages/backend/src/backend_factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { Construct } from 'constructs';
import { ConstructFactory } from '@aws-amplify/plugin-types';
import { Stack } from 'aws-cdk-lib';
import {
NestedStackResolver,
StackResolver,
} from './engine/nested_stack_resolver.js';
import { SingletonConstructContainer } from './engine/singleton_construct_container.js';
import { ToggleableImportPathVerifier } from './engine/toggleable_import_path_verifier.js';
import {
AttributionMetadataStorage,
StackMetadataBackendOutputStorageStrategy,
} from '@aws-amplify/backend-output-storage';
import { createDefaultStack } from './default_stack_factory.js';
import { getUniqueBackendIdentifier } from './backend_identifier.js';
import {
BackendDeploymentType,
SandboxBackendIdentifier,
} from '@aws-amplify/platform-core';
import { stackOutputKey } from '@aws-amplify/backend-output-schemas';
import { fileURLToPath } from 'url';
import { Backend } from './backend.js';

// Be very careful editing this value. It is the value used in the BI metrics to attribute stacks as Amplify root stacks
const rootStackTypeIdentifier = 'root';

/**
* Factory that collects and instantiates all the Amplify backend constructs
*/
export class BackendFactory<
T extends Record<string, ConstructFactory<Construct>>
> implements Backend<T>
{
private readonly stackResolver: StackResolver;
/**
* These are the resolved CDK constructs that are created by the inputs to the constructor
* Used for overriding properties of underlying CDK constructs or to reference in custom CDK code
*/
readonly resources: {
[K in keyof T]: ReturnType<T[K]['getInstance']>;
};
/**
* Initialize an Amplify backend with the given construct factories and in the given CDK App.
* If no CDK App is specified a new one is created
*/
constructor(constructFactories: T, stack: Stack = createDefaultStack()) {
new AttributionMetadataStorage().storeAttributionMetadata(
stack,
rootStackTypeIdentifier,
fileURLToPath(new URL('../package.json', import.meta.url))
);
this.stackResolver = new NestedStackResolver(stack);

const constructContainer = new SingletonConstructContainer(
this.stackResolver
);

const outputStorageStrategy = new StackMetadataBackendOutputStorageStrategy(
stack
);

const uniqueBackendIdentifier = getUniqueBackendIdentifier(stack);
outputStorageStrategy.addBackendOutputEntry(stackOutputKey, {
version: '1',
payload: {
deploymentType:
uniqueBackendIdentifier instanceof SandboxBackendIdentifier
? BackendDeploymentType.SANDBOX
: BackendDeploymentType.BRANCH,
},
});

const importPathVerifier = new ToggleableImportPathVerifier();

// register providers but don't actually execute anything yet
Object.values(constructFactories).forEach((factory) => {
if (typeof factory.provides === 'string') {
constructContainer.registerConstructFactory(factory.provides, factory);
}
});

// now invoke all the factories and collect the constructs into this.resources
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.resources = {} as any;
Object.entries(constructFactories).forEach(
([resourceName, constructFactory]) => {
// The type inference on this.resources is not happy about this assignment because it doesn't know the exact type of .getInstance()
// However, the assignment is okay because we are iterating over the entries of constructFactories and assigning the resource name to the corresponding instance
this.resources[resourceName as keyof T] = constructFactory.getInstance(
{
constructContainer,
outputStorageStrategy,
importPathVerifier,
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
) as any;
}
);
}

/**
* Returns a CDK stack within the Amplify project that can be used for creating custom resources
*/
getOrCreateStack = (name: string): Stack => {
return this.stackResolver.getStackFor(name);
};
}

/**
* Creates a new Amplify backend instance and returns it
* @param constructFactories - list of backend factories such as those created by `defineAuth` or `defineData`
*/
export const defineBackend = <
T extends Record<string, ConstructFactory<Construct>>
>(
constructFactories: T
): Backend<T> => new BackendFactory(constructFactories);
1 change: 1 addition & 0 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { defineBackend } from './backend_factory.js';
export * from './backend.js';
export * from './secret.js';

Expand Down
Loading

0 comments on commit a59cf5a

Please sign in to comment.