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 - modify and integrate #892

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
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
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
12 changes: 11 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,9 @@ 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';

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

export const plugin: PluginFunction<RawAppSyncModelConfig> = (
schema: GraphQLSchema,
rawDocuments: Types.DocumentFile[],
config: RawAppSyncModelConfig,
) => {
return pluginSync(schema, rawDocuments, config);
};
72 changes: 49 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,15 @@ export type AppSyncModelCodeGenPresetConfig = {
isDataStoreEnabled?: boolean;
};

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 +109,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 +156,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 +185,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 +238,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 +268,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 +299,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 +316,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 +349,28 @@ export const preset: Types.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
)}`,
);
}
},
};
};

export const presetSync: SyncTypes.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
buildGeneratesSection: (options: SyncTypes.PresetFnArgs<AppSyncModelCodeGenPresetConfig>): SyncTypes.GenerateOptions[] => {
const {cache, pluginMap, ...otherOptions} = options;

return buildGenerations(otherOptions).map((config: GenerateOptions) => ({
pluginMap,
cache,
...config,
}))
}
}

export const preset: Types.OutputPreset<AppSyncModelCodeGenPresetConfig> = {
buildGeneratesSection: (options: Types.PresetFnArgs<AppSyncModelCodeGenPresetConfig>): Types.GenerateOptions[] => {
const {cache, pluginMap, ...otherOptions} = options;

return buildGenerations(otherOptions).map((config: GenerateOptions) => ({
pluginMap,
cache,
...config,
}))
}
}
46 changes: 46 additions & 0 deletions packages/appsync-modelgen-plugin/src/types/sync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Types, PluginFunction as PluginFunctionAsync, CodegenPlugin as CodegenPluginAsync } from "@graphql-codegen/plugin-helpers";

type PluginMapContainer = Pick<Types.GenerateOptions, 'pluginMap'>;
type CacheContainer = Pick<Types.GenerateOptions, 'cache'>;

export type SyncPluginMap<Obj extends PluginMapContainer> = Omit<Obj, 'pluginMap'> & {
pluginMap: {
[name: string]: Omit<Obj['pluginMap'][string], 'plugin'> & {
plugin: (
...args: Parameters<Obj['pluginMap'][string]['plugin']>
) => Awaited<ReturnType<Obj['pluginMap'][string]['plugin']>>;
};
};
};

export type SyncCache<Obj extends CacheContainer> = Omit<Obj, 'cache'> & {
cache?: (<T>(namespace: string, key: string, factory: () => T) => T) | undefined
};
Comment on lines +6 to +18
Copy link
Member Author

Choose a reason for hiding this comment

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

These two helper types do most of the lifting here. They unwrap the Promise/Await type expectations so that the types can be used for sync versions of the target behavior.

Awaited behavior is used/allowed for:

  • Plugin execution
  • Profilers
  • Caches


export declare namespace SyncTypes {
type GenerateOptions = SyncCache<SyncPluginMap<Types.GenerateOptions>>;

type PresetFnArgs<
Config = any,
PluginConfig = {
[key: string]: any;
}
> = SyncCache<SyncPluginMap<Types.PresetFnArgs<Config, PluginConfig>>>;

type OutputPreset<TPresetConfig = any> = {
buildGeneratesSection: (options: PresetFnArgs<TPresetConfig>) => GenerateOptions[];
};

type PluginFunction<T> = (...args: Parameters<PluginFunctionAsync<T>>) => Awaited<ReturnType<PluginFunctionAsync<T>>>;

type CodegenPlugin<T = any> = Omit<CodegenPluginAsync<T>, 'plugin'> & {
plugin: PluginFunction<T>;
}

// Reiterating these types so that SyncTypes is a drop in replacement for Types
type DocumentFile = Types.DocumentFile;
type PluginConfig = Types.PluginConfig;
type ConfiguredPlugin = Types.ConfiguredPlugin;
type SkipDocumentsValidationOptions = Types.SkipDocumentsValidationOptions;
type PluginOutput = Types.PluginOutput
};
Loading
Loading