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

fix: use standalone ajv validation for model introspection schema #807

Merged
merged 2 commits into from
Apr 12, 2024
Merged
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
5 changes: 4 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ module.exports = {
'/packages/*/CHANGELOG.md',

// Ignore lint in e2e test apps
'test-apps'
'test-apps',

// Ignore lint for standalone JSON validation function
'/packages/appsync-modelgen-plugin/src/validate-cjs.js'
]
};
7 changes: 4 additions & 3 deletions packages/appsync-modelgen-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
"codegen"
],
"scripts": {
"build": "tsc",
"build": "yarn generate-schemas && yarn generate-standalone-validation-function && tsc",
"watch": "tsc -w",
"test-watch": "jest --watch",
"test": "jest",
"generate-schemas": "ts-node ./scripts/generateSchemas.ts",
"generate-schemas": "ts-node ./scripts/generateSchemas.ts --overwrite",
"generate-standalone-validation-function": "ts-node ./scripts/generateStandaloneValidationFunction.ts",
"extract-api": "ts-node ../../scripts/extract-api.ts"
},
"dependencies": {
"@graphql-codegen/plugin-helpers": "^1.18.8",
"@graphql-codegen/visitor-plugin-common": "^1.22.0",
"@graphql-tools/utils": "^6.0.18",
"ajv": "^6.10.0",
"chalk": "^3.0.0",
"change-case": "^4.1.1",
"graphql-transformer-common": "^4.25.1",
Expand All @@ -45,6 +45,7 @@
"@types/fs-extra": "^8.1.2",
"@types/node": "^12.12.6",
"@types/pluralize": "0.0.29",
"ajv": "^8.12.0",
"graphql": "^15.5.0",
"java-ast": "^0.3.0",
"ts-json-schema-generator": "1.0.0"
Expand Down
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Ajv from 'ajv';
import modelIntrospectionSchemaDefinition from '../schemas/introspection/1/ModelIntrospectionSchema.json'
AaronZyLee marked this conversation as resolved.
Show resolved Hide resolved
import { join } from 'path';
import { writeFileSync } from 'fs';

const standaloneCode = require("ajv/dist/standalone").default

const ajv = new Ajv({ code: { source: true } });
const validate = ajv.compile(modelIntrospectionSchemaDefinition);

let moduleCode = standaloneCode(ajv, validate)

// Now you can write the module code to file
writeFileSync(join(__dirname, "../src/validate-cjs.js"), moduleCode)
41 changes: 41 additions & 0 deletions packages/appsync-modelgen-plugin/src/__tests__/validate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const validateModelIntrospectionSchema = require('../validate-cjs');
import { ModelIntrospectionSchema } from '../interfaces/introspection'

describe('Standalone validation function', () => {
const validSchema: ModelIntrospectionSchema = {
version: 1,
models: {},
nonModels: {},
enums: {},
}
it('should pass on the valid schema', () => {
const result = validateModelIntrospectionSchema(validSchema);
expect(result).toBe(true);
});
describe('should fail on the invalid schema', () => {
it('invalid version', () => {
const schema = {
...validSchema,
version: 100,
};
const result = validateModelIntrospectionSchema(schema);
expect(result).toBe(false);
});
it('invalid fields', () => {
const schema = {
...validSchema,
invalidField: {}
};
const result = validateModelIntrospectionSchema(schema);
expect(result).toBe(false);
});
it('missing required fields', () => {
const schema = {
...validSchema,
};
delete (schema as any).models;
const result = validateModelIntrospectionSchema(schema);
expect(result).toBe(false);
});
});
});
1 change: 1 addition & 0 deletions packages/appsync-modelgen-plugin/src/validate-cjs.js

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { GraphQLSchema } from "graphql";
import { Argument, AssociationType, Field, Fields, FieldType, ModelAttribute, ModelIntrospectionSchema, PrimaryKeyInfo, SchemaEnum, SchemaModel, SchemaMutation, SchemaNonModel, SchemaQuery, SchemaSubscription, Input, InputFieldType } from "../interfaces/introspection";
import { METADATA_SCALAR_MAP } from "../scalars";
import { CodeGenConnectionType } from "../utils/process-connections";
import { RawAppSyncModelConfig, ParsedAppSyncModelConfig, AppSyncModelVisitor, CodeGenEnum, CodeGenField, CodeGenModel, CodeGenPrimaryKeyType, CodeGenQuery, CodeGenSubscription, CodeGenMutation, CodeGenInputObject, CodeGenUnion, CodeGenInterface } from "./appsync-visitor";
import fs from 'fs';
import path from 'path';
import Ajv from 'ajv';
import { RawAppSyncModelConfig, ParsedAppSyncModelConfig, AppSyncModelVisitor, CodeGenEnum, CodeGenField, CodeGenModel, CodeGenPrimaryKeyType, CodeGenQuery, CodeGenSubscription, CodeGenMutation, CodeGenInputObject } from "./appsync-visitor";

const validateModelIntrospectionSchema = require('../validate-cjs');

type UnionFieldType = { union: string };
type InterfaceFieldType = { interface: string };
Expand All @@ -18,17 +17,13 @@ export class AppSyncModelIntrospectionVisitor<
TPluginConfig extends ParsedAppSyncModelIntrospectionConfig = ParsedAppSyncModelIntrospectionConfig
> extends AppSyncModelVisitor<TRawConfig, TPluginConfig> {
private readonly introspectionVersion = 1;
private schemaValidator: Ajv.ValidateFunction;
constructor(
schema: GraphQLSchema,
rawConfig: TRawConfig,
additionalConfig: Partial<TPluginConfig>,
defaultScalars: NormalizedScalarsMap = DEFAULT_SCALARS,
) {
super(schema, rawConfig, additionalConfig, defaultScalars);
const modelIntrospectionSchemaText = fs.readFileSync(path.join(__dirname, '..', '..', 'schemas', 'introspection', this.introspectionVersion.toString(), 'ModelIntrospectionSchema.json'), 'utf8');
const modelIntrospectionSchema = JSON.parse(modelIntrospectionSchemaText);
this.schemaValidator = new Ajv().compile(modelIntrospectionSchema);
}

generate(): string {
Expand All @@ -43,8 +38,8 @@ export class AppSyncModelIntrospectionVisitor<
);

const modelIntrosepctionSchema = this.generateModelIntrospectionSchema();
if (!this.schemaValidator(modelIntrosepctionSchema)) {
throw new Error(`Data did not validate against the supplied schema. Underlying errors were ${JSON.stringify(this.schemaValidator.errors)}`);
if (!validateModelIntrospectionSchema(modelIntrosepctionSchema)) {
throw new Error(`Data did not validate against the supplied schema. Underlying errors were ${JSON.stringify(validateModelIntrospectionSchema.errors)}`);
}
return JSON.stringify(modelIntrosepctionSchema, null, 4);
}
Expand Down
6 changes: 4 additions & 2 deletions packages/appsync-modelgen-plugin/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "lib"
"outDir": "lib",
"allowJs": true,
},
"exclude": [
"scripts",
"schemas",
"lib",
"src/__tests__"
"src/__tests__",
"coverage",
]
}
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6125,7 +6125,7 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.6, ajv@~6.12.6:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"

ajv@^8.0.1, ajv@^8.11.0:
ajv@^8.0.1, ajv@^8.11.0, ajv@^8.12.0:
version "8.12.0"
resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
Expand Down
Loading