Created
November 9, 2020 16:50
-
-
Save pbassham/0da8bc73929e2fabc1c40351b5bd9a04 to your computer and use it in GitHub Desktop.
A script that reads DGraph's introspection schema and auto-generates a fragment file based on your schema.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Reads DGraph's introspection schema and generates a fragment file | |
| /* eslint-disable prefer-template */ | |
| const { default: fetch } = require('node-fetch') | |
| const fs = require('fs') | |
| //GraphQL Introspection Specification https://spec.graphql.org/June2018/ | |
| fetch('https://ENDPOINT_HERE.aws.cloud.dgraph.io/graphql', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| query: `{ | |
| __schema { | |
| types { | |
| name | |
| ofType {name} | |
| fields { | |
| name | |
| type {kind name ofType{name kind} interfaces{name} possibleTypes {name} enumValues {name}}} | |
| kind | |
| } | |
| } | |
| }`, | |
| }), | |
| }) | |
| .then((res) => res.json()) | |
| .then((res) => { | |
| // console.log(res.data) | |
| const header = `import gql from 'graphql-tag'\n\n` | |
| // FRAGS = Scalar fields only | |
| let fragmentsObj1 = `export const FRAGS = {` | |
| // FRAGS1 = Scalar fields + nested fields 1 level deep | |
| let fragmentsObj2 = `\n\n\n\n\nexport const FRAGS1 = {` | |
| // FRAGS1 = Scalar fields + nested fields 2 levels deep | |
| let fragmentsObj3 = `\n\n\n\n\nexport const FRAGS2 = {` | |
| // FRAGS1 = Scalar fields + access fields (custom for us, but might want to customize it) | |
| let fragmentsObj4 = `\n\n\n\n\nexport const FRAGS_ACCESS = {` | |
| const { types } = res.data.__schema // eslint-disable-line no-underscore-dangle | |
| //Filter out DGRAPH types to generate fragments for basic queries only. This could be changed to generate fragments of different types. | |
| const regex = /((^(Add|Update|Delete|__)[A-Z])|^Mutation|^Query|^Subscription)/m | |
| types.forEach((type) => { | |
| // Skip TYPES that match the regex pattern | |
| if (regex.test(type.name)) { | |
| // console.log(type.name+ ' SKIPPED TYPE via regex') | |
| return | |
| } | |
| // console.log(type.name) | |
| const { fields } = type | |
| let count = 0 | |
| let nestedFields1 = '' | |
| let nestedFields2 = '' | |
| const framentVarsForOtherFragment = [] | |
| const framentVarsForOtherFragment2 = [] | |
| const typename = `${type.name.charAt(0).toLowerCase()}${type.name.slice(1)}` | |
| const fragmentName = `\n${typename}Fields: gql\`\n fragment ${typename}Fields on ${type.name} {` | |
| const fragmentName1 = `\n${typename}Fields1: gql\`\n fragment ${typename}Fields1 on ${type.name} {` | |
| const fragmentName2 = `\n${typename}Fields2: gql\`\n fragment ${typename}Fields2 on ${type.name} {` | |
| const fragmentNameAccess = `\n${typename}FieldsAccess: gql\`\n fragment ${typename}FieldsAccess on ${type.name} {` | |
| let keyedFragment1 = '' | |
| let keyedFragment2 = '' | |
| let keyedFragment3 = '' | |
| let keyedFragment4 = '' | |
| let scalarFields = '' | |
| let access = '' | |
| const regex2 = /(^(add|update|delete|__)[A-Z])/m | |
| fields.forEach((field) => { | |
| // Skip FIELDS that match regex2 pattern | |
| if (regex2.test(field.name)) { | |
| // console.log(`${field.name} SKIPPED FIELD via regex`) | |
| return | |
| } | |
| // let notused// = false | |
| const fieldType = field.type.name ? field.type.name : | |
| field.type.ofType ? field.type.ofType.name : | |
| "" | |
| //TypeName -> typeName | |
| const fieldTypeVarName = fieldType ? fieldType.charAt(0).toLowerCase() + fieldType.slice(1) : "" | |
| const typeComment = '#' + fieldType + `${field.type.kind === 'NON_NULL' ? '!' : | |
| field.type.kind === 'SCALAR' ? " Scalar" : | |
| field.type.kind === 'LIST' ? ` ${field.type.ofType.kind}` : | |
| ` ${field.type.kind}`}` | |
| //FIXME: fieldType conditional only returns null on Event.Occurances.name that i know of | |
| const fragmentRef = fieldType ? `\n \${FRAGS.${fieldTypeVarName}Fields}` : "" | |
| const fragmentRef2 = fieldType ? `\n \${FRAGS1.${fieldTypeVarName}Fields1}` : "" | |
| const nestedField1 = fieldType ? `\n ${field.name} {\n ...${fieldTypeVarName}Fields\n }` : "" | |
| const nestedField2 = fieldType ? `\n ${field.name} {\n ...${fieldTypeVarName}Fields1\n }` : "" | |
| //SCALAR | |
| if (field.type.kind === 'SCALAR' || | |
| field.type.kind === 'ENUM' || | |
| ((field.type.kind === 'LIST' || | |
| field.type.kind === 'NON_NULL') && | |
| (field.type.ofType.kind === 'ENUM' || | |
| field.type.ofType.kind === 'String' || | |
| field.type.ofType.kind === 'Boolean' || | |
| field.type.ofType.kind === 'DateTime' || | |
| field.type.ofType.kind === 'Float' || | |
| field.type.ofType.kind === 'Int')) || | |
| fieldType === 'String' || | |
| fieldType === 'Boolean' || | |
| fieldType === 'DateTime' || | |
| fieldType === 'Int' || | |
| fieldType === 'Float' || | |
| fieldType === 'ID') { | |
| // count1++ | |
| count++ | |
| // console.log(field.name) | |
| // Temporary Exceptions bc of whatever reason (schema or data errors, etc.) | |
| if (type.name === 'Contact' && field.name === 'name') { | |
| // Comment out the 'name' field in contacts until custom directive scripts are available in DGraph | |
| scalarFields += `\n #${field.name} ${typeComment}` | |
| } else { | |
| scalarFields += `\n ${field.name} ${typeComment}` | |
| } | |
| } else | |
| //SUBFIELDS of LIST, NON_NULL (fields that arent scalar values on their own) and OBJECTS types | |
| if (field.type.kind === 'LIST' || | |
| field.type.kind === 'OBJECT' || | |
| field.type.kind === 'NON_NULL') { | |
| // count2++ | |
| count++ | |
| // notused = false | |
| framentVarsForOtherFragment.push(fragmentRef) | |
| framentVarsForOtherFragment2.push(fragmentRef2) | |
| nestedFields1 += `${nestedField1} ${typeComment} #added by ${field.type.kind}` | |
| nestedFields2 += `${nestedField2} ${typeComment} #added by ${field.type.kind}` | |
| if (fieldType === 'ACL') { | |
| access += `\n ${field.name} {\n ...accessFields \n } ${typeComment}` | |
| } | |
| } else { | |
| console.log(` Field STILL not used: ${type.name} - ${field.name} [${field.type.kind} ${field.type.name}] ${fieldType}`) | |
| } | |
| }) | |
| //Create Final fragment for Type | |
| if (count >= 1) { | |
| //remove duplicate fragments | |
| const uniqueFragments = [...new Set(framentVarsForOtherFragment)] | |
| const uniqueFragments2 = [...new Set(framentVarsForOtherFragment2)] | |
| // const typeFragmentFooter = `\n }\`` | |
| // const typeFragmentFooter2 = `\n }`+uniqueFragments+`\`` | |
| const keyedFragmentFooter1 = `\n }\`,` | |
| const keyedFragmentFooter2 = `\n }${uniqueFragments}\`,` | |
| const keyedFragmentFooter3 = `\n }${uniqueFragments2}\`,` | |
| const keyedFragmentFooter4 = `\n }${access ? `\n \${accessFields}` : ""}\`,` | |
| // typeFragments += fragmentHeaderWithVarName + typeFragmentFields + typeFragmentFooter | |
| // fieldsNestedL2 += fragmentHeaderWithVarName2 + keyedFragmentFields + typeFragmentFooter2 | |
| keyedFragment1 += fragmentName + scalarFields + keyedFragmentFooter1 | |
| keyedFragment2 += fragmentName1 + scalarFields + nestedFields1 + keyedFragmentFooter2 | |
| keyedFragment3 += fragmentName2 + scalarFields + nestedFields2 + keyedFragmentFooter3 | |
| keyedFragment4 += fragmentNameAccess + scalarFields + access + keyedFragmentFooter4 | |
| fragmentsObj1 += `${keyedFragment1}\n` | |
| fragmentsObj2 += `${keyedFragment2}\n` | |
| fragmentsObj3 += `${keyedFragment3}\n` | |
| fragmentsObj4 += `${keyedFragment4}\n` | |
| } | |
| }) | |
| fragmentsObj1 += '}' | |
| fragmentsObj2 += '}' | |
| fragmentsObj3 += '}' | |
| fragmentsObj4 += '}' | |
| // console.log(fragments) | |
| // A hand written fragment to include manaully | |
| const accessFragment = `export const accessFields = gql\`\n fragment accessFields on ACL { | |
| level | |
| grants { | |
| id | |
| firstName | |
| lastName | |
| organization | |
| gravatar | |
| isUser { | |
| username | |
| } | |
| isGroup { | |
| slug | |
| } | |
| } | |
| }\`\n\n` | |
| const fileContent = header + accessFragment + fragmentsObj1 + fragmentsObj2 + fragmentsObj3 + fragmentsObj4 | |
| fs.writeFile('./src/contexts/Fragments/codeGenFragments.js', fileContent, (err) => { | |
| if (err) { | |
| return console.log('ERROR: ', err) | |
| } | |
| return console.log('wrote codeGenFragments.js. first 200 chars: \n', fileContent.substring(0, 200)) | |
| }) | |
| }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment