Skip to content

Commit

Permalink
Generate name for query input type
Browse files Browse the repository at this point in the history
  • Loading branch information
Pho Nguyen committed Mar 13, 2021
1 parent 0658def commit e49f79e
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ dist
coverage
.env
play
dump.rdb
89 changes: 89 additions & 0 deletions src/core/query-input-type-builder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as td from 'testdouble'
import * as graphql from 'graphql'
import { TestUtils } from '../helpers'
import * as _ from 'lodash'
import { expect } from 'chai'
import { QueryInputTypeBuilder } from './query-input-type-builder'

describe(TestUtils.getTestTitle(__filename), () => {
it('#build', () => {
const { type } = new QueryInputTypeBuilder()
.build()

expect(type).instanceOf(graphql.GraphQLInputObjectType)
})

it('#addQueryField', () => {
const builder = new QueryInputTypeBuilder()

builder
.addQueryField({
field: 'created',
operators: ['gte', 'lte'],
required: true,
type: graphql.GraphQLString,
})
.addQueryField({
field: 'status',
operators: ['in'],
required: false,
type: graphql.GraphQLString,
})

expect(builder['queryFields'].length).to.equal(2)
})

it('#getFields', async () => {
const builder = new QueryInputTypeBuilder()
td.replace(builder, 'queryFields', [
{
field: 'email',
operators: ['eq', 'in'],
type: graphql.GraphQLString,
},
{
field: 'email',
operators: ['exists'],
type: graphql.GraphQLString,
},
{
field: 'role',
operators: ['in'],
require: true,
type: graphql.GraphQLInt,
},
])

expect(_.keys(builder['getFields']())).to.deep.equal(
['email', 'role']
)
})

it('#getInputTypeByFields', () => {
const builder = new QueryInputTypeBuilder()
const inputType = builder['getInputTypeByFields']([
{
field: 'email',
operators: ['eq', 'in'],
type: graphql.GraphQLString,
},
{
field: 'email',
operators: ['exists'],
type: graphql.GraphQLString,
required: true,
},
])

const expected = new graphql.GraphQLInputObjectType({
name: 'RandomName',
fields: {
eq: { type: graphql.GraphQLString },
in: { type: graphql.GraphQLList(graphql.GraphQLString) },
exists: { type: graphql.GraphQLNonNull(graphql.GraphQLBoolean) },
},
})

expect(inputType.toConfig().fields).to.deep.equal(expected.toConfig().fields)
})
})
68 changes: 68 additions & 0 deletions src/core/query-input-type-builder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as graphql from 'graphql'
import * as _ from 'lodash'
import { customAlphabet } from 'nanoid'

interface IQueryField {
field: string,
type: graphql.GraphQLScalarType
operators: string[]
required?: boolean
}

const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 8)

export class QueryInputTypeBuilder {
private queryFields: IQueryField[] = []

build() {
const required = _.some(this.queryFields, 'required')
const type = new graphql.GraphQLInputObjectType({
name: `GeneratedQueryType_${nanoid()}`,
fields: this.getFields(),
})
if (required) return { type: graphql.GraphQLNonNull(type) }
return { type }
}

addQueryField(queryField: IQueryField) {
this.queryFields.push(queryField)
return this
}

private getFields() {
return _.chain(this.queryFields)
.groupBy('field')
.mapValues(fields => {
const type = this.getInputTypeByFields(fields)
if (_.some(fields, 'required')) return ({ type: graphql.GraphQLNonNull(type) })
return { type }
})
.value()
}

private getInputTypeByFields(queryFields: IQueryField[]): graphql.GraphQLInputObjectType {
const { type, field } = _.first(queryFields)!
const operatorConfigs = {
eq: type,
gte: type,
lte: type,
in: graphql.GraphQLList(type),
exists: graphql.GraphQLBoolean,
}

const fields = _.chain(queryFields)
.map(({ operators, required }) => operators.map(operator => ({ operator, required })))
.flatMap()
.map(({ operator, required }) => {
const operatorType = operatorConfigs[operator]
return { [operator]: { type: required ? graphql.GraphQLNonNull(operatorType) : operatorType } }
})
.reduce((a, b) => ({ ...a, ...b }))
.value()

return new graphql.GraphQLInputObjectType({
name: `QueryQueryTypeForField${_.capitalize(field)}_${nanoid()}`,
fields,
})
}
}

0 comments on commit e49f79e

Please sign in to comment.