Skip to content

Commit

Permalink
Merge branch 'xolvio:master' into codgenv2
Browse files Browse the repository at this point in the history
  • Loading branch information
jmvtrinidad authored Sep 26, 2022
2 parents f5475de + 2275fc5 commit d3c50f5
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 22 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ name: Release
on: push
jobs:
test:
runs-on: ubuntu-16.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '12'
node-version: '14'
- run: yarn
- run: yarn test
- run: yarn end-to-end-test
Expand Down
2 changes: 1 addition & 1 deletion scaffold/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@
"testdouble-jest": "2.0.0",
"ts-jest": "26.1.3",
"ts-node": "8.10.2",
"typescript": "3.9.7"
"typescript": "4.7.4"
}
}
43 changes: 30 additions & 13 deletions src/generate/generate-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import getScalars from './parse-graphql/getScalars';
import { saveRenderedTemplate } from './helpers/saveRenderedTemplate';
import { findProjectMainPath } from './helpers/findProjectMainPath';
import { execQuietly } from './helpers/execQuietly';
import getUnions from './parse-graphql/getUnions';

const debug = configureDebug('generate-module');

Expand Down Expand Up @@ -247,36 +248,38 @@ export const executeGeneration = async (appPrefix = '~app', generatedPrefix = '~

const federatedEntities = getFederatedEntities(schemaString);
const interfaces = getInterfaces(schemaString);
const unions = getUnions(schemaString);

// Leaving this for now
// eslint-disable-next-line no-param-reassign
schemaString = schemaString.replace(/extend type/g, 'type');
const source = new Source(schemaString);
const schema = buildSchema(source, { assumeValidSDL: true });
shelljs.mkdir('-p', `${projectMainPath}/src/${graphqlFileRootPath}/types/`);

const createInterfaceType = (interfaceName: string) => {
const createResolveType = (resolverTypeName: string) => {
const templateName = './templates/typeTypeResolvers.handlebars';
const capitalizedFieldName = capitalize('__resolveType');
const context = {
typeName: interfaceName,
typeName: resolverTypeName,
fieldName: '__resolveType',
moduleName: name,
resolveReferenceType: true,
capitalizedFieldName,
generatedPrefix,
};
const filePath = `${projectMainPath}/src/${graphqlFileRootPath}/types/`;
const fileName = `${interfaceName}${capitalizedFieldName}.ts`;
const fileName = `${resolverTypeName}${capitalizedFieldName}.ts`;
const keepIfExists = true;

saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists);
};

const createInterfaceSpec = (interfaceName: string) => {
const createResolveTypeSpec = (resoverTypeName: string) => {
const templateName = './templates/typeTypeResolvers.spec.handlebars';
const capitalizedFieldName = capitalize('__resolveType');
const context = {
typeName: interfaceName,
typeName: resoverTypeName,
fieldName: '__resolveType',
moduleName: name,
hasArguments: false,
Expand All @@ -285,17 +288,17 @@ export const executeGeneration = async (appPrefix = '~app', generatedPrefix = '~
generatedPrefix,
};
const filePath = `${projectMainPath}/src/${graphqlFileRootPath}/types/`;
const fileName = `${interfaceName}${capitalizedFieldName}.spec.ts`;
const fileName = `${resoverTypeName}${capitalizedFieldName}.spec.ts`;
const keepIfExists = true;

saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists);
};

const createInterfaceSpecWrapper = (interfaceName: string) => {
const createResolveTypeSpecWrapper = (resolverTypeName: string) => {
const templateName = './templates/typeTypeResolversSpecWrapper.handlebars';
const capitalizedFieldName = capitalize('__resolveType');
const context = {
typeName: interfaceName,
typeName: resolverTypeName,
fieldName: '__resolveType',
moduleName: name,
hasArguments: false,
Expand All @@ -306,20 +309,31 @@ export const executeGeneration = async (appPrefix = '~app', generatedPrefix = '~
graphqlFileRootPath,
};
const filePath = `${projectMainPath}/generated/graphql/helpers/`;
const fileName = `${interfaceName}${capitalizedFieldName}SpecWrapper.ts`;
const fileName = `${resolverTypeName}${capitalizedFieldName}SpecWrapper.ts`;
const keepIfExists = false;

saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists);
};
interfaces.forEach((interfaceName) => {
createInterfaceType(interfaceName);
createInterfaceSpec(interfaceName);
createInterfaceSpecWrapper(interfaceName);
createResolveType(interfaceName);
createResolveTypeSpec(interfaceName);
createResolveTypeSpecWrapper(interfaceName);
typeResolvers.push({
typeName: interfaceName,
fieldName: [{ name: '__resolveType', capitalizedName: capitalize('__resolveType') }],
});
});

unions.forEach((unionName) => {
createResolveType(unionName);
createResolveTypeSpec(unionName);
createResolveTypeSpecWrapper(unionName);
typeResolvers.push({
typeName: unionName,
fieldName: [{ name: '__resolveType', capitalizedName: capitalize('__resolveType') }],
});
});

type FilteredType = { name: { value: string }; resolveReferenceType: boolean; arguments?: string[] };
typeDefinitions.forEach((typeDef) => {
let filtered: FilteredType[] = [];
Expand All @@ -333,7 +347,10 @@ export const executeGeneration = async (appPrefix = '~app', generatedPrefix = '~
filtered = type.astNode.fields.filter((field) =>
field.directives.find(
(d: { name: { value: string } }) =>
d.name.value === 'computed' || d.name.value === 'link' || d.name.value === 'requires',
d.name.value === 'computed' ||
d.name.value === 'link' ||
d.name.value === 'requires' ||
d.name.value === 'map',
),
);
}
Expand Down
31 changes: 31 additions & 0 deletions src/generate/parse-graphql/getInterfaces.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,34 @@ test('get the interfaces', () => {

expect(res).toEqual(['Home']);
});
test('should throw error if duplicate interface names are found', () => {
const schemaString = gql`
type TodoItem @key(fields: "id") {
id: ID!
list: List
}
interface Home {
address: string
}
extend type List {
id: ID!
todos: [TodoItem!]!
incompleteCount: Int!
}
type InMemory {
id: ID!
}
type Query {
homes: [Home]
}
interface Home {
address: string
}
`;

expect(() => getInterfaces(schemaString)).toThrow('Duplicate interface name found: Home');
});
17 changes: 11 additions & 6 deletions src/generate/parse-graphql/getInterfaces.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import gql from 'graphql-tag';
import validateUniqueName from './validateUniqueName';

export default (graphqlString: string) => {
const graphqlAST = gql`
${graphqlString}
`;

return (
graphqlAST.definitions
.filter((d) => ['InterfaceTypeDefinition'].indexOf(d.kind) > -1)
// @ts-ignore
.map((f) => f.name.value)
);
const interfacesTypeDefs = graphqlAST.definitions
.filter((d) => ['InterfaceTypeDefinition'].indexOf(d.kind) > -1)
// @ts-ignore
.map((f) => f.name.value);

validateUniqueName(interfacesTypeDefs, (name: string) => {
throw new Error(`Duplicate interface name found: ${name}`);
});

return interfacesTypeDefs;
};
51 changes: 51 additions & 0 deletions src/generate/parse-graphql/getUnions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import getUnions from './getUnions';

const gql = (a: TemplateStringsArray) => a[0];

test('get the unions', () => {
const schemaString = gql`
type Query {
homes: [Home]
}
type Cottage {
id: ID!
address: String!
}
type Villa {
id: ID!
address: String!
owner: String!
}
union Home = Cottage | Villa
`;

const res = getUnions(schemaString);

expect(res).toEqual(['Home']);
});
test('should throw exception if duplicate union names are found', () => {
const schemaString = gql`
type Query {
homes: [Home]
}
type Cottage {
id: ID!
address: String!
}
type Villa {
id: ID!
address: String!
owner: String!
}
union Home = Cottage | Villa
union Home = Cottage | Villa
`;

expect(() => getUnions(schemaString)).toThrow('Duplicate union name found: Home');
});
18 changes: 18 additions & 0 deletions src/generate/parse-graphql/getUnions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import gql from 'graphql-tag';
import validateUniqueName from './validateUniqueName';

export default (graphqlString: string) => {
const graphqlAST = gql`
${graphqlString}
`;

const unionTypeDefinitions = graphqlAST.definitions
.filter((d) => ['UnionTypeDefinition'].indexOf(d.kind) > -1)
// @ts-ignore
.map((f) => f.name.value);

validateUniqueName(unionTypeDefinitions, (name: string) => {
throw new Error(`Duplicate union name found: ${name}`);
});
return unionTypeDefinitions;
};
19 changes: 19 additions & 0 deletions src/generate/parse-graphql/validateUniqueName.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import validateUniqueName from './validateUniqueName';

test('should throw error if duplicate entry found', () => {
const names = ['apple', 'banana', 'orange', 'banana'];
expect(() =>
validateUniqueName(names, (name: any) => {
throw new Error(`duplicate record found: ${name}`);
}),
).toThrow('duplicate record found: banana');
});

test('should not throw error if no duplicate entry found', () => {
const names = ['apple', 'banana', 'orange'];
expect(() =>
validateUniqueName(names, () => {
throw new Error('duplicate record found');
}),
).not.toThrow();
});
8 changes: 8 additions & 0 deletions src/generate/parse-graphql/validateUniqueName.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function (names: string[], exceptionHandler: Function) {
const nameCounts: Map<string, number> = new Map();
names.forEach((name) => {
const val = (nameCounts.get(name) ?? 0) + 1;
if (val > 1) exceptionHandler(name);
nameCounts.set(name, val);
});
}

0 comments on commit d3c50f5

Please sign in to comment.