@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
289 lines (288 loc) • 12.4 kB
JavaScript
import { InvalidPayloadError } from '@directus/errors';
import { isSystemField } from '@directus/system-data';
import { GraphQLBoolean, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from 'graphql';
import { SchemaComposer, toInputObjectType } from 'graphql-compose';
import { fromZodError } from 'zod-validation-error';
import { CollectionsService } from '../../collections.js';
import { ExtensionsService } from '../../extensions.js';
import { FieldsService, systemFieldUpdateSchema } from '../../fields.js';
import { RelationsService } from '../../relations.js';
import { GraphQLService } from '../index.js';
import { getCollectionType } from './get-collection-type.js';
import { getFieldType } from './get-field-type.js';
import { getRelationType } from './get-relation-type.js';
export function resolveSystemAdmin(gql, schema, schemaComposer) {
if (!gql.accountability?.admin) {
return;
}
const Collection = getCollectionType(schemaComposer, schema, 'write');
const Field = getFieldType(schemaComposer, schema, 'write');
const Relation = getRelationType(schemaComposer, schema, 'write');
schemaComposer.Mutation.addFields({
create_collections_item: {
type: Collection,
args: {
data: toInputObjectType(Collection, {
postfix: '_input',
}).addFields({
fields: [toInputObjectType(Field, { postfix: '_input' }).NonNull],
}).NonNull,
concurrentIndexCreation: { type: GraphQLBoolean, defaultValue: false },
},
resolve: async (_, args) => {
const collectionsService = new CollectionsService({
accountability: gql.accountability,
schema: gql.schema,
});
const collectionKey = await collectionsService.createOne(args['data'], {
attemptConcurrentIndex: Boolean(args['concurrentIndexCreation']),
});
return await collectionsService.readOne(collectionKey);
},
},
update_collections_item: {
type: Collection,
args: {
collection: new GraphQLNonNull(GraphQLString),
data: toInputObjectType(Collection, {
postfix: '_input',
}).removeField(['collection', 'schema']).NonNull,
},
resolve: async (_, args) => {
const collectionsService = new CollectionsService({
accountability: gql.accountability,
schema: gql.schema,
});
const collectionKey = await collectionsService.updateOne(args['collection'], args['data']);
return await collectionsService.readOne(collectionKey);
},
},
delete_collections_item: {
type: schemaComposer.createObjectTC({
name: 'delete_collection',
fields: {
collection: GraphQLString,
},
}),
args: {
collection: new GraphQLNonNull(GraphQLString),
},
resolve: async (_, args) => {
const collectionsService = new CollectionsService({
accountability: gql.accountability,
schema: gql.schema,
});
await collectionsService.deleteOne(args['collection']);
return { collection: args['collection'] };
},
},
});
schemaComposer.Mutation.addFields({
create_fields_item: {
type: Field,
args: {
collection: new GraphQLNonNull(GraphQLString),
data: toInputObjectType(Field, { postfix: '_input' }).NonNull,
concurrentIndexCreation: { type: GraphQLBoolean, defaultValue: false },
},
resolve: async (_, args) => {
const service = new FieldsService({
accountability: gql.accountability,
schema: gql.schema,
});
await service.createField(args['collection'], args['data'], undefined, {
attemptConcurrentIndex: Boolean(args['concurrentIndexCreation']),
});
return await service.readOne(args['collection'], args['data'].field);
},
},
update_fields_item: {
type: Field,
args: {
collection: new GraphQLNonNull(GraphQLString),
field: new GraphQLNonNull(GraphQLString),
data: toInputObjectType(Field, { postfix: '_input' }).NonNull,
concurrentIndexCreation: { type: GraphQLBoolean, defaultValue: false },
},
resolve: async (_, args) => {
const service = new FieldsService({
accountability: gql.accountability,
schema: gql.schema,
});
if (isSystemField(args['collection'], args['field'])) {
const validationResult = systemFieldUpdateSchema.safeParse(args['data']);
if (!validationResult.success) {
throw new InvalidPayloadError({ reason: fromZodError(validationResult.error).message });
}
}
await service.updateField(args['collection'], {
...args['data'],
field: args['field'],
}, {
attemptConcurrentIndex: Boolean(args['concurrentIndexCreation']),
});
return await service.readOne(args['collection'], args['field']);
},
},
update_fields_items: {
type: Field,
args: {
collection: new GraphQLNonNull(GraphQLString),
data: [toInputObjectType(Field, { postfix: '_input' }).NonNull],
concurrentIndexCreation: { type: GraphQLBoolean, defaultValue: false },
},
resolve: async (_, args) => {
const service = new FieldsService({
accountability: gql.accountability,
schema: gql.schema,
});
for (const fieldData of args['data']) {
if (isSystemField(args['collection'], fieldData['field'])) {
const validationResult = systemFieldUpdateSchema.safeParse(fieldData);
if (!validationResult.success) {
throw new InvalidPayloadError({ reason: fromZodError(validationResult.error).message });
}
}
}
await service.updateFields(args['collection'], args['data'], {
attemptConcurrentIndex: Boolean(args['concurrentIndexCreation']),
});
return await service.readOne(args['collection'], args['field']);
},
},
delete_fields_item: {
type: schemaComposer.createObjectTC({
name: 'delete_field',
fields: {
collection: GraphQLString,
field: GraphQLString,
},
}),
args: {
collection: new GraphQLNonNull(GraphQLString),
field: new GraphQLNonNull(GraphQLString),
},
resolve: async (_, args) => {
const service = new FieldsService({
accountability: gql.accountability,
schema: gql.schema,
});
await service.deleteField(args['collection'], args['field']);
const { collection, field } = args;
return { collection, field };
},
},
});
schemaComposer.Mutation.addFields({
create_relations_item: {
type: Relation,
args: {
data: toInputObjectType(Relation, { postfix: '_input' }).NonNull,
},
resolve: async (_, args) => {
const relationsService = new RelationsService({
accountability: gql.accountability,
schema: gql.schema,
});
await relationsService.createOne(args['data']);
return await relationsService.readOne(args['data'].collection, args['data'].field);
},
},
update_relations_item: {
type: Relation,
args: {
collection: new GraphQLNonNull(GraphQLString),
field: new GraphQLNonNull(GraphQLString),
data: toInputObjectType(Relation, { postfix: '_input' }).NonNull,
},
resolve: async (_, args) => {
const relationsService = new RelationsService({
accountability: gql.accountability,
schema: gql.schema,
});
await relationsService.updateOne(args['collection'], args['field'], args['data']);
return await relationsService.readOne(args['data'].collection, args['data'].field);
},
},
delete_relations_item: {
type: schemaComposer.createObjectTC({
name: 'delete_relation',
fields: {
collection: GraphQLString,
field: GraphQLString,
},
}),
args: {
collection: new GraphQLNonNull(GraphQLString),
field: new GraphQLNonNull(GraphQLString),
},
resolve: async (_, args) => {
const relationsService = new RelationsService({
accountability: gql.accountability,
schema: gql.schema,
});
await relationsService.deleteOne(args['collection'], args['field']);
return { collection: args['collection'], field: args['field'] };
},
},
});
const Extension = schemaComposer.createObjectTC({
name: 'directus_extensions',
fields: {
bundle: GraphQLString,
name: new GraphQLNonNull(GraphQLString),
schema: schemaComposer.createObjectTC({
name: 'directus_extensions_schema',
fields: {
type: GraphQLString,
local: GraphQLBoolean,
},
}),
meta: schemaComposer.createObjectTC({
name: 'directus_extensions_meta',
fields: {
enabled: GraphQLBoolean,
},
}),
},
});
schemaComposer.Query.addFields({
extensions: {
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(Extension.getType()))),
resolve: async () => {
const service = new ExtensionsService({
accountability: gql.accountability,
schema: gql.schema,
});
return await service.readAll();
},
},
});
schemaComposer.Mutation.addFields({
update_extensions_item: {
type: Extension,
args: {
id: GraphQLID,
data: toInputObjectType(schemaComposer.createObjectTC({
name: 'update_directus_extensions_input',
fields: {
meta: schemaComposer.createObjectTC({
name: 'update_directus_extensions_input_meta',
fields: {
enabled: GraphQLBoolean,
},
}),
},
})),
},
resolve: async (_, args) => {
const extensionsService = new ExtensionsService({
accountability: gql.accountability,
schema: gql.schema,
});
await extensionsService.updateOne(args['id'], args['data']);
return await extensionsService.readOne(args['id']);
},
},
});
}