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: generate api code #269

Merged
merged 6 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
7 changes: 7 additions & 0 deletions .changeset/fuzzy-socks-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@aws-amplify/model-generator': minor
---

Add wrapper to around types, documents, and model generation (`generateAPICode`).

Change `createGraphqlDocumentGenerator` and `createGraphqlTypesGenerator` to use backendIdentifier and credentialProvider.
3 changes: 3 additions & 0 deletions .eslint_dictionary.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ export default [
'inheritdoc',
'invokable',
'invoker',
'javascript',
'jsdoc',
'lang',
'lsof',
'lstat',
'mfas',
'mkdtemp',
'modelgen',
'multifactor',
'nodejs',
'npmrc',
Expand All @@ -65,6 +67,7 @@ export default [
'repo',
'resolvers',
'saml',
'scala',
'schema',
'schemas',
'searchable',
Expand Down
2 changes: 2 additions & 0 deletions package-lock.json

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

58 changes: 56 additions & 2 deletions packages/model-generator/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,62 @@

```ts

import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { BackendIdentifier } from '@aws-amplify/deployed-backend-client';
import { ModelsTarget } from '@aws-amplify/graphql-generator';
import { StatementsTarget } from '@aws-amplify/graphql-generator';
import { TypesTarget } from '@aws-amplify/graphql-generator';

// @public
export const createGraphqlDocumentGenerator: ({ apiId, }: GraphqlDocumentGeneratorFactoryParams) => GraphqlDocumentGenerator;
export const createGraphqlDocumentGenerator: ({ backendIdentifier, credentialProvider, }: GraphqlDocumentGeneratorFactoryParams) => GraphqlDocumentGenerator;

// @public (undocumented)
export type DocumentGenerationParameters = {
language: TargetLanguage;
maxDepth?: number;
typenameIntrospection?: boolean;
dpilch marked this conversation as resolved.
Show resolved Hide resolved
};

// @public
export const generateApiCode: (props: GenerateApiCodeProps) => Promise<GenerationResult>;

// @public (undocumented)
export type GenerateApiCodeProps = GenerateOptions & BackendIdentifier & {
credentialProvider: AwsCredentialIdentityProvider;
};
Comment on lines +23 to +29
Copy link
Member

Choose a reason for hiding this comment

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

One more suggestion.
Rest of APIs are explicit about Graphql
Should we use generateGraphqlApiCode (and similar in props/return) ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Rene wanted customer facing APIs to use "API" over "Graphql". We'll be syncing with him on naming next week to finalize.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, we'll be able to close on this by EOD Monday, but I wouldn't make it block this, since we want this in for the pentest ready date, which is also monday (easy to change)

Copy link
Member

Choose a reason for hiding this comment

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

ok

Copy link
Member

Choose a reason for hiding this comment

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

Also. Then
If this is customer facing I suggest to consider moving other types to namespaces.

So that import * as generator from '@aws-amplify/model-generator' does not cause explosion in IDE when customer types generator. .

For example other types can be moved to @aws-amplify/model-generator/documents @aws-amplify/model-generator/types what not. We do that in other packages that are customer facing like here:
https://github.com/aws-amplify/samsara-cli/blob/4fd18b126ba075d054ddc4b34859e67e519355e5/packages/client-config/package.json#L8-L18


// @public (undocumented)
export type GenerateGraphqlCodegenOptions = {
format: 'graphql-codegen';
statementTarget: 'javascript' | 'graphql' | 'flow' | 'typescript' | 'angular';
maxDepth?: number;
typeNameIntrospection?: boolean;
typeTarget?: 'json' | 'swift' | 'typescript' | 'flow' | 'scala' | 'flow-modern' | 'angular';
multipleSwiftFiles?: boolean;
};

// @public (undocumented)
export type GenerateIntrospectionOptions = {
format: 'introspection';
};

// @public (undocumented)
export type GenerateModelsOptions = {
format: 'modelgen';
modelTarget: 'java' | 'swift' | 'javascript' | 'typescript' | 'dart';
generateIndexRules?: boolean;
emitAuthProvider?: boolean;
useExperimentalPipelinedTransformer?: boolean;
transformerVersion?: boolean;
respectPrimaryKeyAttributesOnConnectionField?: boolean;
generateModelsForLazyLoadAndCustomSelectionSet?: boolean;
addTimestampFields?: boolean;
handleListNullabilityTransparently?: boolean;
};

// @public (undocumented)
export type GenerateOptions = GenerateGraphqlCodegenOptions | GenerateModelsOptions | GenerateIntrospectionOptions;

// @public (undocumented)
export type GenerationResult = {
writeToDirectory: (directoryPath: string) => Promise<void>;
Expand All @@ -28,7 +72,8 @@ export type GraphqlDocumentGenerator = {

// @public (undocumented)
export type GraphqlDocumentGeneratorFactoryParams = {
apiId: string;
backendIdentifier: BackendIdentifier;
credentialProvider: AwsCredentialIdentityProvider;
};

// @public (undocumented)
Expand All @@ -44,6 +89,14 @@ export type GraphqlTypesGenerator = {
// @public (undocumented)
export type ModelsGenerationParameters = {
target: ModelsTarget;
generateIndexRules?: boolean;
emitAuthProvider?: boolean;
useExperimentalPipelinedTransformer?: boolean;
transformerVersion?: boolean;
respectPrimaryKeyAttributesOnConnectionField?: boolean;
generateModelsForLazyLoadAndCustomSelectionSet?: boolean;
addTimestampFields?: boolean;
handleListNullabilityTransparently?: boolean;
};

// @public (undocumented)
Expand All @@ -52,6 +105,7 @@ export type TargetLanguage = StatementsTarget;
// @public (undocumented)
export type TypesGenerationParameters = {
target: TypesTarget;
multipleSwiftFiles?: boolean;
};

// (No @packageDocumentation comment for this package)
Expand Down
1 change: 1 addition & 0 deletions packages/model-generator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@aws-amplify/graphql-generator": "^0.1.0",
"@aws-sdk/client-appsync": "^3.398.0",
"@aws-sdk/client-s3": "^3.414.0",
"@aws-sdk/credential-providers": "^3.414.0",
"@aws-sdk/types": "^3.413.0"
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import assert from 'node:assert';
import { describe, it } from 'node:test';
import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { BackendIdentifier } from '@aws-amplify/deployed-backend-client';
import { createGraphqlDocumentGenerator } from './create_graphql_document_generator.js';

describe('model generator factory', () => {
it('throws an error if a null apiId is passed in', async () => {
it('throws an error if a null backendIdentifier is passed in', async () => {
assert.throws(() =>
createGraphqlDocumentGenerator({ apiId: null as unknown as string })
createGraphqlDocumentGenerator({
backendIdentifier: null as unknown as BackendIdentifier,
credentialProvider: null as unknown as AwsCredentialIdentityProvider,
})
);
});

it('throws an error if a null backendIdentifier is passed in', async () => {
assert.throws(() =>
createGraphqlDocumentGenerator({
backendIdentifier: { stackName: 'foo' },
credentialProvider: null as unknown as AwsCredentialIdentityProvider,
})
);
});
});
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
import { AppSyncClient } from '@aws-sdk/client-appsync';
import {
BackendIdentifier,
BackendOutputClient,
} from '@aws-amplify/deployed-backend-client';
import { graphqlOutputKey } from '@aws-amplify/backend-output-schemas';
import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { AppsyncGraphqlGenerationResult } from './appsync_graphql_generation_result.js';
import { AppSyncIntrospectionSchemaFetcher } from './appsync_schema_fetcher.js';
import { AppSyncGraphqlDocumentGenerator } from './graphql_document_generator.js';
import { GraphqlDocumentGenerator } from './model_generator.js';

export type GraphqlDocumentGeneratorFactoryParams = {
apiId: string;
backendIdentifier: BackendIdentifier;
credentialProvider: AwsCredentialIdentityProvider;
};

/**
* Factory function to compose a model generator
*/
export const createGraphqlDocumentGenerator = ({
apiId,
backendIdentifier,
credentialProvider,
}: GraphqlDocumentGeneratorFactoryParams): GraphqlDocumentGenerator => {
if (!apiId) {
throw new Error('`apiId` must be defined');
if (!backendIdentifier) {
throw new Error('`backendIdentifier` must be defined');
}
if (!credentialProvider) {
throw new Error('`credentialProvider` must be defined');
}

const fetchSchema = async () => {
const backendOutputClient = new BackendOutputClient(
credentialProvider,
backendIdentifier
);
const output = await backendOutputClient.getOutput();
const apiId = output[graphqlOutputKey]?.payload.awsAppsyncApiId;
if (!apiId) {
throw new Error(`Unable to determine AppSync API ID.`);
}

return new AppSyncIntrospectionSchemaFetcher(new AppSyncClient()).fetch(
apiId
);
};
return new AppSyncGraphqlDocumentGenerator(
() =>
new AppSyncIntrospectionSchemaFetcher(new AppSyncClient()).fetch(apiId),
fetchSchema,
(fileMap) => new AppsyncGraphqlGenerationResult(fileMap)
);
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import assert from 'node:assert';
import { describe, it } from 'node:test';
import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { BackendIdentifier } from '@aws-amplify/deployed-backend-client';
import { createGraphqlTypesGenerator } from './create_graphql_types_generator.js';

describe('types generator factory', () => {
it('throws an error if a null apiId is passed in', async () => {
it('throws an error if a null backendIdentifier is passed in', async () => {
assert.throws(() =>
createGraphqlTypesGenerator({ apiId: null as unknown as string })
createGraphqlTypesGenerator({
backendIdentifier: null as unknown as BackendIdentifier,
credentialProvider: null as unknown as AwsCredentialIdentityProvider,
})
);
});

it('throws an error if a null backendIdentifier is passed in', async () => {
assert.throws(() =>
createGraphqlTypesGenerator({
backendIdentifier: { stackName: 'foo' },
credentialProvider: null as unknown as AwsCredentialIdentityProvider,
})
);
});
});
38 changes: 32 additions & 6 deletions packages/model-generator/src/create_graphql_types_generator.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,51 @@
import { AppSyncClient } from '@aws-sdk/client-appsync';
import {
BackendIdentifier,
BackendOutputClient,
} from '@aws-amplify/deployed-backend-client';
import { graphqlOutputKey } from '@aws-amplify/backend-output-schemas';
import { AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { AppsyncGraphqlGenerationResult } from './appsync_graphql_generation_result.js';
import { AppSyncIntrospectionSchemaFetcher } from './appsync_schema_fetcher.js';
import { AppSyncGraphqlTypesGenerator } from './graphql_types_generator.js';
import { GraphqlTypesGenerator } from './model_generator.js';

export type GraphqlTypesGeneratorFactoryParams = {
apiId: string;
backendIdentifier: BackendIdentifier;
credentialProvider: AwsCredentialIdentityProvider;
};

/**
* Factory function to compose a model generator
*/
export const createGraphqlTypesGenerator = ({
apiId,
backendIdentifier,
credentialProvider,
}: GraphqlTypesGeneratorFactoryParams): GraphqlTypesGenerator => {
if (!apiId) {
throw new Error('`apiId` must be defined');
if (!backendIdentifier) {
throw new Error('`backendIdentifier` must be defined');
}
if (!credentialProvider) {
throw new Error('`credentialProvider` must be defined');
}

const fetchSchema = async () => {
const backendOutputClient = new BackendOutputClient(
credentialProvider,
backendIdentifier
);
const output = await backendOutputClient.getOutput();
const apiId = output[graphqlOutputKey]?.payload.awsAppsyncApiId;
if (!apiId) {
throw new Error(`Unable to determine AppSync API ID.`);
}

return new AppSyncIntrospectionSchemaFetcher(new AppSyncClient()).fetch(
apiId
);
};
return new AppSyncGraphqlTypesGenerator(
() =>
new AppSyncIntrospectionSchemaFetcher(new AppSyncClient()).fetch(apiId),
fetchSchema,
(fileMap) => new AppsyncGraphqlGenerationResult(fileMap)
);
};
17 changes: 17 additions & 0 deletions packages/model-generator/src/generate_api_code.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import assert from 'assert';
import { describe, it } from 'node:test';
import { GenerateApiCodeProps, generateApiCode } from './generate_api_code.js';

describe('generateAPICode', () => {
describe('graphql-codegen', () => {});
describe('modelgen', () => {});
describe('introspection', () => {});

it('throws error on unknown format', async () => {
const props = {
format: 'unsupported',
stackName: 'stack_name',
} as unknown as GenerateApiCodeProps;
await assert.rejects(() => generateApiCode(props));
});
});
Loading