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: Sync codegen behavior implementation adding generateModelsSync #894

Merged
merged 12 commits into from
Oct 23, 2024
Merged
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
# API approval - public surface and dependencies.
**/API.md @aws-amplify/amplify-data-admins

# Vendor code changes
packages/**/vendor/** @aws-amplify/amplify-data-admins

# Dependency Licensing approval
dependency_licenses.txt @aws-amplify/amplify-data-admins

Expand Down
4 changes: 2 additions & 2 deletions dependency_licenses.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9556,7 +9556,7 @@ The following software may be included in this product: change-case-all. A copy

MIT License

Modified work: Copyright (c) 2020 Jonas Gnioui <btxtiger@outlook.com>
Modified work: Copyright (c) 2020 Jonas Gnioui <btxtiger@icloud.com>

Original work:

Expand Down Expand Up @@ -9586,7 +9586,7 @@ The following software may be included in this product: change-case-all. A copy

MIT License

Modified work: Copyright (c) 2020 Jonas Gnioui <btxtiger@icloud.com>
Modified work: Copyright (c) 2020 Jonas Gnioui <btxtiger@outlook.com>

Original work:

Expand Down
2 changes: 1 addition & 1 deletion packages/amplify-codegen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@aws-amplify/graphql-docs-generator": "4.2.1",
"@aws-amplify/graphql-generator": "0.4.7",
"@aws-amplify/graphql-types-generator": "3.6.0",
"@graphql-codegen/core": "2.6.6",
Copy link
Member Author

Choose a reason for hiding this comment

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

Without this change, our code depends on 2 versions of core. Applying this so that we only have a single version in our dependency list for the monorepo

"@graphql-codegen/core": "^2.6.6",
"chalk": "^3.0.0",
"fs-extra": "^8.1.0",
"glob-parent": "^6.0.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { generateModelIntrospection, getModelIntrospection } = require('../../src/commands/model-intropection');
const graphqlGenerator = require('@aws-amplify/graphql-generator');
const codegen_core = require('@graphql-codegen/core');
const mockFs = require('mock-fs');
const path = require('path');
const fs = require('fs');
Expand All @@ -14,7 +15,6 @@ jest.mock('@aws-amplify/graphql-generator', () => {
jest.mock('@graphql-codegen/core', () => {
const originalModule = jest.requireActual('@graphql-codegen/core');
const codegen = jest.fn();
codegen.mockReturnValue(MOCK_GENERATED_CODE);
return {
...originalModule,
codegen,
Expand All @@ -27,6 +27,7 @@ const MOCK_PROJECT_NAME = 'myapp';
const MOCK_BACKEND_DIRECTORY = 'backend';
const MOCK_GENERATED_INTROSPECTION = { schemaVersion: 1 };
const MOCK_GENERATED_CODE = JSON.stringify(MOCK_GENERATED_INTROSPECTION);

const MOCK_CONTEXT = {
print: {
info: jest.fn(),
Expand Down Expand Up @@ -55,7 +56,6 @@ const MOCK_CONTEXT = {
};

describe('generateModelIntrospection', () => {
graphqlGenerator.generateModels.mockReturnValue({ 'model-introspection.json': MOCK_GENERATED_CODE });
const schemaFilePath = path.join(MOCK_BACKEND_DIRECTORY, 'api', MOCK_PROJECT_NAME);
const outputDirectory = path.join(MOCK_PROJECT_ROOT, MOCK_OUTPUT_DIR);
const mockedFiles = {};
Expand All @@ -64,6 +64,11 @@ describe('generateModelIntrospection', () => {
};
mockedFiles[outputDirectory] = {};

beforeAll(() => {
codegen_core.codegen.mockReturnValue(MOCK_GENERATED_CODE);
graphqlGenerator.generateModels.mockReturnValue({ 'model-introspection.json': MOCK_GENERATED_CODE });
});

beforeEach(() => {
jest.clearAllMocks();
});
Expand All @@ -76,7 +81,7 @@ describe('generateModelIntrospection', () => {
...MOCK_CONTEXT,
parameters: {
options: {
['output-dir']: MOCK_OUTPUT_DIR,
'output-dir': MOCK_OUTPUT_DIR,
},
},
};
Expand All @@ -99,7 +104,7 @@ describe('generateModelIntrospection', () => {
});

describe('getModelIntrospection', () => {
graphqlGenerator.generateModels.mockReturnValue({ 'model-introspection.json': MOCK_GENERATED_CODE });

const schemaFilePath = path.join(MOCK_BACKEND_DIRECTORY, 'api', MOCK_PROJECT_NAME);
const outputDirectory = path.join(MOCK_PROJECT_ROOT, MOCK_OUTPUT_DIR);
const mockedFiles = {};
Expand All @@ -108,6 +113,11 @@ describe('getModelIntrospection', () => {
};
mockedFiles[outputDirectory] = {};

beforeAll(() => {
codegen_core.codegen.mockReturnValue(MOCK_GENERATED_CODE);
graphqlGenerator.generateModels.mockReturnValue({ 'model-introspection.json': MOCK_GENERATED_CODE });
});

beforeEach(() => {
jest.clearAllMocks();
});
Expand Down
40 changes: 40 additions & 0 deletions packages/appsync-modelgen-plugin/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

```ts

import { CodegenPlugin } from '@graphql-codegen/plugin-helpers';
import { PluginFunction } from '@graphql-codegen/plugin-helpers';
import { RawConfig } from '@graphql-codegen/visitor-plugin-common';
import { RawDocumentsConfig } from '@graphql-codegen/visitor-plugin-common';
Expand Down Expand Up @@ -142,9 +143,15 @@ export type ModelIntrospectionSchema = {
// @public (undocumented)
export const plugin: PluginFunction<RawAppSyncModelConfig>;

// @public (undocumented)
export const pluginSync: SyncTypes.PluginFunction<RawAppSyncModelConfig>;

// @public (undocumented)
export const preset: Types.OutputPreset<AppSyncModelCodeGenPresetConfig>;

// @public (undocumented)
export const presetSync: SyncTypes.OutputPreset<AppSyncModelCodeGenPresetConfig>;

// @public (undocumented)
export type PrimaryKeyInfo = {
isCustomPrimaryKey: boolean;
Expand Down Expand Up @@ -235,6 +242,39 @@ export type SchemaSubscription = SchemaQuery;
// @public (undocumented)
export type SchemaSubscriptions = Record<string, SchemaSubscription>;

// @public (undocumented)
export namespace SyncTypes {
// (undocumented)
export type CodegenPlugin<T = any> = Omit<CodegenPlugin<T>, 'plugin'> & {
plugin: PluginFunction<T>;
};
// (undocumented)
export type ConfiguredPlugin = Types.ConfiguredPlugin;
// (undocumented)
export type DocumentFile = Types.DocumentFile;
// Warning: (ae-forgotten-export) The symbol "SyncCache" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "SyncPluginMap" needs to be exported by the entry point index.d.ts
//
// (undocumented)
export type GenerateOptions = SyncCache<SyncPluginMap<Types.GenerateOptions>>;
// (undocumented)
export type OutputPreset<TPresetConfig = any> = {
buildGeneratesSection: (options: PresetFnArgs<TPresetConfig>) => GenerateOptions[];
};
// (undocumented)
export type PluginConfig = Types.PluginConfig;
// (undocumented)
export type PluginFunction<T> = (...args: Parameters<PluginFunction<T>>) => Awaited<ReturnType<PluginFunction<T>>>;
// (undocumented)
export type PluginOutput = Types.PluginOutput;
// (undocumented)
export type PresetFnArgs<Config = any, PluginConfig = {
[key: string]: any;
}> = SyncCache<SyncPluginMap<Types.PresetFnArgs<Config, PluginConfig>>>;
// (undocumented)
export type SkipDocumentsValidationOptions = Types.SkipDocumentsValidationOptions;
}

// @public (undocumented)
export type Target = 'java' | 'swift' | 'javascript' | 'typescript' | 'dart' | 'introspection';

Expand Down
2 changes: 1 addition & 1 deletion packages/appsync-modelgen-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"extract-api": "ts-node ../../scripts/extract-api.ts"
},
"dependencies": {
"@graphql-codegen/plugin-helpers": "^1.18.8",
"@graphql-codegen/plugin-helpers": "^3.1.1",
"@graphql-codegen/visitor-plugin-common": "^1.22.0",
"@graphql-tools/utils": "^6.0.18",
"chalk": "^3.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/appsync-modelgen-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface AppSyncModelPluginConfig extends RawDocumentsConfig {
export * from './plugin';
export * from './preset';
export * from './interfaces/introspection';
export { SyncTypes } from './types/sync'

export const addToSchema = (config: AppSyncModelPluginConfig) => {
const result: string[] = [];
Expand Down
15 changes: 14 additions & 1 deletion packages/appsync-modelgen-plugin/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import { AppSyncModelTypeScriptVisitor } from './visitors/appsync-typescript-vis
import { AppSyncModelJavascriptVisitor } from './visitors/appsync-javascript-visitor';
import { AppSyncModelDartVisitor } from './visitors/appsync-dart-visitor';
import { AppSyncModelIntrospectionVisitor } from './visitors/appsync-model-introspection-visitor';
export const plugin: PluginFunction<RawAppSyncModelConfig> = (
import { SyncTypes } from './types/sync';

/**
* @internal
*/
export const pluginSync: SyncTypes.PluginFunction<RawAppSyncModelConfig> = (
schema: GraphQLSchema,
rawDocuments: Types.DocumentFile[],
config: RawAppSyncModelConfig,
Expand Down Expand Up @@ -59,3 +64,11 @@ export const plugin: PluginFunction<RawAppSyncModelConfig> = (
}
return '';
};

