Compare commits
1 Commits
main
...
chore/grap
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7e036e1b8 |
@@ -6,65 +6,10 @@
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Supported timezones in IANA format.
|
||||
*
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "supportedTimezones".
|
||||
*/
|
||||
export type SupportedTimezones =
|
||||
| 'Pacific/Midway'
|
||||
| 'Pacific/Niue'
|
||||
| 'Pacific/Honolulu'
|
||||
| 'Pacific/Rarotonga'
|
||||
| 'America/Anchorage'
|
||||
| 'Pacific/Gambier'
|
||||
| 'America/Los_Angeles'
|
||||
| 'America/Tijuana'
|
||||
| 'America/Denver'
|
||||
| 'America/Phoenix'
|
||||
| 'America/Chicago'
|
||||
| 'America/Guatemala'
|
||||
| 'America/New_York'
|
||||
| 'America/Bogota'
|
||||
| 'America/Caracas'
|
||||
| 'America/Santiago'
|
||||
| 'America/Buenos_Aires'
|
||||
| 'America/Sao_Paulo'
|
||||
| 'Atlantic/South_Georgia'
|
||||
| 'Atlantic/Azores'
|
||||
| 'Atlantic/Cape_Verde'
|
||||
| 'Europe/London'
|
||||
| 'Europe/Berlin'
|
||||
| 'Africa/Lagos'
|
||||
| 'Europe/Athens'
|
||||
| 'Africa/Cairo'
|
||||
| 'Europe/Moscow'
|
||||
| 'Asia/Riyadh'
|
||||
| 'Asia/Dubai'
|
||||
| 'Asia/Baku'
|
||||
| 'Asia/Karachi'
|
||||
| 'Asia/Tashkent'
|
||||
| 'Asia/Calcutta'
|
||||
| 'Asia/Dhaka'
|
||||
| 'Asia/Almaty'
|
||||
| 'Asia/Jakarta'
|
||||
| 'Asia/Bangkok'
|
||||
| 'Asia/Shanghai'
|
||||
| 'Asia/Singapore'
|
||||
| 'Asia/Tokyo'
|
||||
| 'Asia/Seoul'
|
||||
| 'Australia/Sydney'
|
||||
| 'Pacific/Guam'
|
||||
| 'Pacific/Noumea'
|
||||
| 'Pacific/Auckland'
|
||||
| 'Pacific/Fiji';
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
blocks: {};
|
||||
collections: {
|
||||
posts: Post;
|
||||
media: Media;
|
||||
|
||||
@@ -4,6 +4,7 @@ import path from 'path'
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||
import { devUser } from '../credentials.js'
|
||||
import { opsCounterPlugin } from './opsCounter.js'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
@@ -31,6 +32,80 @@ export default buildConfigWithDefaults({
|
||||
complexity: 801,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts-2',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts-3',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts-4',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts-5',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts-6',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
label: 'Title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -53,8 +128,14 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
graphQL: {
|
||||
maxComplexity: 800,
|
||||
validationRules: () => [NoIntrospection],
|
||||
// validationRules: () => [NoIntrospection],
|
||||
},
|
||||
plugins: [
|
||||
opsCounterPlugin({
|
||||
warnAt: 5,
|
||||
max: 20,
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
const NoIntrospection: GraphQL.ValidationRule = (context) => ({
|
||||
|
||||
@@ -104,5 +104,51 @@ describe('graphql', () => {
|
||||
|
||||
expect(res.hyphenated_name).toStrictEqual('example-hyphenated-name')
|
||||
})
|
||||
|
||||
it('should leverage expected amount of operations per query', async () => {
|
||||
const query = `query pageTypeBySlug($slug: String!, $draft: Boolean = false) {
|
||||
Posts(where: { slug: { equals: $slug } }, draft: $draft) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
Posts2s(where: { slug: { equals: $slug } }, draft: $draft) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
Posts3s(where: { slug: { equals: $slug } }, draft: $draft) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
Posts4s(where: { slug: { equals: $slug } }, draft: $draft) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
Posts5s(where: { slug: { equals: $slug } }, draft: $draft) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
Posts6s(where: { slug: { equals: $slug } }, draft: $draft) {
|
||||
docs {
|
||||
id
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
const { data } = await restClient
|
||||
.GRAPHQL_POST({ body: JSON.stringify({ query, variables: { slug: 'hello' } }) })
|
||||
.then((res) => res.json())
|
||||
|
||||
expect(data.Posts.docs).toBeDefined()
|
||||
expect(data.Posts2s.docs).toBeDefined()
|
||||
expect(data.Posts3s.docs).toBeDefined()
|
||||
expect(data.Posts4s.docs).toBeDefined()
|
||||
expect(data.Posts5s.docs).toBeDefined()
|
||||
expect(data.Posts6s.docs).toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
52
test/graphql/opsCounter.ts
Normal file
52
test/graphql/opsCounter.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import type { CollectionBeforeOperationHook, Plugin } from 'payload'
|
||||
|
||||
import { APIError } from 'payload'
|
||||
|
||||
type Args = {
|
||||
max?: number
|
||||
warnAt?: number
|
||||
}
|
||||
|
||||
export const opsCounterPlugin =
|
||||
(args?: Args): Plugin =>
|
||||
(config) => {
|
||||
const max = args?.max || 50
|
||||
const warnAt = args?.warnAt || 10
|
||||
|
||||
const beforeOperationHook: CollectionBeforeOperationHook = ({
|
||||
req,
|
||||
collection,
|
||||
operation,
|
||||
args,
|
||||
}) => {
|
||||
const currentCount = req.context.opsCount
|
||||
|
||||
if (typeof currentCount === 'number') {
|
||||
req.context.opsCount = currentCount + 1
|
||||
|
||||
if (warnAt && currentCount >= warnAt) {
|
||||
req.payload.logger.error(
|
||||
`Detected a ${operation} in the "${collection.slug}" collection which has run ${warnAt} times or more.`,
|
||||
)
|
||||
}
|
||||
|
||||
if (currentCount > max) {
|
||||
throw new APIError(`Maximum operations of ${max} detected.`)
|
||||
}
|
||||
} else {
|
||||
req.context.opsCount = 1
|
||||
}
|
||||
}
|
||||
|
||||
;(config.collections || []).forEach((collection) => {
|
||||
if (!collection.hooks) {
|
||||
collection.hooks = {}
|
||||
}
|
||||
if (!collection.hooks.beforeOperation) {
|
||||
collection.hooks.beforeOperation = []
|
||||
}
|
||||
|
||||
collection.hooks.beforeOperation.push(beforeOperationHook)
|
||||
})
|
||||
return config
|
||||
}
|
||||
@@ -6,66 +6,17 @@
|
||||
* and re-run `payload generate:types` to regenerate this file.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Supported timezones in IANA format.
|
||||
*
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "supportedTimezones".
|
||||
*/
|
||||
export type SupportedTimezones =
|
||||
| 'Pacific/Midway'
|
||||
| 'Pacific/Niue'
|
||||
| 'Pacific/Honolulu'
|
||||
| 'Pacific/Rarotonga'
|
||||
| 'America/Anchorage'
|
||||
| 'Pacific/Gambier'
|
||||
| 'America/Los_Angeles'
|
||||
| 'America/Tijuana'
|
||||
| 'America/Denver'
|
||||
| 'America/Phoenix'
|
||||
| 'America/Chicago'
|
||||
| 'America/Guatemala'
|
||||
| 'America/New_York'
|
||||
| 'America/Bogota'
|
||||
| 'America/Caracas'
|
||||
| 'America/Santiago'
|
||||
| 'America/Buenos_Aires'
|
||||
| 'America/Sao_Paulo'
|
||||
| 'Atlantic/South_Georgia'
|
||||
| 'Atlantic/Azores'
|
||||
| 'Atlantic/Cape_Verde'
|
||||
| 'Europe/London'
|
||||
| 'Europe/Berlin'
|
||||
| 'Africa/Lagos'
|
||||
| 'Europe/Athens'
|
||||
| 'Africa/Cairo'
|
||||
| 'Europe/Moscow'
|
||||
| 'Asia/Riyadh'
|
||||
| 'Asia/Dubai'
|
||||
| 'Asia/Baku'
|
||||
| 'Asia/Karachi'
|
||||
| 'Asia/Tashkent'
|
||||
| 'Asia/Calcutta'
|
||||
| 'Asia/Dhaka'
|
||||
| 'Asia/Almaty'
|
||||
| 'Asia/Jakarta'
|
||||
| 'Asia/Bangkok'
|
||||
| 'Asia/Shanghai'
|
||||
| 'Asia/Singapore'
|
||||
| 'Asia/Tokyo'
|
||||
| 'Asia/Seoul'
|
||||
| 'Australia/Sydney'
|
||||
| 'Pacific/Guam'
|
||||
| 'Pacific/Noumea'
|
||||
| 'Pacific/Auckland'
|
||||
| 'Pacific/Fiji';
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
collections: {
|
||||
posts: Post;
|
||||
'posts-2': Posts2;
|
||||
'posts-3': Posts3;
|
||||
'posts-4': Posts4;
|
||||
'posts-5': Posts5;
|
||||
'posts-6': Posts6;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
@@ -74,6 +25,11 @@ export interface Config {
|
||||
collectionsJoins: {};
|
||||
collectionsSelect: {
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
'posts-2': Posts2Select<false> | Posts2Select<true>;
|
||||
'posts-3': Posts3Select<false> | Posts3Select<true>;
|
||||
'posts-4': Posts4Select<false> | Posts4Select<true>;
|
||||
'posts-5': Posts5Select<false> | Posts5Select<true>;
|
||||
'posts-6': Posts6Select<false> | Posts6Select<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
@@ -120,6 +76,62 @@ export interface Post {
|
||||
title?: string | null;
|
||||
'hyphenated-name'?: string | null;
|
||||
relationToSelf?: (string | null) | Post;
|
||||
slug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-2".
|
||||
*/
|
||||
export interface Posts2 {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
slug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-3".
|
||||
*/
|
||||
export interface Posts3 {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
slug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-4".
|
||||
*/
|
||||
export interface Posts4 {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
slug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-5".
|
||||
*/
|
||||
export interface Posts5 {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
slug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-6".
|
||||
*/
|
||||
export interface Posts6 {
|
||||
id: string;
|
||||
title?: string | null;
|
||||
slug?: string | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -151,6 +163,26 @@ export interface PayloadLockedDocument {
|
||||
relationTo: 'posts';
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-2';
|
||||
value: string | Posts2;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-3';
|
||||
value: string | Posts3;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-4';
|
||||
value: string | Posts4;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-5';
|
||||
value: string | Posts5;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'posts-6';
|
||||
value: string | Posts6;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
@@ -205,6 +237,57 @@ export interface PostsSelect<T extends boolean = true> {
|
||||
title?: T;
|
||||
'hyphenated-name'?: T;
|
||||
relationToSelf?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-2_select".
|
||||
*/
|
||||
export interface Posts2Select<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-3_select".
|
||||
*/
|
||||
export interface Posts3Select<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-4_select".
|
||||
*/
|
||||
export interface Posts4Select<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-5_select".
|
||||
*/
|
||||
export interface Posts5Select<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts-6_select".
|
||||
*/
|
||||
export interface Posts6Select<T extends boolean = true> {
|
||||
title?: T;
|
||||
slug?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ export default withBundleAnalyzer(
|
||||
|
||||
return webpackConfig
|
||||
},
|
||||
serverExternalPackages: ['graphql'],
|
||||
},
|
||||
{ devBundleServerPackages: false },
|
||||
),
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@payload-config": ["./test/fields/config.ts"],
|
||||
"@payload-config": ["./test/graphql/config.ts"],
|
||||
"@payloadcms/admin-bar": ["./packages/admin-bar/src"],
|
||||
"@payloadcms/live-preview": ["./packages/live-preview/src"],
|
||||
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user