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/Support-apiId(Multiple-cloudformation-stacks) #3

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
9426060
apiId
HumbleBeck Apr 26, 2023
3c5c8f5
update snapshots
HumbleBeck Apr 26, 2023
489896d
cleanup
HumbleBeck Apr 26, 2023
5c699d1
drop depends on schema if apiId is provided
HumbleBeck Apr 27, 2023
de3280d
Merge branch 'master' into feat/Support-apiId(Multiple-cloudformation…
plezan Nov 15, 2024
5626a0d
fix: missing type import
plezan Nov 15, 2024
440dedf
WIP
plezan Nov 15, 2024
8ba627b
wip
Nov 15, 2024
3c38de7
wip
Nov 15, 2024
32c9e07
wip
Nov 15, 2024
b4f131d
Added checks wherethe naming module is used
Nov 18, 2024
f385b73
Added typegards in tests without changing their functionality
Nov 18, 2024
38e039d
Handle SharedAppsyncConfig in getAppSyncConfig
Nov 18, 2024
6cc691b
clean some todos
Nov 18, 2024
fe2061d
Updated types and validation
plezan Nov 18, 2024
9a87a18
fix circular definition
plezan Nov 18, 2024
6bfb87e
fix tests
plezan Nov 18, 2024
6374e63
cleanup
plezan Nov 18, 2024
a203d3a
build tsconf for a newer version
Nov 19, 2024
74e5737
build to module
Nov 19, 2024
531daeb
switched from commonjs to esmodule for sls v4
Nov 19, 2024
25ae992
better errors handeling
Nov 19, 2024
78abeb6
better handeling of disabled functionalities for shared config
Nov 19, 2024
aa51d57
Display service output section
Nov 19, 2024
35497cb
cleanup
Nov 19, 2024
cbdc2d8
cleanup
Nov 19, 2024
6396da3
cleanup
Nov 19, 2024
6cbcf2e
added loggingfor schema generation
Nov 20, 2024
4109ae3
TODO
plezan Nov 27, 2024
976992d
feat/handle datasources from other stacks
plezan Nov 28, 2024
14a4602
Handle Substitutions
plezan Nov 29, 2024
5b70750
Handle pipelines functions on child stack
plezan Nov 29, 2024
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
31 changes: 31 additions & 0 deletions doc/general-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ appSync:
- `resolverCountLimit`: Optional. The maximum number of resolvers a query can process. Must be between 1 and 1000. If not specified: unlimited.
- `tags`: A key-value pair for tagging this AppSync API
- `esbuild`: Custom esbuild options, or `false` See [Esbuild](#Esbuild)
- `apiId`: See [ApiId](#ApiId)

## Schema

Expand Down Expand Up @@ -210,3 +211,33 @@ appSync:
target: 'es2020',
sourcemap: false
```


## ApiId
If you want to manage your existing AppSync Api through the serverless, you can specify `apiId.`
This is handy if you
- defined your API in the AWS console
- defined your API through the cloudformation in the current or another stack

To point your resources into existing AppSync API, you must provide apiId, which can be a string or imported value from another stack.
```yaml
appSync:
name: my-api
apiId: "existing api id"
```

The following configuration options are only associated with the creation of a new AppSync endpoint and will be ignored if you provide the apiId parameter:
- name
- authentication
- additionalAuthentications
- schema
- domain
- apiKeys
- xrayEnabled
- logging
- waf
- Tags
> Note: you should never specify this parameter if you're managing your AppSync through this plugin since it results in removing your API.

### Schema
After specifying this parameter, you need to manually keep your schema up to date or from the main stack where your root AppSync API is defined. The plugin is not taking into account schema property due to AppSync limitation and inability to merge schemas across multiple stacks
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/sid88in/serverless-appsync-plugin/pull/597/files#r1223263789

This whole section is confusing and probably not necessary.

Instead, we should add clear guidelines on how and why you should use the apiId parameters. A bit like described here for API Gateway.

12 changes: 11 additions & 1 deletion package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "serverless-appsync-plugin",
"version": "0.0.0-development",
"type": "module",
"description": "AWS AppSync support for the Serverless Framework",
"main": "lib/index.js",
"types": "lib/types/index.d.ts",
Expand Down Expand Up @@ -48,7 +49,7 @@
"esbuild": "^0.17.11",
"globby": "^11.1.0",
"graphql": "^16.6.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"luxon": "^2.5.0",
"open": "^8.4.0",
"terminal-link": "^2.1.1"
Expand Down
52 changes: 52 additions & 0 deletions src/__tests__/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ describe('Api', () => {
expect(api.compileEndpoint()).toMatchSnapshot();
expect(api.functions).toMatchSnapshot();
});
it('should not compile the Api Resource when apiId is provided', () => {
const api = new Api(given.appSyncConfig({ apiId: '123' }), plugin);
expect(api.compileEndpoint()).toMatchInlineSnapshot(`Object {}`);
});
});

describe('Logs', () => {
Expand All @@ -342,6 +346,13 @@ describe('Api', () => {
);
});

it('should not compile CloudWatch Resources when apiId is provided', () => {
const api = new Api(given.appSyncConfig({ apiId: '1234' }), plugin);
expect(api.compileCloudWatchLogGroup()).toMatchInlineSnapshot(
`Object {}`,
);
});

it('should compile CloudWatch Resources when enaabled', () => {
const api = new Api(
given.appSyncConfig({
Expand Down Expand Up @@ -568,6 +579,20 @@ describe('Api', () => {
}
`);
});
it('should not generate an api key resources when apiId is provided', () => {
const api = new Api(
given.appSyncConfig({
apiId: '1234',
}),
plugin,
);
expect(
api.compileApiKey({
name: 'Default',
description: 'Default Key',
}),
).toMatchInlineSnapshot(`Object {}`);
});
});

