Vulcan/packages/nova-lib/lib/graphql.js
2016-12-12 11:34:28 +09:00

145 lines
No EOL
3.4 KiB
JavaScript

/*
Utilities to generate the app's GraphQL schema
*/
import deepmerge from 'deepmerge';
import GraphQLJSON from 'graphql-type-json';
import { Utils } from './utils.js';
// convert a JSON schema to a GraphQL schema
const jsTypeToGraphQLType = typeName => {
switch (typeName) {
case "Date":
return "String";
case "Number":
return "Float";
// assume all arrays contains strings for now
case "Array":
return "[String]";
case "Object":
return "???";
default:
return typeName;
}
}
export const GraphQLSchema = {
// collections used to auto-generate schemas
collections: [],
addCollection(collection) {
this.collections.push(collection);
},
// generate GraphQL schemas for all registered collections
getCollectionsSchemas() {
const collectionsSchemas = this.collections.map(collection => {
return this.generateSchema(collection);
}).join('\n');
return collectionsSchemas;
},
// additional schemas
schemas: [],
addSchema(schema) {
this.schemas.push(schema);
},
// get extra schemas defined manually
getAdditionalSchemas() {
const additionalSchemas = this.schemas.join('\n');
return additionalSchemas;
},
// queries
queries: [],
addQuery(query) {
this.queries.push(query);
},
// mutations
mutations: [],
addMutation(mutation) {
this.mutations.push(mutation);
},
// add resolvers
resolvers: {
JSON: GraphQLJSON,
},
addResolvers(resolvers) {
this.resolvers = deepmerge(this.resolvers, resolvers);
},
// add objects to context
context: {},
addToContext(object) {
this.context = deepmerge(this.context, object);
},
// generate a GraphQL schema corresponding to a given collection
generateSchema(collection) {
const collectionName = collection._name;
const mainTypeName = collection.typeName ? collection.typeName : Utils.camelToSpaces(_.initial(collectionName).join('')); // default to posts -> Post
// backward-compatibility code: we do not want user.telescope fields in the graphql schema
const schema = Utils.stripTelescopeNamespace(collection.simpleSchema()._schema);
let mainSchema = [], inputSchema = [], unsetSchema = [];
_.forEach(schema, (field, key) => {
// console.log(field, key)
const fieldType = jsTypeToGraphQLType(field.type.name);
if (key.indexOf('$') === -1 && fieldType !== "???") { // skip fields with "$" and unknown fields
// 1. main schema
mainSchema.push(`${key}: ${fieldType}`);
// if field has a resolver, also push it to schema
if (field.resolveAs) {
mainSchema.push(field.resolveAs);
}
if (field.insertableBy || field.editableBy) {
const isRequired = field.optional ? '' : '!';
// 2. input schema
inputSchema.push(`${key}: ${fieldType}${isRequired}`);
// 3. unset schema
unsetSchema.push(`${key}: Boolean`);
}
}
});
let graphQLSchema = `
scalar JSON
type ${mainTypeName} {
${mainSchema.join('\n ')}
}
`
if (inputSchema.length) {
graphQLSchema += `
input ${collectionName}Input {
${inputSchema.join('\n ')}
}
input ${collectionName}Unset {
${unsetSchema.join('\n ')}
}
`
}
return graphQLSchema;
}
};