From 6614480194f547191c3d8b48dc7abbb37e997d65 Mon Sep 17 00:00:00 2001 From: Daiki Ihara Date: Mon, 23 Mar 2020 23:46:28 +0900 Subject: [PATCH] chore(gatsby): migrate type of filter to TypeScript --- packages/gatsby/src/schema/schema.js | 2 +- .../schema/types/__tests__/sort-and-filter.js | 2 +- .../src/schema/types/{filter.js => filter.ts} | 258 +++++++++++------- .../gatsby/src/schema/types/node-interface.js | 2 +- 4 files changed, 157 insertions(+), 107 deletions(-) rename packages/gatsby/src/schema/types/{filter.js => filter.ts} (71%) diff --git a/packages/gatsby/src/schema/schema.js b/packages/gatsby/src/schema/schema.js index 15f09c8a4276a..02036719505dc 100644 --- a/packages/gatsby/src/schema/schema.js +++ b/packages/gatsby/src/schema/schema.js @@ -36,7 +36,7 @@ const { } = require(`./extensions`) import { getPagination } from "./types/pagination" const { getSortInput, SORTABLE_ENUM } = require(`./types/sort`) -const { getFilterInput, SEARCHABLE_ENUM } = require(`./types/filter`) +import { getFilterInput, SEARCHABLE_ENUM } from "./types/filter" const { isGatsbyType, GatsbyGraphQLTypeKind } = require(`./types/type-builders`) const { isASTDocument, diff --git a/packages/gatsby/src/schema/types/__tests__/sort-and-filter.js b/packages/gatsby/src/schema/types/__tests__/sort-and-filter.js index f1e138501cf2a..44f8d812a7693 100644 --- a/packages/gatsby/src/schema/types/__tests__/sort-and-filter.js +++ b/packages/gatsby/src/schema/types/__tests__/sort-and-filter.js @@ -1,7 +1,7 @@ // NOTE: Previously `infer-graphql-input-from-fields-test.js` const { createSchemaComposer } = require(`../../schema-composer`) -const { getFilterInput } = require(`../filter`) +import { getFilterInput } from "../filter" const { getSortInput } = require(`../sort`) const { diff --git a/packages/gatsby/src/schema/types/filter.js b/packages/gatsby/src/schema/types/filter.ts similarity index 71% rename from packages/gatsby/src/schema/types/filter.js rename to packages/gatsby/src/schema/types/filter.ts index 503e8f1e234c1..06b22fd99ecb3 100644 --- a/packages/gatsby/src/schema/types/filter.js +++ b/packages/gatsby/src/schema/types/filter.ts @@ -1,20 +1,148 @@ -const { +import { getNamedType, getNullableType, GraphQLInputObjectType, GraphQLEnumType, GraphQLList, isSpecifiedScalarType, -} = require(`graphql`) + GraphQLScalarType, +} from "graphql" import { addDerivedType } from "./derived-types" -const { InputTypeComposer } = require(`graphql-compose`) -const { GraphQLJSON } = require(`graphql-compose`) +import { + InputTypeComposer, + GraphQLJSON, + SchemaComposer, + ObjectTypeComposer, + EnumTypeComposer, + InterfaceTypeComposer, + UnionTypeComposer, + ScalarTypeComposer, +} from "graphql-compose" import { GraphQLDate } from "./date" -const SEARCHABLE_ENUM = { +type Context = any + +type AnyComposeType = + | ObjectTypeComposer + | InputTypeComposer + | EnumTypeComposer + | InterfaceTypeComposer + | UnionTypeComposer + | ScalarTypeComposer + +export const SEARCHABLE_ENUM = { SEARCHABLE: `SEARCHABLE`, NOT_SEARCHABLE: `NON_SEARCHABLE`, DEPRECATED_SEARCHABLE: `DERPECATED_SEARCHABLE`, +} as const + +const getQueryOperatorListInput = ({ + schemaComposer, + inputTypeComposer, +}: { + schemaComposer: SchemaComposer + inputTypeComposer: InputTypeComposer +}): InputTypeComposer => { + const typeName = inputTypeComposer.getTypeName().replace(/Input/, `ListInput`) + return schemaComposer.getOrCreateITC(typeName, itc => { + itc.addFields({ + elemMatch: inputTypeComposer, + }) + }) +} + +const removeEmptyFields = ( + { inputTypeComposer }: { inputTypeComposer: InputTypeComposer }, + cache = new Set() +): InputTypeComposer => { + const convert = (itc: InputTypeComposer): InputTypeComposer => { + if (cache.has(itc)) { + return itc + } + cache.add(itc) + const fields = itc.getFields() + const nonEmptyFields = {} + Object.keys(fields).forEach(fieldName => { + const fieldITC = fields[fieldName] + if (fieldITC instanceof InputTypeComposer) { + const convertedITC = convert(fieldITC) + if (convertedITC.getFieldNames().length) { + nonEmptyFields[fieldName] = convertedITC + } + } else { + nonEmptyFields[fieldName] = fieldITC + } + }) + itc.setFields(nonEmptyFields) + return itc + } + return convert(inputTypeComposer) +} + +const EQ = `eq` +const NE = `ne` +const GT = `gt` +const GTE = `gte` +const LT = `lt` +const LTE = `lte` +const IN = `in` +const NIN = `nin` +const REGEX = `regex` +const GLOB = `glob` + +const ALLOWED_OPERATORS = { + Boolean: [EQ, NE, IN, NIN], + Date: [EQ, NE, GT, GTE, LT, LTE, IN, NIN], + Float: [EQ, NE, GT, GTE, LT, LTE, IN, NIN], + ID: [EQ, NE, IN, NIN], + Int: [EQ, NE, GT, GTE, LT, LTE, IN, NIN], + JSON: [EQ, NE, IN, NIN, REGEX, GLOB], + String: [EQ, NE, IN, NIN, REGEX, GLOB], + Enum: [EQ, NE, IN, NIN], + CustomScalar: [EQ, NE, IN, NIN], +} + +type TypeName = keyof typeof ALLOWED_OPERATORS + +const ARRAY_OPERATORS = [IN, NIN] + +const getOperatorFields = ( + fieldType: string, + operators: string[] +): Record => { + const result = {} + operators.forEach(op => { + if (ARRAY_OPERATORS.includes(op)) { + result[op] = [fieldType] + } else { + result[op] = fieldType + } + }) + return result +} + +const isBuiltInScalarType = (type: any): type is GraphQLScalarType => + isSpecifiedScalarType(type) || type === GraphQLDate || type === GraphQLJSON + +const getQueryOperatorInput = ({ + schemaComposer, + type, +}: { + schemaComposer: SchemaComposer + type: any +}): InputTypeComposer => { + let typeName: TypeName + if (type instanceof GraphQLEnumType) { + typeName = `Enum` + } else if (isBuiltInScalarType(type)) { + typeName = type.name as Exclude + } else { + typeName = `CustomScalar` + } + const operators = ALLOWED_OPERATORS[typeName] + return schemaComposer.getOrCreateITC(type.name + `QueryOperatorInput`, itc => + itc.addFields(getOperatorFields(type, operators)) + ) } const convert = ({ @@ -23,7 +151,13 @@ const convert = ({ inputTypeComposer, filterInputComposer, deprecationReason, -}) => { +}: { + schemaComposer: SchemaComposer + typeComposer: AnyComposeType + inputTypeComposer: InputTypeComposer + filterInputComposer?: InputTypeComposer + deprecationReason?: any +}): InputTypeComposer => { const inputTypeName = inputTypeComposer .getTypeName() .replace(/Input$/, `FilterInput`) @@ -52,7 +186,11 @@ const convert = ({ fieldNames.forEach(fieldName => { const fieldConfig = inputTypeComposer.getFieldConfig(fieldName) const type = getNamedType(fieldConfig.type) - const searchable = typeComposer.getFieldExtension(fieldName, `searchable`) + const searchable = + typeComposer instanceof UnionTypeComposer || + typeComposer instanceof ScalarTypeComposer + ? undefined + : typeComposer.getFieldExtension(fieldName, `searchable`) if (searchable === SEARCHABLE_ENUM.NOT_SEARCHABLE) { return @@ -103,35 +241,13 @@ const convert = ({ return convertedITC } -const removeEmptyFields = ( - { schemaComposer, inputTypeComposer }, - cache = new Set() -) => { - const convert = itc => { - if (cache.has(itc)) { - return itc - } - cache.add(itc) - const fields = itc.getFields() - const nonEmptyFields = {} - Object.keys(fields).forEach(fieldName => { - const fieldITC = fields[fieldName] - if (fieldITC instanceof InputTypeComposer) { - const convertedITC = convert(fieldITC) - if (convertedITC.getFieldNames().length) { - nonEmptyFields[fieldName] = convertedITC - } - } else { - nonEmptyFields[fieldName] = fieldITC - } - }) - itc.setFields(nonEmptyFields) - return itc - } - return convert(inputTypeComposer) -} - -const getFilterInput = ({ schemaComposer, typeComposer }) => { +export const getFilterInput = ({ + schemaComposer, + typeComposer, +}: { + schemaComposer: SchemaComposer + typeComposer: ObjectTypeComposer | InterfaceTypeComposer +}): InputTypeComposer => { const typeName = typeComposer.getTypeName() const filterInputComposer = schemaComposer.getOrCreateITC( `${typeName}FilterInput` @@ -141,7 +257,7 @@ const getFilterInput = ({ schemaComposer, typeComposer }) => { // TODO: In Gatsby v2, the NodeInput.id field is of type String, not ID. // Remove this workaround for v3. if ( - inputTypeComposer.hasField(`id`) && + inputTypeComposer?.hasField(`id`) && getNamedType(inputTypeComposer.getFieldType(`id`)).name === `ID` ) { inputTypeComposer.extendField(`id`, { type: `String` }) @@ -154,71 +270,5 @@ const getFilterInput = ({ schemaComposer, typeComposer }) => { filterInputComposer, }) - return removeEmptyFields({ schemaComposer, inputTypeComposer: filterInputTC }) + return removeEmptyFields({ inputTypeComposer: filterInputTC }) } - -module.exports = { getFilterInput, SEARCHABLE_ENUM } - -const EQ = `eq` -const NE = `ne` -const GT = `gt` -const GTE = `gte` -const LT = `lt` -const LTE = `lte` -const IN = `in` -const NIN = `nin` -const REGEX = `regex` -const GLOB = `glob` - -const ALLOWED_OPERATORS = { - Boolean: [EQ, NE, IN, NIN], - Date: [EQ, NE, GT, GTE, LT, LTE, IN, NIN], - Float: [EQ, NE, GT, GTE, LT, LTE, IN, NIN], - ID: [EQ, NE, IN, NIN], - Int: [EQ, NE, GT, GTE, LT, LTE, IN, NIN], - JSON: [EQ, NE, IN, NIN, REGEX, GLOB], - String: [EQ, NE, IN, NIN, REGEX, GLOB], - Enum: [EQ, NE, IN, NIN], - CustomScalar: [EQ, NE, IN, NIN], -} - -const ARRAY_OPERATORS = [IN, NIN] - -const getOperatorFields = (fieldType, operators) => { - const result = {} - operators.forEach(op => { - if (ARRAY_OPERATORS.includes(op)) { - result[op] = [fieldType] - } else { - result[op] = fieldType - } - }) - return result -} - -const getQueryOperatorInput = ({ schemaComposer, type }) => { - let typeName - if (type instanceof GraphQLEnumType) { - typeName = `Enum` - } else if (isBuiltInScalarType(type)) { - typeName = type.name - } else { - typeName = `CustomScalar` - } - const operators = ALLOWED_OPERATORS[typeName] - return schemaComposer.getOrCreateITC(type.name + `QueryOperatorInput`, itc => - itc.addFields(getOperatorFields(type, operators)) - ) -} - -const getQueryOperatorListInput = ({ schemaComposer, inputTypeComposer }) => { - const typeName = inputTypeComposer.getTypeName().replace(/Input/, `ListInput`) - return schemaComposer.getOrCreateITC(typeName, itc => { - itc.addFields({ - elemMatch: inputTypeComposer, - }) - }) -} - -const isBuiltInScalarType = type => - isSpecifiedScalarType(type) || type === GraphQLDate || type === GraphQLJSON diff --git a/packages/gatsby/src/schema/types/node-interface.js b/packages/gatsby/src/schema/types/node-interface.js index b08de93003024..fc16b2cf3ff1d 100644 --- a/packages/gatsby/src/schema/types/node-interface.js +++ b/packages/gatsby/src/schema/types/node-interface.js @@ -1,5 +1,5 @@ const { SORTABLE_ENUM } = require(`./sort`) -const { SEARCHABLE_ENUM } = require(`./filter`) +import { SEARCHABLE_ENUM } from "./filter" const NodeInterfaceFields = [`id`, `parent`, `children`, `internal`]