mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 07:03:38 +00:00
chore(graphql): migration to basic ts (#18696)
This commit is contained in:
parent
6129ef3c6c
commit
5c5ae949bf
@ -3,6 +3,7 @@ import type { Utils } from '../..';
|
||||
|
||||
export interface EnumerationProperties<TValues extends string[] = []> {
|
||||
enum: TValues;
|
||||
enumName?: string;
|
||||
}
|
||||
|
||||
export type Enumeration<TValues extends string[] = []> = Attribute.OfType<'enumeration'> &
|
||||
|
||||
@ -16,9 +16,9 @@ export type RouteConfig = {
|
||||
};
|
||||
|
||||
export type Route = {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'ALL' | 'OPTIONS' | 'HEAD';
|
||||
path: string;
|
||||
handler: string | MiddlewareHandler;
|
||||
handler: string | MiddlewareHandler | MiddlewareHandler[];
|
||||
info: RouteInfo;
|
||||
config?: RouteConfig;
|
||||
};
|
||||
|
||||
@ -146,3 +146,5 @@ export interface Component extends Schema {
|
||||
|
||||
category: string;
|
||||
}
|
||||
|
||||
export type Any = SingleType | CollectionType | Component;
|
||||
|
||||
@ -1 +1 @@
|
||||
dist
|
||||
dist
|
||||
|
||||
@ -2,12 +2,8 @@
|
||||
"root": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["admin/**/*"],
|
||||
"extends": ["custom/front"]
|
||||
},
|
||||
{
|
||||
"files": ["**/*"],
|
||||
"excludedFiles": ["admin/**/*"],
|
||||
"files": ["**"],
|
||||
"excludedFiles": ["admin/**/*", "server/**/*"],
|
||||
"extends": ["custom/back"]
|
||||
}
|
||||
]
|
||||
|
||||
2
packages/plugins/graphql/.gitignore
vendored
2
packages/plugins/graphql/.gitignore
vendored
@ -11,3 +11,5 @@ npm-debug.log
|
||||
.idea
|
||||
|
||||
generated
|
||||
|
||||
dist/
|
||||
4
packages/plugins/graphql/admin/.eslintrc
Normal file
4
packages/plugins/graphql/admin/.eslintrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["custom/front/typescript"]
|
||||
}
|
||||
@ -2,19 +2,21 @@ import { prefixPluginTranslations } from '@strapi/helper-plugin';
|
||||
|
||||
import pluginPkg from '../../package.json';
|
||||
|
||||
import pluginId from './pluginId';
|
||||
import { pluginId } from './pluginId';
|
||||
|
||||
const name = pluginPkg.strapi.name;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default {
|
||||
register(app) {
|
||||
// TODO: we need to have the type for StrapiApp done from `@strapi/admin` package.
|
||||
register(app: any) {
|
||||
app.registerPlugin({
|
||||
id: pluginId,
|
||||
name,
|
||||
});
|
||||
},
|
||||
bootstrap() {},
|
||||
async registerTrads({ locales }) {
|
||||
async registerTrads({ locales }: { locales: string[] }) {
|
||||
const importedTrads = await Promise.all(
|
||||
locales.map((locale) => {
|
||||
return import(`./translations/${locale}.json`)
|
||||
@ -1,5 +0,0 @@
|
||||
import pluginPkg from '../../package.json';
|
||||
|
||||
const pluginId = pluginPkg.name.replace(/^@strapi\/plugin-/i, '');
|
||||
|
||||
export default pluginId;
|
||||
1
packages/plugins/graphql/admin/src/pluginId.ts
Normal file
1
packages/plugins/graphql/admin/src/pluginId.ts
Normal file
@ -0,0 +1 @@
|
||||
export const pluginId = 'graphql';
|
||||
8
packages/plugins/graphql/admin/tsconfig.build.json
Normal file
8
packages/plugins/graphql/admin/tsconfig.build.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./admin/tsconfig.json",
|
||||
"include": ["./admin/src"],
|
||||
"exclude": ["**/*.test.tsx"],
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/admin"
|
||||
}
|
||||
}
|
||||
5
packages/plugins/graphql/admin/tsconfig.json
Normal file
5
packages/plugins/graphql/admin/tsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "tsconfig/client.json",
|
||||
"include": ["./src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@ -22,23 +22,31 @@
|
||||
],
|
||||
"exports": {
|
||||
"./strapi-admin": {
|
||||
"source": "./admin/src/index.js",
|
||||
"types": "./dist/admin/index.d.ts",
|
||||
"source": "./admin/src/index.ts",
|
||||
"import": "./dist/admin/index.mjs",
|
||||
"require": "./dist/admin/index.js",
|
||||
"default": "./dist/admin/index.js"
|
||||
},
|
||||
"./strapi-server": {
|
||||
"source": "./strapi-server.js",
|
||||
"require": "./strapi-server.js",
|
||||
"default": "./strapi-server.js"
|
||||
"types": "./dist/server/index.d.ts",
|
||||
"source": "./server/src/index.ts",
|
||||
"import": "./dist/server/index.mjs",
|
||||
"require": "./dist/server/index.js",
|
||||
"default": "./dist/server/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"./dist",
|
||||
"strapi-server.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pack-up build",
|
||||
"clean": "run -T rimraf dist",
|
||||
"build": "strapi plugin:build --force",
|
||||
"clean": "run -T rimraf ./dist",
|
||||
"lint": "run -T eslint .",
|
||||
"watch": "pack-up watch"
|
||||
"prepublishOnly": "yarn clean && yarn build",
|
||||
"watch": "strapi plugin:watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@graphql-tools/schema": "8.5.1",
|
||||
@ -60,14 +68,19 @@
|
||||
"pluralize": "8.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@strapi/pack-up": "workspace:*",
|
||||
"@strapi/strapi": "4.15.1",
|
||||
"@strapi/types": "4.15.1",
|
||||
"@types/graphql-depth-limit": "1.1.5",
|
||||
"@types/graphql-upload": "8.0.12",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint-config-custom": "4.15.1",
|
||||
"koa": "2.13.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "5.3.4",
|
||||
"styled-components": "5.3.3"
|
||||
"styled-components": "5.3.3",
|
||||
"tsconfig": "4.15.1",
|
||||
"typescript": "5.2.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@strapi/strapi": "^4.0.0",
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import { Config, defineConfig } from '@strapi/pack-up';
|
||||
import { transformWithEsbuild } from 'vite';
|
||||
|
||||
const config: Config = defineConfig({
|
||||
bundles: [
|
||||
{
|
||||
source: './admin/src/index.js',
|
||||
import: './dist/admin/index.mjs',
|
||||
require: './dist/admin/index.js',
|
||||
runtime: 'web',
|
||||
},
|
||||
],
|
||||
dist: './dist',
|
||||
/**
|
||||
* Because we're exporting a server & client package
|
||||
* which have different runtimes we want to ignore
|
||||
* what they look like in the package.json
|
||||
*/
|
||||
exports: {},
|
||||
plugins: [
|
||||
{
|
||||
name: 'treat-js-files-as-jsx',
|
||||
async transform(code, id) {
|
||||
/**
|
||||
* Matches all files in src/ and ee/ that end with .js
|
||||
*/
|
||||
if (!id.match(/src\/.*\.js$/) && !id.match(/ee\/.*\.js$/)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use the exposed transform from vite, instead of directly
|
||||
// transforming with esbuild
|
||||
return transformWithEsbuild(code, id, {
|
||||
loader: 'tsx',
|
||||
jsx: 'automatic',
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default config;
|
||||
7
packages/plugins/graphql/server/.eslintrc
Normal file
7
packages/plugins/graphql/server/.eslintrc
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"root": true,
|
||||
"extends": ["custom/back/typescript"],
|
||||
"parserOptions": {
|
||||
"project": ["./server/tsconfig.eslint.json"]
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const defaultConfig = require('./default-config');
|
||||
|
||||
module.exports = {
|
||||
default: defaultConfig,
|
||||
};
|
||||
@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const contentType = require('./content-type');
|
||||
|
||||
module.exports = (context) => ({
|
||||
...contentType(context),
|
||||
});
|
||||
@ -1,15 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { list } = require('nexus');
|
||||
|
||||
const AND_FIELD_NAME = 'and';
|
||||
|
||||
module.exports = () => ({
|
||||
fieldName: AND_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$and',
|
||||
|
||||
add(t, type) {
|
||||
t.field(AND_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,15 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { list } = require('nexus');
|
||||
|
||||
const IN_FIELD_NAME = 'in';
|
||||
|
||||
module.exports = () => ({
|
||||
fieldName: IN_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$in',
|
||||
|
||||
add(t, type) {
|
||||
t.field(IN_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,40 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { mapValues } = require('lodash/fp');
|
||||
|
||||
const operators = {
|
||||
and: require('./and'),
|
||||
or: require('./or'),
|
||||
not: require('./not'),
|
||||
|
||||
eq: require('./eq'),
|
||||
eqi: require('./eqi'),
|
||||
ne: require('./ne'),
|
||||
nei: require('./nei'),
|
||||
|
||||
startsWith: require('./starts-with'),
|
||||
endsWith: require('./ends-with'),
|
||||
|
||||
contains: require('./contains'),
|
||||
notContains: require('./not-contains'),
|
||||
|
||||
containsi: require('./containsi'),
|
||||
notContainsi: require('./not-containsi'),
|
||||
|
||||
gt: require('./gt'),
|
||||
gte: require('./gte'),
|
||||
|
||||
lt: require('./lt'),
|
||||
lte: require('./lte'),
|
||||
|
||||
null: require('./null'),
|
||||
notNull: require('./not-null'),
|
||||
|
||||
in: require('./in'),
|
||||
notIn: require('./not-in'),
|
||||
|
||||
between: require('./between'),
|
||||
};
|
||||
|
||||
// Instantiate every operator with the Strapi instance
|
||||
module.exports = (context) => mapValues((opCtor) => opCtor(context), operators);
|
||||
@ -1,15 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { list } = require('nexus');
|
||||
|
||||
const OR_FIELD_NAME = 'or';
|
||||
|
||||
module.exports = () => ({
|
||||
fieldName: OR_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$or',
|
||||
|
||||
add(t, type) {
|
||||
t.field(OR_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const createCollectionTypeMutationsBuilder = require('./collection-type');
|
||||
const createSingleTypeMutationsBuilder = require('./single-type');
|
||||
|
||||
module.exports = (context) => ({
|
||||
...createCollectionTypeMutationsBuilder(context),
|
||||
...createSingleTypeMutationsBuilder(context),
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const createCollectionTypeQueriesBuilder = require('./collection-type');
|
||||
const createSingleTypeQueriesBuilder = require('./single-type');
|
||||
|
||||
module.exports = (context) => ({
|
||||
...createCollectionTypeQueriesBuilder(context),
|
||||
...createSingleTypeQueriesBuilder(context),
|
||||
});
|
||||
@ -1,9 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
buildDynamicZoneResolver({ contentTypeUID, attributeName }) {
|
||||
return async (parent) => {
|
||||
return strapi.entityService.load(contentTypeUID, parent, attributeName);
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -1,18 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const associationResolvers = require('./association');
|
||||
const queriesResolvers = require('./query');
|
||||
const mutationsResolvers = require('./mutation');
|
||||
const componentResolvers = require('./component');
|
||||
const dynamicZoneResolvers = require('./dynamic-zone');
|
||||
|
||||
module.exports = (context) => ({
|
||||
// Generics
|
||||
...associationResolvers(context),
|
||||
|
||||
// Builders
|
||||
...mutationsResolvers(context),
|
||||
...queriesResolvers(context),
|
||||
...componentResolvers(context),
|
||||
...dynamicZoneResolvers(context),
|
||||
});
|
||||
@ -1,34 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { omit } = require('lodash/fp');
|
||||
const { sanitize, validate } = require('@strapi/utils');
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
buildQueriesResolvers({ contentType }) {
|
||||
const { uid } = contentType;
|
||||
|
||||
return {
|
||||
async find(parent, args, ctx) {
|
||||
await validate.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
const sanitizedQuery = await sanitize.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
|
||||
return strapi.entityService.findMany(uid, sanitizedQuery);
|
||||
},
|
||||
|
||||
async findOne(parent, args, ctx) {
|
||||
await validate.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
const sanitizedQuery = await sanitize.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
|
||||
return strapi.entityService.findOne(uid, args.id, omit('id', sanitizedQuery));
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -1,60 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { propOr } = require('lodash/fp');
|
||||
const { policy: policyUtils } = require('@strapi/utils');
|
||||
const { PolicyError } = require('@strapi/utils').errors;
|
||||
|
||||
const getPoliciesConfig = propOr([], 'policies');
|
||||
|
||||
const createPoliciesMiddleware = (resolverConfig, { strapi }) => {
|
||||
const resolverPolicies = getPoliciesConfig(resolverConfig);
|
||||
const policies = policyUtils.resolve(resolverPolicies);
|
||||
|
||||
return async (resolve, ...rest) => {
|
||||
// Create a graphql policy context
|
||||
const context = createGraphQLPolicyContext(...rest);
|
||||
|
||||
// Run policies & throw an error if one of them fails
|
||||
for (const { handler, config } of policies) {
|
||||
const result = await handler(context, config, { strapi });
|
||||
|
||||
if (![true, undefined].includes(result)) {
|
||||
throw new PolicyError();
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(...rest);
|
||||
};
|
||||
};
|
||||
|
||||
const createGraphQLPolicyContext = (parent, args, context, info) => {
|
||||
return policyUtils.createPolicyContext('graphql', {
|
||||
get parent() {
|
||||
return parent;
|
||||
},
|
||||
|
||||
get args() {
|
||||
return args;
|
||||
},
|
||||
|
||||
get context() {
|
||||
return context;
|
||||
},
|
||||
|
||||
get info() {
|
||||
return info;
|
||||
},
|
||||
|
||||
get state() {
|
||||
return this.context.state;
|
||||
},
|
||||
|
||||
get http() {
|
||||
return this.context.koaContext;
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createPoliciesMiddleware,
|
||||
};
|
||||
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { registerDynamicZonesDefinition } = require('./dynamic-zones');
|
||||
const { registerEnumsDefinition } = require('./enums');
|
||||
const { registerInputsDefinition } = require('./inputs');
|
||||
const { registerFiltersDefinition } = require('./filters');
|
||||
|
||||
module.exports = {
|
||||
registerDynamicZonesDefinition,
|
||||
registerFiltersDefinition,
|
||||
registerInputsDefinition,
|
||||
registerEnumsDefinition,
|
||||
};
|
||||
@ -1,22 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { registerCollectionType } = require('./collection-type');
|
||||
const { registerSingleType } = require('./single-type');
|
||||
const { registerComponent } = require('./component');
|
||||
const { registerPolymorphicContentType } = require('./polymorphic');
|
||||
|
||||
const { registerScalars } = require('./scalars');
|
||||
const { registerInternals } = require('./internals');
|
||||
|
||||
const contentType = require('./content-type');
|
||||
|
||||
module.exports = {
|
||||
registerCollectionType,
|
||||
registerSingleType,
|
||||
registerComponent,
|
||||
registerPolymorphicContentType,
|
||||
registerInternals,
|
||||
registerScalars,
|
||||
|
||||
contentType,
|
||||
};
|
||||
@ -1,5 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const createExtension = require('./extension');
|
||||
|
||||
module.exports = createExtension;
|
||||
@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const returnTypes = require('./return-types');
|
||||
|
||||
module.exports = (context) => ({
|
||||
returnTypes: returnTypes(context),
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = () => ({
|
||||
/**
|
||||
* @param {object} value
|
||||
* @param {object} info
|
||||
* @param {object} info.args
|
||||
* @param {string} info.resourceUID
|
||||
*/
|
||||
toEntityResponse(value, info = {}) {
|
||||
const { args = {}, resourceUID } = info;
|
||||
|
||||
return { value, info: { args, resourceUID } };
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object[]} nodes
|
||||
* @param {object} info
|
||||
* @param {object} info.args
|
||||
* @param {string} info.resourceUID
|
||||
*/
|
||||
toEntityResponseCollection(nodes, info = {}) {
|
||||
const { args = {}, resourceUID } = info;
|
||||
|
||||
return { nodes, info: { args, resourceUID } };
|
||||
},
|
||||
});
|
||||
@ -1,21 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const contentAPI = require('./content-api');
|
||||
const typeRegistry = require('./type-registry');
|
||||
const utils = require('./utils');
|
||||
const constants = require('./constants');
|
||||
const internals = require('./internals');
|
||||
const builders = require('./builders');
|
||||
const extension = require('./extension');
|
||||
const format = require('./format');
|
||||
|
||||
module.exports = {
|
||||
builders,
|
||||
'content-api': contentAPI,
|
||||
constants,
|
||||
extension,
|
||||
format,
|
||||
internals,
|
||||
'type-registry': typeRegistry,
|
||||
utils,
|
||||
};
|
||||
@ -1,11 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const SortArg = require('./sort');
|
||||
const publicationState = require('./publication-state');
|
||||
const PaginationArg = require('./pagination');
|
||||
|
||||
module.exports = (context) => ({
|
||||
SortArg,
|
||||
PaginationArg,
|
||||
PublicationStateArg: publicationState(context),
|
||||
});
|
||||
@ -1,10 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { arg, list } = require('nexus');
|
||||
|
||||
const SortArg = arg({
|
||||
type: list('String'),
|
||||
default: [],
|
||||
});
|
||||
|
||||
module.exports = SortArg;
|
||||
@ -1,7 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const getEnabledScalars = require('./get-enabled-scalars');
|
||||
|
||||
module.exports = (context) => ({
|
||||
getEnabledScalars: getEnabledScalars(context),
|
||||
});
|
||||
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const args = require('./args');
|
||||
const scalars = require('./scalars');
|
||||
const types = require('./types');
|
||||
const helpers = require('./helpers');
|
||||
|
||||
module.exports = (context) => ({
|
||||
args: args(context),
|
||||
scalars: scalars(context),
|
||||
buildInternalTypes: types(context),
|
||||
helpers: helpers(context),
|
||||
});
|
||||
@ -1,17 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { GraphQLDateTime, GraphQLLong, GraphQLJSON } = require('graphql-scalars');
|
||||
const { GraphQLUpload } = require('graphql-upload');
|
||||
const { asNexusMethod } = require('nexus');
|
||||
|
||||
const TimeScalar = require('./time');
|
||||
const GraphQLDate = require('./date');
|
||||
|
||||
module.exports = () => ({
|
||||
JSON: asNexusMethod(GraphQLJSON, 'json'),
|
||||
DateTime: asNexusMethod(GraphQLDateTime, 'dateTime'),
|
||||
Time: asNexusMethod(TimeScalar, 'time'),
|
||||
Date: asNexusMethod(GraphQLDate, 'date'),
|
||||
Long: asNexusMethod(GraphQLLong, 'long'),
|
||||
Upload: asNexusMethod(GraphQLUpload, 'upload'),
|
||||
});
|
||||
@ -1,11 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const mappers = require('./mappers');
|
||||
const attributes = require('./attributes');
|
||||
const naming = require('./naming');
|
||||
|
||||
module.exports = (context) => ({
|
||||
naming: naming(context),
|
||||
attributes: attributes(context),
|
||||
mappers: mappers(context),
|
||||
});
|
||||
@ -1,12 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { map } = require('lodash/fp');
|
||||
|
||||
const entityToResponseEntity = (entity) => ({ id: entity.id, attributes: entity });
|
||||
|
||||
const entitiesToResponseEntities = map(entityToResponseEntity);
|
||||
|
||||
module.exports = () => ({
|
||||
entityToResponseEntity,
|
||||
entitiesToResponseEntities,
|
||||
});
|
||||
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const strapiScalarToGraphQLScalar = require('./strapi-scalar-to-graphql-scalar');
|
||||
const graphQLFiltersToStrapiQuery = require('./graphql-filters-to-strapi-query');
|
||||
const graphqlScalarToOperators = require('./graphql-scalar-to-operators');
|
||||
const entityToResponseEntity = require('./entity-to-response-entity');
|
||||
|
||||
module.exports = (context) => ({
|
||||
...strapiScalarToGraphQLScalar(context),
|
||||
...graphQLFiltersToStrapiQuery(context),
|
||||
...graphqlScalarToOperators(context),
|
||||
...entityToResponseEntity(context),
|
||||
});
|
||||
@ -1,14 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
const { isEmpty, mergeWith, isArray } = require('lodash/fp');
|
||||
const { ApolloServer } = require('apollo-server-koa');
|
||||
const {
|
||||
import { isEmpty, mergeWith, isArray } from 'lodash/fp';
|
||||
import { ApolloServer } from 'apollo-server-koa';
|
||||
import {
|
||||
ApolloServerPluginLandingPageDisabled,
|
||||
ApolloServerPluginLandingPageGraphQLPlayground,
|
||||
} = require('apollo-server-core');
|
||||
const depthLimit = require('graphql-depth-limit');
|
||||
const { graphqlUploadKoa } = require('graphql-upload');
|
||||
const formatGraphqlError = require('./format-graphql-error');
|
||||
} from 'apollo-server-core';
|
||||
import depthLimit from 'graphql-depth-limit';
|
||||
import { graphqlUploadKoa } from 'graphql-upload';
|
||||
import type { Config } from 'apollo-server-core';
|
||||
import type { Strapi } from '@strapi/types';
|
||||
|
||||
import { formatGraphqlError } from './format-graphql-error';
|
||||
|
||||
const merge = mergeWith((a, b) => {
|
||||
if (isArray(a) && isArray(b)) {
|
||||
@ -21,7 +22,7 @@ const merge = mergeWith((a, b) => {
|
||||
* @param {object} strapi
|
||||
* @param {string} path
|
||||
*/
|
||||
const useUploadMiddleware = (strapi, path) => {
|
||||
const useUploadMiddleware = (strapi: Strapi, path: string): void => {
|
||||
const uploadMiddleware = graphqlUploadKoa();
|
||||
|
||||
strapi.server.app.use((ctx, next) => {
|
||||
@ -33,7 +34,7 @@ const useUploadMiddleware = (strapi, path) => {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = async ({ strapi }) => {
|
||||
export async function bootstrap({ strapi }: { strapi: Strapi }) {
|
||||
// Generate the GraphQL schema for the content API
|
||||
const schema = strapi.plugin('graphql').service('content-api').buildSchema();
|
||||
|
||||
@ -45,9 +46,13 @@ module.exports = async ({ strapi }) => {
|
||||
|
||||
const { config } = strapi.plugin('graphql');
|
||||
|
||||
const path = config('endpoint');
|
||||
const path: string = config('endpoint');
|
||||
|
||||
const defaultServerConfig = {
|
||||
const defaultServerConfig: Config & {
|
||||
cors: boolean;
|
||||
uploads: boolean;
|
||||
bodyParserConfig: boolean;
|
||||
} = {
|
||||
// Schema
|
||||
schema,
|
||||
|
||||
@ -58,7 +63,7 @@ module.exports = async ({ strapi }) => {
|
||||
}),
|
||||
|
||||
// Validation
|
||||
validationRules: [depthLimit(config('depthLimit'))],
|
||||
validationRules: [depthLimit(config('depthLimit') as number) as any],
|
||||
|
||||
// Errors
|
||||
formatError: formatGraphqlError,
|
||||
@ -74,7 +79,7 @@ module.exports = async ({ strapi }) => {
|
||||
: ApolloServerPluginLandingPageGraphQLPlayground(),
|
||||
],
|
||||
|
||||
cache: 'bounded',
|
||||
cache: 'bounded' as const,
|
||||
};
|
||||
|
||||
const serverConfig = merge(defaultServerConfig, config('apolloServer'));
|
||||
@ -88,8 +93,12 @@ module.exports = async ({ strapi }) => {
|
||||
try {
|
||||
// Since Apollo-Server v3, server.start() must be called before using server.applyMiddleware()
|
||||
await server.start();
|
||||
} catch (e) {
|
||||
strapi.log.error('Failed to start the Apollo server', e.message);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
strapi.log.error('Failed to start the Apollo server', error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Link the Apollo server & the Strapi app
|
||||
@ -131,4 +140,4 @@ module.exports = async ({ strapi }) => {
|
||||
strapi.plugin('graphql').destroy = async () => {
|
||||
await server.stop();
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -1,13 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
shadowCRUD: true,
|
||||
|
||||
endpoint: '/graphql',
|
||||
|
||||
subscriptions: false,
|
||||
|
||||
maxLimit: -1,
|
||||
|
||||
apolloServer: {},
|
||||
};
|
||||
5
packages/plugins/graphql/server/src/config/index.ts
Normal file
5
packages/plugins/graphql/server/src/config/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import defaultConfig from './default-config';
|
||||
|
||||
export const config = {
|
||||
default: defaultConfig,
|
||||
};
|
||||
@ -1,19 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const { toUpper, snakeCase, pick, isEmpty } = require('lodash/fp');
|
||||
const { HttpError, ForbiddenError, UnauthorizedError, ApplicationError, ValidationError } =
|
||||
require('@strapi/utils').errors;
|
||||
const {
|
||||
import { toUpper, snakeCase, pick, isEmpty } from 'lodash/fp';
|
||||
import { errors } from '@strapi/utils';
|
||||
import {
|
||||
ApolloError,
|
||||
UserInputError: ApolloUserInputError,
|
||||
ForbiddenError: ApolloForbiddenError,
|
||||
} = require('apollo-server-koa');
|
||||
const { GraphQLError } = require('graphql');
|
||||
UserInputError as ApolloUserInputError,
|
||||
ForbiddenError as ApolloForbiddenError,
|
||||
} from 'apollo-server-koa';
|
||||
import { GraphQLError } from 'graphql';
|
||||
|
||||
const formatToCode = (name) => `STRAPI_${toUpper(snakeCase(name))}`;
|
||||
const formatErrorToExtension = (error) => ({ error: pick(['name', 'message', 'details'])(error) });
|
||||
const { HttpError, ForbiddenError, UnauthorizedError, ApplicationError, ValidationError } = errors;
|
||||
|
||||
const formatGraphqlError = (error) => {
|
||||
const formatToCode = (name: string) => `STRAPI_${toUpper(snakeCase(name))}`;
|
||||
const formatErrorToExtension = (error: any) => ({
|
||||
error: pick(['name', 'message', 'details'])(error),
|
||||
});
|
||||
|
||||
export function formatGraphqlError(error: GraphQLError) {
|
||||
const { originalError } = error;
|
||||
|
||||
if (isEmpty(originalError)) {
|
||||
@ -40,6 +41,4 @@ const formatGraphqlError = (error) => {
|
||||
// Internal server error
|
||||
strapi.log.error(originalError);
|
||||
return new ApolloError('Internal Server Error', 'INTERNAL_SERVER_ERROR');
|
||||
};
|
||||
|
||||
module.exports = formatGraphqlError;
|
||||
}
|
||||
9
packages/plugins/graphql/server/src/index.ts
Normal file
9
packages/plugins/graphql/server/src/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { config } from './config';
|
||||
import { bootstrap } from './bootstrap';
|
||||
import { services } from './services';
|
||||
|
||||
export default {
|
||||
config,
|
||||
bootstrap,
|
||||
services,
|
||||
};
|
||||
@ -1,12 +1,15 @@
|
||||
'use strict';
|
||||
import { Kind, valueFromASTUntyped } from 'graphql';
|
||||
import { omit } from 'lodash/fp';
|
||||
import { unionType, scalarType } from 'nexus';
|
||||
import { errors } from '@strapi/utils';
|
||||
import type { UID, Attribute } from '@strapi/types';
|
||||
|
||||
const { Kind, valueFromASTUntyped } = require('graphql');
|
||||
const { omit } = require('lodash/fp');
|
||||
const { unionType, scalarType } = require('nexus');
|
||||
const { ApplicationError } = require('@strapi/utils').errors;
|
||||
import type { Context } from '../types';
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
const buildTypeDefinition = (name, components) => {
|
||||
const { ApplicationError } = errors;
|
||||
|
||||
export default ({ strapi }: Context) => {
|
||||
const buildTypeDefinition = (name: string, components: UID.Component[]) => {
|
||||
const { ERROR_TYPE_NAME } = strapi.plugin('graphql').service('constants');
|
||||
const isEmpty = components.length === 0;
|
||||
|
||||
@ -39,8 +42,8 @@ module.exports = ({ strapi }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const buildInputDefinition = (name, components) => {
|
||||
const parseData = (value) => {
|
||||
const buildInputDefinition = (name: string, components: UID.Component[]) => {
|
||||
const parseData = (value: any) => {
|
||||
const component = Object.values(strapi.components).find(
|
||||
(component) => component.globalId === value.__typename
|
||||
);
|
||||
@ -85,7 +88,7 @@ module.exports = ({ strapi }) => {
|
||||
* @param {string} inputName - the name of the dynamic zone's input
|
||||
* @return {[NexusUnionTypeDef, NexusScalarTypeDef]}
|
||||
*/
|
||||
buildDynamicZoneDefinition(definition, name, inputName) {
|
||||
buildDynamicZoneDefinition(definition: Attribute.DynamicZone, name: string, inputName: string) {
|
||||
const { components } = definition;
|
||||
|
||||
const typeDefinition = buildTypeDefinition(name, components);
|
||||
@ -1,7 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
function buildEntityMetaDefinition(/* contentType */) {}
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
buildEntityMetaDefinition,
|
||||
});
|
||||
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
import { objectType } from 'nexus';
|
||||
import { prop, identity, isEmpty } from 'lodash/fp';
|
||||
import type { Schema } from '@strapi/types';
|
||||
|
||||
const { objectType } = require('nexus');
|
||||
const { prop, identity, isEmpty } = require('lodash/fp');
|
||||
import type { Context } from '../types';
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { naming } = strapi.plugin('graphql').service('utils');
|
||||
|
||||
return {
|
||||
@ -12,7 +13,7 @@ module.exports = ({ strapi }) => {
|
||||
* @param {object} contentType The content type which will be used to build its entity type
|
||||
* @return {NexusObjectTypeDef}
|
||||
*/
|
||||
buildEntityDefinition(contentType) {
|
||||
buildEntityDefinition(contentType: Schema.ContentType) {
|
||||
const { attributes } = contentType;
|
||||
|
||||
const name = naming.getEntityName(contentType);
|
||||
@ -1,8 +1,10 @@
|
||||
'use strict';
|
||||
import { enumType } from 'nexus';
|
||||
import { set } from 'lodash/fp';
|
||||
import { toRegressedEnumValue } from '@strapi/utils';
|
||||
|
||||
const { enumType } = require('nexus');
|
||||
const { set } = require('lodash/fp');
|
||||
const { toRegressedEnumValue } = require('@strapi/utils');
|
||||
interface Definition {
|
||||
enum: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Nexus enum type from a Strapi enum attribute
|
||||
@ -11,7 +13,7 @@ const { toRegressedEnumValue } = require('@strapi/utils');
|
||||
* @param {string} name - The name of the enum
|
||||
* @return {NexusEnumTypeDef}
|
||||
*/
|
||||
const buildEnumTypeDefinition = (definition, name) => {
|
||||
const buildEnumTypeDefinition = (definition: Definition, name: string) => {
|
||||
return enumType({
|
||||
name,
|
||||
members: definition.enum.reduce(
|
||||
@ -21,6 +23,6 @@ const buildEnumTypeDefinition = (definition, name) => {
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
buildEnumTypeDefinition,
|
||||
});
|
||||
@ -1,15 +1,20 @@
|
||||
'use strict';
|
||||
import { inputObjectType } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Attribute, Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const { inputObjectType } = require('nexus');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const rootLevelOperators = () => {
|
||||
const { operators } = strapi.plugin('graphql').service('builders').filters;
|
||||
|
||||
return [operators.and, operators.or, operators.not];
|
||||
};
|
||||
|
||||
const addScalarAttribute = (builder, attributeName, attribute) => {
|
||||
const addScalarAttribute = (
|
||||
builder: Nexus.blocks.InputDefinitionBlock<any>,
|
||||
attributeName: string,
|
||||
attribute: Attribute.Any
|
||||
) => {
|
||||
const { naming, mappers } = strapi.plugin('graphql').service('utils');
|
||||
|
||||
const gqlType = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
||||
@ -17,13 +22,17 @@ module.exports = ({ strapi }) => {
|
||||
builder.field(attributeName, { type: naming.getScalarFilterInputTypeName(gqlType) });
|
||||
};
|
||||
|
||||
const addRelationalAttribute = (builder, attributeName, attribute) => {
|
||||
const addRelationalAttribute = (
|
||||
builder: Nexus.blocks.InputDefinitionBlock<any>,
|
||||
attributeName: string,
|
||||
attribute: Attribute.Relation
|
||||
) => {
|
||||
const utils = strapi.plugin('graphql').service('utils');
|
||||
const extension = strapi.plugin('graphql').service('extension');
|
||||
const { getFiltersInputTypeName } = utils.naming;
|
||||
const { isMorphRelation } = utils.attributes;
|
||||
|
||||
const model = strapi.getModel(attribute.target);
|
||||
const model = 'target' in attribute && strapi.getModel(attribute.target);
|
||||
|
||||
// If there is no model corresponding to the attribute configuration
|
||||
// or if the attribute is a polymorphic relation, then ignore it
|
||||
@ -35,7 +44,11 @@ module.exports = ({ strapi }) => {
|
||||
builder.field(attributeName, { type: getFiltersInputTypeName(model) });
|
||||
};
|
||||
|
||||
const addComponentAttribute = (builder, attributeName, attribute) => {
|
||||
const addComponentAttribute = (
|
||||
builder: Nexus.blocks.InputDefinitionBlock<any>,
|
||||
attributeName: string,
|
||||
attribute: Attribute.Component
|
||||
) => {
|
||||
const utils = strapi.plugin('graphql').service('utils');
|
||||
const extension = strapi.plugin('graphql').service('extension');
|
||||
const { getFiltersInputTypeName } = utils.naming;
|
||||
@ -51,7 +64,7 @@ module.exports = ({ strapi }) => {
|
||||
builder.field(attributeName, { type: getFiltersInputTypeName(component) });
|
||||
};
|
||||
|
||||
const buildContentTypeFilters = (contentType) => {
|
||||
const buildContentTypeFilters = (contentType: Schema.ContentType) => {
|
||||
const utils = strapi.plugin('graphql').service('utils');
|
||||
const extension = strapi.plugin('graphql').service('extension');
|
||||
|
||||
@ -88,12 +101,12 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
// Handle relations
|
||||
else if (isRelation(attribute)) {
|
||||
addRelationalAttribute(t, attributeName, attribute);
|
||||
addRelationalAttribute(t, attributeName, attribute as Attribute.Relation);
|
||||
}
|
||||
|
||||
// Handle components
|
||||
else if (isComponent(attribute)) {
|
||||
addComponentAttribute(t, attributeName, attribute);
|
||||
addComponentAttribute(t, attributeName, attribute as Attribute.Component);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import contentType from './content-type';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
export default (context: Context) => ({
|
||||
...contentType(context),
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
import { list } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const AND_FIELD_NAME = 'and';
|
||||
|
||||
export default () => ({
|
||||
fieldName: AND_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$and',
|
||||
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(AND_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,15 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const { list } = require('nexus');
|
||||
import { list } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const BETWEEN_FIELD_NAME = 'between';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: BETWEEN_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$between',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(BETWEEN_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const CONTAINS_FIELD_NAME = 'contains';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: CONTAINS_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$contains',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(CONTAINS_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const CONTAINSI_FIELD_NAME = 'containsi';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: CONTAINSI_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$containsi',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(CONTAINSI_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const ENDS_WITH_FIELD_NAME = 'endsWith';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: ENDS_WITH_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$endsWith',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(ENDS_WITH_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,15 +1,17 @@
|
||||
'use strict';
|
||||
import { errors } from '@strapi/utils';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Strapi } from '@strapi/types';
|
||||
|
||||
const { ValidationError } = require('@strapi/utils').errors;
|
||||
const { ValidationError } = errors;
|
||||
|
||||
const EQ_FIELD_NAME = 'eq';
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
export default ({ strapi }: { strapi: Strapi }) => ({
|
||||
fieldName: EQ_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$eq',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
const { GRAPHQL_SCALARS } = strapi.plugin('graphql').service('constants');
|
||||
|
||||
if (!GRAPHQL_SCALARS.includes(type)) {
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const EQI_FIELD_NAME = 'eqi';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: EQI_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$eqi',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(EQI_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const GT_FIELD_NAME = 'gt';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: GT_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$gt',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(GT_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const GTE_FIELD_NAME = 'gte';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: GTE_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$gte',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(GTE_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
import { list } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const IN_FIELD_NAME = 'in';
|
||||
|
||||
export default () => ({
|
||||
fieldName: IN_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$in',
|
||||
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(IN_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,54 @@
|
||||
import type { Strapi } from '@strapi/types';
|
||||
|
||||
import { mapValues } from 'lodash/fp';
|
||||
import andOperator from './and';
|
||||
import orOperator from './or';
|
||||
import notOperator from './not';
|
||||
import eqOperator from './eq';
|
||||
import eqiOperator from './eqi';
|
||||
import neOperator from './ne';
|
||||
import neiOperator from './nei';
|
||||
import startsWithOperator from './starts-with';
|
||||
import endsWithOperator from './ends-with';
|
||||
import containsOperator from './contains';
|
||||
import notContainsOperator from './not-contains';
|
||||
import containsiOperator from './containsi';
|
||||
import notContainsiOperator from './not-containsi';
|
||||
import gtOperator from './gt';
|
||||
import gteOperator from './gte';
|
||||
import ltOperator from './lt';
|
||||
import lteOperator from './lte';
|
||||
import nullOperator from './null';
|
||||
import notNullOperator from './not-null';
|
||||
import inOperator from './in';
|
||||
import notInOperator from './not-in';
|
||||
import betweenOperator from './between';
|
||||
|
||||
const operators = {
|
||||
and: andOperator,
|
||||
or: orOperator,
|
||||
not: notOperator,
|
||||
eq: eqOperator,
|
||||
eqi: eqiOperator,
|
||||
ne: neOperator,
|
||||
nei: neiOperator,
|
||||
startsWith: startsWithOperator,
|
||||
endsWith: endsWithOperator,
|
||||
contains: containsOperator,
|
||||
notContains: notContainsOperator,
|
||||
containsi: containsiOperator,
|
||||
notContainsi: notContainsiOperator,
|
||||
gt: gtOperator,
|
||||
gte: gteOperator,
|
||||
lt: ltOperator,
|
||||
lte: lteOperator,
|
||||
null: nullOperator,
|
||||
notNull: notNullOperator,
|
||||
in: inOperator,
|
||||
notIn: notInOperator,
|
||||
between: betweenOperator,
|
||||
};
|
||||
|
||||
// Instantiate every operator with the Strapi instance
|
||||
export default ({ strapi }: { strapi: Strapi }) =>
|
||||
mapValues((opCtor) => opCtor({ strapi }), operators);
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const LT_FIELD_NAME = 'lt';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: LT_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$lt',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(LT_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const LTE_FIELD_NAME = 'lte';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: LTE_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$lte',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(LTE_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NE_FIELD_NAME = 'ne';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NE_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$ne',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(NE_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NEI_FIELD_NAME = 'nei';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NEI_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$nei',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(NEI_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NOT_CONTAINS_FIELD_NAME = 'notContains';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NOT_CONTAINS_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$notContains',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(NOT_CONTAINS_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NOT_CONTAINSI_FIELD_NAME = 'notContainsi';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NOT_CONTAINSI_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$notContainsi',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(NOT_CONTAINSI_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,15 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
const { list } = require('nexus');
|
||||
import { list } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NOT_IN_FIELD_NAME = 'notIn';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NOT_IN_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$notIn',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(NOT_IN_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NOT_NULL_FIELD_NAME = 'notNull';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NOT_NULL_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$notNull',
|
||||
|
||||
add(t) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>) {
|
||||
t.boolean(NOT_NULL_FIELD_NAME);
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,14 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Strapi } from '@strapi/types';
|
||||
|
||||
const NOT_FIELD_NAME = 'not';
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
export default ({ strapi }: { strapi: Strapi }) => ({
|
||||
fieldName: NOT_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$not',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
const { naming, attributes } = strapi.plugin('graphql').service('utils');
|
||||
|
||||
if (attributes.isGraphQLScalar({ type })) {
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const NULL_FIELD_NAME = 'null';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: NULL_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$null',
|
||||
|
||||
add(t) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>) {
|
||||
t.boolean(NULL_FIELD_NAME);
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,14 @@
|
||||
import { list } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const OR_FIELD_NAME = 'or';
|
||||
|
||||
export default () => ({
|
||||
fieldName: OR_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$or',
|
||||
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(OR_FIELD_NAME, { type: list(type) });
|
||||
},
|
||||
});
|
||||
@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
import type * as Nexus from 'nexus';
|
||||
|
||||
const STARTS_WITH_FIELD_NAME = 'startsWith';
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
fieldName: STARTS_WITH_FIELD_NAME,
|
||||
|
||||
strapiOperator: '$startsWith',
|
||||
|
||||
add(t, type) {
|
||||
add(t: Nexus.blocks.ObjectDefinitionBlock<string>, type: string) {
|
||||
t.field(STARTS_WITH_FIELD_NAME, { type });
|
||||
},
|
||||
});
|
||||
@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
import { unionType } from 'nexus';
|
||||
import { prop } from 'lodash/fp';
|
||||
|
||||
const { unionType } = require('nexus');
|
||||
const { prop } = require('lodash/fp');
|
||||
import type { Context } from '../types';
|
||||
|
||||
module.exports = ({ strapi, registry }) => {
|
||||
export default ({ strapi, registry }: Context) => {
|
||||
const { naming } = strapi.plugin('graphql').service('utils');
|
||||
const { KINDS, GENERIC_MORPH_TYPENAME } = strapi.plugin('graphql').service('constants');
|
||||
|
||||
@ -12,7 +12,7 @@ module.exports = ({ strapi, registry }) => {
|
||||
return unionType({
|
||||
name: GENERIC_MORPH_TYPENAME,
|
||||
|
||||
resolveType(obj) {
|
||||
resolveType(obj: any) {
|
||||
const contentType = strapi.getModel(obj.__type);
|
||||
|
||||
if (!contentType) {
|
||||
@ -26,7 +26,7 @@ module.exports = ({ strapi, registry }) => {
|
||||
return naming.getTypeName(contentType);
|
||||
},
|
||||
|
||||
definition(t) {
|
||||
definition(t: any) {
|
||||
const members = registry
|
||||
// Resolve every content-type or component
|
||||
.where(({ config }) => [KINDS.type, KINDS.component].includes(config.kind))
|
||||
@ -1,35 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const { merge, map, pipe, reduce } = require('lodash/fp');
|
||||
import { merge, map, pipe, reduce } from 'lodash/fp';
|
||||
import type { Strapi } from '@strapi/types';
|
||||
|
||||
// Builders Factories
|
||||
|
||||
const enums = require('./enums');
|
||||
const dynamicZone = require('./dynamic-zones');
|
||||
const entity = require('./entity');
|
||||
const entityMeta = require('./entity-meta');
|
||||
const type = require('./type');
|
||||
const response = require('./response');
|
||||
const responseCollection = require('./response-collection');
|
||||
const relationResponseCollection = require('./relation-response-collection');
|
||||
const queries = require('./queries');
|
||||
const mutations = require('./mutations');
|
||||
const filters = require('./filters');
|
||||
const inputs = require('./input');
|
||||
const genericMorph = require('./generic-morph');
|
||||
const resolvers = require('./resolvers');
|
||||
import enums from './enums';
|
||||
import dynamicZone from './dynamic-zones';
|
||||
import entity from './entity';
|
||||
import entityMeta from './entity-meta';
|
||||
import typeBuilder from './type';
|
||||
import response from './response';
|
||||
import responseCollection from './response-collection';
|
||||
import relationResponseCollection from './relation-response-collection';
|
||||
import queries from './queries';
|
||||
import mutations from './mutations';
|
||||
import filters from './filters';
|
||||
import inputs from './input';
|
||||
import genericMorph from './generic-morph';
|
||||
import resolvers from './resolvers';
|
||||
|
||||
// Misc
|
||||
|
||||
const operators = require('./filters/operators');
|
||||
const utils = require('./utils');
|
||||
import operators from './filters/operators';
|
||||
import utils from './utils';
|
||||
import type { TypeRegistry } from '../type-registry';
|
||||
|
||||
const buildersFactories = [
|
||||
enums,
|
||||
dynamicZone,
|
||||
entity,
|
||||
entityMeta,
|
||||
type,
|
||||
typeBuilder,
|
||||
response,
|
||||
responseCollection,
|
||||
relationResponseCollection,
|
||||
@ -41,21 +41,19 @@ const buildersFactories = [
|
||||
resolvers,
|
||||
];
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: { strapi: Strapi }) => {
|
||||
const buildersMap = new Map();
|
||||
|
||||
return {
|
||||
/**
|
||||
* Instantiate every builder with a strapi instance & a type registry
|
||||
* @param {string} name
|
||||
* @param {object} registry
|
||||
*/
|
||||
new(name, registry) {
|
||||
new(name: string, registry: TypeRegistry) {
|
||||
const context = { strapi, registry };
|
||||
|
||||
const builders = pipe(
|
||||
// Create a new instance of every builders
|
||||
map((factory) => factory(context)),
|
||||
map((factory: any) => factory(context)),
|
||||
// Merge every builder into the same object
|
||||
reduce(merge, {})
|
||||
).call(null, buildersFactories);
|
||||
@ -68,18 +66,16 @@ module.exports = ({ strapi }) => {
|
||||
/**
|
||||
* Delete a set of builders instances from
|
||||
* the builders map for a given name
|
||||
* @param {string} name
|
||||
*/
|
||||
delete(name) {
|
||||
delete(name: string) {
|
||||
buildersMap.delete(name);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve a set of builders instances from
|
||||
* the builders map for a given name
|
||||
* @param {string} name
|
||||
*/
|
||||
get(name) {
|
||||
get(name: string) {
|
||||
return buildersMap.get(name);
|
||||
},
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
'use strict';
|
||||
import { inputObjectType, nonNull } from 'nexus';
|
||||
import { contentTypes } from '@strapi/utils';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../types';
|
||||
|
||||
const { inputObjectType, nonNull } = require('nexus');
|
||||
const {
|
||||
contentTypes: { isWritableAttribute },
|
||||
} = require('@strapi/utils');
|
||||
|
||||
module.exports = (context) => {
|
||||
const { strapi } = context;
|
||||
const { isWritableAttribute } = contentTypes;
|
||||
|
||||
export default ({ strapi }: Context) => {
|
||||
const { naming, mappers, attributes } = strapi.plugin('graphql').service('utils');
|
||||
const extension = strapi.plugin('graphql').service('extension');
|
||||
|
||||
@ -25,7 +23,7 @@ module.exports = (context) => {
|
||||
} = attributes;
|
||||
|
||||
return {
|
||||
buildInputType(contentType) {
|
||||
buildInputType(contentType: Schema.Any) {
|
||||
const { attributes, modelType } = contentType;
|
||||
|
||||
const name = (
|
||||
@ -36,7 +34,7 @@ module.exports = (context) => {
|
||||
name,
|
||||
|
||||
definition(t) {
|
||||
const isFieldEnabled = (fieldName) => {
|
||||
const isFieldEnabled = (fieldName: string) => {
|
||||
return extension.shadowCRUD(contentType.uid).field(fieldName).hasInputEnabled();
|
||||
};
|
||||
|
||||
@ -49,7 +47,7 @@ module.exports = (context) => {
|
||||
t.id('id');
|
||||
}
|
||||
|
||||
validAttributes.forEach(([attributeName, attribute]) => {
|
||||
validAttributes.forEach(([attributeName, attribute]: [string, any]) => {
|
||||
// Enums
|
||||
if (isEnumeration(attribute)) {
|
||||
const enumTypeName = getEnumName(contentType, attributeName);
|
||||
@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
import { extendType, nonNull } from 'nexus';
|
||||
import { sanitize } from '@strapi/utils';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const { extendType, nonNull } = require('nexus');
|
||||
const { sanitize } = require('@strapi/utils');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
const { naming } = getService('utils');
|
||||
@ -18,7 +19,10 @@ module.exports = ({ strapi }) => {
|
||||
getContentTypeInputName,
|
||||
} = naming;
|
||||
|
||||
const addCreateMutation = (t, contentType) => {
|
||||
const addCreateMutation = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Mutation'>,
|
||||
contentType: Schema.CollectionType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const createMutationName = getCreateMutationTypeName(contentType);
|
||||
@ -56,7 +60,10 @@ module.exports = ({ strapi }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const addUpdateMutation = (t, contentType) => {
|
||||
const addUpdateMutation = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Mutation'>,
|
||||
contentType: Schema.CollectionType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const updateMutationName = getUpdateMutationTypeName(contentType);
|
||||
@ -103,7 +110,10 @@ module.exports = ({ strapi }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const addDeleteMutation = (t, contentType) => {
|
||||
const addDeleteMutation = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Mutation'>,
|
||||
contentType: Schema.CollectionType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const deleteMutationName = getDeleteMutationTypeName(contentType);
|
||||
@ -138,18 +148,18 @@ module.exports = ({ strapi }) => {
|
||||
};
|
||||
|
||||
return {
|
||||
buildCollectionTypeMutations(contentType) {
|
||||
buildCollectionTypeMutations(contentType: Schema.CollectionType) {
|
||||
const createMutationName = `Mutation.${getCreateMutationTypeName(contentType)}`;
|
||||
const updateMutationName = `Mutation.${getUpdateMutationTypeName(contentType)}`;
|
||||
const deleteMutationName = `Mutation.${getDeleteMutationTypeName(contentType)}`;
|
||||
|
||||
const extension = getService('extension');
|
||||
|
||||
const registerAuthConfig = (action, auth) => {
|
||||
const registerAuthConfig = (action: string, auth: any) => {
|
||||
return extension.use({ resolversConfig: { [action]: { auth } } });
|
||||
};
|
||||
|
||||
const isActionEnabled = (action) => {
|
||||
const isActionEnabled = (action: string) => {
|
||||
return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
|
||||
};
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import createCollectionTypeMutationsBuilder from './collection-type';
|
||||
import createSingleTypeMutationsBuilder from './single-type';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
export default (context: Context) => ({
|
||||
...createCollectionTypeMutationsBuilder(context),
|
||||
...createSingleTypeMutationsBuilder(context),
|
||||
});
|
||||
@ -1,14 +1,13 @@
|
||||
'use strict';
|
||||
import { extendType, nonNull } from 'nexus';
|
||||
import { omit, isNil } from 'lodash/fp';
|
||||
import { sanitize, validate, errors } from '@strapi/utils';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const { extendType, nonNull } = require('nexus');
|
||||
const { omit, isNil } = require('lodash/fp');
|
||||
const { NotFoundError } = errors;
|
||||
|
||||
const utils = require('@strapi/utils');
|
||||
|
||||
const { sanitize, validate } = utils;
|
||||
const { NotFoundError } = utils.errors;
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
const { naming } = getService('utils');
|
||||
@ -22,7 +21,10 @@ module.exports = ({ strapi }) => {
|
||||
getDeleteMutationTypeName,
|
||||
} = naming;
|
||||
|
||||
const addUpdateMutation = (t, contentType) => {
|
||||
const addUpdateMutation = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Mutation'>,
|
||||
contentType: Schema.SingleType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const updateMutationName = getUpdateMutationTypeName(contentType);
|
||||
@ -68,7 +70,7 @@ module.exports = ({ strapi }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const entity = await strapi.entityService.findMany(uid, sanitizedQuery);
|
||||
const entity = (await strapi.entityService!.findMany(uid, sanitizedQuery)) as any;
|
||||
|
||||
// Create or update
|
||||
const value = isNil(entity)
|
||||
@ -80,7 +82,10 @@ module.exports = ({ strapi }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const addDeleteMutation = (t, contentType) => {
|
||||
const addDeleteMutation = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Mutation'>,
|
||||
contentType: Schema.SingleType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const deleteMutationName = getDeleteMutationTypeName(contentType);
|
||||
@ -107,7 +112,7 @@ module.exports = ({ strapi }) => {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
|
||||
const entity = await strapi.entityService.findMany(uid, sanitizedQuery);
|
||||
const entity = (await strapi.entityService!.findMany(uid, sanitizedQuery)) as any;
|
||||
|
||||
if (!entity) {
|
||||
throw new NotFoundError('Entity not found');
|
||||
@ -121,17 +126,17 @@ module.exports = ({ strapi }) => {
|
||||
};
|
||||
|
||||
return {
|
||||
buildSingleTypeMutations(contentType) {
|
||||
buildSingleTypeMutations(contentType: Schema.SingleType) {
|
||||
const updateMutationName = `Mutation.${getUpdateMutationTypeName(contentType)}`;
|
||||
const deleteMutationName = `Mutation.${getDeleteMutationTypeName(contentType)}`;
|
||||
|
||||
const extension = getService('extension');
|
||||
|
||||
const registerAuthConfig = (action, auth) => {
|
||||
const registerAuthConfig = (action: string, auth: any) => {
|
||||
return extension.use({ resolversConfig: { [action]: { auth } } });
|
||||
};
|
||||
|
||||
const isActionEnabled = (action) => {
|
||||
const isActionEnabled = (action: string) => {
|
||||
return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
|
||||
};
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
import { extendType } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const { extendType } = require('nexus');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
const { naming } = getService('utils');
|
||||
@ -16,17 +17,17 @@ module.exports = ({ strapi }) => {
|
||||
getEntityResponseCollectionName,
|
||||
} = naming;
|
||||
|
||||
const buildCollectionTypeQueries = (contentType) => {
|
||||
const buildCollectionTypeQueries = (contentType: Schema.CollectionType) => {
|
||||
const findOneQueryName = `Query.${getFindOneQueryName(contentType)}`;
|
||||
const findQueryName = `Query.${getFindQueryName(contentType)}`;
|
||||
|
||||
const extension = getService('extension');
|
||||
|
||||
const registerAuthConfig = (action, auth) => {
|
||||
const registerAuthConfig = (action: string, auth: any) => {
|
||||
return extension.use({ resolversConfig: { [action]: { auth } } });
|
||||
};
|
||||
|
||||
const isActionEnabled = (action) => {
|
||||
const isActionEnabled = (action: string) => {
|
||||
return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
|
||||
};
|
||||
|
||||
@ -58,10 +59,11 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
/**
|
||||
* Register a "find one" query field to the nexus type definition
|
||||
* @param {OutputDefinitionBlock<Query>} t
|
||||
* @param contentType
|
||||
*/
|
||||
const addFindOneQuery = (t, contentType) => {
|
||||
const addFindOneQuery = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Query'>,
|
||||
contentType: Schema.CollectionType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const findOneQueryName = getFindOneQueryName(contentType);
|
||||
@ -89,10 +91,11 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
/**
|
||||
* Register a "find" query field to the nexus type definition
|
||||
* @param {OutputDefinitionBlock<Query>} t
|
||||
* @param contentType
|
||||
*/
|
||||
const addFindQuery = (t, contentType) => {
|
||||
const addFindQuery = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<'Query'>,
|
||||
contentType: Schema.CollectionType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const findQueryName = getFindQueryName(contentType);
|
||||
@ -0,0 +1,8 @@
|
||||
import createCollectionTypeQueriesBuilder from './collection-type';
|
||||
import createSingleTypeQueriesBuilder from './single-type';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
export default (context: Context) => ({
|
||||
...createCollectionTypeQueriesBuilder(context),
|
||||
...createSingleTypeQueriesBuilder(context),
|
||||
});
|
||||
@ -1,8 +1,9 @@
|
||||
'use strict';
|
||||
import { extendType } from 'nexus';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const { extendType } = require('nexus');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
const { naming } = getService('utils');
|
||||
@ -11,16 +12,16 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
const { getFindOneQueryName, getEntityResponseName } = naming;
|
||||
|
||||
const buildSingleTypeQueries = (contentType) => {
|
||||
const buildSingleTypeQueries = (contentType: Schema.SingleType) => {
|
||||
const findQueryName = `Query.${getFindOneQueryName(contentType)}`;
|
||||
|
||||
const extension = getService('extension');
|
||||
|
||||
const registerAuthConfig = (action, auth) => {
|
||||
const registerAuthConfig = (action: string, auth: any) => {
|
||||
return extension.use({ resolversConfig: { [action]: { auth } } });
|
||||
};
|
||||
|
||||
const isActionEnabled = (action) => {
|
||||
const isActionEnabled = (action: string) => {
|
||||
return extension.shadowCRUD(contentType.uid).isActionEnabled(action);
|
||||
};
|
||||
|
||||
@ -41,7 +42,10 @@ module.exports = ({ strapi }) => {
|
||||
});
|
||||
};
|
||||
|
||||
const addFindQuery = (t, contentType) => {
|
||||
const addFindQuery = (
|
||||
t: Nexus.blocks.ObjectDefinitionBlock<string>,
|
||||
contentType: Schema.SingleType
|
||||
) => {
|
||||
const { uid } = contentType;
|
||||
|
||||
const findQueryName = getFindOneQueryName(contentType);
|
||||
@ -1,18 +1,16 @@
|
||||
'use strict';
|
||||
import { objectType, nonNull } from 'nexus';
|
||||
import { defaultTo, prop, pipe } from 'lodash/fp';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../types';
|
||||
|
||||
const { objectType, nonNull } = require('nexus');
|
||||
const { defaultTo, prop, pipe } = require('lodash/fp');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { naming } = strapi.plugin('graphql').service('utils');
|
||||
|
||||
return {
|
||||
/**
|
||||
* Build a type definition for a content API relation's collection response for a given content type
|
||||
* @param {object} contentType The content type which will be used to build its content API response definition
|
||||
* @return {NexusObjectTypeDef}
|
||||
*/
|
||||
buildRelationResponseCollectionDefinition(contentType) {
|
||||
buildRelationResponseCollectionDefinition(contentType: Schema.ContentType) {
|
||||
const name = naming.getRelationResponseCollectionName(contentType);
|
||||
const entityName = naming.getEntityName(contentType);
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
'use strict';
|
||||
import { get } from 'lodash/fp';
|
||||
import { sanitize, validate, pipeAsync, errors } from '@strapi/utils';
|
||||
import type { UID } from '@strapi/types';
|
||||
|
||||
const { get } = require('lodash/fp');
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const utils = require('@strapi/utils');
|
||||
const { ApplicationError } = errors;
|
||||
|
||||
const { sanitize, validate, pipeAsync } = utils;
|
||||
const { ApplicationError } = utils.errors;
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { service: getGraphQLService } = strapi.plugin('graphql');
|
||||
|
||||
const { isMorphRelation, isMedia } = getGraphQLService('utils').attributes;
|
||||
@ -15,9 +14,15 @@ module.exports = ({ strapi }) => {
|
||||
const { toEntityResponse, toEntityResponseCollection } = getGraphQLService('format').returnTypes;
|
||||
|
||||
return {
|
||||
buildAssociationResolver({ contentTypeUID, attributeName }) {
|
||||
buildAssociationResolver({
|
||||
contentTypeUID,
|
||||
attributeName,
|
||||
}: {
|
||||
contentTypeUID: UID.ContentType;
|
||||
attributeName: string;
|
||||
}) {
|
||||
const contentType = strapi.getModel(contentTypeUID);
|
||||
const attribute = contentType.attributes[attributeName];
|
||||
const attribute: any = contentType.attributes[attributeName];
|
||||
|
||||
if (!attribute) {
|
||||
throw new ApplicationError(
|
||||
@ -33,7 +38,7 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
const targetContentType = strapi.getModel(targetUID);
|
||||
|
||||
return async (parent, args = {}, context = {}) => {
|
||||
return async (parent: any, args: any = {}, context: any = {}) => {
|
||||
const { auth } = context.state;
|
||||
|
||||
const transformedArgs = transformArgs(args, {
|
||||
@ -48,7 +53,7 @@ module.exports = ({ strapi }) => {
|
||||
auth,
|
||||
});
|
||||
|
||||
const data = await strapi.entityService.load(
|
||||
const data = await strapi.entityService!.load(
|
||||
contentTypeUID,
|
||||
parent,
|
||||
attributeName,
|
||||
@ -65,8 +70,8 @@ module.exports = ({ strapi }) => {
|
||||
// so that the sanitize util can work properly.
|
||||
if (isMorphAttribute) {
|
||||
// Helpers used for the data cleanup
|
||||
const wrapData = (dataToWrap) => ({ [attributeName]: dataToWrap });
|
||||
const sanitizeData = (dataToSanitize) => {
|
||||
const wrapData = (dataToWrap: any) => ({ [attributeName]: dataToWrap });
|
||||
const sanitizeData = (dataToSanitize: any) => {
|
||||
return sanitize.contentAPI.output(dataToSanitize, contentType, { auth });
|
||||
};
|
||||
const unwrapData = get(attributeName);
|
||||
@ -1,15 +1,24 @@
|
||||
'use strict';
|
||||
import { sanitize, validate } from '@strapi/utils';
|
||||
import type { Attribute, UID } from '@strapi/types';
|
||||
|
||||
const { sanitize, validate } = require('@strapi/utils');
|
||||
import type { Context } from '../../types';
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
buildComponentResolver({ contentTypeUID, attributeName }) {
|
||||
export default ({ strapi }: Context) => ({
|
||||
buildComponentResolver({
|
||||
contentTypeUID,
|
||||
attributeName,
|
||||
}: {
|
||||
contentTypeUID: UID.ContentType;
|
||||
attributeName: string;
|
||||
}) {
|
||||
const { transformArgs } = strapi.plugin('graphql').service('builders').utils;
|
||||
|
||||
return async (parent, args = {}, ctx) => {
|
||||
return async (parent: any, args: any, ctx: any) => {
|
||||
const contentType = strapi.getModel(contentTypeUID);
|
||||
|
||||
const { component: componentName } = contentType.attributes[attributeName];
|
||||
const { component: componentName } = contentType.attributes[
|
||||
attributeName
|
||||
] as Attribute.Component;
|
||||
const component = strapi.getModel(componentName);
|
||||
|
||||
const transformedArgs = transformArgs(args, { contentType: component, usePagination: true });
|
||||
@ -19,7 +28,7 @@ module.exports = ({ strapi }) => ({
|
||||
const sanitizedQuery = await sanitize.contentAPI.query(transformedArgs, component, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
return strapi.entityService.load(contentTypeUID, parent, attributeName, sanitizedQuery);
|
||||
return strapi.entityService!.load(contentTypeUID, parent, attributeName, sanitizedQuery);
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,17 @@
|
||||
import type { UID } from '@strapi/types';
|
||||
|
||||
import type { Context } from '../../types';
|
||||
|
||||
export default ({ strapi }: Context) => ({
|
||||
buildDynamicZoneResolver({
|
||||
contentTypeUID,
|
||||
attributeName,
|
||||
}: {
|
||||
contentTypeUID: UID.ContentType;
|
||||
attributeName: string;
|
||||
}) {
|
||||
return async (parent: any) => {
|
||||
return strapi.entityService!.load(contentTypeUID, parent, attributeName);
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -0,0 +1,18 @@
|
||||
import associationResolvers from './association';
|
||||
import queriesResolvers from './query';
|
||||
import mutationsResolvers from './mutation';
|
||||
import componentResolvers from './component';
|
||||
import dynamicZoneResolvers from './dynamic-zone';
|
||||
|
||||
import type { Context } from '../../types';
|
||||
|
||||
export default (context: Context) => ({
|
||||
// Generics
|
||||
...associationResolvers(context),
|
||||
|
||||
// Builders
|
||||
...mutationsResolvers(context),
|
||||
...queriesResolvers(context),
|
||||
...componentResolvers(context),
|
||||
...dynamicZoneResolvers(context),
|
||||
});
|
||||
@ -1,29 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const { pick } = require('lodash/fp');
|
||||
const { sanitize, validate } = require('@strapi/utils');
|
||||
import { pick } from 'lodash/fp';
|
||||
import { sanitize, validate } from '@strapi/utils';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
const pickCreateArgs = pick(['params', 'data', 'files']);
|
||||
|
||||
module.exports = ({ strapi }) => ({
|
||||
buildMutationsResolvers({ contentType }) {
|
||||
export default ({ strapi }: Context) => ({
|
||||
buildMutationsResolvers({ contentType }: { contentType: Schema.ContentType }) {
|
||||
const { uid } = contentType;
|
||||
|
||||
return {
|
||||
async create(parent, args) {
|
||||
async create(parent: any, args: any) {
|
||||
// todo[v4]: Might be interesting to generate dynamic yup schema to validate payloads with more complex checks (on top of graphql validation)
|
||||
const params = pickCreateArgs(args);
|
||||
|
||||
// todo[v4]: Sanitize args to only keep params / data / files (or do it in the base resolver)
|
||||
return strapi.entityService.create(uid, params);
|
||||
return strapi.entityService!.create(uid, params);
|
||||
},
|
||||
|
||||
async update(parent, args) {
|
||||
async update(parent: any, args: any) {
|
||||
const { id, data } = args;
|
||||
return strapi.entityService.update(uid, id, { data });
|
||||
return strapi.entityService!.update(uid, id, { data });
|
||||
},
|
||||
|
||||
async delete(parent, args, ctx) {
|
||||
async delete(parent: any, args: any, ctx: any) {
|
||||
const { id, ...rest } = args;
|
||||
await validate.contentAPI.query(rest, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
@ -31,7 +31,7 @@ module.exports = ({ strapi }) => ({
|
||||
const sanitizedQuery = await sanitize.contentAPI.query(rest, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
return strapi.entityService.delete(uid, id, sanitizedQuery);
|
||||
return strapi.entityService!.delete(uid, id, sanitizedQuery);
|
||||
},
|
||||
};
|
||||
},
|
||||
@ -0,0 +1,34 @@
|
||||
import { omit } from 'lodash/fp';
|
||||
import { sanitize, validate } from '@strapi/utils';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../../types';
|
||||
|
||||
export default ({ strapi }: Context) => ({
|
||||
buildQueriesResolvers({ contentType }: { contentType: Schema.ContentType }) {
|
||||
const { uid } = contentType;
|
||||
|
||||
return {
|
||||
async find(parent: any, args: any, ctx: any) {
|
||||
await validate.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
const sanitizedQuery = await sanitize.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
|
||||
return strapi.entityService!.findMany(uid, sanitizedQuery);
|
||||
},
|
||||
|
||||
async findOne(parent: any, args: any, ctx: any) {
|
||||
await validate.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
const sanitizedQuery = await sanitize.contentAPI.query(args, contentType, {
|
||||
auth: ctx?.state?.auth,
|
||||
});
|
||||
|
||||
return strapi.entityService!.findOne(uid, args.id, omit('id', sanitizedQuery));
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
@ -1,19 +1,19 @@
|
||||
'use strict';
|
||||
import { objectType, nonNull } from 'nexus';
|
||||
import { defaultTo, prop, pipe } from 'lodash/fp';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../types';
|
||||
|
||||
const { objectType, nonNull } = require('nexus');
|
||||
const { defaultTo, prop, pipe } = require('lodash/fp');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { naming } = strapi.plugin('graphql').service('utils');
|
||||
const { RESPONSE_COLLECTION_META_TYPE_NAME } = strapi.plugin('graphql').service('constants');
|
||||
|
||||
return {
|
||||
/**
|
||||
* Build a type definition for a content API collection response for a given content type
|
||||
* @param {object} contentType The content type which will be used to build its content API response definition
|
||||
* @param {Schema.ContentType} contentType The content type which will be used to build its content API response definition
|
||||
* @return {NexusObjectTypeDef}
|
||||
*/
|
||||
buildResponseCollectionDefinition(contentType) {
|
||||
buildResponseCollectionDefinition(contentType: Schema.ContentType) {
|
||||
const name = naming.getEntityResponseCollectionName(contentType);
|
||||
const entityName = naming.getEntityName(contentType);
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
'use strict';
|
||||
import { objectType } from 'nexus';
|
||||
import { prop } from 'lodash/fp';
|
||||
import type { Schema } from '@strapi/types';
|
||||
import type { Context } from '../types';
|
||||
|
||||
const { objectType } = require('nexus');
|
||||
const { prop } = require('lodash/fp');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: Context) => {
|
||||
const { naming } = strapi.plugin('graphql').service('utils');
|
||||
|
||||
return {
|
||||
/**
|
||||
* Build a type definition for a content API response for a given content type
|
||||
* @param {object} contentType The content type which will be used to build its content API response definition
|
||||
* @return {NexusObjectTypeDef}
|
||||
*/
|
||||
buildResponseDefinition(contentType) {
|
||||
buildResponseDefinition(contentType: Schema.ContentType) {
|
||||
const name = naming.getEntityResponseName(contentType);
|
||||
const entityName = naming.getEntityName(contentType);
|
||||
|
||||
@ -1,24 +1,20 @@
|
||||
'use strict';
|
||||
import { isArray, isString, isUndefined, constant } from 'lodash/fp';
|
||||
import { objectType } from 'nexus';
|
||||
import { contentTypes } from '@strapi/utils';
|
||||
import type { Schema } from '@strapi/types';
|
||||
|
||||
const { isArray, isString, isUndefined, constant } = require('lodash/fp');
|
||||
const { objectType } = require('nexus');
|
||||
import type { Context } from '../types';
|
||||
|
||||
const { contentTypes } = require('@strapi/utils');
|
||||
export type TypeBuildersOptions = {
|
||||
builder: any;
|
||||
attributeName: string;
|
||||
attribute: any;
|
||||
contentType: any;
|
||||
context: Context;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef TypeBuildersOptions
|
||||
*
|
||||
* @property {ObjectDefinitionBlock} builder
|
||||
* @property {string} attributeName
|
||||
* @property {object} attribute
|
||||
* @property {object} contentType
|
||||
* @property {object} context
|
||||
* @property {object} context.strapi
|
||||
* @property {object} context.registry
|
||||
*/
|
||||
module.exports = (context) => {
|
||||
export default (context: Context) => {
|
||||
const { strapi } = context;
|
||||
|
||||
const getGraphQLService = strapi.plugin('graphql').service;
|
||||
|
||||
const extension = getGraphQLService('extension');
|
||||
@ -28,10 +24,10 @@ module.exports = (context) => {
|
||||
*
|
||||
* The attribute is added based on a simple association between a Strapi
|
||||
* type and a GraphQL type (the map is defined in `strapiTypeToGraphQLScalar`)
|
||||
*
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addScalarAttribute = ({ builder, attributeName, attribute }) => {
|
||||
const addScalarAttribute = (options: TypeBuildersOptions) => {
|
||||
const { builder, attributeName, attribute } = options;
|
||||
|
||||
const { mappers } = getGraphQLService('utils');
|
||||
|
||||
const gqlType = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
||||
@ -44,10 +40,10 @@ module.exports = (context) => {
|
||||
*
|
||||
* The attribute is added by fetching the component's type
|
||||
* name and using it as the attribute's type
|
||||
*
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addComponentAttribute = ({ builder, attributeName, contentType, attribute }) => {
|
||||
const addComponentAttribute = (options: TypeBuildersOptions) => {
|
||||
const { builder, attributeName, contentType, attribute } = options;
|
||||
|
||||
let localBuilder = builder;
|
||||
|
||||
const { naming } = getGraphQLService('utils');
|
||||
@ -78,10 +74,10 @@ module.exports = (context) => {
|
||||
*
|
||||
* The attribute is added by fetching the dynamic zone's
|
||||
* type name and using it as the attribute's type
|
||||
*
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addDynamicZoneAttribute = ({ builder, attributeName, contentType }) => {
|
||||
const addDynamicZoneAttribute = (options: TypeBuildersOptions) => {
|
||||
const { builder, attributeName, contentType } = options;
|
||||
|
||||
const { naming } = getGraphQLService('utils');
|
||||
const { ERROR_CODES } = getGraphQLService('constants');
|
||||
const { buildDynamicZoneResolver } = getGraphQLService('builders').get('content-api');
|
||||
@ -111,10 +107,10 @@ module.exports = (context) => {
|
||||
*
|
||||
* The attribute is added by fetching the enum's type
|
||||
* name and using it as the attribute's type
|
||||
*
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addEnumAttribute = ({ builder, attributeName, contentType }) => {
|
||||
const addEnumAttribute = (options: TypeBuildersOptions) => {
|
||||
const { builder, attributeName, contentType } = options;
|
||||
|
||||
const { naming } = getGraphQLService('utils');
|
||||
|
||||
const type = naming.getEnumName(contentType, attributeName);
|
||||
@ -124,9 +120,8 @@ module.exports = (context) => {
|
||||
|
||||
/**
|
||||
* Add a media attribute to the type definition
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addMediaAttribute = (options) => {
|
||||
const addMediaAttribute = (options: TypeBuildersOptions) => {
|
||||
const { naming } = getGraphQLService('utils');
|
||||
const { getContentTypeArgs } = getGraphQLService('builders').utils;
|
||||
const { buildAssociationResolver } = getGraphQLService('builders').get('content-api');
|
||||
@ -158,9 +153,8 @@ module.exports = (context) => {
|
||||
|
||||
/**
|
||||
* Add a polymorphic relational attribute to the type definition
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addPolymorphicRelationalAttribute = (options) => {
|
||||
const addPolymorphicRelationalAttribute = (options: TypeBuildersOptions) => {
|
||||
const { GENERIC_MORPH_TYPENAME } = getGraphQLService('constants');
|
||||
const { naming } = getGraphQLService('utils');
|
||||
const { buildAssociationResolver } = getGraphQLService('builders').get('content-api');
|
||||
@ -201,9 +195,8 @@ module.exports = (context) => {
|
||||
|
||||
/**
|
||||
* Add a regular relational attribute to the type definition
|
||||
* @param {TypeBuildersOptions} options
|
||||
*/
|
||||
const addRegularRelationalAttribute = (options) => {
|
||||
const addRegularRelationalAttribute = (options: TypeBuildersOptions) => {
|
||||
const { naming } = getGraphQLService('utils');
|
||||
const { getContentTypeArgs } = getGraphQLService('builders').utils;
|
||||
const { buildAssociationResolver } = getGraphQLService('builders').get('content-api');
|
||||
@ -240,11 +233,11 @@ module.exports = (context) => {
|
||||
builder.field(attributeName, { type, resolve, args });
|
||||
};
|
||||
|
||||
const isNotPrivate = (contentType) => (attributeName) => {
|
||||
const isNotPrivate = (contentType: Schema.Any) => (attributeName: string) => {
|
||||
return !contentTypes.isPrivateAttribute(contentType, attributeName);
|
||||
};
|
||||
|
||||
const isNotDisabled = (contentType) => (attributeName) => {
|
||||
const isNotDisabled = (contentType: Schema.Any) => (attributeName: string) => {
|
||||
return extension.shadowCRUD(contentType.uid).field(attributeName).hasOutputEnabled();
|
||||
};
|
||||
|
||||
@ -254,7 +247,7 @@ module.exports = (context) => {
|
||||
* @param contentType - The content type used to created the definition
|
||||
* @return {NexusObjectTypeDef}
|
||||
*/
|
||||
buildTypeDefinition(contentType) {
|
||||
buildTypeDefinition(contentType: Schema.Any) {
|
||||
const utils = getGraphQLService('utils');
|
||||
|
||||
const { getComponentName, getTypeName } = utils.naming;
|
||||
@ -310,16 +303,19 @@ module.exports = (context) => {
|
||||
|
||||
// We create a copy of the builder (t) to apply custom
|
||||
// rules only on the current attribute (eg: nonNull, list, ...)
|
||||
let builder = t;
|
||||
let builder: any = t;
|
||||
|
||||
if (attribute.required) {
|
||||
builder = builder.nonNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {TypeBuildersOptions}
|
||||
*/
|
||||
const options = { builder, attributeName, attribute, contentType, context };
|
||||
const options: TypeBuildersOptions = {
|
||||
builder,
|
||||
attributeName,
|
||||
attribute,
|
||||
contentType,
|
||||
context,
|
||||
};
|
||||
|
||||
// Enums
|
||||
if (isEnumeration(attribute)) {
|
||||
@ -1,12 +1,11 @@
|
||||
'use strict';
|
||||
import { entries, mapValues, omit } from 'lodash/fp';
|
||||
import { pagination, contentTypes } from '@strapi/utils';
|
||||
import type { Strapi, Schema } from '@strapi/types';
|
||||
|
||||
const { entries, mapValues, omit } = require('lodash/fp');
|
||||
const {
|
||||
pagination: { withDefaultPagination },
|
||||
contentTypes: { hasDraftAndPublish },
|
||||
} = require('@strapi/utils');
|
||||
const { withDefaultPagination } = pagination;
|
||||
const { hasDraftAndPublish } = contentTypes;
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
export default ({ strapi }: { strapi: Strapi }) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
return {
|
||||
@ -17,11 +16,11 @@ module.exports = ({ strapi }) => {
|
||||
* @param {boolean} options.multiple
|
||||
* @return {object}
|
||||
*/
|
||||
getContentTypeArgs(contentType, { multiple = true } = {}) {
|
||||
getContentTypeArgs(contentType: Schema.Any, { multiple = true } = {}) {
|
||||
const { naming } = getService('utils');
|
||||
const { args } = getService('internals');
|
||||
|
||||
const { kind, modelType } = contentType;
|
||||
const { modelType } = contentType;
|
||||
|
||||
// Components
|
||||
if (modelType === 'component') {
|
||||
@ -34,6 +33,8 @@ module.exports = ({ strapi }) => {
|
||||
};
|
||||
}
|
||||
|
||||
const { kind } = contentType;
|
||||
|
||||
// Collection Types
|
||||
if (kind === 'collectionType') {
|
||||
if (!multiple) {
|
||||
@ -67,14 +68,12 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
/**
|
||||
* Filter an object entries and keep only those whose value is a unique scalar attribute
|
||||
* @param {object} attributes
|
||||
* @return {Object<string, object>}
|
||||
*/
|
||||
getUniqueScalarAttributes(attributes) {
|
||||
getUniqueScalarAttributes(attributes: Schema.Attributes) {
|
||||
const { isStrapiScalar } = getService('utils').attributes;
|
||||
|
||||
const uniqueAttributes = entries(attributes).filter(
|
||||
([, attribute]) => isStrapiScalar(attribute) && attribute.unique
|
||||
([, attribute]) => isStrapiScalar(attribute) && 'unique' in attribute && attribute.unique
|
||||
);
|
||||
|
||||
return Object.fromEntries(uniqueAttributes);
|
||||
@ -85,18 +84,26 @@ module.exports = ({ strapi }) => {
|
||||
* @param {object} attributes - The attributes object to transform
|
||||
* @return {Object<string, string>}
|
||||
*/
|
||||
scalarAttributesToFiltersMap: mapValues((attribute) => {
|
||||
const { mappers, naming } = getService('utils');
|
||||
scalarAttributesToFiltersMap(attributes: Schema.Attributes) {
|
||||
return mapValues((attribute) => {
|
||||
const { mappers, naming } = getService('utils');
|
||||
|
||||
const gqlScalar = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
||||
const gqlScalar = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
||||
|
||||
return naming.getScalarFilterInputTypeName(gqlScalar);
|
||||
}),
|
||||
return naming.getScalarFilterInputTypeName(gqlScalar);
|
||||
}, attributes);
|
||||
},
|
||||
|
||||
/**
|
||||
* Apply basic transform to GQL args
|
||||
*/
|
||||
transformArgs(args, { contentType, usePagination = false } = {}) {
|
||||
transformArgs(
|
||||
args: any,
|
||||
{
|
||||
contentType,
|
||||
usePagination = false,
|
||||
}: { contentType: Schema.ContentType; usePagination?: boolean }
|
||||
) {
|
||||
const { mappers } = getService('utils');
|
||||
const { config } = strapi.plugin('graphql');
|
||||
const { pagination = {}, filters = {} } = args;
|
||||
@ -106,8 +113,8 @@ module.exports = ({ strapi }) => {
|
||||
|
||||
// Pagination
|
||||
if (usePagination) {
|
||||
const defaultLimit = config('defaultLimit');
|
||||
const maxLimit = config('maxLimit');
|
||||
const defaultLimit: number = config('defaultLimit');
|
||||
const maxLimit: number = config('maxLimit');
|
||||
|
||||
Object.assign(
|
||||
newArgs,
|
||||
@ -1,5 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
const PAGINATION_TYPE_NAME = 'Pagination';
|
||||
const PUBLICATION_STATE_TYPE_NAME = 'PublicationState';
|
||||
const ERROR_TYPE_NAME = 'Error';
|
||||
@ -17,7 +15,7 @@ const GRAPHQL_SCALARS = [
|
||||
'Date',
|
||||
'Time',
|
||||
'DateTime',
|
||||
];
|
||||
] as const;
|
||||
|
||||
const STRAPI_SCALARS = [
|
||||
'boolean',
|
||||
@ -38,7 +36,7 @@ const STRAPI_SCALARS = [
|
||||
'email',
|
||||
'password',
|
||||
'text',
|
||||
];
|
||||
] as const;
|
||||
|
||||
const SCALARS_ASSOCIATIONS = {
|
||||
uid: 'String',
|
||||
@ -59,7 +57,7 @@ const SCALARS_ASSOCIATIONS = {
|
||||
time: 'Time',
|
||||
datetime: 'DateTime',
|
||||
timestamp: 'DateTime',
|
||||
};
|
||||
} as const;
|
||||
|
||||
const GENERIC_MORPH_TYPENAME = 'GenericMorph';
|
||||
|
||||
@ -79,7 +77,7 @@ const KINDS = {
|
||||
scalar: 'scalar',
|
||||
morph: 'polymorphic',
|
||||
internal: 'internal',
|
||||
};
|
||||
} as const;
|
||||
|
||||
const allOperators = [
|
||||
'and',
|
||||
@ -113,7 +111,7 @@ const allOperators = [
|
||||
'notIn',
|
||||
|
||||
'between',
|
||||
];
|
||||
] as const;
|
||||
|
||||
const GRAPHQL_SCALAR_OPERATORS = {
|
||||
// ID
|
||||
@ -132,13 +130,27 @@ const GRAPHQL_SCALAR_OPERATORS = {
|
||||
DateTime: allOperators,
|
||||
// Others
|
||||
JSON: allOperators,
|
||||
};
|
||||
} as const;
|
||||
|
||||
const ERROR_CODES = {
|
||||
emptyDynamicZone: 'dynamiczone.empty',
|
||||
} as const;
|
||||
|
||||
export type Constants = {
|
||||
PAGINATION_TYPE_NAME: string;
|
||||
RESPONSE_COLLECTION_META_TYPE_NAME: string;
|
||||
PUBLICATION_STATE_TYPE_NAME: string;
|
||||
GRAPHQL_SCALARS: string[];
|
||||
STRAPI_SCALARS: string[];
|
||||
GENERIC_MORPH_TYPENAME: string;
|
||||
KINDS: typeof KINDS;
|
||||
GRAPHQL_SCALAR_OPERATORS: typeof GRAPHQL_SCALAR_OPERATORS;
|
||||
SCALARS_ASSOCIATIONS: typeof SCALARS_ASSOCIATIONS;
|
||||
ERROR_CODES: typeof ERROR_CODES;
|
||||
ERROR_TYPE_NAME: string;
|
||||
};
|
||||
|
||||
module.exports = () => ({
|
||||
export default () => ({
|
||||
PAGINATION_TYPE_NAME,
|
||||
RESPONSE_COLLECTION_META_TYPE_NAME,
|
||||
PUBLICATION_STATE_TYPE_NAME,
|
||||
@ -1,27 +1,32 @@
|
||||
'use strict';
|
||||
import { pruneSchema } from '@graphql-tools/utils';
|
||||
import { makeSchema } from 'nexus';
|
||||
import { prop, startsWith } from 'lodash/fp';
|
||||
import type * as Nexus from 'nexus';
|
||||
import type { Schema, Strapi } from '@strapi/types';
|
||||
|
||||
const { mergeSchemas, addResolversToSchema } = require('@graphql-tools/schema');
|
||||
const { pruneSchema } = require('@graphql-tools/utils');
|
||||
const { makeSchema } = require('nexus');
|
||||
const { prop, startsWith } = require('lodash/fp');
|
||||
|
||||
const { wrapResolvers } = require('./wrap-resolvers');
|
||||
const {
|
||||
import { wrapResolvers } from './wrap-resolvers';
|
||||
import {
|
||||
registerSingleType,
|
||||
registerCollectionType,
|
||||
registerComponent,
|
||||
registerScalars,
|
||||
registerInternals,
|
||||
registerPolymorphicContentType,
|
||||
contentType: {
|
||||
registerEnumsDefinition,
|
||||
registerInputsDefinition,
|
||||
registerFiltersDefinition,
|
||||
registerDynamicZonesDefinition,
|
||||
},
|
||||
} = require('./register-functions');
|
||||
contentType,
|
||||
} from './register-functions';
|
||||
import { TypeRegistry } from '../type-registry';
|
||||
|
||||
const {
|
||||
registerEnumsDefinition,
|
||||
registerInputsDefinition,
|
||||
registerFiltersDefinition,
|
||||
registerDynamicZonesDefinition,
|
||||
} = contentType;
|
||||
|
||||
export default ({ strapi }: { strapi: Strapi }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { mergeSchemas, addResolversToSchema } = require('@graphql-tools/schema');
|
||||
|
||||
module.exports = ({ strapi }) => {
|
||||
const { service: getGraphQLService } = strapi.plugin('graphql');
|
||||
const { config } = strapi.plugin('graphql');
|
||||
|
||||
@ -29,9 +34,9 @@ module.exports = ({ strapi }) => {
|
||||
const extensionService = getGraphQLService('extension');
|
||||
|
||||
// Type Registry
|
||||
let registry;
|
||||
let registry: any;
|
||||
// Builders Instances
|
||||
let builders;
|
||||
let builders: any;
|
||||
|
||||
const buildSchema = () => {
|
||||
const isShadowCRUDEnabled = !!config('shadowCRUD');
|
||||
@ -63,7 +68,7 @@ module.exports = ({ strapi }) => {
|
||||
const schemaWithResolvers = addResolversToSchema(schema, extension.resolvers);
|
||||
|
||||
// Create a configuration object for the artifacts generation
|
||||
const outputs = {
|
||||
const outputs: Nexus.core.SchemaConfig['outputs'] = {
|
||||
schema: config('artifacts.schema', false),
|
||||
typegen: config('artifacts.typegen', false),
|
||||
};
|
||||
@ -71,6 +76,7 @@ module.exports = ({ strapi }) => {
|
||||
const currentEnv = strapi.config.get('environment');
|
||||
|
||||
const nexusSchema = makeSchema({
|
||||
types: [],
|
||||
// Build the schema from the merged GraphQL schema.
|
||||
// Since we're passing the schema to the mergeSchema property, it'll transform our SDL type definitions
|
||||
// into Nexus type definition, thus allowing them to be handled by Nexus plugins & other processing
|
||||
@ -97,7 +103,7 @@ module.exports = ({ strapi }) => {
|
||||
return prunedNexusSchema;
|
||||
};
|
||||
|
||||
const buildMergedSchema = ({ registry }) => {
|
||||
const buildMergedSchema = ({ registry }: { registry: TypeRegistry }) => {
|
||||
// Here we extract types, plugins & typeDefs from a temporary generated
|
||||
// extension since there won't be any addition allowed after schemas generation
|
||||
const { types, typeDefs = [] } = extensionService.generate({ typeRegistry: registry });
|
||||
@ -144,9 +150,9 @@ module.exports = ({ strapi }) => {
|
||||
* Register needed GraphQL types for every content type
|
||||
* @param {object[]} contentTypes
|
||||
*/
|
||||
const registerAPITypes = (contentTypes) => {
|
||||
const registerAPITypes = (contentTypes: Schema.Any[]) => {
|
||||
for (const contentType of contentTypes) {
|
||||
const { kind, modelType } = contentType;
|
||||
const { modelType } = contentType;
|
||||
|
||||
const registerOptions = { registry, strapi, builders };
|
||||
|
||||
@ -160,10 +166,13 @@ module.exports = ({ strapi }) => {
|
||||
// Generate & register component's definition
|
||||
if (modelType === 'component') {
|
||||
registerComponent(contentType, registerOptions);
|
||||
continue;
|
||||
}
|
||||
|
||||
const { kind } = contentType;
|
||||
|
||||
// Generate & register single type's definition
|
||||
else if (kind === 'singleType') {
|
||||
if (kind === 'singleType') {
|
||||
registerSingleType(contentType, registerOptions);
|
||||
}
|
||||
|
||||
@ -174,7 +183,7 @@ module.exports = ({ strapi }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const registerMorphTypes = (contentTypes) => {
|
||||
const registerMorphTypes = (contentTypes: Schema.Any[]) => {
|
||||
// Create & register a union type that includes every type or component registered
|
||||
const genericMorphType = builders.buildGenericMorphDefinition();
|
||||
registry.register(GENERIC_MORPH_TYPENAME, genericMorphType, { kind: KINDS.morph });
|
||||
@ -0,0 +1,72 @@
|
||||
import { propOr } from 'lodash/fp';
|
||||
import { GraphQLFieldResolver, GraphQLResolveInfo } from 'graphql';
|
||||
import { policy as policyUtils, errors } from '@strapi/utils';
|
||||
import type { Strapi } from '@strapi/types';
|
||||
|
||||
const { PolicyError } = errors;
|
||||
|
||||
const getPoliciesConfig = propOr([], 'policies');
|
||||
|
||||
const createPoliciesMiddleware = (resolverConfig: any, { strapi }: { strapi: Strapi }) => {
|
||||
const resolverPolicies = getPoliciesConfig(resolverConfig);
|
||||
const policies = policyUtils.resolve(resolverPolicies, {});
|
||||
|
||||
return async (
|
||||
resolve: GraphQLFieldResolver<any, any>,
|
||||
parent: any,
|
||||
args: any,
|
||||
context: any,
|
||||
info: GraphQLResolveInfo
|
||||
) => {
|
||||
// Create a graphql policy context
|
||||
const policyContext = createGraphQLPolicyContext(parent, args, context, info);
|
||||
|
||||
// Run policies & throw an error if one of them fails
|
||||
for (const { handler, config } of policies) {
|
||||
const result = await handler(policyContext, config, { strapi });
|
||||
|
||||
if (![true, undefined].includes(result)) {
|
||||
throw new PolicyError();
|
||||
}
|
||||
}
|
||||
|
||||
return resolve(parent, args, context, info);
|
||||
};
|
||||
};
|
||||
|
||||
const createGraphQLPolicyContext = (
|
||||
parent: any,
|
||||
args: any,
|
||||
context: any,
|
||||
info: GraphQLResolveInfo
|
||||
) => {
|
||||
const policyContext = {
|
||||
get parent() {
|
||||
return parent;
|
||||
},
|
||||
|
||||
get args() {
|
||||
return args;
|
||||
},
|
||||
|
||||
get context() {
|
||||
return context;
|
||||
},
|
||||
|
||||
get info() {
|
||||
return info;
|
||||
},
|
||||
|
||||
get state() {
|
||||
return this.context.state;
|
||||
},
|
||||
|
||||
get http() {
|
||||
return this.context.koaContext;
|
||||
},
|
||||
};
|
||||
|
||||
return policyUtils.createPolicyContext('graphql', policyContext);
|
||||
};
|
||||
|
||||
export { createPoliciesMiddleware };
|
||||
@ -1,6 +1,18 @@
|
||||
'use strict';
|
||||
import type { Strapi, Schema } from '@strapi/types';
|
||||
import type { TypeRegistry } from '../../type-registry';
|
||||
|
||||
const registerCollectionType = (contentType, { registry, strapi, builders }) => {
|
||||
const registerCollectionType = (
|
||||
contentType: Schema.CollectionType,
|
||||
{
|
||||
registry,
|
||||
strapi,
|
||||
builders,
|
||||
}: {
|
||||
registry: TypeRegistry;
|
||||
strapi: Strapi;
|
||||
builders: any;
|
||||
}
|
||||
) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
const { naming } = getService('utils');
|
||||
@ -19,7 +31,7 @@ const registerCollectionType = (contentType, { registry, strapi, builders }) =>
|
||||
mutations: naming.getEntityMutationsTypeName(contentType),
|
||||
};
|
||||
|
||||
const getConfig = (kind) => ({ kind, contentType });
|
||||
const getConfig = (kind: string) => ({ kind, contentType });
|
||||
|
||||
// Type definition
|
||||
registry.register(types.base, builders.buildTypeDefinition(contentType), getConfig(KINDS.type));
|
||||
@ -69,4 +81,4 @@ const registerCollectionType = (contentType, { registry, strapi, builders }) =>
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { registerCollectionType };
|
||||
export { registerCollectionType };
|
||||
@ -1,6 +1,18 @@
|
||||
'use strict';
|
||||
import type { Strapi, Schema } from '@strapi/types';
|
||||
import type { TypeRegistry } from '../../type-registry';
|
||||
|
||||
const registerComponent = (contentType, { registry, strapi, builders }) => {
|
||||
const registerComponent = (
|
||||
contentType: Schema.Component,
|
||||
{
|
||||
registry,
|
||||
strapi,
|
||||
builders,
|
||||
}: {
|
||||
registry: TypeRegistry;
|
||||
strapi: Strapi;
|
||||
builders: any;
|
||||
}
|
||||
) => {
|
||||
const { service: getService } = strapi.plugin('graphql');
|
||||
|
||||
const { getComponentName } = getService('utils').naming;
|
||||
@ -12,4 +24,4 @@ const registerComponent = (contentType, { registry, strapi, builders }) => {
|
||||
registry.register(name, definition, { kind: KINDS.component, contentType });
|
||||
};
|
||||
|
||||
module.exports = { registerComponent };
|
||||
export { registerComponent };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user