graphql-engine/community/action-scaffolders/typescript-express.js
Aravind Shankar bb63d7e60e
cli: allow managing actions (#3859)
Co-authored-by: Rishichandra Wawhal <rishichandra.wawhal@gmail.com>
Co-authored-by: Rikin Kachhia <54616969+rikinsk@users.noreply.github.com>
Co-authored-by: Aravind <aravindkp@outlook.in>
Co-authored-by: Anon Ray <ecthiender@users.noreply.github.com>
Co-authored-by: Shahidh K Muhammed <muhammedshahid.k@gmail.com>
2020-02-24 21:44:46 +05:30

193 lines
4.3 KiB
JavaScript

const { buildSchema, printSchema, parse } = require('graphql');
const { codegen } = require('@graphql-codegen/core');
const typescriptPlugin = require('@graphql-codegen/typescript');
const { camelize } = require('inflection');
/*Utils*/
const getWrappingTypeMetadata = (_type) => {
let type = JSON.parse(JSON.stringify(_type));
const wrapperStack = [];
while (type.kind !== 'NamedType') {
if (type.kind === 'ListType') {
wrapperStack.push('l');
}
if(type.kind === 'NonNullType') {
wrapperStack.push('n');
}
type = type.type;
}
const typename = type.name.value;
return {
typename,
stack: wrapperStack.reverse()
}
}
const getTypescriptTypename = (_typename, wrapperStack) => {
let typename = _typename;
if (!wrapperStack.length || wrapperStack[0] === 'l') {
typename = `Maybe<${typename}>`
}
wrapperStack.forEach((w, i) => {
if (w === 'l') {
if (wrapperStack[i+1] === 'n') {
typename = `Array <${typename}>`
} else {
typename = `Maybe <Array<${typename}>>`
}
}
});
return typename;
}
/*Templater*/
const templater = async (
actionName,
actionsSdl,
derive
) => {
const ast = parse(`${actionsSdl}`);
const typesAst = {
...ast,
definitions: ast.definitions.filter(d => d.name.value !== 'Mutation')
};
const codegenConfig = {
schema: typesAst,
plugins: [
{
typescript: {},
},
],
pluginMap: {
typescript: typescriptPlugin
}
}
const typesCodegen = await codegen(codegenConfig);
let mutationDef;
const mutationAst = {
...ast,
definitions: ast.definitions.filter(d => {
if (d.name.value === 'Mutation') {
if (mutationDef) return false
mutationDef = d.fields.find(f => f.name.value === actionName);
if (!mutationDef) {
return false;
} {
return true;
}
}
return false;
})
}
const mutationName = mutationDef.name.value;
const mutationArguments = mutationDef.arguments;
const typesFileMetadata = {
content: typesCodegen,
name: `hasuraCustomTypes.ts`
}
const requiredTypesToImport = { 'Scalars': true, 'Maybe': true };
const getHandlerFileContent = () => {
const argumentType = {
name: camelize(`Action_${mutationName}_input`),
fields: []
};
mutationArguments.forEach(ma => {
const argTypeMetadata = getWrappingTypeMetadata(ma.type);
const existingType = typesAst.definitions.find(d => d.name.value === argTypeMetadata.typename)
if (existingType) {
if (existingType.kind === 'ScalarTypeDefinition') {
argumentType.fields.push({
name: ma.name.value,
type: getTypescriptTypename(`Scalar['${argTypeMetadata.typename}']`, argTypeMetadata.stack)
})
} else {
argumentType.fields.push({
name: ma.name.value,
type: getTypescriptTypename(argTypeMetadata.typename, argTypeMetadata.stack)
});
requiredTypesToImport[argTypeMetadata.typename] = true;
}
} else {
argumentType.fields.push({
name: ma.name.value,
type: getTypescriptTypename(`Scalar['${argTypeMetadata.typename}']`, argTypeMetadata.stack)
})
}
});
const getImports = () => {
return `
import { Request, Response } from 'express';
import {
${Object.keys(requiredTypesToImport).map(rt => ` ${rt}`).join(',\n')}
} from './hasuraCustomTypes';
`;
};
const getMutationInputType = () => {
return `
type ${argumentType.name} {
${argumentType.fields.map(f => ` ${f.name}: ${f.type}`).join(',\n')}
}
`;
}
const getHandler = () => {
return `
const handler = async (request: Request, response: Response) => {
const mutationInput: ${argumentType.name} = request.body.input;
// perform your business logic here
/*
In case of error,
return response.status(400).json({
errors: {
code: '<error code>',
message: 'error happened'
}
})
*/
// return the output type
return response.json({
data: {}
});
}
`;
}
return `
${getImports()}
${getMutationInputType()}
${getHandler()}
export default handler;
`;
}
const handlerFileMetadata = {
name: `${mutationName}.handler.ts`,
content: getHandlerFileContent()
}
return [typesFileMetadata, handlerFileMetadata];
}
module.exports = templater