Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/backend-function/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
"@aws-amplify/backend-output-schemas": "^1.4.0",
"@aws-amplify/backend-output-storage": "^1.1.3",
"@aws-amplify/plugin-types": "^1.4.0",
"@aws-sdk/client-s3": "^3.624.0",
"execa": "^8.0.1"
},
"devDependencies": {
"@aws-amplify/backend-platform-test-stubs": "^0.3.6",
"@aws-amplify/platform-core": "^1.1.0",
"@aws-sdk/client-s3": "^3.624.0",
"@aws-sdk/client-ssm": "^3.624.0",
"aws-sdk": "^2.1550.0",
"uuid": "^9.0.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ const getResourceConfig = (
endpoint: env.AMPLIFY_DATA_GRAPHQL_ENDPOINT,
region: env.AWS_REGION,
defaultAuthMode: 'iam' as const,
modelIntrospection: modelIntrospectionSchema,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
modelIntrospection: modelIntrospectionSchema as any,
},
},
};
Expand All @@ -74,10 +75,15 @@ const getLibraryOptions = (env: DataClientEnv) => {
};
};

type InvalidConfig = unknown & {
invalidType: 'This function needs to be granted `authorization((allow) => [allow.resource(fcn)])` on the data schema.';
};

type DataClientError = {
resourceConfig: Record<string, unknown>;
libraryOptions: Record<string, unknown>;
resourceConfig: InvalidConfig;
libraryOptions: InvalidConfig;
};