export const plugin: PluginFunction<RawAppSyncModelConfig> = (
schema: GraphQLSchema,
rawDocuments: Types.DocumentFile[],
config: RawAppSyncModelConfig,
) => {
return pluginSync(schema, rawDocuments, config);
};
96 changes: 73 additions & 23 deletions packages/appsync-modelgen-plugin/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { join } from 'path';
import { JAVA_SCALAR_MAP, SWIFT_SCALAR_MAP, TYPESCRIPT_SCALAR_MAP, DART_SCALAR_MAP, METADATA_SCALAR_MAP } from './scalars';
import { LOADER_CLASS_NAME, GENERATED_PACKAGE_NAME } from './configs/java-config';
import { graphqlName, toUpper } from 'graphql-transformer-common';
import { SyncTypes } from './types/sync';

const APPSYNC_DATA_STORE_CODEGEN_TARGETS = ['java', 'swift', 'javascript', 'typescript', 'dart', 'introspection'];

Expand Down Expand Up @@ -31,12 +32,25 @@ export type AppSyncModelCodeGenPresetConfig = {
isDataStoreEnabled?: boolean;
};

/**
* NOTE: The different codegen target presets restructure the options to meet the needs of the target plugin
* None of this remapping interacts with the pluginMap or cache interface, so we can reuse all logic if we strip out
* the pluginMap and cache, then re-introduce them in the returned preset.
*/

