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

Make required fields required; to-one relations don’t need arguments #15

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
9 changes: 7 additions & 2 deletions lib/SchemaBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,14 @@ class SchemaBuilder {
if (relation instanceof objection.HasOneRelation
|| relation instanceof objection.BelongsToOneRelation
|| relation instanceof objection.HasOneThroughRelation) {
const isRequired = _.intersection(relation.ownerModelClass.jsonSchema.required, relation.ownerProp).length === relation.ownerProp.length;
let type = this._typeForModel(modelData);
if(isRequired) {
type = new graphqlRoot.GraphQLNonNull(type);
}
return {
type: this._typeForModel(modelData),
args: _.omit(modelData.args, OMIT_FROM_RELATION_ARGS)
type: type,
args: {}
};
} else if (relation instanceof objection.HasManyRelation || relation instanceof objection.ManyToManyRelation) {
return {
Expand Down
6 changes: 5 additions & 1 deletion lib/argFactories.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,11 @@ function reducePrimitiveFields(fields, modelClass, func) {

for (var i = 0, l = propNames.length; i < l; ++i) {
const propName = propNames[i];
const field = fields[propName];
let field = fields[propName];

if(field.type.constructor.name == "GraphQLNonNull") {
field = {type: field.type.ofType};
}

if (field.type instanceof GraphQLObjectType || field.type instanceof GraphQLList) {
continue;
Expand Down
44 changes: 27 additions & 17 deletions lib/jsonSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const _ = require('lodash')
, graphqlRoot = require('graphql')
, GraphQLObjectType = graphqlRoot.GraphQLObjectType
, GraphQLEnumType = graphqlRoot.GraphQLEnumType
, GraphQLNonNull = graphqlRoot.GraphQLNonNull
, GraphQLBoolean = graphqlRoot.GraphQLBoolean
, GraphQLString = graphqlRoot.GraphQLString
, GraphQLFloat = graphqlRoot.GraphQLFloat
Expand All @@ -20,20 +21,21 @@ function jsonSchemaToGraphQLFields(jsonSchema, opt) {
typeCache: {}
});

const requiredFields = jsonSchema.required || [];
const fields = {};

_.forOwn(jsonSchema.properties, (propSchema, propName) => {
if (utils.isExcluded(ctx, propName)) {
return;
}

fields[propName] = toGraphQLField(propSchema, propName, ctx);
fields[propName] = toGraphQLField(propSchema, propName, ctx, requiredFields.indexOf(propName) !== -1);
});

return fields;
}

function toGraphQLField(jsonSchema, propName, ctx) {
function toGraphQLField(jsonSchema, propName, ctx, required) {
let schemas;

if (jsonSchema.anyOf || jsonSchema.oneOf) {
Expand All @@ -48,26 +50,26 @@ function toGraphQLField(jsonSchema, propName, ctx) {
const type = _.reject(jsonSchema.type, isNullType);

if (type.length === 1) {
return typeToGraphQLField(type[0], jsonSchema, propName, ctx);
return typeToGraphQLField(type[0], jsonSchema, propName, ctx, required);
} else {
throw new Error('multiple values in json schema `type` property not supported. schema: ' + JSON.stringify(jsonSchema));
}
} else {
return typeToGraphQLField(jsonSchema.type, jsonSchema, propName, ctx);
return typeToGraphQLField(jsonSchema.type, jsonSchema, propName, ctx, required);
}
}

function typeToGraphQLField(type, jsonSchema, propName, ctx) {
function typeToGraphQLField(type, jsonSchema, propName, ctx, required) {
let graphQlField;

if (_.isArray(jsonSchema.enum)) {
graphQlField = enumToGraphQLField(jsonSchema.enum, propName, ctx);
graphQlField = enumToGraphQLField(jsonSchema.enum, propName, ctx, required);
} else if (type === 'object') {
graphQlField = objectToGraphQLField(jsonSchema, propName, ctx);
graphQlField = objectToGraphQLField(jsonSchema, propName, ctx, required);
}else if (type === 'array') {
graphQlField = arrayToGraphQLField(jsonSchema, propName, ctx);
graphQlField = arrayToGraphQLField(jsonSchema, propName, ctx, required);
} else {
graphQlField = primitiveToGraphQLField(type);
graphQlField = primitiveToGraphQLField(type, required);
}

if (jsonSchema.description) {
Expand All @@ -77,23 +79,27 @@ function typeToGraphQLField(type, jsonSchema, propName, ctx) {
return graphQlField;
}

function enumToGraphQLField(enumeration, propName, ctx) {
function enumToGraphQLField(enumeration, propName, ctx, required) {
var typeName = ctx.typeNamePrefix + _.upperFirst(_.camelCase(propName)) + 'Enum' + (ctx.typeIndex++);

if (!ctx.typeCache[typeName]) {
ctx.typeCache[typeName] = new GraphQLEnumType({
let type = new GraphQLEnumType({
name: typeName,
values: _.reduce(enumeration, (values, enumValue) => {
values[enumValue] = {value: enumValue};
return values;
}, {})
});
if(required) {
type = new GraphQLNonNull(type);
}
ctx.typeCache[typeName] = type;
}

return {type: ctx.typeCache[typeName]};
}

function objectToGraphQLField(jsonSchema, propName, ctx) {
function objectToGraphQLField(jsonSchema, propName, ctx, required) {
const typeName = ctx.typeNamePrefix + _.upperFirst(_.camelCase(propName)) + 'JsonType' + (ctx.typeIndex++);

if (!ctx.typeIndex[typeName]) {
Expand All @@ -103,7 +109,7 @@ function objectToGraphQLField(jsonSchema, propName, ctx) {
const fields = {};

_.forOwn(jsonSchema.properties, (propSchema, propName) => {
fields[propName] = toGraphQLField(propSchema, propName, ctx);
fields[propName] = toGraphQLField(propSchema, propName, ctx, required);
});

return fields;
Expand All @@ -114,23 +120,27 @@ function objectToGraphQLField(jsonSchema, propName, ctx) {
return {type: ctx.typeCache[typeName]};
}

function arrayToGraphQLField(jsonSchema, propName, ctx) {
function arrayToGraphQLField(jsonSchema, propName, ctx, required) {
if (_.isArray(jsonSchema.items)) {
throw new Error('multiple values in `items` of array type is not supported. schema: ' + JSON.stringify(jsonSchema));
}

return {
type: new GraphQLList(toGraphQLField(jsonSchema.items, propName, ctx).type)
type: new GraphQLList(toGraphQLField(jsonSchema.items, propName, ctx, required).type)
};
}

function primitiveToGraphQLField(type) {
const graphQlType = primitiveToGraphQLType(type);
function primitiveToGraphQLField(type, required) {
let graphQlType = primitiveToGraphQLType(type);

if (!graphQlType) {
throw new Error('cannot convert json schema type "' + type + '" into GraphQL type');
}

if (required) {
graphQlType = new GraphQLNonNull(graphQlType);
}

return {type: graphQlType};
}

Expand Down
28 changes: 28 additions & 0 deletions tests/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const _ = require('lodash')
, path = require('path')
, expect = require('expect.js')
, graphql = require('graphql').graphql
, printType = require('graphql').printType
, GraphQLList = require('graphql').GraphQLList
, GraphQLObjectType = require('graphql').GraphQLObjectType
, mainModule = require('../')
Expand Down Expand Up @@ -881,4 +882,31 @@ describe('integration tests', () => {
});
});

describe('Types', () => {
let schema;

before(() => {
schema = mainModule
.builder()
.model(session.models.Person, {listFieldName: 'people'})
.model(session.models.Movie)
.model(session.models.Review)
.build();
});

it('should make required fields required', () => {
const expectedSchema = `type Review {
id: Int
title: String
stars: Int
text: String
reviewerId: Int!
movieId: Int!
reviewer: Person!
movie: Movie!
}`;
expect(expectedSchema).to.eql(printType(schema.getType("Review")));
});
});

});
1 change: 1 addition & 0 deletions tests/setup/models/Review.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Review extends Model {
static get jsonSchema() {
return {
type: 'object',
required: ['reviewerId', 'movieId'],

properties: {
id: {type: 'integer'},
Expand Down