type DataClientConfig = {
resourceConfig: ReturnType<typeof getResourceConfig>;
libraryOptions: ReturnType<typeof getLibraryOptions>;
Expand Down Expand Up @@ -114,6 +120,7 @@ export const getAmplifyClientsConfigurationRetriever = async <T>(
s3Client: S3Client
): Promise<DataClientReturn<T>> => {
if (!isDataClientEnv(env)) {
// TEST
return { resourceConfig: {}, libraryOptions: {} } as DataClientReturn<T>;
}
let modelIntrospectionSchema: object;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { defineDeploymentTest } from './deployment.test.template.js';
import { DataAndFunctionTestProjectCreator } from '../../test-project-setup/data_and_function_project.js';

defineDeploymentTest(new DataAndFunctionTestProjectCreator());
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { defineSandboxTest } from './sandbox.test.template.js';
import { DataAndFunctionTestProjectCreator } from '../../test-project-setup/data_and_function_project.js';

defineSandboxTest(new DataAndFunctionTestProjectCreator());
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { TestProjectBase } from './test_project_base.js';
import fs from 'fs/promises';
import { createEmptyAmplifyProject } from './create_empty_amplify_project.js';
import { CloudFormationClient } from '@aws-sdk/client-cloudformation';
import { TestProjectCreator } from './test_project_creator.js';
import { AmplifyClient } from '@aws-sdk/client-amplify';
import { BackendIdentifier } from '@aws-amplify/plugin-types';
import { LambdaClient } from '@aws-sdk/client-lambda';
import { DeployedResourcesFinder } from '../find_deployed_resource.js';
import { generateClientConfig } from '@aws-amplify/client-config';
import { CognitoIdentityProviderClient } from '@aws-sdk/client-cognito-identity-provider';
import { SemVer } from 'semver';
import crypto from 'node:crypto';
import {
ApolloClient,
ApolloLink,
HttpLink,
InMemoryCache,
} from '@apollo/client/core';
import { AUTH_TYPE, createAuthLink } from 'aws-appsync-auth-link';
import { gql } from 'graphql-tag';
import assert from 'assert';
import { NormalizedCacheObject } from '@apollo/client';
import { e2eToolingClientConfig } from '../e2e_tooling_client_config.js';

// TODO: this is a work around
// it seems like as of amplify v6 , some of the code only runs in the browser ...
// see https://github.com/aws-amplify/amplify-js/issues/12751
if (process.versions.node) {
// node >= 20 now exposes crypto by default. This workaround is not needed: https://github.com/nodejs/node/pull/42083
if (new SemVer(process.versions.node).major < 20) {
// @ts-expect-error altering typing for global to make compiler happy is not worth the effort assuming this is temporary workaround
globalThis.crypto = crypto;
}
}

/**
* Creates the data and function test project.
*/
export class DataAndFunctionTestProjectCreator implements TestProjectCreator {
readonly name = 'data-and-function';

/**
* Creates project creator.
*/
constructor(
private readonly cfnClient: CloudFormationClient = new CloudFormationClient(
e2eToolingClientConfig
),
private readonly amplifyClient: AmplifyClient = new AmplifyClient(
e2eToolingClientConfig
),
private readonly lambdaClient: LambdaClient = new LambdaClient(
e2eToolingClientConfig
),
private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient(
e2eToolingClientConfig
),
private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder()
) {}

createProject = async (e2eProjectDir: string): Promise<TestProjectBase> => {
const { projectName, projectRoot, projectAmplifyDir } =
await createEmptyAmplifyProject(this.name, e2eProjectDir);

const project = new DataAndFunctionTestProject(
projectName,
projectRoot,
projectAmplifyDir,
this.cfnClient,
this.amplifyClient,
this.lambdaClient,
this.cognitoIdentityProviderClient,
this.resourceFinder
);
await fs.cp(
project.sourceProjectAmplifyDirURL,
project.projectAmplifyDirPath,
{
recursive: true,
}
);
return project;
};
}

/**
* The data and function test project.
*/
class DataAndFunctionTestProject extends TestProjectBase {
readonly sourceProjectDirPath = '../../src/test-projects/data-and-function';

readonly sourceProjectAmplifyDirSuffix = `${this.sourceProjectDirPath}/amplify`;

readonly sourceProjectAmplifyDirURL: URL = new URL(
this.sourceProjectAmplifyDirSuffix,
import.meta.url
);

/**
* Create a test project instance.
*/
constructor(
name: string,
projectDirPath: string,
projectAmplifyDirPath: string,
cfnClient: CloudFormationClient,
amplifyClient: AmplifyClient,
private readonly lambdaClient: LambdaClient = new LambdaClient(
e2eToolingClientConfig
),
private readonly cognitoIdentityProviderClient: CognitoIdentityProviderClient = new CognitoIdentityProviderClient(
e2eToolingClientConfig
),
private readonly resourceFinder: DeployedResourcesFinder = new DeployedResourcesFinder()
) {
super(
name,
projectDirPath,
projectAmplifyDirPath,
cfnClient,
amplifyClient
);
}

override async assertPostDeployment(
backendId: BackendIdentifier
): Promise<void> {
await super.assertPostDeployment(backendId);

const clientConfig = await generateClientConfig(backendId, '1.1');
if (!clientConfig.data?.url) {
throw new Error('Data and function project must include data');
}
if (!clientConfig.data.api_key) {
throw new Error('Data and function project must include api_key');
}

// const dataUrl = clientConfig.data?.url;

const httpLink = new HttpLink({ uri: clientConfig.data.url });
const link = ApolloLink.from([
createAuthLink({
url: clientConfig.data.url,
region: clientConfig.data.aws_region,
auth: {
type: AUTH_TYPE.API_KEY,
apiKey: clientConfig.data.api_key,
},
}),
// see https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/473#issuecomment-543029072
httpLink,
]);
const apolloClient = new ApolloClient({
link,
cache: new InMemoryCache(),
});

await this.assertDataFunctionCallSucceeds(apolloClient);
}

private assertDataFunctionCallSucceeds = async (
apolloClient: ApolloClient<NormalizedCacheObject>
): Promise<void> => {
const response = await apolloClient.query<number>({
query: gql`
query todoCount {
todoCount
}
`,
variables: {},
});

assert.deepEqual(response.data, { todoCount: 0 });
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { defineBackend } from '@aws-amplify/backend';
import { data } from './data/resource.js';
import { todoCount } from './functions/todo-count/resource.js';

const backend = defineBackend({ data, todoCount });
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { a, ClientSchema, defineData } from '@aws-amplify/backend';
import { todoCount } from '../functions/todo-count/resource.js';

const schema = a
.schema({
Todo: a
.model({
title: a.string().required(),
done: a.boolean().default(false), // default value is false
})
.authorization((allow) => [allow.publicApiKey()]),
todoCount: a
.query()
.arguments({})
.returns(a.integer())
.handler(a.handler.function(todoCount))
.authorization((allow) => [allow.publicApiKey()]),
})
.authorization((allow) => [allow.resource(todoCount)]);

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'apiKey',
// API Key is used for a.allow.public() rules
apiKeyAuthorizationMode: {
expiresInDays: 30,
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Handler } from 'aws-lambda';
import { Amplify } from 'aws-amplify';
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../../data/resource.js';
// @ts-ignore
import {
resourceConfig,
libraryOptions,
} from '$amplify/data-config/todo-count';

Amplify.configure(resourceConfig, libraryOptions);

const client = generateClient<Schema>();

export const handler: Handler = async () => {
const todos = await client.models.Todo.list();
return todos.data.length;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineFunction } from '@aws-amplify/backend';

export const todoCount = defineFunction({
name: 'todo-count',
entry: './handler.ts',
timeoutSeconds: 30,
});
7 changes: 6 additions & 1 deletion packages/integration-tests/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,10 @@
{ "path": "../plugin-types" },
{ "path": "./src/test-projects/data-storage-auth-with-triggers-ts" }
],
"exclude": ["**/node_modules", "**/lib", "src/e2e-tests"]
"exclude": [
"**/node_modules",
"**/lib",
"src/e2e-tests",
"**/amplify/**/handler.ts"
]
}