describe('LambdaAuthorizer', () => {
Expand All @@ -585,6 +610,18 @@ describe('Api', () => {
);
});

it('should not generate the Lambda Authorizer Resources when apiId is provided', () => {
const api = new Api(
given.appSyncConfig({
apiId: '123',
}),
plugin,
);
expect(api.compileLambdaAuthorizerPermission()).toMatchInlineSnapshot(
`Object {}`,
);
});

it('should generate the Lambda Authorizer Resources from basic auth', () => {
const api = new Api(
given.appSyncConfig({
Expand Down Expand Up @@ -663,6 +700,11 @@ describe('Caching', () => {
expect(api.compileCachingResources()).toEqual({});
});

it('should not generate Resources when apiId is provided', () => {
const api = new Api(given.appSyncConfig({ apiId: '1234' }), plugin);
expect(api.compileCachingResources()).toEqual({});
});

it('should generate Resources with defaults', () => {
const api = new Api(
given.appSyncConfig({
Expand Down Expand Up @@ -826,4 +868,14 @@ describe('Domains', () => {
);
expect(api.compileCustomDomain()).toMatchSnapshot();
});

it('should not generate domain resources when apiId is provided', () => {
const api = new Api(
given.appSyncConfig({
apiId: '123',
}),
plugin,
);
expect(api.compileCustomDomain()).toMatchInlineSnapshot(`Object {}`);
});
});
2 changes: 1 addition & 1 deletion src/__tests__/basicConfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppSyncConfig } from '../types';
import { AppSyncConfig } from '../types/index.js';

export const basicConfig: AppSyncConfig = {
name: 'My Api',
Expand Down
82 changes: 44 additions & 38 deletions src/__tests__/getAppSyncConfig.test.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,67 @@
import { pick } from 'lodash';
import { getAppSyncConfig } from '../getAppSyncConfig';
import { basicConfig } from './basicConfig';
import { ResolverConfig } from '../types';
import { pick } from 'lodash-es';
import { getAppSyncConfig } from '../getAppSyncConfig.js';
import { basicConfig } from './basicConfig.js';
import { ResolverConfig } from '../types/index.js';

test('returns basic config', async () => {
expect(getAppSyncConfig(basicConfig)).toMatchSnapshot();
});

describe('Schema', () => {
it('should return the default schema', () => {
expect(
getAppSyncConfig({ ...basicConfig, schema: undefined }).schema,
).toMatchSnapshot();
const config = getAppSyncConfig({ ...basicConfig, schema: undefined });
const schema = 'schema' in config ? config.schema : undefined;
expect(schema).toMatchSnapshot();
});

it('should return a single schema as an array', () => {
expect(
getAppSyncConfig({ ...basicConfig, schema: 'mySchema.graphql' }).schema,
).toMatchSnapshot();
const config = getAppSyncConfig({
...basicConfig,
schema: 'mySchema.graphql',
});
const schema = 'schema' in config ? config.schema : undefined;
expect(schema).toMatchSnapshot();
});

it('should return a schema array unchanged', () => {
expect(
getAppSyncConfig({
...basicConfig,
schema: ['users.graphql', 'posts.graphql'],
}).schema,
).toMatchSnapshot();
const config = getAppSyncConfig({
...basicConfig,
schema: ['users.graphql', 'posts.graphql'],
});
const schema = 'schema' in config ? config.schema : undefined;
expect(schema).toMatchSnapshot();
});
});

describe('Api Keys', () => {
it('should not generate a default Api Key when auth is not API_KEY', () => {
expect(
getAppSyncConfig({ ...basicConfig, authentication: { type: 'AWS_IAM' } })
.apiKeys,
).toBeUndefined();
const config = getAppSyncConfig({
...basicConfig,
authentication: { type: 'AWS_IAM' },
});
const apiKeys = 'apiKeys' in config ? config.apiKeys : undefined;
expect(apiKeys).toBeUndefined();
});

it('should generate api keys', () => {
expect(
getAppSyncConfig({
...basicConfig,
apiKeys: [
{
name: 'John',
description: "John's key",
expiresAt: '2021-03-09T16:00:00+00:00',
},
{
name: 'Jane',
expiresAfter: '1y',
},
'InlineKey',
],
}).apiKeys,
).toMatchInlineSnapshot(`
const config = getAppSyncConfig({
...basicConfig,
apiKeys: [
{
name: 'John',
description: "John's key",
expiresAt: '2021-03-09T16:00:00+00:00',
},
{
name: 'Jane',
expiresAfter: '1y',
},
'InlineKey',
],
});
const apiKeys = 'apiKeys' in config ? config.apiKeys : undefined;

expect(apiKeys).toMatchInlineSnapshot(`
Object {
"InlineKey": Object {
"name": "InlineKey",
Expand Down Expand Up @@ -229,7 +235,7 @@ describe('Resolvers', () => {
field: 'getUsers',
},
},
] as Record<string, ResolverConfig>[],
] satisfies Record<string, ResolverConfig>[],
});
expect(config.resolvers).toMatchSnapshot();
});
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/given.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { set } from 'lodash';
import { set } from 'lodash-es';
import Serverless from 'serverless/lib/serverless';
import AwsProvider from 'serverless/lib/plugins/aws/provider.js';
import { AppSyncConfig } from '../types/plugin';
import { AppSyncConfig } from '../types/plugin.js';
import ServerlessAppsyncPlugin from '..';

export const createServerless = (): Serverless => {
Expand Down
11 changes: 11 additions & 0 deletions src/__tests__/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ describe('schema', () => {
`);
});

it('should generate a schema resource if apiId is provided', () => {
const api = new Api(
given.appSyncConfig({
apiId: '123',
}),
plugin,
);

expect(api.compileSchema()).toMatchInlineSnapshot(`Object {}`);
});

it('should merge the schemas', () => {
const api = new Api(given.appSyncConfig(), plugin);
const schema = new Schema(api, [
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import runServerlessFixtureEngine from '@serverless/test/setup-run-serverless-fixtures-engine';
import { merge } from 'lodash';
import { merge } from 'lodash-es';
import path from 'path';
import Serverless from 'serverless/lib/Serverless';
import Serverless from 'serverless';

type RunSlsOptions = {
fixture: 'appsync';
Expand Down
5 changes: 3 additions & 2 deletions src/__tests__/validation/__snapshots__/base.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ exports[`Valdiation Waf Invalid should validate a Throttle limit 1`] = `
`;

exports[`Valdiation should validate 1`] = `
": must have required property 'name'
: must have required property 'authentication'
": must have required property 'authentication'
/unknownPorp: invalid (unknown) property
/xrayEnabled: must be boolean
/visibility: must be \\"GLOBAL\\" or \\"PRIVATE\\"
Expand All @@ -68,3 +67,5 @@ exports[`Valdiation should validate 2`] = `
"/queryDepthLimit: must be <= 75
/resolverCountLimit: must be <= 1000"
`;

exports[`Valdiation should validate 3`] = `"Invalid configuration: must contain either \\\"apiId\\\" or \\\"name\\\""`;
16 changes: 15 additions & 1 deletion src/__tests__/validation/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('Valdiation', () => {

expect(function () {
validateConfig({
name: 'FOO',
visibility: 'FOO',
introspection: 10,
queryDepthLimit: 'foo',
Expand All @@ -47,6 +48,19 @@ describe('Valdiation', () => {
resolverCountLimit: 1001,
});
}).toThrowErrorMatchingSnapshot();

expect(function () {
validateConfig({
visibility: 'FOO',
introspection: 10,
queryDepthLimit: 'foo',
resolverCountLimit: 'bar',
xrayEnabled: 'BAR',
unknownPorp: 'foo',
esbuild: 'bad',
environment: 'Bad',
});
}).toThrowErrorMatchingSnapshot();
});

describe('Log', () => {
Expand All @@ -69,7 +83,7 @@ describe('Valdiation', () => {
level: 'ALL',
retentionInDays: 14,
excludeVerboseContent: true,
loggingRoleArn: { Ref: 'MyLogGorupArn' },
// loggingRoleArn: { Ref: 'MyLogGorupArn' }, // TODO : why was it only in the tests ?
},
} as AppSyncConfig,
},
Expand Down
Loading