/**
* Internal types that represent the options without the pluginMap and cache, which we will use in each of our
* target preset option construction implementations
*/
type GenerateOptions = Omit<SyncTypes.GenerateOptions, 'cache' | 'pluginMap'>;
type PresetFnArgs = Omit<SyncTypes.PresetFnArgs<AppSyncModelCodeGenPresetConfig>, 'cache' | 'pluginMap'>;

const generateJavaPreset = (
options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>,
options: PresetFnArgs,
models: TypeDefinitionNode[],
manyToManyJoinModels: TypeDefinitionNode[],
): Types.GenerateOptions[] => {
const config: Types.GenerateOptions[] = [];
): GenerateOptions[] => {
const config: GenerateOptions[] = [];
const modelFolder = options.config.overrideOutputDir
? [options.config.overrideOutputDir]
: [options.baseOutputDir, ...GENERATED_PACKAGE_NAME.split('.')];
Expand Down Expand Up @@ -105,10 +119,10 @@ const generateJavaPreset = (
};

const generateSwiftPreset = (
options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>,
options: PresetFnArgs,
models: TypeDefinitionNode[],
): Types.GenerateOptions[] => {
const config: Types.GenerateOptions[] = [];
): GenerateOptions[] => {
const config: GenerateOptions[] = [];
const modelFolder = options.config.overrideOutputDir ? options.config.overrideOutputDir : options.baseOutputDir;
models.forEach(model => {
const modelName = model.name.value;
Expand Down Expand Up @@ -152,10 +166,10 @@ const generateSwiftPreset = (
};

const generateTypeScriptPreset = (
options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>,
options: PresetFnArgs,
models: TypeDefinitionNode[],
): Types.GenerateOptions[] => {
const config: Types.GenerateOptions[] = [];
): GenerateOptions[] => {
const config: GenerateOptions[] = [];
const modelFolder = options.config.overrideOutputDir ? options.config.overrideOutputDir : join(options.baseOutputDir);
config.push({
...options,
Expand All @@ -181,10 +195,10 @@ const generateTypeScriptPreset = (
};

const generateJavasScriptPreset = (
options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>,
options: PresetFnArgs,
models: TypeDefinitionNode[],
): Types.GenerateOptions[] => {
const config: Types.GenerateOptions[] = [];
): GenerateOptions[] => {
const config: GenerateOptions[] = [];
const modelFolder = options.config.overrideOutputDir ? options.config.overrideOutputDir : join(options.baseOutputDir);
config.push({
...options,
Expand Down Expand Up @@ -234,10 +248,10 @@ const generateJavasScriptPreset = (
};

const generateDartPreset = (
options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>,
options: PresetFnArgs,
models: TypeDefinitionNode[],
): Types.GenerateOptions[] => {
const config: Types.GenerateOptions[] = [];
): GenerateOptions[] => {
const config: GenerateOptions[] = [];
const modelFolder = options.config.overrideOutputDir ?? options.baseOutputDir;
models.forEach(model => {
const modelName = model.name.value;
Expand All @@ -264,7 +278,7 @@ const generateDartPreset = (
return config;
};

const generateManyToManyModelStubs = (options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>): TypeDefinitionNode[] => {
const generateManyToManyModelStubs = (options: PresetFnArgs): TypeDefinitionNode[] => {
let models = new Array<TypeDefinitionNode>();
let manyToManySet = new Set<string>();
options.schema.definitions.forEach(def => {
Expand Down Expand Up @@ -295,10 +309,10 @@ const generateManyToManyModelStubs = (options: Types.PresetFnArgs<AppSyncModelCo
};

const generateIntrospectionPreset = (
options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>,
options: PresetFnArgs,
models: TypeDefinitionNode[],
): Types.GenerateOptions[] => {
const config: Types.GenerateOptions[] = [];
): GenerateOptions[] => {
const config: GenerateOptions[] = [];
// model-intropection.json
config.push({
...options,
Expand All @@ -312,8 +326,7 @@ const generateIntrospectionPreset = (
return config;
};

export const preset: Types.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
buildGeneratesSection: (options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>): Types.GenerateOptions[] => {
const buildGenerations = (options: PresetFnArgs): GenerateOptions[] => {
const codeGenTarget = options.config.target;
const typesToSkip: string[] = ['Query', 'Mutation', 'Subscription'];
const models: TypeDefinitionNode[] = options.schema.definitions.filter(
Expand Down Expand Up @@ -346,5 +359,42 @@ export const preset: Types.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
)}`,
);
}
},
};
};



/**
* @internal
* The presetSync interface uses our SyncTypes __without__ promise/async typing
*/
export const presetSync: SyncTypes.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
buildGeneratesSection: (options: SyncTypes.PresetFnArgs<AppSyncModelCodeGenPresetConfig>): SyncTypes.GenerateOptions[] => {
// Extract cache and pluginMap from the options
const {cache, pluginMap, ...otherOptions} = options;

// Generate the list of options and re-introduce the pluginMap and cache
return buildGenerations(otherOptions).map((config: GenerateOptions) => ({
pluginMap,
cache,
...config,
}))
}
}

/**
* @internal
* The preset interface uses the @graphql-codegen/core interfaces __with__ promise/async typing
*/
export const preset: Types.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
buildGeneratesSection: (options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>): Types.GenerateOptions[] => {
// Extract cache and pluginMap from the options
const {cache, pluginMap, ...otherOptions} = options;

// Generate the list of options and re-introduce the pluginMap and cache
return buildGenerations(otherOptions).map((config: GenerateOptions) => ({
pluginMap,
cache,
...config,
}))
}
}
Loading
Loading