UNPKG

@dossierhq/graphql

Version:

A library for creating GraphQL servers with Dossier.

1,186 lines (1,185 loc) 62.6 kB
/// <reference types="./GraphQLSchemaGenerator.d.ts" /> import { EventType, FieldType, isComponentItemField, notOk, } from '@dossierhq/core'; import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLString, } from 'graphql'; import { isComponent, loadAdminEntities, loadAdminEntitiesSample, loadAdminEntity, loadAdminEntityList, loadChangelogEvents, loadPublishedEntities, loadPublishedEntitiesSample, loadPublishedEntity, loadPublishedEntityList, } from './DataLoaders.js'; import * as Mutations from './Mutations.js'; import { toAdminComponentInputTypeName, toAdminCreateInputTypeName, toAdminCreatePayloadTypeName, toAdminTypeName, toAdminUpdateInputTypeName, toAdminUpdatePayloadTypeName, toAdminUpsertInputTypeName, toAdminUpsertPayloadTypeName, toPublishedTypeName, } from './NameGenerator.js'; import { DateTimeScalar } from './scalars/DateTimeScalar.js'; import { LocationScalar } from './scalars/LocationScalar.js'; import { TypeRepository } from './TypeRepository.js'; import { assertExhaustive } from './utils/AssertUtils.js'; import { GraphQLJSONObject } from './vendor/GraphQLJsonScalar.js'; function fieldConfigWithArgs(config) { return config; } export class GraphQLSchemaGenerator extends TypeRepository { schema; publishedSchema; constructor({ schema, publishedSchema, }) { super(); this.schema = schema; this.publishedSchema = publishedSchema; } addSharedSupportingTypes() { // Node this.addType(new GraphQLInterfaceType({ name: 'Node', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, }, })); // PageInfo this.addType(new GraphQLObjectType({ name: 'PageInfo', fields: { hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) }, hasPreviousPage: { type: new GraphQLNonNull(GraphQLBoolean) }, startCursor: { type: new GraphQLNonNull(GraphQLString) }, endCursor: { type: new GraphQLNonNull(GraphQLString) }, }, })); const containsEntityTypes = !!(this.schema && this.schema.getEntityTypeCount() > 0) || !!(this.publishedSchema && this.publishedSchema.getEntityTypeCount() > 0); if (!containsEntityTypes) { return; } // EntityReferenceInput this.addType(new GraphQLInputObjectType({ name: 'EntityReferenceInput', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, }, })); // BoundingBoxInput this.addType(new GraphQLInputObjectType({ name: 'BoundingBoxInput', fields: { minLat: { type: new GraphQLNonNull(GraphQLFloat) }, maxLat: { type: new GraphQLNonNull(GraphQLFloat) }, minLng: { type: new GraphQLNonNull(GraphQLFloat) }, maxLng: { type: new GraphQLNonNull(GraphQLFloat) }, }, })); } addPublishedSupportingTypes(publishedSchema) { if (publishedSchema.getEntityTypeCount() === 0) { return; } // PublishedEntityType const entityTypeEnumValues = {}; for (const entitySpec of publishedSchema.spec.entityTypes) { entityTypeEnumValues[entitySpec.name] = {}; } this.addType(new GraphQLEnumType({ name: 'PublishedEntityType', values: entityTypeEnumValues, })); if (publishedSchema.getComponentTypeCount() > 0) { // PublishedComponentType const componentTypeEnumValues = {}; for (const componentSpec of publishedSchema.spec.componentTypes) { componentTypeEnumValues[componentSpec.name] = {}; } this.addType(new GraphQLEnumType({ name: 'PublishedComponentType', values: componentTypeEnumValues, })); } // PublishedEntityInfo this.addType(new GraphQLObjectType({ name: 'PublishedEntityInfo', fields: { name: { type: new GraphQLNonNull(GraphQLString) }, authKey: { type: new GraphQLNonNull(GraphQLString) }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, valid: { type: new GraphQLNonNull(GraphQLBoolean) }, }, })); // PublishedEntity this.addType(new GraphQLInterfaceType({ name: 'PublishedEntity', interfaces: this.getInterfaces('Node'), fields: { id: { type: new GraphQLNonNull(GraphQLID) }, info: { type: new GraphQLNonNull(this.getOutputType('PublishedEntityInfo')) }, }, })); if (publishedSchema.getComponentTypeCount() > 0) { // PublishedValue this.addType(new GraphQLInterfaceType({ name: toPublishedTypeName('Component'), fields: { type: { type: new GraphQLNonNull(this.getEnumType('PublishedComponentType')) }, }, })); } // PublishedRichText this.addType(new GraphQLObjectType({ name: 'PublishedRichText', fields: { root: { type: new GraphQLNonNull(GraphQLJSONObject) }, entities: { type: new GraphQLList(this.getInterface('PublishedEntity')) }, }, })); // PublishedUniqueIndex const uniqueIndexNames = publishedSchema.spec.indexes .filter((it) => it.type === 'unique') .map((it) => it.name); if (uniqueIndexNames.length > 0) { const uniqueIndexEnumValues = {}; for (const indexName of uniqueIndexNames) { uniqueIndexEnumValues[indexName] = {}; } this.addType(new GraphQLEnumType({ name: 'PublishedUniqueIndex', values: uniqueIndexEnumValues, })); } // PublishedEntityEdge this.addType(new GraphQLObjectType({ name: 'PublishedEntityEdge', fields: { node: { type: this.getOutputType('PublishedEntity') }, cursor: { type: new GraphQLNonNull(GraphQLString) }, }, })); // PublishedEntityConnection this.addType(new GraphQLObjectType({ name: 'PublishedEntityConnection', fields: { pageInfo: { type: new GraphQLNonNull(this.getOutputType('PageInfo')) }, edges: { type: new GraphQLList(this.getOutputType('PublishedEntityEdge')) }, totalCount: { type: new GraphQLNonNull(GraphQLInt) }, }, })); // PublishedEntitySamplingPayload this.addType(new GraphQLObjectType({ name: 'PublishedEntitySamplingPayload', fields: { seed: { type: new GraphQLNonNull(GraphQLInt) }, totalCount: { type: new GraphQLNonNull(GraphQLInt) }, items: { type: new GraphQLList(this.getOutputType('PublishedEntity')) }, }, })); // PublishedEntityQueryOrder this.addType(new GraphQLEnumType({ name: 'PublishedEntityQueryOrder', values: { createdAt: {}, name: {} }, })); // PublishedQueryInput const sharedQueryInputFields = { authKeys: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) }, entityTypes: { type: new GraphQLList(new GraphQLNonNull(this.getEnumType('PublishedEntityType'))), }, ...(publishedSchema.getComponentTypeCount() > 0 ? { componentTypes: { type: new GraphQLList(new GraphQLNonNull(this.getEnumType('PublishedComponentType'))), }, } : {}), linksTo: { type: this.getInputType('EntityReferenceInput') }, linksFrom: { type: this.getInputType('EntityReferenceInput') }, boundingBox: { type: this.getInputType('BoundingBoxInput') }, text: { type: GraphQLString }, }; this.addType(new GraphQLInputObjectType({ name: 'PublishedQueryInput', fields: sharedQueryInputFields, })); // PublishedEntitiesQueryInput this.addType(new GraphQLInputObjectType({ name: 'PublishedEntitiesQueryInput', fields: { ...sharedQueryInputFields, order: { type: this.getEnumType('PublishedEntityQueryOrder') }, reverse: { type: GraphQLBoolean }, }, })); } addPublishedEntityTypes(publishedSchema) { for (const entitySpec of publishedSchema.spec.entityTypes) { this.addPublishedEntityType(entitySpec); } } addPublishedEntityType(entitySpec) { // PublishedFooFields const fieldsTypeName = `Published${entitySpec.name}Fields`; if (entitySpec.fields.length > 0) { this.addType(new GraphQLObjectType({ name: fieldsTypeName, fields: () => { const fields = {}; this.addTypeSpecificationOutputFields(entitySpec, fields, false); return fields; }, })); } // PublishedFoo this.addType(new GraphQLObjectType({ name: toPublishedTypeName(entitySpec.name), interfaces: this.getInterfaces('Node', 'PublishedEntity'), isTypeOf: (source, _context, _info) => source.info.type === entitySpec.name, fields: () => { const fields = { id: { type: new GraphQLNonNull(GraphQLID) }, info: { type: new GraphQLNonNull(this.getOutputType('PublishedEntityInfo')) }, }; if (entitySpec.fields.length > 0) { fields.fields = { type: new GraphQLNonNull(this.getOutputType(fieldsTypeName)) }; } return fields; }, })); } addPublishedComponentTypes(publishedSchema) { for (const componentSpec of publishedSchema.spec.componentTypes) { this.addPublishedComponentType(componentSpec); } } addPublishedComponentType(componentSpec) { // PublishedFoo this.addType(new GraphQLObjectType({ name: toPublishedTypeName(componentSpec.name), interfaces: this.getInterfaces(toPublishedTypeName('Component')), isTypeOf: (source, _context, _info) => source.type === componentSpec.name, fields: () => { const fields = { type: { type: new GraphQLNonNull(this.getEnumType('PublishedComponentType')) }, }; this.addTypeSpecificationOutputFields(componentSpec, fields, false); return fields; }, })); } addAdminSupportingTypes(schema) { if (schema.getEntityTypeCount() === 0) { return; } this.addChangelogSupportingTypes(); // EntityType const entityTypeEnumValues = {}; for (const entitySpec of schema.spec.entityTypes) { entityTypeEnumValues[entitySpec.name] = {}; } this.addType(new GraphQLEnumType({ name: 'EntityType', values: entityTypeEnumValues, })); if (schema.getComponentTypeCount() > 0) { // ComponentType const componentTypeEnumValues = {}; for (const componentSpec of schema.spec.componentTypes) { componentTypeEnumValues[componentSpec.name] = {}; } this.addType(new GraphQLEnumType({ name: 'ComponentType', values: componentTypeEnumValues, })); } // EntityStatus this.addType(new GraphQLEnumType({ name: 'EntityStatus', values: { draft: {}, published: {}, modified: {}, withdrawn: {}, archived: {}, }, })); // EntityInfo this.addType(new GraphQLObjectType({ name: 'EntityInfo', fields: { type: { type: new GraphQLNonNull(this.getOutputType('EntityType')) }, name: { type: new GraphQLNonNull(GraphQLString) }, version: { type: new GraphQLNonNull(GraphQLInt) }, authKey: { type: new GraphQLNonNull(GraphQLString) }, status: { type: new GraphQLNonNull(this.getOutputType('EntityStatus')) }, valid: { type: new GraphQLNonNull(GraphQLBoolean) }, validPublished: { type: GraphQLBoolean }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, updatedAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // EntityCreateInfo this.addType(new GraphQLInputObjectType({ name: 'EntityCreateInfo', fields: { type: { type: this.getEnumType('EntityType') }, name: { type: new GraphQLNonNull(GraphQLString) }, version: { type: GraphQLInt }, authKey: { type: GraphQLString }, }, })); // EntityCreateEffect this.addType(new GraphQLEnumType({ name: 'EntityCreateEffect', values: { created: {}, createdAndPublished: {}, none: {}, }, })); // EntityUpdateInfo this.addType(new GraphQLInputObjectType({ name: 'EntityUpdateInfo', fields: { type: { type: this.getEnumType('EntityType') }, name: { type: GraphQLString }, version: { type: GraphQLInt }, authKey: { type: GraphQLString }, }, })); // EntityUpdateEffect this.addType(new GraphQLEnumType({ name: 'EntityUpdateEffect', values: { updated: {}, updatedAndPublished: {}, published: {}, none: {}, }, })); // EntityUpsertInfo this.addType(new GraphQLInputObjectType({ name: 'EntityUpsertInfo', fields: { type: { type: new GraphQLNonNull(this.getEnumType('EntityType')) }, name: { type: new GraphQLNonNull(GraphQLString) }, authKey: { type: GraphQLString }, }, })); // EntityUpsertEffect this.addType(new GraphQLEnumType({ name: 'EntityUpsertEffect', values: { created: {}, createdAndPublished: {}, updated: {}, updatedAndPublished: {}, published: {}, none: {}, }, })); // Entity this.addType(new GraphQLInterfaceType({ name: 'Entity', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, info: { type: new GraphQLNonNull(this.getOutputType('EntityInfo')) }, changelogEvents: { type: this.getOutputType('EntityChangelogEventConnection'), args: { query: { type: this.getInputType('ChangelogEventQueryInput') }, first: { type: GraphQLInt }, after: { type: GraphQLString }, last: { type: GraphQLInt }, before: { type: GraphQLString }, }, }, }, })); // UniqueIndex const uniqueIndexNames = schema.spec.indexes .filter((it) => it.type === 'unique') .map((it) => it.name); if (uniqueIndexNames.length > 0) { const uniqueIndexEnumValues = {}; for (const indexName of uniqueIndexNames) { uniqueIndexEnumValues[indexName] = {}; } this.addType(new GraphQLEnumType({ name: 'UniqueIndex', values: uniqueIndexEnumValues, })); } // EntityEdge this.addType(new GraphQLObjectType({ name: 'EntityEdge', fields: { node: { type: this.getOutputType('Entity') }, cursor: { type: new GraphQLNonNull(GraphQLString) }, }, })); // EntityConnection this.addType(new GraphQLObjectType({ name: 'EntityConnection', fields: { pageInfo: { type: new GraphQLNonNull(this.getOutputType('PageInfo')) }, edges: { type: new GraphQLList(this.getOutputType('EntityEdge')) }, totalCount: { type: new GraphQLNonNull(GraphQLInt) }, }, })); // EntitySamplingPayload this.addType(new GraphQLObjectType({ name: 'EntitySamplingPayload', fields: { seed: { type: new GraphQLNonNull(GraphQLInt) }, totalCount: { type: new GraphQLNonNull(GraphQLInt) }, items: { type: new GraphQLList(this.getOutputType('Entity')) }, }, })); // EntityQueryOrder this.addType(new GraphQLEnumType({ name: 'EntityQueryOrder', values: { createdAt: {}, updatedAt: {}, name: {} }, })); // EntitySharedQueryInput const sharedQueryInputFields = { authKeys: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) }, entityTypes: { type: new GraphQLList(new GraphQLNonNull(this.getEnumType('EntityType'))), }, ...(schema.getComponentTypeCount() > 0 ? { componentTypes: { type: new GraphQLList(new GraphQLNonNull(this.getEnumType('ComponentType'))), }, } : {}), status: { type: new GraphQLList(new GraphQLNonNull(this.getEnumType('EntityStatus'))) }, valid: { type: GraphQLBoolean }, linksTo: { type: this.getInputType('EntityReferenceInput') }, linksFrom: { type: this.getInputType('EntityReferenceInput') }, boundingBox: { type: this.getInputType('BoundingBoxInput') }, text: { type: GraphQLString }, }; this.addType(new GraphQLInputObjectType({ name: 'EntitySharedQueryInput', fields: sharedQueryInputFields, })); // EntityQueryInput this.addType(new GraphQLInputObjectType({ name: 'EntityQueryInput', fields: { ...sharedQueryInputFields, order: { type: this.getEnumType('EntityQueryOrder') }, reverse: { type: GraphQLBoolean }, }, })); // EntityVersionReferenceInput this.addType(new GraphQLInputObjectType({ name: 'EntityVersionReferenceInput', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, version: { type: new GraphQLNonNull(GraphQLInt) }, }, })); if (this.schema && this.schema.getComponentTypeCount() > 0) { // Component this.addType(new GraphQLInterfaceType({ name: 'Component', fields: { type: { type: new GraphQLNonNull(this.getEnumType('ComponentType')) }, }, })); } // RichText this.addType(new GraphQLObjectType({ name: 'RichText', fields: { root: { type: new GraphQLNonNull(GraphQLJSONObject) }, entities: { type: new GraphQLList(this.getInterface('Entity')) }, }, })); // RichTextInput this.addType(new GraphQLInputObjectType({ name: 'RichTextInput', fields: { root: { type: new GraphQLNonNull(GraphQLJSONObject) }, }, })); // EntityVersionInfo this.addType(new GraphQLObjectType({ name: 'EntityVersionInfo', fields: { version: { type: new GraphQLNonNull(GraphQLInt) }, published: { type: new GraphQLNonNull(GraphQLBoolean) }, createdBy: { type: new GraphQLNonNull(GraphQLID) }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // EntityPublishEffect this.addType(new GraphQLEnumType({ name: 'EntityPublishEffect', values: { published: {}, none: {}, }, })); // EntityPublishPayload this.addType(new GraphQLObjectType({ name: 'EntityPublishPayload', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, status: { type: new GraphQLNonNull(this.getEnumType('EntityStatus')) }, effect: { type: new GraphQLNonNull(this.getEnumType('EntityPublishEffect')) }, updatedAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // EntityUnpublishEffect this.addType(new GraphQLEnumType({ name: 'EntityUnpublishEffect', values: { unpublished: {}, none: {}, }, })); // EntityUnpublishPayload this.addType(new GraphQLObjectType({ name: 'EntityUnpublishPayload', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, status: { type: new GraphQLNonNull(this.getEnumType('EntityStatus')) }, effect: { type: new GraphQLNonNull(this.getEnumType('EntityUnpublishEffect')) }, updatedAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // EntityArchiveEffect this.addType(new GraphQLEnumType({ name: 'EntityArchiveEffect', values: { archived: {}, none: {}, }, })); // EntityArchivePayload this.addType(new GraphQLObjectType({ name: 'EntityArchivePayload', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, status: { type: new GraphQLNonNull(this.getEnumType('EntityStatus')) }, effect: { type: new GraphQLNonNull(this.getEnumType('EntityArchiveEffect')) }, updatedAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // EntityUnarchiveEffect this.addType(new GraphQLEnumType({ name: 'EntityUnarchiveEffect', values: { unarchived: {}, none: {}, }, })); // EntityUnarchivePayload this.addType(new GraphQLObjectType({ name: 'EntityUnarchivePayload', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, status: { type: new GraphQLNonNull(this.getEnumType('EntityStatus')) }, effect: { type: new GraphQLNonNull(this.getEnumType('EntityUnarchiveEffect')) }, updatedAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // EntityDeleteEffect this.addType(new GraphQLEnumType({ name: 'EntityDeleteEffect', values: { deleted: {}, }, })); // EntityDeletePayload this.addType(new GraphQLObjectType({ name: 'EntityDeletePayload', fields: { effect: { type: new GraphQLNonNull(this.getEnumType('EntityDeleteEffect')) }, deletedAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // AdvisoryLockPayload this.addType(new GraphQLObjectType({ name: 'AdvisoryLockPayload', fields: { name: { type: new GraphQLNonNull(GraphQLString) }, handle: { type: new GraphQLNonNull(GraphQLInt) }, }, })); // AdvisoryLockReleasePayload this.addType(new GraphQLObjectType({ name: 'AdvisoryLockReleasePayload', fields: { name: { type: new GraphQLNonNull(GraphQLString) }, }, })); } addChangelogSupportingTypes() { // EventType this.addType(new GraphQLEnumType({ name: 'EventType', values: { createPrincipal: {}, updateSchema: {}, createEntity: {}, createAndPublishEntity: {}, updateEntity: {}, updateAndPublishEntity: {}, publishEntities: {}, unpublishEntities: {}, archiveEntity: {}, unarchiveEntity: {}, deleteEntities: {}, }, })); // ChangelogEventQueryInput this.addType(new GraphQLInputObjectType({ name: 'ChangelogEventQueryInput', fields: { reverse: { type: GraphQLBoolean }, createdBy: { type: GraphQLID }, types: { type: new GraphQLList(new GraphQLNonNull(this.getEnumType('EventType'))) }, }, })); // ChangelogEvent this.addType(new GraphQLInterfaceType({ name: 'ChangelogEvent', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, type: { type: new GraphQLNonNull(this.getEnumType('EventType')) }, createdBy: { type: new GraphQLNonNull(GraphQLID) }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // CreatePrincipalChangelogEvent this.addType(new GraphQLObjectType({ name: 'CreatePrincipalChangelogEvent', interfaces: this.getInterfaces('ChangelogEvent'), isTypeOf: (source, _context, _info) => source.type === EventType.createPrincipal, fields: { id: { type: new GraphQLNonNull(GraphQLID) }, type: { type: new GraphQLNonNull(this.getEnumType('EventType')) }, createdBy: { type: new GraphQLNonNull(GraphQLID) }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, }, })); // SchemaChangelogEvent this.addType(new GraphQLObjectType({ name: 'SchemaChangelogEvent', interfaces: this.getInterfaces('ChangelogEvent'), isTypeOf: (source, _context, _info) => source.type === EventType.updateSchema, fields: { id: { type: new GraphQLNonNull(GraphQLID) }, type: { type: new GraphQLNonNull(this.getEnumType('EventType')) }, createdBy: { type: new GraphQLNonNull(GraphQLID) }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, version: { type: new GraphQLNonNull(GraphQLInt) }, }, })); // EntityChangelogEventEntityInfo this.addType(new GraphQLObjectType({ name: 'EntityChangelogEventEntityInfo', fields: { id: { type: new GraphQLNonNull(GraphQLID) }, version: { type: new GraphQLNonNull(GraphQLInt) }, type: { type: new GraphQLNonNull(GraphQLString) }, name: { type: new GraphQLNonNull(GraphQLString) }, }, })); // EntityChangelogEvent this.addType(new GraphQLObjectType({ name: 'EntityChangelogEvent', interfaces: this.getInterfaces('ChangelogEvent'), isTypeOf: (source, _context, _info) => source.type !== EventType.updateSchema && source.type !== EventType.createPrincipal, fields: { id: { type: new GraphQLNonNull(GraphQLID) }, type: { type: new GraphQLNonNull(this.getEnumType('EventType')) }, createdBy: { type: new GraphQLNonNull(GraphQLID) }, createdAt: { type: new GraphQLNonNull(DateTimeScalar) }, entities: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(this.getOutputType('EntityChangelogEventEntityInfo')))), }, unauthorizedEntityCount: { type: new GraphQLNonNull(GraphQLInt) }, }, })); // ChangelogEventEdge this.addType(new GraphQLObjectType({ name: 'ChangelogEventEdge', fields: { node: { type: this.getOutputType('ChangelogEvent') }, cursor: { type: new GraphQLNonNull(GraphQLString) }, }, })); // ChangelogEventConnection this.addType(new GraphQLObjectType({ name: 'ChangelogEventConnection', fields: { pageInfo: { type: new GraphQLNonNull(this.getOutputType('PageInfo')) }, edges: { type: new GraphQLList(this.getOutputType('ChangelogEventEdge')) }, totalCount: { type: new GraphQLNonNull(GraphQLInt) }, }, })); // EntityChangelogEventEdge this.addType(new GraphQLObjectType({ name: 'EntityChangelogEventEdge', fields: { node: { type: this.getOutputType('EntityChangelogEvent') }, cursor: { type: new GraphQLNonNull(GraphQLString) }, }, })); // EntityChangelogEventConnection this.addType(new GraphQLObjectType({ name: 'EntityChangelogEventConnection', fields: { pageInfo: { type: new GraphQLNonNull(this.getOutputType('PageInfo')) }, edges: { type: new GraphQLList(this.getOutputType('EntityChangelogEventEdge')) }, totalCount: { type: new GraphQLNonNull(GraphQLInt) }, }, })); } addEntityTypes(schema) { for (const entitySpec of schema.spec.entityTypes) { this.addEntityType(entitySpec); } } addEntityType(entitySpec) { // FooFields const fieldsName = entitySpec.fields.length > 0 ? `${toAdminTypeName(entitySpec.name)}Fields` : null; if (fieldsName) { this.addType(new GraphQLObjectType({ name: fieldsName, fields: () => { const fields = {}; this.addTypeSpecificationOutputFields(entitySpec, fields, true); return fields; }, })); } // Foo this.addType(new GraphQLObjectType({ name: toAdminTypeName(entitySpec.name), interfaces: this.getInterfaces(toAdminTypeName('Entity')), isTypeOf: (source, _context, _info) => source.info.type === entitySpec.name, fields: () => { const fields = { id: { type: new GraphQLNonNull(GraphQLID) }, info: { type: new GraphQLNonNull(this.getOutputType('EntityInfo')) }, changelogEvents: { type: this.getOutputType('EntityChangelogEventConnection'), args: { query: { type: this.getInputType('ChangelogEventQueryInput') }, first: { type: GraphQLInt }, after: { type: GraphQLString }, last: { type: GraphQLInt }, before: { type: GraphQLString }, }, }, }; if (fieldsName) { fields.fields = { type: new GraphQLNonNull(this.getOutputType(fieldsName)) }; } return fields; }, })); // FooFieldsInput const inputFieldsName = entitySpec.fields.length > 0 ? `${toAdminTypeName(entitySpec.name)}FieldsInput` : null; if (inputFieldsName) { this.addType(new GraphQLInputObjectType({ name: inputFieldsName, fields: () => { const fields = {}; this.addTypeSpecificationInputFields(entitySpec, fields); return fields; }, })); } // FooCreateInput this.addType(new GraphQLInputObjectType({ name: toAdminCreateInputTypeName(entitySpec.name), fields: () => { const fields = { id: { type: GraphQLID }, info: { type: new GraphQLNonNull(this.getInputType('EntityCreateInfo')) }, }; if (inputFieldsName) { fields.fields = { type: new GraphQLNonNull(this.getInputType(inputFieldsName)) }; } return fields; }, })); // FooCreatePayload this.addType(new GraphQLObjectType({ name: toAdminCreatePayloadTypeName(entitySpec.name), fields: () => { const fields = { effect: { type: new GraphQLNonNull(this.getEnumType('EntityCreateEffect')) }, entity: { type: new GraphQLNonNull(this.getOutputType(toAdminTypeName(entitySpec.name))), }, }; return fields; }, })); // FooUpdateInput this.addType(new GraphQLInputObjectType({ name: toAdminUpdateInputTypeName(entitySpec.name), fields: () => { const fields = { id: { type: new GraphQLNonNull(GraphQLID) }, info: { type: this.getInputType('EntityUpdateInfo') }, }; if (inputFieldsName) { fields.fields = { type: new GraphQLNonNull(this.getInputType(inputFieldsName)) }; } return fields; }, })); // FooUpdatePayload this.addType(new GraphQLObjectType({ name: toAdminUpdatePayloadTypeName(entitySpec.name), fields: () => { const fields = { effect: { type: new GraphQLNonNull(this.getEnumType('EntityUpdateEffect')) }, entity: { type: new GraphQLNonNull(this.getOutputType(toAdminTypeName(entitySpec.name))), }, }; return fields; }, })); // FooUpsertInput this.addType(new GraphQLInputObjectType({ name: toAdminUpsertInputTypeName(entitySpec.name), fields: () => { const fields = { id: { type: new GraphQLNonNull(GraphQLID) }, info: { type: new GraphQLNonNull(this.getInputType('EntityUpsertInfo')) }, }; if (inputFieldsName) { fields.fields = { type: new GraphQLNonNull(this.getInputType(inputFieldsName)) }; } return fields; }, })); // AdminFooUpsertPayload this.addType(new GraphQLObjectType({ name: toAdminUpsertPayloadTypeName(entitySpec.name), fields: () => { const fields = { effect: { type: new GraphQLNonNull(this.getEnumType('EntityUpsertEffect')) }, entity: { type: new GraphQLNonNull(this.getOutputType(toAdminTypeName(entitySpec.name))), }, }; return fields; }, })); } addAdminComponentTypes(schema) { for (const componentSpec of schema.spec.componentTypes) { this.addAdminComponentType(componentSpec); } } addAdminComponentType(componentSpec) { // Foo this.addType(new GraphQLObjectType({ name: toAdminTypeName(componentSpec.name), interfaces: this.getInterfaces(toAdminTypeName('Component')), isTypeOf: (source, _context, _info) => source.type === componentSpec.name, fields: () => { const fields = { type: { type: new GraphQLNonNull(this.getEnumType('ComponentType')) }, }; this.addTypeSpecificationOutputFields(componentSpec, fields, true); return fields; }, })); this.addType(new GraphQLInputObjectType({ name: toAdminComponentInputTypeName(componentSpec.name), fields: () => { const fields = { type: { type: new GraphQLNonNull(this.getEnumType('ComponentType')) }, }; this.addTypeSpecificationInputFields(componentSpec, fields); return fields; }, })); } addTypeSpecificationOutputFields(typeSpec, fields, isAdmin) { for (const fieldSpec of typeSpec.fields) { let fieldType; switch (fieldSpec.type) { case FieldType.Boolean: fieldType = GraphQLBoolean; break; case FieldType.Component: fieldType = this.getOrCreateValueUnion(isAdmin, fieldSpec.componentTypes ?? []); break; case FieldType.Reference: fieldType = this.getOrCreateEntityUnion(isAdmin, fieldSpec.entityTypes ?? []); break; case FieldType.Location: fieldType = LocationScalar; break; case FieldType.Number: fieldType = fieldSpec.integer ? GraphQLInt : GraphQLFloat; break; case FieldType.RichText: fieldType = this.getOutputType(toAdminTypeName('RichText', isAdmin)); break; case FieldType.String: fieldType = GraphQLString; break; default: assertExhaustive(fieldSpec); } if (fieldSpec.list) { fieldType = new GraphQLList(new GraphQLNonNull(fieldType)); } if (fieldSpec.required && !isAdmin) { fieldType = new GraphQLNonNull(fieldType); } fields[fieldSpec.name] = { type: fieldType }; } } addTypeSpecificationInputFields(typeSpec, fields) { for (const fieldSpec of typeSpec.fields) { let fieldType; switch (fieldSpec.type) { case FieldType.Boolean: fieldType = GraphQLBoolean; break; case FieldType.Reference: fieldType = this.getInputType('EntityReferenceInput'); break; case FieldType.Location: fieldType = LocationScalar; break; case FieldType.Number: fieldType = fieldSpec.integer ? GraphQLInt : GraphQLFloat; break; case FieldType.RichText: fieldType = this.getInputType('RichTextInput'); break; case FieldType.String: fieldType = GraphQLString; break; case FieldType.Component: { //TODO use GraphQLJSON. Is it still needed or is normal fieldType enough? fields[`${fieldSpec.name}Json`] = { type: GraphQLString }; fieldType = this.getValueInputType(fieldSpec.componentTypes ?? []); break; } default: assertExhaustive(fieldSpec); } if (fieldType) { fields[fieldSpec.name] = { type: fieldSpec.list ? new GraphQLList(new GraphQLNonNull(fieldType)) : fieldType, }; } } } buildQueryFieldNode(publishedSchema) { return fieldConfigWithArgs({ type: this.getInterface('Node'), args: { id: { type: new GraphQLNonNull(GraphQLID) }, }, resolve: async (_source, args, context, _info) => { return await loadPublishedEntity(publishedSchema, context, args); }, }); } buildQueryFieldNodes(publishedSchema) { return fieldConfigWithArgs({ type: new GraphQLList(this.getInterface('Node')), args: { ids: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))) }, }, resolve: async (_source, args, context, _info) => { return await loadPublishedEntityList(publishedSchema, context, args.ids); }, }); } buildQueryFieldPublishedEntity(publishedSchema) { if (publishedSchema.spec.indexes.length === 0) { return fieldConfigWithArgs({ type: this.getInterface('PublishedEntity'), args: { id: { type: new GraphQLNonNull(GraphQLID) }, }, resolve: async (_source, args, context, _info) => { return await loadPublishedEntity(publishedSchema, context, args); }, }); } return fieldConfigWithArgs({ type: this.getInterface('PublishedEntity'), args: { id: { type: GraphQLID }, index: { type: this.getInputType('PublishedUniqueIndex') }, value: { type: GraphQLString }, }, resolve: async (_source, args, context, _info) => { let reference; if (args.id) { reference = { id: args.id }; } else if (args.index && args.value) { reference = { index: args.index, value: args.value }; } else { throw new Error('Either id or index and value must be specified'); } return await loadPublishedEntity(publishedSchema, context, reference); }, }); } buildQueryFieldAdminEntity(schema) { if (schema.spec.indexes.length === 0) { return fieldConfigWithArgs({ type: this.getInterface('Entity'), args: { id: { type: new GraphQLNonNull(GraphQLID) }, version: { type: GraphQLInt }, }, resolve: async (_source, args, context, _info) => { let reference; if (typeof args.version === 'number') { reference = { id: args.id, version: args.version }; } else { reference = { id: args.id }; } return await loadAdminEntity(schema, context, reference); }, }); } return fieldConfigWithArgs({ type: this.getInterface('Entity'), args: { id: { type: GraphQLID }, version: { type: GraphQLInt }, index: { type: this.getInputType('UniqueIndex') }, value: { type: GraphQLString }, }, resolve: async (_source, args, context, _info) => { let reference; if (args.id && typeof args.version === 'number') { reference = { id: args.id, version: args.version }; } else if (args.id) { reference = { id: args.id }; } else if (args.index && args.value) { reference = { index: args.index, value: args.value }; } else { throw new Error('Either (id), (id and version) or (index and value) must be specified'); } return await loadAdminEntity(schema, context, reference); }, }); } buildQueryFieldAdminEntityList(schema) { return fieldConfigWithArgs({ type: new GraphQLList(this.getInterface('Entity')), args: { ids: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))) }, }, resolve: async (_source, args, context, _info) => { return await loadAdminEntityList(schema, context, args.ids); }, }); } buildQueryFieldAdminEntitiesSample(schema) { return fieldConfigWithArgs({ type: this.getOutputType('EntitySamplingPayload'), args: { query: { type: this.getInputType('EntitySharedQueryInput') }, seed: { type: GraphQLInt }, count: { type: GraphQLInt }, }, resolve: async (_source, args, context, _info) => { const { query, seed, count } = args; const options = { seed, count }; return await loadAdminEntitiesSample(schema, context, query, options); }, }); } buildQueryFieldAdminEntities(schema) { return fieldConfigWithArgs({ type: this.getOutputType('EntityConnection'), args: { query: { type: this.getInputType('EntityQueryInput') }, first: { type: GraphQLInt }, after: { type: GraphQLString }, last: { type: GraphQLInt }, before: { type: GraphQLString }, }, resolve: async (_source, args, context, info) => { const { query, first, after, last, before } = args; const paging = { first, after, last, before }; return await loadAdminEntities(schema, context, query, paging, info); }, }); } buildQueryFieldPublishedSampleEntities(publishedSchema) { return fieldConfigWithArgs({ type: this.getOutputType('PublishedEntitySamplingPayload'), args: { query: { type: this.getInputType('PublishedQueryInput') }, seed: { type: GraphQLInt }, count: { type: GraphQLInt }, }, resolve: async (_source, args, context, _info) => { const { query, count, seed } = args; const options = { count, seed }; return await loadPublishedEntitiesSample(publishedSchema, context, query, options); }, }); } buildQueryFieldPublishedEntities(publishedSchema) { return fieldConfigWithArgs({ type: this.getOutputType('PublishedEntityConnection'), args: { query: { type: this.getInputType('PublishedEntitiesQueryInput') }, first: { type: GraphQLInt }, after: { type: GraphQLString }, last: { type: GraphQLInt }, before: { type: GraphQLString }, }, resolve: async (_source, args, context, info) => { const { query, first, after, last, before } = args; const paging = { first, after, last, before }; return await loadPublishedEntities(publishedSchema, context, query, paging, info); }, }); } buildQueryFieldChangelogEvents() { return fieldConfigWithArgs({ type: this.getOutputType('ChangelogEventConnection'), args: { query: { type: this.getInputType('ChangelogEventQueryInput') }, first: { type: GraphQLInt }, after: { type: GraphQLString }, last: { type: GraphQLInt }, before: { type: GraphQLString }, }, resolve: async (_source, args, context, info) => { const { query, first, after, last, before } = args; const paging = { first, after, last, before }; return await loadChangelogEvents(context, query, paging, info); }, }); } buildQueryType() { return new GraphQLObjectType({ name: 'Query', fields: { ...(this.publishedSchema && this.publishedSchema.getEntityTypeCount() > 0 ? { node: this.buildQueryFieldNode(this.publishedSchema), nodes: this.buildQueryFieldNodes(this.publishedSchema), publishedEntity: this.buildQueryFieldPublishedEntity(this.publishedSchema), publishedEntities: this.buildQueryFieldPublishedEntities(this.publishedSchema), publishedEntitiesSample: this.buildQueryFieldPublishedSampleEntities(this.publishedSchema), } : {}), ...(this.schema && this.schema.getEntityTypeCount() > 0 ? { entity: this.buildQueryFieldAdminEntity(this.schema), entityList: this.buildQueryFieldAdminEntityList(this.schema), entities: this.buildQueryFieldAdminEntities(this.schema),