Compare commits
17 Commits
db-postgre
...
v2.14.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87a1d698b2 | ||
|
|
c11600aac3 | ||
|
|
ad01c6784d | ||
|
|
62601c54a7 | ||
|
|
4a144ddc44 | ||
|
|
9152a238d2 | ||
|
|
fc8b835264 | ||
|
|
28ee5e34c3 | ||
|
|
e25886649f | ||
|
|
985796be54 | ||
|
|
bd8b5123b0 | ||
|
|
c380deee4a | ||
|
|
0b12aac895 | ||
|
|
90d3f178ab | ||
|
|
a8c9625cde | ||
|
|
938d069523 | ||
|
|
1a337ec223 |
70
.github/workflows/main.yml
vendored
70
.github/workflows/main.yml
vendored
@@ -52,14 +52,14 @@ jobs:
|
||||
# https://github.com/actions/virtual-environments/issues/1187
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
@@ -69,7 +69,7 @@ jobs:
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
- run: pnpm run build
|
||||
|
||||
- name: Cache build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -108,19 +108,19 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -193,19 +193,19 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -234,19 +234,19 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -278,19 +278,19 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -319,19 +319,19 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Restore build
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./*
|
||||
key: ${{ github.sha }}-${{ github.run_number }}
|
||||
@@ -361,10 +361,10 @@ jobs:
|
||||
- name: tune linux network
|
||||
run: sudo ethtool -K eth0 tx off rx off
|
||||
|
||||
- name: Use Node.js 18
|
||||
uses: actions/setup-node@v3
|
||||
- name: Use Node.js 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
|
||||
- name: Start MongoDB
|
||||
uses: supercharge/mongodb-github-action@1.10.0
|
||||
|
||||
11
.github/workflows/pr-title.yml
vendored
Normal file
11
.github/workflows/pr-title.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: pr-title
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Echo
|
||||
run: echo "Register pr-title workflow"
|
||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -5,21 +5,21 @@
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
},
|
||||
"[json]": {
|
||||
|
||||
17
CHANGELOG.md
17
CHANGELOG.md
@@ -1,3 +1,20 @@
|
||||
## [2.14.0](https://github.com/payloadcms/payload/compare/v2.13.0...v2.14.0) (2024-04-24)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add count operation to collections ([#5936](https://github.com/payloadcms/payload/issues/5936)) ([c380dee](https://github.com/payloadcms/payload/commit/c380deee4a1db82bce9fea264060000957a53eee))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bulk publish ([#6006](https://github.com/payloadcms/payload/issues/6006)) ([c11600a](https://github.com/payloadcms/payload/commit/c11600aac38cd67019765faf2a41e62df13e50cc))
|
||||
* **db-postgres:** extra version suffix added to table names ([#5939](https://github.com/payloadcms/payload/issues/5939)) ([bd8b512](https://github.com/payloadcms/payload/commit/bd8b5123b0991e53eb209315897dbca10d14d45e))
|
||||
* **db-postgres:** Fixes nested groups inside nested blocks ([#5882](https://github.com/payloadcms/payload/issues/5882)) ([e258866](https://github.com/payloadcms/payload/commit/e25886649fce414d5d47918f35ba2d4d2ba59174))
|
||||
* **db-postgres:** row table names were not being built properly - v2 ([#5961](https://github.com/payloadcms/payload/issues/5961)) ([9152a23](https://github.com/payloadcms/payload/commit/9152a238d2982503e7f509350651b0ba3f83b1ec))
|
||||
* header filters ([#5997](https://github.com/payloadcms/payload/issues/5997)) ([ad01c67](https://github.com/payloadcms/payload/commit/ad01c6784d283386dc819dfcd47455cad5accfaa))
|
||||
* min/max attributes missing from number input ([#5779](https://github.com/payloadcms/payload/issues/5779)) ([985796b](https://github.com/payloadcms/payload/commit/985796be54b593af0a4934685ab8621b9badda10))
|
||||
* removes `equals` & `not_equals` operators from fields with `hasMany` ([#5885](https://github.com/payloadcms/payload/issues/5885)) ([a8c9625](https://github.com/payloadcms/payload/commit/a8c9625cdec33476a5da87bcd9f010f9d7fb9a94))
|
||||
|
||||
## [2.13.0](https://github.com/payloadcms/payload/compare/v2.12.1...v2.13.0) (2024-04-19)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<hr/>
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 🎉 <strong>Payload 2.0 is now available!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/payload-2-0" rel="dofollow"><strong>announcement post</strong></a>.
|
||||
> 🎉 <strong>Payload 3.0 beta announced!</strong> Read more in the <a target="_blank" href="https://payloadcms.com/blog/30-beta-install-payload-into-any-nextjs-app-with-one-line" rel="dofollow"><strong>announcement post</strong></a>.
|
||||
|
||||
<h3>Benefits over a regular CMS</h3>
|
||||
<ul>
|
||||
|
||||
@@ -657,7 +657,7 @@ As your admin customizations gets more complex you may want to share state betwe
|
||||
|
||||
### Styling Custom Components
|
||||
|
||||
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-ini styling, so it blends more thoroughly into the existing admin UI.
|
||||
Payload exports its SCSS variables and mixins for reuse in your own custom components. This is helpful in cases where you might want to style a custom input similarly to Payload's built-in styling, so it blends more thoroughly into the existing admin UI.
|
||||
|
||||
To make use of Payload SCSS variables / mixins to use directly in your own components, you can import them as follows:
|
||||
|
||||
|
||||
@@ -43,11 +43,12 @@ export const PublicUser: CollectionConfig = {
|
||||
|
||||
**Payload will automatically open up the following queries:**
|
||||
|
||||
| Query Name | Operation |
|
||||
| ------------------ | ------------------- |
|
||||
| **`PublicUser`** | `findByID` |
|
||||
| **`PublicUsers`** | `find` |
|
||||
| **`mePublicUser`** | `me` auth operation |
|
||||
| Query Name | Operation |
|
||||
| ------------------ | ------------------- |
|
||||
| **`PublicUser`** | `findByID` |
|
||||
| **`PublicUsers`** | `find` |
|
||||
| **`countPublicUsers`** | `count` |
|
||||
| **`mePublicUser`** | `me` auth operation |
|
||||
|
||||
**And the following mutations:**
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ keywords: live preview, frontend, react, next.js, vue, nuxt.js, svelte, hook, us
|
||||
|
||||
While using Live Preview, the Admin panel emits a new `window.postMessage` event every time a change is made to the document. Your front-end application can listen for these events and re-render accordingly.
|
||||
|
||||
Wiring your front-end into Live Preview is easy. If your front-end application is built with React or Next.js, use the [`useLivePreview`](#react) React hook that Payload provides. In the future, all other major frameworks like Vue, Svelte, etc will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.
|
||||
Wiring your front-end into Live Preview is easy. If your front-end application is built with React, Next.js, Vue or Nuxt.js, use the `useLivePreview` hook that Payload provides. In the future, all other major frameworks like Svelte will be officially supported. If you are using any of these frameworks today, you can still integrate with Live Preview yourself using the underlying tooling that Payload provides. See [building your own hook](#building-your-own-hook) for more information.
|
||||
|
||||
By default, all hooks accept the following args:
|
||||
|
||||
@@ -32,6 +32,10 @@ And return the following values:
|
||||
If your front-end is tightly coupled to required fields, you should ensure that your UI does not break when these fields are removed. For example, if you are rendering something like `data.relatedPosts[0].title`, your page will break once you remove the first related post. To get around this, use conditional logic, optional chaining, or default values in your UI where needed. For example, `data?.relatedPosts?.[0]?.title`.
|
||||
</Banner>
|
||||
|
||||
<Banner type="info">
|
||||
If is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../getting-started/concepts#depth) for more information.
|
||||
</Banner>
|
||||
|
||||
### React
|
||||
|
||||
If your front-end application is built with React or Next.js, you can use the `useLivePreview` hook that Payload provides.
|
||||
@@ -69,9 +73,40 @@ export const PageClient: React.FC<{
|
||||
}
|
||||
```
|
||||
|
||||
<Banner type="info">
|
||||
If is important that the `depth` argument matches exactly with the depth of your initial page request. The depth property is used to populated relationships and uploads beyond their IDs. See [Depth](../getting-started/concepts#depth) for more information.
|
||||
</Banner>
|
||||
### Vue
|
||||
|
||||
If your front-end application is built with Vue 3 or Nuxt 3, you can use the `useLivePreview` composable that Payload provides.
|
||||
|
||||
First, install the `@payloadcms/live-preview-vue` package:
|
||||
|
||||
```bash
|
||||
npm install @payloadcms/live-preview-vue
|
||||
```
|
||||
|
||||
Then, use the `useLivePreview` hook in your Vue component:
|
||||
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import type { PageData } from '~/types';
|
||||
import { defineProps } from 'vue';
|
||||
import { useLivePreview } from '@payloadcms/live-preview-vue';
|
||||
|
||||
// Fetch the initial data on the parent component or using async state
|
||||
const props = defineProps<{ initialData: PageData }>();
|
||||
|
||||
// The hook will take over from here and keep the preview in sync with the changes you make.
|
||||
// The `data` property will contain the live data of the document only when viewed from the Preview view of the Admin UI.
|
||||
const { data } = useLivePreview<PageData>({
|
||||
initialData: props.initialData,
|
||||
serverURL: "<PAYLOAD_SERVER_URL>",
|
||||
depth: 2,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ data.title }}</h1>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Building your own hook
|
||||
|
||||
|
||||
@@ -164,6 +164,22 @@ const result = await payload.findByID({
|
||||
})
|
||||
```
|
||||
|
||||
#### Count
|
||||
|
||||
```js
|
||||
// Result will be an object with:
|
||||
// {
|
||||
// totalDocs: 10, // count of the documents satisfies query
|
||||
// }
|
||||
const result = await payload.count({
|
||||
collection: 'posts', // required
|
||||
locale: 'en',
|
||||
where: {}, // pass a `where` query here
|
||||
user: dummyUser,
|
||||
overrideAccess: false,
|
||||
})
|
||||
```
|
||||
|
||||
#### Update by ID
|
||||
|
||||
```js
|
||||
|
||||
@@ -90,6 +90,19 @@ Note: Collection slugs must be formatted in kebab-case
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "Count",
|
||||
method: "GET",
|
||||
path: "/api/{collection-slug}/count",
|
||||
description: "Count the documents",
|
||||
example: {
|
||||
slug: "count",
|
||||
req: true,
|
||||
res: {
|
||||
totalDocs: 10
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "Create",
|
||||
method: "POST",
|
||||
|
||||
49
packages/db-mongodb/src/count.ts
Normal file
49
packages/db-mongodb/src/count.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { QueryOptions } from 'mongoose'
|
||||
import type { Count } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import { flattenWhereToOperators } from 'payload/database'
|
||||
|
||||
import type { MongooseAdapter } from '.'
|
||||
|
||||
import { withSession } from './withSession'
|
||||
|
||||
export const count: Count = async function count(
|
||||
this: MongooseAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const options: QueryOptions = withSession(this, req.transactionID)
|
||||
|
||||
let hasNearConstraint = false
|
||||
|
||||
if (where) {
|
||||
const constraints = flattenWhereToOperators(where)
|
||||
hasNearConstraint = constraints.some((prop) => Object.keys(prop).some((key) => key === 'near'))
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
locale,
|
||||
payload: this.payload,
|
||||
where,
|
||||
})
|
||||
|
||||
// useEstimatedCount is faster, but not accurate, as it ignores any filters. It is thus set to true if there are no filters.
|
||||
const useEstimatedCount = hasNearConstraint || !query || Object.keys(query).length === 0
|
||||
|
||||
if (!useEstimatedCount && Object.keys(query).length === 0 && this.disableIndexHints !== true) {
|
||||
// Improve the performance of the countDocuments query which is used if useEstimatedCount is set to false by adding
|
||||
// a hint. By default, if no hint is provided, MongoDB does not use an indexed field to count the returned documents,
|
||||
// which makes queries very slow. This only happens when no query (filter) is provided. If one is provided, it uses
|
||||
// the correct indexed field
|
||||
options.hint = {
|
||||
_id: 1,
|
||||
}
|
||||
}
|
||||
|
||||
const result = await Model.countDocuments(query, options)
|
||||
|
||||
return {
|
||||
totalDocs: result,
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { createDatabaseAdapter } from 'payload/database'
|
||||
import type { CollectionModel, GlobalModel } from './types'
|
||||
|
||||
import { connect } from './connect'
|
||||
import { count } from './count'
|
||||
import { create } from './create'
|
||||
import { createGlobal } from './createGlobal'
|
||||
import { createGlobalVersion } from './createGlobalVersion'
|
||||
@@ -108,6 +109,7 @@ export function mongooseAdapter({
|
||||
collections: {},
|
||||
connectOptions: connectOptions || {},
|
||||
connection: undefined,
|
||||
count,
|
||||
disableIndexHints,
|
||||
globals: undefined,
|
||||
mongoMemoryServer: undefined,
|
||||
@@ -115,7 +117,6 @@ export function mongooseAdapter({
|
||||
transactionOptions: transactionOptions === false ? undefined : transactionOptions,
|
||||
url,
|
||||
versions: {},
|
||||
|
||||
// DatabaseAdapter
|
||||
beginTransaction: transactionOptions ? beginTransaction : undefined,
|
||||
commitTransaction,
|
||||
|
||||
65
packages/db-postgres/src/count.ts
Normal file
65
packages/db-postgres/src/count.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import type { Count } from 'payload/database'
|
||||
import type { SanitizedCollectionConfig } from 'payload/types'
|
||||
|
||||
import { sql } from 'drizzle-orm'
|
||||
|
||||
import type { ChainedMethods } from './find/chainMethods'
|
||||
import type { PostgresAdapter } from './types'
|
||||
|
||||
import { chainMethods } from './find/chainMethods'
|
||||
import buildQuery from './queries/buildQuery'
|
||||
import { getTableName } from './schema/getTableName'
|
||||
|
||||
export const count: Count = async function count(
|
||||
this: PostgresAdapter,
|
||||
{ collection, locale, req, where: whereArg },
|
||||
) {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
const tableName = getTableName({
|
||||
adapter: this,
|
||||
config: collectionConfig,
|
||||
})
|
||||
|
||||
const db = this.sessions[req.transactionID]?.db || this.drizzle
|
||||
const table = this.tables[tableName]
|
||||
|
||||
const { joinAliases, joins, where } = await buildQuery({
|
||||
adapter: this,
|
||||
fields: collectionConfig.fields,
|
||||
locale,
|
||||
tableName,
|
||||
where: whereArg,
|
||||
})
|
||||
|
||||
const selectCountMethods: ChainedMethods = []
|
||||
|
||||
joinAliases.forEach(({ condition, table }) => {
|
||||
selectCountMethods.push({
|
||||
args: [table, condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
})
|
||||
|
||||
Object.entries(joins).forEach(([joinTable, condition]) => {
|
||||
if (joinTable) {
|
||||
selectCountMethods.push({
|
||||
args: [this.tables[joinTable], condition],
|
||||
method: 'leftJoin',
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const countResult = await chainMethods({
|
||||
methods: selectCountMethods,
|
||||
query: db
|
||||
.select({
|
||||
count: sql<number>`count
|
||||
(DISTINCT ${this.tables[tableName].id})`,
|
||||
})
|
||||
.from(table)
|
||||
.where(where),
|
||||
})
|
||||
|
||||
return { totalDocs: Number(countResult[0].count) }
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { createDatabaseAdapter } from 'payload/database'
|
||||
import type { Args, PostgresAdapter, PostgresAdapterResult } from './types'
|
||||
|
||||
import { connect } from './connect'
|
||||
import { count } from './count'
|
||||
import { create } from './create'
|
||||
import { createGlobal } from './createGlobal'
|
||||
import { createGlobalVersion } from './createGlobalVersion'
|
||||
@@ -70,6 +71,7 @@ export function postgresAdapter(args: Args): PostgresAdapterResult {
|
||||
beginTransaction,
|
||||
commitTransaction,
|
||||
connect,
|
||||
count,
|
||||
create,
|
||||
createGlobal,
|
||||
createGlobalVersion,
|
||||
|
||||
@@ -228,7 +228,6 @@ export const traverseFields = ({
|
||||
prefix: `enum_${newTableName}_`,
|
||||
target: 'enumName',
|
||||
throwValidationError,
|
||||
versions,
|
||||
})
|
||||
|
||||
adapter.enums[enumName] = pgEnum(
|
||||
@@ -249,7 +248,6 @@ export const traverseFields = ({
|
||||
parentTableName: newTableName,
|
||||
prefix: `${newTableName}_`,
|
||||
throwValidationError,
|
||||
versions,
|
||||
})
|
||||
const baseColumns: Record<string, PgColumnBuilder> = {
|
||||
order: integer('order').notNull(),
|
||||
@@ -659,7 +657,7 @@ export const traverseFields = ({
|
||||
indexes,
|
||||
localesColumns,
|
||||
localesIndexes,
|
||||
newTableName: parentTableName,
|
||||
newTableName,
|
||||
parentTableName,
|
||||
relationsToBuild,
|
||||
relationships,
|
||||
|
||||
@@ -28,7 +28,7 @@ const getFlattenedFieldNames = (
|
||||
}
|
||||
|
||||
if (fieldHasSubFields(field)) {
|
||||
fieldPrefix = 'name' in field ? `${prefix}${field.name}.` : prefix
|
||||
fieldPrefix = 'name' in field ? `${prefix}${field.name}_` : prefix
|
||||
return [...fieldsToUse, ...getFlattenedFieldNames(field.fields, fieldPrefix)]
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ const getFlattenedFieldNames = (
|
||||
return [
|
||||
...fieldsToUse,
|
||||
...field.tabs.reduce((tabFields, tab) => {
|
||||
fieldPrefix = 'name' in tab ? `${prefix}.${tab.name}` : prefix
|
||||
fieldPrefix = 'name' in tab ? `${prefix}_${tab.name}` : prefix
|
||||
return [
|
||||
...tabFields,
|
||||
...(tabHasName(tab)
|
||||
@@ -51,7 +51,7 @@ const getFlattenedFieldNames = (
|
||||
return [
|
||||
...fieldsToUse,
|
||||
{
|
||||
name: `${fieldPrefix?.replace('.', '_') || ''}${field.name}`,
|
||||
name: `${fieldPrefix}${field.name}`,
|
||||
localized: field.localized,
|
||||
},
|
||||
]
|
||||
@@ -84,7 +84,11 @@ export const validateExistingBlockIsIdentical = ({
|
||||
|
||||
if (missingField) {
|
||||
throw new InvalidConfiguration(
|
||||
`The table ${rootTableName} has multiple blocks with slug ${block.slug}, but the schemas do not match. One block includes the field ${typeof missingField === 'string' ? missingField : missingField.name}, while the other block does not.`,
|
||||
`The table ${rootTableName} has multiple blocks with slug ${
|
||||
block.slug
|
||||
}, but the schemas do not match. One block includes the field ${
|
||||
typeof missingField === 'string' ? missingField : missingField.name
|
||||
}, while the other block does not.`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
10
packages/live-preview-vue/.eslintignore
Normal file
10
packages/live-preview-vue/.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
37
packages/live-preview-vue/.eslintrc.js
Normal file
37
packages/live-preview-vue/.eslintrc.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
10
packages/live-preview-vue/.prettierignore
Normal file
10
packages/live-preview-vue/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
15
packages/live-preview-vue/.swcrc
Normal file
15
packages/live-preview-vue/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
}
|
||||
}
|
||||
49
packages/live-preview-vue/package.json
Normal file
49
packages/live-preview-vue/package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@payloadcms/live-preview-vue",
|
||||
"version": "0.1.0",
|
||||
"description": "The official live preview Vue SDK for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git",
|
||||
"directory": "packages/live-preview-vue"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"author": "Payload CMS, Inc.",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||
"prepublishOnly": "pnpm clean && pnpm build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/live-preview": "workspace:^0.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"vue": "^3.0.0",
|
||||
"payload": "workspace:*"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"default": "./src/index.ts",
|
||||
"types": "./src/index.ts"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": null,
|
||||
"main": "./dist/index.js",
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
58
packages/live-preview-vue/src/index.ts
Normal file
58
packages/live-preview-vue/src/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
import { ready, subscribe, unsubscribe } from '@payloadcms/live-preview'
|
||||
import { onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
/**
|
||||
* Vue composable to implement Payload CMS Live Preview.
|
||||
*
|
||||
* {@link https://payloadcms.com/docs/live-preview/frontend View the documentation}
|
||||
*/
|
||||
export const useLivePreview = <T>(props: {
|
||||
apiRoute?: string
|
||||
depth?: number
|
||||
initialData: T
|
||||
serverURL: string
|
||||
}): {
|
||||
data: Ref<T>
|
||||
isLoading: Ref<boolean>
|
||||
} => {
|
||||
const { apiRoute, depth, initialData, serverURL } = props
|
||||
const data = ref(initialData) as Ref<T>
|
||||
const isLoading = ref(true)
|
||||
const hasSentReadyMessage = ref(false)
|
||||
|
||||
const onChange = (mergedData: T) => {
|
||||
data.value = mergedData
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
let subscription: (event: MessageEvent) => void
|
||||
|
||||
onMounted(() => {
|
||||
subscription = subscribe({
|
||||
apiRoute,
|
||||
callback: onChange,
|
||||
depth,
|
||||
initialData,
|
||||
serverURL,
|
||||
})
|
||||
|
||||
if (!hasSentReadyMessage.value) {
|
||||
hasSentReadyMessage.value = true
|
||||
|
||||
ready({
|
||||
serverURL,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
unsubscribe(subscription)
|
||||
})
|
||||
|
||||
return {
|
||||
data,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
25
packages/live-preview-vue/tsconfig.json
Normal file
25
packages/live-preview-vue/tsconfig.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false /* Do not emit outputs. */,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */,
|
||||
"jsx": "react"
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
".eslintrc.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }] // db-mongodb depends on payload
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.13.0",
|
||||
"version": "2.14.0",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -77,6 +77,15 @@ const contains = {
|
||||
value: 'contains',
|
||||
}
|
||||
|
||||
const filterOperators = (operators, hasMany = false) => {
|
||||
if (hasMany) {
|
||||
return operators.filter(
|
||||
(operator) => operator.value !== 'equals' && operator.value !== 'not_equals',
|
||||
)
|
||||
}
|
||||
return operators
|
||||
}
|
||||
|
||||
const fieldTypeConditions = {
|
||||
checkbox: {
|
||||
component: 'Text',
|
||||
@@ -100,7 +109,7 @@ const fieldTypeConditions = {
|
||||
},
|
||||
number: {
|
||||
component: 'Number',
|
||||
operators: [...base, ...numeric],
|
||||
operators: (hasMany) => filterOperators([...base, ...numeric], hasMany),
|
||||
},
|
||||
point: {
|
||||
component: 'Point',
|
||||
@@ -120,11 +129,11 @@ const fieldTypeConditions = {
|
||||
},
|
||||
select: {
|
||||
component: 'Select',
|
||||
operators: [...base],
|
||||
operators: (hasMany) => filterOperators([...base], hasMany),
|
||||
},
|
||||
text: {
|
||||
component: 'Text',
|
||||
operators: [...base, like, contains],
|
||||
operators: (hasMany) => filterOperators([...base, like, contains], hasMany),
|
||||
},
|
||||
textarea: {
|
||||
component: 'Text',
|
||||
|
||||
@@ -22,36 +22,44 @@ const baseClass = 'where-builder'
|
||||
|
||||
const reduceFields = (fields, i18n) =>
|
||||
flattenTopLevelFields(fields).reduce((reduced, field) => {
|
||||
let operators = []
|
||||
|
||||
if (typeof fieldTypes[field.type] === 'object') {
|
||||
const operatorKeys = new Set()
|
||||
const operators = fieldTypes[field.type].operators.reduce((acc, operator) => {
|
||||
if (!operatorKeys.has(operator.value)) {
|
||||
operatorKeys.add(operator.value)
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
...operator,
|
||||
label: i18n.t(`operators:${operator.label}`),
|
||||
},
|
||||
]
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const formattedField = {
|
||||
label: getTranslation(field.label || field.name, i18n),
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
operators,
|
||||
props: {
|
||||
...field,
|
||||
},
|
||||
if (typeof fieldTypes[field.type].operators === 'function') {
|
||||
operators = fieldTypes[field.type].operators(
|
||||
'hasMany' in field && field.hasMany ? true : false,
|
||||
)
|
||||
} else {
|
||||
operators = fieldTypes[field.type].operators
|
||||
}
|
||||
|
||||
return [...reduced, formattedField]
|
||||
}
|
||||
|
||||
return reduced
|
||||
const operatorKeys = new Set()
|
||||
const filteredOperators = operators.reduce((acc, operator) => {
|
||||
if (!operatorKeys.has(operator.value)) {
|
||||
operatorKeys.add(operator.value)
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
...operator,
|
||||
label: i18n.t(`operators:${operator.label}`),
|
||||
},
|
||||
]
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const formattedField = {
|
||||
label: getTranslation(field.label || field.name, i18n),
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
operators: filteredOperators,
|
||||
props: {
|
||||
...field,
|
||||
},
|
||||
}
|
||||
|
||||
return [...reduced, formattedField]
|
||||
}, [])
|
||||
|
||||
/**
|
||||
@@ -185,7 +193,7 @@ const WhereBuilder: React.FC<Props> = (props) => {
|
||||
iconStyle="with-border"
|
||||
onClick={() => {
|
||||
if (reducedFields.length > 0)
|
||||
dispatchConditions({ field: reducedFields[0].value, type: 'add' })
|
||||
dispatchConditions({ type: 'add', field: reducedFields[0].value })
|
||||
}}
|
||||
>
|
||||
{t('or')}
|
||||
@@ -203,7 +211,7 @@ const WhereBuilder: React.FC<Props> = (props) => {
|
||||
iconStyle="with-border"
|
||||
onClick={() => {
|
||||
if (reducedFields.length > 0)
|
||||
dispatchConditions({ field: reducedFields[0].value, type: 'add' })
|
||||
dispatchConditions({ type: 'add', field: reducedFields[0].value })
|
||||
}}
|
||||
>
|
||||
{t('addFilter')}
|
||||
|
||||
@@ -166,6 +166,8 @@ const NumberField: React.FC<Props> = (props) => {
|
||||
<input
|
||||
disabled={readOnly}
|
||||
id={`field-${path.replace(/\./g, '__')}`}
|
||||
max={max}
|
||||
min={min}
|
||||
name={path}
|
||||
onChange={handleChange}
|
||||
onWheel={(e) => {
|
||||
|
||||
@@ -11,6 +11,7 @@ import registerFirstUserHandler from '../auth/requestHandlers/registerFirstUser'
|
||||
import resetPassword from '../auth/requestHandlers/resetPassword'
|
||||
import unlock from '../auth/requestHandlers/unlock'
|
||||
import verifyEmail from '../auth/requestHandlers/verifyEmail'
|
||||
import count from './requestHandlers/count'
|
||||
import create from './requestHandlers/create'
|
||||
import deleteHandler from './requestHandlers/delete'
|
||||
import deleteByID from './requestHandlers/deleteByID'
|
||||
@@ -124,6 +125,11 @@ const buildEndpoints = (collection: SanitizedCollectionConfig): Endpoint[] => {
|
||||
method: 'post',
|
||||
path: '/',
|
||||
},
|
||||
{
|
||||
handler: count,
|
||||
method: 'get',
|
||||
path: '/count',
|
||||
},
|
||||
{
|
||||
handler: docAccessRequestHandler,
|
||||
method: 'get',
|
||||
|
||||
@@ -30,6 +30,7 @@ import type { AfterOperationArg, AfterOperationMap } from '../operations/utils'
|
||||
|
||||
export type HookOperationType =
|
||||
| 'autosave'
|
||||
| 'count'
|
||||
| 'create'
|
||||
| 'delete'
|
||||
| 'forgotPassword'
|
||||
@@ -465,6 +466,7 @@ export type Collection = {
|
||||
config: SanitizedCollectionConfig
|
||||
graphQL?: {
|
||||
JWT: GraphQLObjectType
|
||||
countType: GraphQLObjectType
|
||||
mutationInputType: GraphQLNonNull<any>
|
||||
paginatedType: GraphQLObjectType
|
||||
type: GraphQLObjectType
|
||||
|
||||
@@ -30,8 +30,10 @@ import buildPaginatedListType from '../../graphql/schema/buildPaginatedListType'
|
||||
import { buildPolicyType } from '../../graphql/schema/buildPoliciesType'
|
||||
import buildWhereInputType from '../../graphql/schema/buildWhereInputType'
|
||||
import formatName from '../../graphql/utilities/formatName'
|
||||
import flattenFields from '../../utilities/flattenTopLevelFields'
|
||||
import { formatNames, toWords } from '../../utilities/formatLabels'
|
||||
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields'
|
||||
import countResolver from './resolvers/count'
|
||||
import createResolver from './resolvers/create'
|
||||
import getDeleteResolver from './resolvers/delete'
|
||||
import { docAccessResolver } from './resolvers/docAccess'
|
||||
@@ -41,7 +43,6 @@ import findVersionByIDResolver from './resolvers/findVersionByID'
|
||||
import findVersionsResolver from './resolvers/findVersions'
|
||||
import restoreVersionResolver from './resolvers/restoreVersion'
|
||||
import updateResolver from './resolvers/update'
|
||||
import flattenFields from '../../utilities/flattenTopLevelFields'
|
||||
|
||||
function initCollectionsGraphQL(payload: Payload): void {
|
||||
Object.keys(payload.collections).forEach((slug) => {
|
||||
@@ -118,9 +119,9 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
if (config.auth && !config.auth.disableLocalStrategy) {
|
||||
fields.push({
|
||||
name: 'password',
|
||||
type: 'text',
|
||||
label: 'Password',
|
||||
required: true,
|
||||
type: 'text',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -146,6 +147,7 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
}
|
||||
|
||||
payload.Query.fields[singularName] = {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: new GraphQLNonNull(idType) },
|
||||
draft: { type: GraphQLBoolean },
|
||||
@@ -157,10 +159,10 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
: {}),
|
||||
},
|
||||
resolve: findByIDResolver(collection),
|
||||
type: collection.graphQL.type,
|
||||
}
|
||||
|
||||
payload.Query.fields[pluralName] = {
|
||||
type: buildPaginatedListType(pluralName, collection.graphQL.type),
|
||||
args: {
|
||||
draft: { type: GraphQLBoolean },
|
||||
where: { type: collection.graphQL.whereInputType },
|
||||
@@ -175,23 +177,42 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
sort: { type: GraphQLString },
|
||||
},
|
||||
resolve: findResolver(collection),
|
||||
type: buildPaginatedListType(pluralName, collection.graphQL.type),
|
||||
}
|
||||
|
||||
payload.Query.fields[`count${pluralName}`] = {
|
||||
type: new GraphQLObjectType({
|
||||
name: `count${pluralName}`,
|
||||
fields: {
|
||||
totalDocs: { type: GraphQLInt },
|
||||
},
|
||||
}),
|
||||
args: {
|
||||
draft: { type: GraphQLBoolean },
|
||||
where: { type: collection.graphQL.whereInputType },
|
||||
...(payload.config.localization
|
||||
? {
|
||||
locale: { type: payload.types.localeInputType },
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
resolve: countResolver(collection),
|
||||
}
|
||||
|
||||
payload.Query.fields[`docAccess${singularName}`] = {
|
||||
type: buildPolicyType({
|
||||
type: 'collection',
|
||||
entity: config,
|
||||
scope: 'docAccess',
|
||||
typeSuffix: 'DocAccess',
|
||||
}),
|
||||
args: {
|
||||
id: { type: new GraphQLNonNull(idType) },
|
||||
},
|
||||
resolve: docAccessResolver(),
|
||||
type: buildPolicyType({
|
||||
entity: config,
|
||||
scope: 'docAccess',
|
||||
type: 'collection',
|
||||
typeSuffix: 'DocAccess',
|
||||
}),
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`create${singularName}`] = {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
...(createMutationInputType
|
||||
? { data: { type: collection.graphQL.mutationInputType } }
|
||||
@@ -204,10 +225,10 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
: {}),
|
||||
},
|
||||
resolve: createResolver(collection),
|
||||
type: collection.graphQL.type,
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`update${singularName}`] = {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: new GraphQLNonNull(idType) },
|
||||
autosave: { type: GraphQLBoolean },
|
||||
@@ -222,15 +243,14 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
: {}),
|
||||
},
|
||||
resolve: updateResolver(collection),
|
||||
type: collection.graphQL.type,
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`delete${singularName}`] = {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: new GraphQLNonNull(idType) },
|
||||
},
|
||||
resolve: getDeleteResolver(collection),
|
||||
type: collection.graphQL.type,
|
||||
}
|
||||
|
||||
if (config.versions) {
|
||||
@@ -243,13 +263,13 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
},
|
||||
{
|
||||
name: 'createdAt',
|
||||
label: 'Created At',
|
||||
type: 'date',
|
||||
label: 'Created At',
|
||||
},
|
||||
{
|
||||
name: 'updatedAt',
|
||||
label: 'Updated At',
|
||||
type: 'date',
|
||||
label: 'Updated At',
|
||||
},
|
||||
]
|
||||
|
||||
@@ -262,6 +282,7 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
})
|
||||
|
||||
payload.Query.fields[`version${formatName(singularName)}`] = {
|
||||
type: collection.graphQL.versionType,
|
||||
args: {
|
||||
id: { type: versionIDType },
|
||||
...(payload.config.localization
|
||||
@@ -272,9 +293,12 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
: {}),
|
||||
},
|
||||
resolve: findVersionByIDResolver(collection),
|
||||
type: collection.graphQL.versionType,
|
||||
}
|
||||
payload.Query.fields[`versions${pluralName}`] = {
|
||||
type: buildPaginatedListType(
|
||||
`versions${formatName(pluralName)}`,
|
||||
collection.graphQL.versionType,
|
||||
),
|
||||
args: {
|
||||
where: {
|
||||
type: buildWhereInputType({
|
||||
@@ -295,17 +319,13 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
sort: { type: GraphQLString },
|
||||
},
|
||||
resolve: findVersionsResolver(collection),
|
||||
type: buildPaginatedListType(
|
||||
`versions${formatName(pluralName)}`,
|
||||
collection.graphQL.versionType,
|
||||
),
|
||||
}
|
||||
payload.Mutation.fields[`restoreVersion${formatName(singularName)}`] = {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: versionIDType },
|
||||
},
|
||||
resolve: restoreVersionResolver(collection),
|
||||
type: collection.graphQL.type,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,8 +335,8 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
: [
|
||||
{
|
||||
name: 'email',
|
||||
required: true,
|
||||
type: 'email',
|
||||
required: true,
|
||||
},
|
||||
]
|
||||
collection.graphQL.JWT = buildObjectType({
|
||||
@@ -326,8 +346,8 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
...authFields,
|
||||
{
|
||||
name: 'collection',
|
||||
required: true,
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
parentName: formatName(`${slug}JWT`),
|
||||
@@ -335,7 +355,6 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
})
|
||||
|
||||
payload.Query.fields[`me${singularName}`] = {
|
||||
resolve: me(collection),
|
||||
type: new GraphQLObjectType({
|
||||
name: formatName(`${slug}Me`),
|
||||
fields: {
|
||||
@@ -353,18 +372,15 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
},
|
||||
},
|
||||
}),
|
||||
resolve: me(collection),
|
||||
}
|
||||
|
||||
payload.Query.fields[`initialized${singularName}`] = {
|
||||
resolve: init(collection.config.slug),
|
||||
type: GraphQLBoolean,
|
||||
resolve: init(collection.config.slug),
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`refreshToken${singularName}`] = {
|
||||
args: {
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
resolve: refresh(collection),
|
||||
type: new GraphQLObjectType({
|
||||
name: formatName(`${slug}Refreshed${singularName}`),
|
||||
fields: {
|
||||
@@ -379,30 +395,29 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
},
|
||||
},
|
||||
}),
|
||||
args: {
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
resolve: refresh(collection),
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`logout${singularName}`] = {
|
||||
resolve: logout(collection),
|
||||
type: GraphQLString,
|
||||
resolve: logout(collection),
|
||||
}
|
||||
|
||||
if (!config.auth.disableLocalStrategy) {
|
||||
if (config.auth.maxLoginAttempts > 0) {
|
||||
payload.Mutation.fields[`unlock${singularName}`] = {
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
args: {
|
||||
email: { type: new GraphQLNonNull(GraphQLString) },
|
||||
},
|
||||
resolve: unlock(collection),
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
}
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`login${singularName}`] = {
|
||||
args: {
|
||||
email: { type: GraphQLString },
|
||||
password: { type: GraphQLString },
|
||||
},
|
||||
resolve: login(collection),
|
||||
type: new GraphQLObjectType({
|
||||
name: formatName(`${slug}LoginResult`),
|
||||
fields: {
|
||||
@@ -417,24 +432,24 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
},
|
||||
},
|
||||
}),
|
||||
args: {
|
||||
email: { type: GraphQLString },
|
||||
password: { type: GraphQLString },
|
||||
},
|
||||
resolve: login(collection),
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`forgotPassword${singularName}`] = {
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
args: {
|
||||
disableEmail: { type: GraphQLBoolean },
|
||||
email: { type: new GraphQLNonNull(GraphQLString) },
|
||||
expiration: { type: GraphQLInt },
|
||||
},
|
||||
resolve: forgotPassword(collection),
|
||||
type: new GraphQLNonNull(GraphQLBoolean),
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`resetPassword${singularName}`] = {
|
||||
args: {
|
||||
password: { type: GraphQLString },
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
resolve: resetPassword(collection),
|
||||
type: new GraphQLObjectType({
|
||||
name: formatName(`${slug}ResetPassword`),
|
||||
fields: {
|
||||
@@ -442,14 +457,19 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
user: { type: collection.graphQL.type },
|
||||
},
|
||||
}),
|
||||
args: {
|
||||
password: { type: GraphQLString },
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
resolve: resetPassword(collection),
|
||||
}
|
||||
|
||||
payload.Mutation.fields[`verifyEmail${singularName}`] = {
|
||||
type: GraphQLBoolean,
|
||||
args: {
|
||||
token: { type: GraphQLString },
|
||||
},
|
||||
resolve: verifyEmail(collection),
|
||||
type: GraphQLBoolean,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
packages/payload/src/collections/graphql/resolvers/count.ts
Normal file
40
packages/payload/src/collections/graphql/resolvers/count.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { PayloadRequest } from '../../../express/types'
|
||||
import type { Where } from '../../../types'
|
||||
import type { Collection } from '../../config/types'
|
||||
|
||||
import isolateObjectProperty from '../../../utilities/isolateObjectProperty'
|
||||
import count from '../../operations/count'
|
||||
|
||||
export type Resolver = (
|
||||
_: unknown,
|
||||
args: {
|
||||
data: Record<string, unknown>
|
||||
locale?: string
|
||||
where?: Where
|
||||
},
|
||||
context: {
|
||||
req: PayloadRequest
|
||||
res: Response
|
||||
},
|
||||
) => Promise<{ totalDocs: number }>
|
||||
|
||||
export default function findResolver(collection: Collection): Resolver {
|
||||
return async function resolver(_, args, context) {
|
||||
let { req } = context
|
||||
const locale = req.locale
|
||||
const fallbackLocale = req.fallbackLocale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = fallbackLocale
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
where: args.where,
|
||||
}
|
||||
|
||||
const results = await count(options)
|
||||
return results
|
||||
}
|
||||
}
|
||||
113
packages/payload/src/collections/operations/count.ts
Normal file
113
packages/payload/src/collections/operations/count.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import type { AccessResult } from '../../config/types'
|
||||
import type { PayloadRequest, Where } from '../../types/index'
|
||||
import type { Collection, TypeWithID } from '../config/types'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess'
|
||||
import { combineQueries } from '../../database/combineQueries'
|
||||
import { validateQueryPaths } from '../../database/queryValidation/validateQueryPaths'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction'
|
||||
import { initTransaction } from '../../utilities/initTransaction'
|
||||
import { killTransaction } from '../../utilities/killTransaction'
|
||||
import { buildAfterOperation } from './utils'
|
||||
|
||||
export type Arguments = {
|
||||
collection: Collection
|
||||
disableErrors?: boolean
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
where?: Where
|
||||
}
|
||||
|
||||
async function count<T extends TypeWithID & Record<string, unknown>>(
|
||||
incomingArgs: Arguments,
|
||||
): Promise<{ totalDocs: number }> {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
const shouldCommit = await initTransaction(args.req)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// beforeOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
await args.collection.config.hooks.beforeOperation.reduce(async (priorHook, hook) => {
|
||||
await priorHook
|
||||
|
||||
args =
|
||||
(await hook({
|
||||
args,
|
||||
collection: args.collection.config,
|
||||
context: args.req.context,
|
||||
operation: 'count',
|
||||
req: args.req,
|
||||
})) || args
|
||||
}, Promise.resolve())
|
||||
|
||||
const {
|
||||
collection: { config: collectionConfig },
|
||||
disableErrors,
|
||||
overrideAccess,
|
||||
req: { payload },
|
||||
req,
|
||||
where,
|
||||
} = args
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Access
|
||||
// /////////////////////////////////////
|
||||
|
||||
let accessResult: AccessResult
|
||||
|
||||
if (!overrideAccess) {
|
||||
accessResult = await executeAccess({ disableErrors, req }, collectionConfig.access.read)
|
||||
|
||||
// If errors are disabled, and access returns false, return empty results
|
||||
if (accessResult === false) {
|
||||
return {
|
||||
totalDocs: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result: { totalDocs: number }
|
||||
|
||||
const fullWhere = combineQueries(where, accessResult)
|
||||
|
||||
await validateQueryPaths({
|
||||
collectionConfig,
|
||||
overrideAccess,
|
||||
req,
|
||||
where,
|
||||
})
|
||||
|
||||
result = await payload.db.count({
|
||||
collection: collectionConfig.slug,
|
||||
req,
|
||||
where: fullWhere,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterOperation - Collection
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await buildAfterOperation<T>({
|
||||
args,
|
||||
collection: collectionConfig,
|
||||
operation: 'count',
|
||||
result,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Return results
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (shouldCommit) await commitTransaction(req)
|
||||
|
||||
return result
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export default count
|
||||
47
packages/payload/src/collections/operations/local/count.ts
Normal file
47
packages/payload/src/collections/operations/local/count.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { GeneratedTypes } from '../../../'
|
||||
import type { PayloadRequest, RequestContext } from '../../../express/types'
|
||||
import type { Payload } from '../../../payload'
|
||||
import type { Document, Where } from '../../../types'
|
||||
|
||||
import { APIError } from '../../../errors'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq'
|
||||
import count from '../count'
|
||||
|
||||
export type Options<T extends keyof GeneratedTypes['collections']> = {
|
||||
collection: T
|
||||
/**
|
||||
* context, which will then be passed to req.context, which can be read by hooks
|
||||
*/
|
||||
context?: RequestContext
|
||||
disableErrors?: boolean
|
||||
locale?: string
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
user?: Document
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export default async function countLocal<T extends keyof GeneratedTypes['collections']>(
|
||||
payload: Payload,
|
||||
options: Options<T>,
|
||||
): Promise<{ totalDocs: number }> {
|
||||
const { collection: collectionSlug, disableErrors, overrideAccess = true, where } = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
|
||||
if (!collection) {
|
||||
throw new APIError(
|
||||
`The collection with slug ${String(collectionSlug)} can't be found. Find Operation.`,
|
||||
)
|
||||
}
|
||||
|
||||
const req = createLocalReq(options, payload)
|
||||
|
||||
return count<GeneratedTypes['collections'][T]>({
|
||||
collection,
|
||||
disableErrors,
|
||||
overrideAccess,
|
||||
req,
|
||||
where,
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import auth from '../../../auth/operations/local'
|
||||
import count from './count'
|
||||
import create from './create'
|
||||
import deleteLocal from './delete'
|
||||
import find from './find'
|
||||
@@ -10,6 +11,7 @@ import update from './update'
|
||||
|
||||
export default {
|
||||
auth,
|
||||
count,
|
||||
create,
|
||||
deleteLocal,
|
||||
find,
|
||||
|
||||
@@ -276,7 +276,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
// Update
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (!shouldSaveDraft) {
|
||||
if (!shouldSaveDraft || data._status === 'published') {
|
||||
result = await req.payload.db.updateOne({
|
||||
id,
|
||||
collection: collectionConfig.slug,
|
||||
|
||||
@@ -262,7 +262,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
// Update
|
||||
// /////////////////////////////////////
|
||||
|
||||
if (!shouldSaveDraft) {
|
||||
if (!shouldSaveDraft || data._status === 'published') {
|
||||
result = await req.payload.db.updateOne({
|
||||
id,
|
||||
collection: collectionConfig.slug,
|
||||
|
||||
@@ -3,6 +3,7 @@ import type login from '../../auth/operations/login'
|
||||
import type refresh from '../../auth/operations/refresh'
|
||||
import type { PayloadRequest } from '../../express/types'
|
||||
import type { AfterOperationHook, SanitizedCollectionConfig, TypeWithID } from '../config/types'
|
||||
import type countOperation from './count'
|
||||
import type create from './create'
|
||||
import type deleteOperation from './delete'
|
||||
import type deleteByID from './deleteByID'
|
||||
@@ -12,6 +13,7 @@ import type update from './update'
|
||||
import type updateByID from './updateByID'
|
||||
|
||||
export type AfterOperationMap<T extends TypeWithID> = {
|
||||
count: typeof countOperation
|
||||
create: typeof create // todo: pass correct generic
|
||||
delete: typeof deleteOperation // todo: pass correct generic
|
||||
deleteByID: typeof deleteByID // todo: pass correct generic
|
||||
@@ -28,6 +30,11 @@ export type AfterOperationArg<T extends TypeWithID> = {
|
||||
collection: SanitizedCollectionConfig
|
||||
req: PayloadRequest
|
||||
} & (
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['count']>[0]
|
||||
operation: 'count'
|
||||
result: Awaited<ReturnType<AfterOperationMap<T>['count']>>
|
||||
}
|
||||
| {
|
||||
args: Parameters<AfterOperationMap<T>['create']>[0]
|
||||
operation: 'create'
|
||||
|
||||
26
packages/payload/src/collections/requestHandlers/count.ts
Normal file
26
packages/payload/src/collections/requestHandlers/count.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { NextFunction, Response } from 'express'
|
||||
|
||||
import httpStatus from 'http-status'
|
||||
|
||||
import type { PayloadRequest } from '../../express/types'
|
||||
import type { Where } from '../../types'
|
||||
|
||||
import count from '../operations/count'
|
||||
|
||||
export default async function countHandler(
|
||||
req: PayloadRequest,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): Promise<Response<{ totalDocs: number }> | void> {
|
||||
try {
|
||||
const result = await count({
|
||||
collection: req.collection,
|
||||
req,
|
||||
where: req.query.where as Where, // This is a little shady
|
||||
})
|
||||
|
||||
return res.status(httpStatus.OK).json(result)
|
||||
} catch (error) {
|
||||
return next(error)
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ export interface BaseDatabaseAdapter {
|
||||
*/
|
||||
connect?: Connect
|
||||
|
||||
count: Count
|
||||
|
||||
create: Create
|
||||
|
||||
createGlobal: CreateGlobal
|
||||
@@ -197,6 +199,15 @@ export type FindArgs = {
|
||||
|
||||
export type Find = <T = TypeWithID>(args: FindArgs) => Promise<PaginatedDocs<T>>
|
||||
|
||||
export type CountArgs = {
|
||||
collection: string
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export type Count = (args: CountArgs) => Promise<{ totalDocs: number }>
|
||||
|
||||
type BaseVersionArgs = {
|
||||
limit?: number
|
||||
locale?: string
|
||||
|
||||
18
packages/payload/src/errors/FileRetrievalError.ts
Normal file
18
packages/payload/src/errors/FileRetrievalError.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { TFunction } from 'i18next'
|
||||
|
||||
import httpStatus from 'http-status'
|
||||
|
||||
import APIError from './APIError'
|
||||
|
||||
class FileRetrievalError extends APIError {
|
||||
constructor(t?: TFunction, message?: string) {
|
||||
let msg = t ? t('error:problemUploadingFile') : 'There was a problem while retrieving the file.'
|
||||
|
||||
if (message) {
|
||||
msg += ` ${message}`
|
||||
}
|
||||
super(msg, httpStatus.BAD_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
export default FileRetrievalError
|
||||
@@ -3,6 +3,8 @@ export {
|
||||
BeginTransaction,
|
||||
CommitTransaction,
|
||||
Connect,
|
||||
Count,
|
||||
CountArgs,
|
||||
Create,
|
||||
CreateArgs,
|
||||
CreateGlobal,
|
||||
|
||||
@@ -18,6 +18,7 @@ import type { Options as VerifyEmailOptions } from './auth/operations/local/veri
|
||||
import type { Result as LoginResult } from './auth/operations/login'
|
||||
import type { Result as ResetPasswordResult } from './auth/operations/resetPassword'
|
||||
import type { BulkOperationResult, Collection } from './collections/config/types'
|
||||
import type { Options as CountOptions } from './collections/operations/local/count'
|
||||
import type { Options as CreateOptions } from './collections/operations/local/create'
|
||||
import type {
|
||||
ByIDOptions as DeleteByIDOptions,
|
||||
@@ -74,6 +75,18 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
||||
|
||||
config: SanitizedConfig
|
||||
|
||||
/**
|
||||
* @description Performs count operation
|
||||
* @param options
|
||||
* @returns count of documents satisfying query
|
||||
*/
|
||||
count = async <T extends keyof TGeneratedTypes['collections']>(
|
||||
options: CountOptions<T>,
|
||||
): Promise<{ totalDocs: number }> => {
|
||||
const { count } = localOperations
|
||||
return count(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Performs create operation
|
||||
* @param options
|
||||
|
||||
@@ -14,6 +14,7 @@ import type { PayloadRequest } from '../express/types'
|
||||
import type { FileData, FileToSave, ProbedImageSize } from './types'
|
||||
|
||||
import { FileUploadError, MissingFile } from '../errors'
|
||||
import FileRetrievalError from '../errors/FileRetrievalError'
|
||||
import canResizeImage from './canResizeImage'
|
||||
import cropImage from './cropImage'
|
||||
import { getExternalFile } from './getExternalFile'
|
||||
@@ -80,8 +81,10 @@ export const generateFileData = async <T>({
|
||||
})) as UploadedFile
|
||||
overwriteExistingFiles = true
|
||||
}
|
||||
} catch (err) {
|
||||
throw new FileUploadError(req.t)
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error) {
|
||||
throw new FileRetrievalError(req.t, err.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Request } from 'express'
|
||||
import type { IncomingHttpHeaders } from 'http'
|
||||
|
||||
import type { File, FileData, IncomingUploadType } from './types'
|
||||
|
||||
@@ -21,20 +22,15 @@ export const getExternalFile = async ({ data, req, uploadConfig }: Args): Promis
|
||||
|
||||
const { default: fetch } = (await import('node-fetch')) as any
|
||||
|
||||
// Convert headers
|
||||
const convertedHeaders: Record<string, string> = headersToObject(req.headers)
|
||||
|
||||
const headers = uploadConfig.externalFileHeaderFilter
|
||||
? uploadConfig.externalFileHeaderFilter(convertedHeaders)
|
||||
? uploadConfig.externalFileHeaderFilter(headersToObject(req.headers))
|
||||
: {
|
||||
cookie: req.headers['cookie'],
|
||||
}
|
||||
|
||||
const res = await fetch(fileURL, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
headers,
|
||||
},
|
||||
headers,
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
@@ -53,15 +49,17 @@ export const getExternalFile = async ({ data, req, uploadConfig }: Args): Promis
|
||||
throw new APIError('Invalid file url', 400)
|
||||
}
|
||||
|
||||
function headersToObject(headers) {
|
||||
const headersObj = {}
|
||||
headers.forEach((value, key) => {
|
||||
// If the header value is an array, join its elements into a single string
|
||||
if (Array.isArray(value)) {
|
||||
headersObj[key] = value.join(', ')
|
||||
} else {
|
||||
headersObj[key] = value
|
||||
}
|
||||
})
|
||||
return headersObj
|
||||
function headersToObject(headers: IncomingHttpHeaders) {
|
||||
return Object.entries(headers).reduce(
|
||||
(acc, [key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
acc[key] = value.join(',')
|
||||
} else {
|
||||
acc[key] = value
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-seo",
|
||||
"version": "2.3.0",
|
||||
"version": "2.3.1",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.0",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
1221
pnpm-lock.yaml
generated
1221
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@ import type { FieldAccess } from '../../packages/payload/src/fields/config/types
|
||||
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
|
||||
import { devUser } from '../credentials'
|
||||
import { firstArrayText, secondArrayText } from './shared'
|
||||
import { firstArrayText, hiddenAccessCountSlug, secondArrayText } from './shared'
|
||||
import {
|
||||
docLevelAccessSlug,
|
||||
hiddenAccessSlug,
|
||||
@@ -390,6 +390,32 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: hiddenAccessCountSlug,
|
||||
access: {
|
||||
read: ({ req: { user } }) => {
|
||||
if (user) return true
|
||||
|
||||
return {
|
||||
hidden: {
|
||||
not_equals: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'hidden',
|
||||
type: 'checkbox',
|
||||
hidden: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
onInit: async (payload) => {
|
||||
await payload.create({
|
||||
|
||||
@@ -7,6 +7,7 @@ import { initPayloadTest } from '../helpers/configHelpers'
|
||||
import { requestHeaders } from './config'
|
||||
import {
|
||||
firstArrayText,
|
||||
hiddenAccessCountSlug,
|
||||
hiddenAccessSlug,
|
||||
hiddenFieldsSlug,
|
||||
relyOnRequestHeadersSlug,
|
||||
@@ -417,6 +418,30 @@ describe('Access Control', () => {
|
||||
expect(docs).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should respect query constraint using hidden field on count', async () => {
|
||||
await payload.create({
|
||||
collection: hiddenAccessCountSlug,
|
||||
data: {
|
||||
title: 'hello',
|
||||
},
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: hiddenAccessCountSlug,
|
||||
data: {
|
||||
title: 'hello',
|
||||
hidden: true,
|
||||
},
|
||||
})
|
||||
|
||||
const { totalDocs } = await payload.count({
|
||||
collection: hiddenAccessCountSlug,
|
||||
overrideAccess: false,
|
||||
})
|
||||
|
||||
expect(totalDocs).toBe(1)
|
||||
})
|
||||
|
||||
it('should respect query constraint using hidden field on versions', async () => {
|
||||
await payload.create({
|
||||
collection: restrictedVersionsSlug,
|
||||
|
||||
@@ -20,11 +20,16 @@ export interface Config {
|
||||
'doc-level-access': DocLevelAccess
|
||||
'hidden-fields': HiddenField
|
||||
'hidden-access': HiddenAccess
|
||||
'hidden-access-count': HiddenAccessCount
|
||||
'payload-preferences': PayloadPreference
|
||||
'payload-migrations': PayloadMigration
|
||||
}
|
||||
globals: {}
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string
|
||||
roles?: ('admin' | 'user')[] | null
|
||||
@@ -39,6 +44,10 @@ export interface User {
|
||||
lockUntil?: string | null
|
||||
password: string | null
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: string
|
||||
restrictedField?: string | null
|
||||
@@ -50,6 +59,10 @@ export interface Post {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "unrestricted".
|
||||
*/
|
||||
export interface Unrestricted {
|
||||
id: string
|
||||
name?: string | null
|
||||
@@ -57,24 +70,40 @@ export interface Unrestricted {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "user-restricted".
|
||||
*/
|
||||
export interface UserRestricted {
|
||||
id: string
|
||||
name?: string | null
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "restricted".
|
||||
*/
|
||||
export interface Restricted {
|
||||
id: string
|
||||
name?: string | null
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "read-only-collection".
|
||||
*/
|
||||
export interface ReadOnlyCollection {
|
||||
id: string
|
||||
name?: string | null
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "restricted-versions".
|
||||
*/
|
||||
export interface RestrictedVersion {
|
||||
id: string
|
||||
name?: string | null
|
||||
@@ -82,6 +111,10 @@ export interface RestrictedVersion {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "sibling-data".
|
||||
*/
|
||||
export interface SiblingDatum {
|
||||
id: string
|
||||
array?:
|
||||
@@ -94,12 +127,20 @@ export interface SiblingDatum {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "rely-on-request-headers".
|
||||
*/
|
||||
export interface RelyOnRequestHeader {
|
||||
id: string
|
||||
name?: string | null
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "doc-level-access".
|
||||
*/
|
||||
export interface DocLevelAccess {
|
||||
id: string
|
||||
approvedForRemoval?: boolean | null
|
||||
@@ -108,6 +149,10 @@ export interface DocLevelAccess {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "hidden-fields".
|
||||
*/
|
||||
export interface HiddenField {
|
||||
id: string
|
||||
title?: string | null
|
||||
@@ -126,6 +171,10 @@ export interface HiddenField {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "hidden-access".
|
||||
*/
|
||||
export interface HiddenAccess {
|
||||
id: string
|
||||
title: string
|
||||
@@ -133,6 +182,21 @@ export interface HiddenAccess {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "hidden-access-count".
|
||||
*/
|
||||
export interface HiddenAccessCount {
|
||||
id: string
|
||||
title: string
|
||||
hidden?: boolean | null
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: string
|
||||
user: {
|
||||
@@ -152,6 +216,10 @@ export interface PayloadPreference {
|
||||
updatedAt: string
|
||||
createdAt: string
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by `Config`'s JSON-Schema
|
||||
* via the `definition` "payload-migrations".
|
||||
*/
|
||||
export interface PayloadMigration {
|
||||
id: string
|
||||
name?: string | null
|
||||
|
||||
@@ -12,5 +12,5 @@ export const siblingDataSlug = 'sibling-data'
|
||||
export const relyOnRequestHeadersSlug = 'rely-on-request-headers'
|
||||
export const docLevelAccessSlug = 'doc-level-access'
|
||||
export const hiddenFieldsSlug = 'hidden-fields'
|
||||
|
||||
export const hiddenAccessCountSlug = 'hidden-access-count'
|
||||
export const hiddenAccessSlug = 'hidden-access'
|
||||
|
||||
@@ -102,6 +102,18 @@ describe('collections-graphql', () => {
|
||||
expect(docs).toContainEqual(expect.objectContaining({ id: existingDoc.id }))
|
||||
})
|
||||
|
||||
it('should count', async () => {
|
||||
const query = `query {
|
||||
countPosts {
|
||||
totalDocs
|
||||
}
|
||||
}`
|
||||
const response = await client.request<{ countPosts: { totalDocs: number } }>(query)
|
||||
const { totalDocs } = response.countPosts
|
||||
|
||||
expect(typeof totalDocs).toBe('number')
|
||||
})
|
||||
|
||||
it('should read using multiple queries', async () => {
|
||||
const query = `query {
|
||||
postIDs: Posts {
|
||||
|
||||
@@ -1,32 +1,49 @@
|
||||
type Query {
|
||||
User(id: String!, draft: Boolean): User
|
||||
Users(draft: Boolean, where: User_where, limit: Int, page: Int, sort: String): Users
|
||||
countUsers(draft: Boolean, where: User_where): countUsers
|
||||
docAccessUser(id: String!): usersDocAccess
|
||||
meUser: usersMe
|
||||
initializedUser: Boolean
|
||||
Point(id: String!, draft: Boolean): Point
|
||||
Points(draft: Boolean, where: Point_where, limit: Int, page: Int, sort: String): Points
|
||||
countPoints(draft: Boolean, where: Point_where): countPoints
|
||||
docAccessPoint(id: String!): pointDocAccess
|
||||
Post(id: String!, draft: Boolean): Post
|
||||
Posts(draft: Boolean, where: Post_where, limit: Int, page: Int, sort: String): Posts
|
||||
countPosts(draft: Boolean, where: Post_where): countPosts
|
||||
docAccessPost(id: String!): postsDocAccess
|
||||
CustomId(id: Int!, draft: Boolean): CustomId
|
||||
CustomIds(draft: Boolean, where: CustomId_where, limit: Int, page: Int, sort: String): CustomIds
|
||||
countCustomIds(draft: Boolean, where: CustomId_where): countCustomIds
|
||||
docAccessCustomId(id: Int!): custom_idsDocAccess
|
||||
Relation(id: String!, draft: Boolean): Relation
|
||||
Relations(draft: Boolean, where: Relation_where, limit: Int, page: Int, sort: String): Relations
|
||||
countRelations(draft: Boolean, where: Relation_where): countRelations
|
||||
docAccessRelation(id: String!): relationDocAccess
|
||||
Dummy(id: String!, draft: Boolean): Dummy
|
||||
Dummies(draft: Boolean, where: Dummy_where, limit: Int, page: Int, sort: String): Dummies
|
||||
countDummies(draft: Boolean, where: Dummy_where): countDummies
|
||||
docAccessDummy(id: String!): dummyDocAccess
|
||||
ErrorOnHook(id: String!, draft: Boolean): ErrorOnHook
|
||||
ErrorOnHooks(draft: Boolean, where: ErrorOnHook_where, limit: Int, page: Int, sort: String): ErrorOnHooks
|
||||
countErrorOnHooks(draft: Boolean, where: ErrorOnHook_where): countErrorOnHooks
|
||||
docAccessErrorOnHook(id: String!): error_on_hooksDocAccess
|
||||
PayloadApiTestOne(id: String!, draft: Boolean): PayloadApiTestOne
|
||||
PayloadApiTestOnes(draft: Boolean, where: PayloadApiTestOne_where, limit: Int, page: Int, sort: String): PayloadApiTestOnes
|
||||
countPayloadApiTestOnes(draft: Boolean, where: PayloadApiTestOne_where): countPayloadApiTestOnes
|
||||
docAccessPayloadApiTestOne(id: String!): payload_api_test_onesDocAccess
|
||||
PayloadApiTestTwo(id: String!, draft: Boolean): PayloadApiTestTwo
|
||||
PayloadApiTestTwos(draft: Boolean, where: PayloadApiTestTwo_where, limit: Int, page: Int, sort: String): PayloadApiTestTwos
|
||||
countPayloadApiTestTwos(draft: Boolean, where: PayloadApiTestTwo_where): countPayloadApiTestTwos
|
||||
docAccessPayloadApiTestTwo(id: String!): payload_api_test_twosDocAccess
|
||||
ContentType(id: String!, draft: Boolean): ContentType
|
||||
ContentTypes(draft: Boolean, where: ContentType_where, limit: Int, page: Int, sort: String): ContentTypes
|
||||
countContentTypes(draft: Boolean, where: ContentType_where): countContentTypes
|
||||
docAccessContentType(id: String!): content_typeDocAccess
|
||||
PayloadPreference(id: String!, draft: Boolean): PayloadPreference
|
||||
PayloadPreferences(draft: Boolean, where: PayloadPreference_where, limit: Int, page: Int, sort: String): PayloadPreferences
|
||||
countPayloadPreferences(draft: Boolean, where: PayloadPreference_where): countPayloadPreferences
|
||||
docAccessPayloadPreference(id: String!): payload_preferencesDocAccess
|
||||
Access: Access
|
||||
QueryWithInternalError: QueryWithInternalError
|
||||
@@ -140,6 +157,10 @@ input User_where_or {
|
||||
OR: [User_where_or]
|
||||
}
|
||||
|
||||
type countUsers {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type usersDocAccess {
|
||||
fields: UsersDocAccessFields
|
||||
create: UsersCreateDocAccess
|
||||
@@ -389,6 +410,10 @@ input Point_where_or {
|
||||
OR: [Point_where_or]
|
||||
}
|
||||
|
||||
type countPoints {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type pointDocAccess {
|
||||
fields: PointDocAccessFields
|
||||
create: PointCreateDocAccess
|
||||
@@ -842,6 +867,10 @@ input Post_where_or {
|
||||
OR: [Post_where_or]
|
||||
}
|
||||
|
||||
type countPosts {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type postsDocAccess {
|
||||
fields: PostsDocAccessFields
|
||||
create: PostsCreateDocAccess
|
||||
@@ -1509,6 +1538,10 @@ input CustomId_where_or {
|
||||
OR: [CustomId_where_or]
|
||||
}
|
||||
|
||||
type countCustomIds {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type custom_idsDocAccess {
|
||||
fields: CustomIdsDocAccessFields
|
||||
create: CustomIdsCreateDocAccess
|
||||
@@ -1721,6 +1754,10 @@ input Relation_where_or {
|
||||
OR: [Relation_where_or]
|
||||
}
|
||||
|
||||
type countRelations {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type relationDocAccess {
|
||||
fields: RelationDocAccessFields
|
||||
create: RelationCreateDocAccess
|
||||
@@ -1909,6 +1946,10 @@ input Dummy_where_or {
|
||||
OR: [Dummy_where_or]
|
||||
}
|
||||
|
||||
type countDummies {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type dummyDocAccess {
|
||||
fields: DummyDocAccessFields
|
||||
create: DummyCreateDocAccess
|
||||
@@ -2012,6 +2053,239 @@ type DummyDeleteDocAccess {
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHook {
|
||||
id: String
|
||||
title: String
|
||||
errorBeforeChange: Boolean
|
||||
updatedAt: DateTime
|
||||
createdAt: DateTime
|
||||
}
|
||||
|
||||
type ErrorOnHooks {
|
||||
docs: [ErrorOnHook]
|
||||
hasNextPage: Boolean
|
||||
hasPrevPage: Boolean
|
||||
limit: Int
|
||||
nextPage: Int
|
||||
offset: Int
|
||||
page: Int
|
||||
pagingCounter: Int
|
||||
prevPage: Int
|
||||
totalDocs: Int
|
||||
totalPages: Int
|
||||
}
|
||||
|
||||
input ErrorOnHook_where {
|
||||
title: ErrorOnHook_title_operator
|
||||
errorBeforeChange: ErrorOnHook_errorBeforeChange_operator
|
||||
updatedAt: ErrorOnHook_updatedAt_operator
|
||||
createdAt: ErrorOnHook_createdAt_operator
|
||||
id: ErrorOnHook_id_operator
|
||||
AND: [ErrorOnHook_where_and]
|
||||
OR: [ErrorOnHook_where_or]
|
||||
}
|
||||
|
||||
input ErrorOnHook_title_operator {
|
||||
equals: String
|
||||
not_equals: String
|
||||
like: String
|
||||
contains: String
|
||||
in: [String]
|
||||
not_in: [String]
|
||||
all: [String]
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ErrorOnHook_errorBeforeChange_operator {
|
||||
equals: Boolean
|
||||
not_equals: Boolean
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ErrorOnHook_updatedAt_operator {
|
||||
equals: DateTime
|
||||
not_equals: DateTime
|
||||
greater_than_equal: DateTime
|
||||
greater_than: DateTime
|
||||
less_than_equal: DateTime
|
||||
less_than: DateTime
|
||||
like: DateTime
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ErrorOnHook_createdAt_operator {
|
||||
equals: DateTime
|
||||
not_equals: DateTime
|
||||
greater_than_equal: DateTime
|
||||
greater_than: DateTime
|
||||
less_than_equal: DateTime
|
||||
less_than: DateTime
|
||||
like: DateTime
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ErrorOnHook_id_operator {
|
||||
equals: String
|
||||
not_equals: String
|
||||
like: String
|
||||
contains: String
|
||||
in: [String]
|
||||
not_in: [String]
|
||||
all: [String]
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ErrorOnHook_where_and {
|
||||
title: ErrorOnHook_title_operator
|
||||
errorBeforeChange: ErrorOnHook_errorBeforeChange_operator
|
||||
updatedAt: ErrorOnHook_updatedAt_operator
|
||||
createdAt: ErrorOnHook_createdAt_operator
|
||||
id: ErrorOnHook_id_operator
|
||||
AND: [ErrorOnHook_where_and]
|
||||
OR: [ErrorOnHook_where_or]
|
||||
}
|
||||
|
||||
input ErrorOnHook_where_or {
|
||||
title: ErrorOnHook_title_operator
|
||||
errorBeforeChange: ErrorOnHook_errorBeforeChange_operator
|
||||
updatedAt: ErrorOnHook_updatedAt_operator
|
||||
createdAt: ErrorOnHook_createdAt_operator
|
||||
id: ErrorOnHook_id_operator
|
||||
AND: [ErrorOnHook_where_and]
|
||||
OR: [ErrorOnHook_where_or]
|
||||
}
|
||||
|
||||
type countErrorOnHooks {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type error_on_hooksDocAccess {
|
||||
fields: ErrorOnHooksDocAccessFields
|
||||
create: ErrorOnHooksCreateDocAccess
|
||||
read: ErrorOnHooksReadDocAccess
|
||||
update: ErrorOnHooksUpdateDocAccess
|
||||
delete: ErrorOnHooksDeleteDocAccess
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields {
|
||||
title: ErrorOnHooksDocAccessFields_title
|
||||
errorBeforeChange: ErrorOnHooksDocAccessFields_errorBeforeChange
|
||||
updatedAt: ErrorOnHooksDocAccessFields_updatedAt
|
||||
createdAt: ErrorOnHooksDocAccessFields_createdAt
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_title {
|
||||
create: ErrorOnHooksDocAccessFields_title_Create
|
||||
read: ErrorOnHooksDocAccessFields_title_Read
|
||||
update: ErrorOnHooksDocAccessFields_title_Update
|
||||
delete: ErrorOnHooksDocAccessFields_title_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_title_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_title_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_title_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_title_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_errorBeforeChange {
|
||||
create: ErrorOnHooksDocAccessFields_errorBeforeChange_Create
|
||||
read: ErrorOnHooksDocAccessFields_errorBeforeChange_Read
|
||||
update: ErrorOnHooksDocAccessFields_errorBeforeChange_Update
|
||||
delete: ErrorOnHooksDocAccessFields_errorBeforeChange_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_errorBeforeChange_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_errorBeforeChange_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_errorBeforeChange_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_errorBeforeChange_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_updatedAt {
|
||||
create: ErrorOnHooksDocAccessFields_updatedAt_Create
|
||||
read: ErrorOnHooksDocAccessFields_updatedAt_Read
|
||||
update: ErrorOnHooksDocAccessFields_updatedAt_Update
|
||||
delete: ErrorOnHooksDocAccessFields_updatedAt_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_updatedAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_updatedAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_updatedAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_updatedAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_createdAt {
|
||||
create: ErrorOnHooksDocAccessFields_createdAt_Create
|
||||
read: ErrorOnHooksDocAccessFields_createdAt_Read
|
||||
update: ErrorOnHooksDocAccessFields_createdAt_Update
|
||||
delete: ErrorOnHooksDocAccessFields_createdAt_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_createdAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_createdAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_createdAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksDocAccessFields_createdAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksCreateDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHooksReadDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHooksUpdateDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHooksDeleteDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type PayloadApiTestOne {
|
||||
id: String
|
||||
payloadAPI: String
|
||||
@@ -2104,6 +2378,10 @@ input PayloadApiTestOne_where_or {
|
||||
OR: [PayloadApiTestOne_where_or]
|
||||
}
|
||||
|
||||
type countPayloadApiTestOnes {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type payload_api_test_onesDocAccess {
|
||||
fields: PayloadApiTestOnesDocAccessFields
|
||||
create: PayloadApiTestOnesCreateDocAccess
|
||||
@@ -2312,6 +2590,10 @@ input PayloadApiTestTwo_where_or {
|
||||
OR: [PayloadApiTestTwo_where_or]
|
||||
}
|
||||
|
||||
type countPayloadApiTestTwos {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type payload_api_test_twosDocAccess {
|
||||
fields: PayloadApiTestTwosDocAccessFields
|
||||
create: PayloadApiTestTwosCreateDocAccess
|
||||
@@ -2439,6 +2721,205 @@ type PayloadApiTestTwosDeleteDocAccess {
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentType {
|
||||
id: String
|
||||
contentType: String
|
||||
updatedAt: DateTime
|
||||
createdAt: DateTime
|
||||
}
|
||||
|
||||
type ContentTypes {
|
||||
docs: [ContentType]
|
||||
hasNextPage: Boolean
|
||||
hasPrevPage: Boolean
|
||||
limit: Int
|
||||
nextPage: Int
|
||||
offset: Int
|
||||
page: Int
|
||||
pagingCounter: Int
|
||||
prevPage: Int
|
||||
totalDocs: Int
|
||||
totalPages: Int
|
||||
}
|
||||
|
||||
input ContentType_where {
|
||||
contentType: ContentType_contentType_operator
|
||||
updatedAt: ContentType_updatedAt_operator
|
||||
createdAt: ContentType_createdAt_operator
|
||||
id: ContentType_id_operator
|
||||
AND: [ContentType_where_and]
|
||||
OR: [ContentType_where_or]
|
||||
}
|
||||
|
||||
input ContentType_contentType_operator {
|
||||
equals: String
|
||||
not_equals: String
|
||||
like: String
|
||||
contains: String
|
||||
in: [String]
|
||||
not_in: [String]
|
||||
all: [String]
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ContentType_updatedAt_operator {
|
||||
equals: DateTime
|
||||
not_equals: DateTime
|
||||
greater_than_equal: DateTime
|
||||
greater_than: DateTime
|
||||
less_than_equal: DateTime
|
||||
less_than: DateTime
|
||||
like: DateTime
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ContentType_createdAt_operator {
|
||||
equals: DateTime
|
||||
not_equals: DateTime
|
||||
greater_than_equal: DateTime
|
||||
greater_than: DateTime
|
||||
less_than_equal: DateTime
|
||||
less_than: DateTime
|
||||
like: DateTime
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ContentType_id_operator {
|
||||
equals: String
|
||||
not_equals: String
|
||||
like: String
|
||||
contains: String
|
||||
in: [String]
|
||||
not_in: [String]
|
||||
all: [String]
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input ContentType_where_and {
|
||||
contentType: ContentType_contentType_operator
|
||||
updatedAt: ContentType_updatedAt_operator
|
||||
createdAt: ContentType_createdAt_operator
|
||||
id: ContentType_id_operator
|
||||
AND: [ContentType_where_and]
|
||||
OR: [ContentType_where_or]
|
||||
}
|
||||
|
||||
input ContentType_where_or {
|
||||
contentType: ContentType_contentType_operator
|
||||
updatedAt: ContentType_updatedAt_operator
|
||||
createdAt: ContentType_createdAt_operator
|
||||
id: ContentType_id_operator
|
||||
AND: [ContentType_where_and]
|
||||
OR: [ContentType_where_or]
|
||||
}
|
||||
|
||||
type countContentTypes {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type content_typeDocAccess {
|
||||
fields: ContentTypeDocAccessFields
|
||||
create: ContentTypeCreateDocAccess
|
||||
read: ContentTypeReadDocAccess
|
||||
update: ContentTypeUpdateDocAccess
|
||||
delete: ContentTypeDeleteDocAccess
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields {
|
||||
contentType: ContentTypeDocAccessFields_contentType
|
||||
updatedAt: ContentTypeDocAccessFields_updatedAt
|
||||
createdAt: ContentTypeDocAccessFields_createdAt
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_contentType {
|
||||
create: ContentTypeDocAccessFields_contentType_Create
|
||||
read: ContentTypeDocAccessFields_contentType_Read
|
||||
update: ContentTypeDocAccessFields_contentType_Update
|
||||
delete: ContentTypeDocAccessFields_contentType_Delete
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_contentType_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_contentType_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_contentType_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_contentType_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_updatedAt {
|
||||
create: ContentTypeDocAccessFields_updatedAt_Create
|
||||
read: ContentTypeDocAccessFields_updatedAt_Read
|
||||
update: ContentTypeDocAccessFields_updatedAt_Update
|
||||
delete: ContentTypeDocAccessFields_updatedAt_Delete
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_updatedAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_updatedAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_updatedAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_updatedAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_createdAt {
|
||||
create: ContentTypeDocAccessFields_createdAt_Create
|
||||
read: ContentTypeDocAccessFields_createdAt_Read
|
||||
update: ContentTypeDocAccessFields_createdAt_Update
|
||||
delete: ContentTypeDocAccessFields_createdAt_Delete
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_createdAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_createdAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_createdAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeDocAccessFields_createdAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeCreateDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentTypeReadDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentTypeUpdateDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentTypeDeleteDocAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type PayloadPreference {
|
||||
id: String
|
||||
user: PayloadPreference_User_Relationship!
|
||||
@@ -2569,6 +3050,10 @@ input PayloadPreference_where_or {
|
||||
OR: [PayloadPreference_where_or]
|
||||
}
|
||||
|
||||
type countPayloadPreferences {
|
||||
totalDocs: Int
|
||||
}
|
||||
|
||||
type payload_preferencesDocAccess {
|
||||
fields: PayloadPreferencesDocAccessFields
|
||||
create: PayloadPreferencesCreateDocAccess
|
||||
@@ -2728,8 +3213,10 @@ type Access {
|
||||
custom_ids: custom_idsAccess
|
||||
relation: relationAccess
|
||||
dummy: dummyAccess
|
||||
error_on_hooks: error_on_hooksAccess
|
||||
payload_api_test_ones: payload_api_test_onesAccess
|
||||
payload_api_test_twos: payload_api_test_twosAccess
|
||||
content_type: content_typeAccess
|
||||
payload_preferences: payload_preferencesAccess
|
||||
}
|
||||
|
||||
@@ -3885,6 +4372,133 @@ type DummyDeleteAccess {
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type error_on_hooksAccess {
|
||||
fields: ErrorOnHooksFields
|
||||
create: ErrorOnHooksCreateAccess
|
||||
read: ErrorOnHooksReadAccess
|
||||
update: ErrorOnHooksUpdateAccess
|
||||
delete: ErrorOnHooksDeleteAccess
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields {
|
||||
title: ErrorOnHooksFields_title
|
||||
errorBeforeChange: ErrorOnHooksFields_errorBeforeChange
|
||||
updatedAt: ErrorOnHooksFields_updatedAt
|
||||
createdAt: ErrorOnHooksFields_createdAt
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_title {
|
||||
create: ErrorOnHooksFields_title_Create
|
||||
read: ErrorOnHooksFields_title_Read
|
||||
update: ErrorOnHooksFields_title_Update
|
||||
delete: ErrorOnHooksFields_title_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_title_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_title_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_title_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_title_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_errorBeforeChange {
|
||||
create: ErrorOnHooksFields_errorBeforeChange_Create
|
||||
read: ErrorOnHooksFields_errorBeforeChange_Read
|
||||
update: ErrorOnHooksFields_errorBeforeChange_Update
|
||||
delete: ErrorOnHooksFields_errorBeforeChange_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_errorBeforeChange_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_errorBeforeChange_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_errorBeforeChange_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_errorBeforeChange_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_updatedAt {
|
||||
create: ErrorOnHooksFields_updatedAt_Create
|
||||
read: ErrorOnHooksFields_updatedAt_Read
|
||||
update: ErrorOnHooksFields_updatedAt_Update
|
||||
delete: ErrorOnHooksFields_updatedAt_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_updatedAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_updatedAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_updatedAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_updatedAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_createdAt {
|
||||
create: ErrorOnHooksFields_createdAt_Create
|
||||
read: ErrorOnHooksFields_createdAt_Read
|
||||
update: ErrorOnHooksFields_createdAt_Update
|
||||
delete: ErrorOnHooksFields_createdAt_Delete
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_createdAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_createdAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_createdAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksFields_createdAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ErrorOnHooksCreateAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHooksReadAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHooksUpdateAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ErrorOnHooksDeleteAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type payload_api_test_onesAccess {
|
||||
fields: PayloadApiTestOnesFields
|
||||
create: PayloadApiTestOnesCreateAccess
|
||||
@@ -4115,6 +4729,109 @@ type PayloadApiTestTwosDeleteAccess {
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type content_typeAccess {
|
||||
fields: ContentTypeFields
|
||||
create: ContentTypeCreateAccess
|
||||
read: ContentTypeReadAccess
|
||||
update: ContentTypeUpdateAccess
|
||||
delete: ContentTypeDeleteAccess
|
||||
}
|
||||
|
||||
type ContentTypeFields {
|
||||
contentType: ContentTypeFields_contentType
|
||||
updatedAt: ContentTypeFields_updatedAt
|
||||
createdAt: ContentTypeFields_createdAt
|
||||
}
|
||||
|
||||
type ContentTypeFields_contentType {
|
||||
create: ContentTypeFields_contentType_Create
|
||||
read: ContentTypeFields_contentType_Read
|
||||
update: ContentTypeFields_contentType_Update
|
||||
delete: ContentTypeFields_contentType_Delete
|
||||
}
|
||||
|
||||
type ContentTypeFields_contentType_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_contentType_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_contentType_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_contentType_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_updatedAt {
|
||||
create: ContentTypeFields_updatedAt_Create
|
||||
read: ContentTypeFields_updatedAt_Read
|
||||
update: ContentTypeFields_updatedAt_Update
|
||||
delete: ContentTypeFields_updatedAt_Delete
|
||||
}
|
||||
|
||||
type ContentTypeFields_updatedAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_updatedAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_updatedAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_updatedAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_createdAt {
|
||||
create: ContentTypeFields_createdAt_Create
|
||||
read: ContentTypeFields_createdAt_Read
|
||||
update: ContentTypeFields_createdAt_Update
|
||||
delete: ContentTypeFields_createdAt_Delete
|
||||
}
|
||||
|
||||
type ContentTypeFields_createdAt_Create {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_createdAt_Read {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_createdAt_Update {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeFields_createdAt_Delete {
|
||||
permission: Boolean!
|
||||
}
|
||||
|
||||
type ContentTypeCreateAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentTypeReadAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentTypeUpdateAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type ContentTypeDeleteAccess {
|
||||
permission: Boolean!
|
||||
where: JSONObject
|
||||
}
|
||||
|
||||
type payload_preferencesAccess {
|
||||
fields: PayloadPreferencesFields
|
||||
create: PayloadPreferencesCreateAccess
|
||||
@@ -4296,12 +5013,18 @@ type Mutation {
|
||||
createDummy(data: mutationDummyInput!, draft: Boolean): Dummy
|
||||
updateDummy(id: String!, autosave: Boolean, data: mutationDummyUpdateInput!, draft: Boolean): Dummy
|
||||
deleteDummy(id: String!): Dummy
|
||||
createErrorOnHook(data: mutationErrorOnHookInput!, draft: Boolean): ErrorOnHook
|
||||
updateErrorOnHook(id: String!, autosave: Boolean, data: mutationErrorOnHookUpdateInput!, draft: Boolean): ErrorOnHook
|
||||
deleteErrorOnHook(id: String!): ErrorOnHook
|
||||
createPayloadApiTestOne(data: mutationPayloadApiTestOneInput!, draft: Boolean): PayloadApiTestOne
|
||||
updatePayloadApiTestOne(id: String!, autosave: Boolean, data: mutationPayloadApiTestOneUpdateInput!, draft: Boolean): PayloadApiTestOne
|
||||
deletePayloadApiTestOne(id: String!): PayloadApiTestOne
|
||||
createPayloadApiTestTwo(data: mutationPayloadApiTestTwoInput!, draft: Boolean): PayloadApiTestTwo
|
||||
updatePayloadApiTestTwo(id: String!, autosave: Boolean, data: mutationPayloadApiTestTwoUpdateInput!, draft: Boolean): PayloadApiTestTwo
|
||||
deletePayloadApiTestTwo(id: String!): PayloadApiTestTwo
|
||||
createContentType(data: mutationContentTypeInput!, draft: Boolean): ContentType
|
||||
updateContentType(id: String!, autosave: Boolean, data: mutationContentTypeUpdateInput!, draft: Boolean): ContentType
|
||||
deleteContentType(id: String!): ContentType
|
||||
createPayloadPreference(data: mutationPayloadPreferenceInput!, draft: Boolean): PayloadPreference
|
||||
updatePayloadPreference(id: String!, autosave: Boolean, data: mutationPayloadPreferenceUpdateInput!, draft: Boolean): PayloadPreference
|
||||
deletePayloadPreference(id: String!): PayloadPreference
|
||||
@@ -4538,6 +5261,20 @@ input mutationDummyUpdateInput {
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
input mutationErrorOnHookInput {
|
||||
title: String
|
||||
errorBeforeChange: Boolean
|
||||
updatedAt: String
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
input mutationErrorOnHookUpdateInput {
|
||||
title: String
|
||||
errorBeforeChange: Boolean
|
||||
updatedAt: String
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
input mutationPayloadApiTestOneInput {
|
||||
payloadAPI: String
|
||||
updatedAt: String
|
||||
@@ -4564,6 +5301,18 @@ input mutationPayloadApiTestTwoUpdateInput {
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
input mutationContentTypeInput {
|
||||
contentType: String
|
||||
updatedAt: String
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
input mutationContentTypeUpdateInput {
|
||||
contentType: String
|
||||
updatedAt: String
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
input mutationPayloadPreferenceInput {
|
||||
user: PayloadPreference_UserRelationshipInput
|
||||
key: String
|
||||
|
||||
@@ -67,6 +67,15 @@ describe('collections-rest', () => {
|
||||
expect(result.docs).toEqual(expect.arrayContaining(expectedDocs))
|
||||
})
|
||||
|
||||
it('should count', async () => {
|
||||
await createPost()
|
||||
await createPost()
|
||||
const { result, status } = await client.count()
|
||||
|
||||
expect(status).toEqual(200)
|
||||
expect(result).toEqual({ totalDocs: 2 })
|
||||
})
|
||||
|
||||
it('should find where id', async () => {
|
||||
const post1 = await createPost()
|
||||
await createPost()
|
||||
|
||||
@@ -142,6 +142,21 @@ const TabsFields: CollectionConfig = {
|
||||
type: 'text',
|
||||
defaultValue: namedTabDefaultValue,
|
||||
},
|
||||
{
|
||||
type: 'row',
|
||||
fields: [
|
||||
{
|
||||
name: 'arrayInRow',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'textInArrayInRow',
|
||||
type: 'text',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -35,6 +35,11 @@ export const tabsDoc: Partial<TabsField> = {
|
||||
},
|
||||
],
|
||||
text: namedTabText,
|
||||
arrayInRow: [
|
||||
{
|
||||
text: "Hello, I'm some text in an array in a row",
|
||||
},
|
||||
],
|
||||
},
|
||||
localizedTab: {
|
||||
text: localizedTextValue,
|
||||
|
||||
@@ -355,6 +355,29 @@ describe('fields', () => {
|
||||
await saveDocAndAssert(page)
|
||||
await expect(field.locator('.rs__value-container')).toContainText('One')
|
||||
})
|
||||
|
||||
test('should not allow filtering by hasMany field / equals / not equals', async () => {
|
||||
await page.goto(url.list)
|
||||
|
||||
await page.locator('.list-controls__toggle-columns').click()
|
||||
await page.locator('.list-controls__toggle-where').click()
|
||||
await page.waitForSelector('.list-controls__where.rah-static--height-auto')
|
||||
await page.locator('.where-builder__add-first-filter').click()
|
||||
|
||||
const conditionField = page.locator('.condition__field')
|
||||
await conditionField.click()
|
||||
|
||||
const dropdownFieldOptions = conditionField.locator('.rs__option')
|
||||
await dropdownFieldOptions.locator('text=Select Has Many').nth(0).click()
|
||||
|
||||
const operatorField = page.locator('.condition__operator')
|
||||
await operatorField.click()
|
||||
|
||||
const dropdownOperatorOptions = operatorField.locator('.rs__option')
|
||||
|
||||
await expect(dropdownOperatorOptions.locator('text=equals')).toBeHidden()
|
||||
await expect(dropdownOperatorOptions.locator('text=not equals')).toBeHidden()
|
||||
})
|
||||
})
|
||||
|
||||
describe('point', () => {
|
||||
|
||||
@@ -1083,6 +1083,10 @@ export interface TabsField {
|
||||
}[]
|
||||
text?: string | null
|
||||
defaultValue?: string | null
|
||||
arrayInRow?: {
|
||||
text: string
|
||||
id?: string | null
|
||||
}[]
|
||||
}
|
||||
namedTabWithDefaultValue: {
|
||||
defaultValue?: string | null
|
||||
|
||||
@@ -30,6 +30,12 @@ type CreateArgs<T = any> = {
|
||||
slug?: string
|
||||
}
|
||||
|
||||
type CountArgs = {
|
||||
auth?: boolean
|
||||
query?: Where
|
||||
slug?: string
|
||||
}
|
||||
|
||||
type FindArgs = {
|
||||
auth?: boolean
|
||||
depth?: number
|
||||
@@ -112,6 +118,13 @@ type QueryResponse<T> = {
|
||||
status: number
|
||||
}
|
||||
|
||||
type CountResponse = {
|
||||
result: {
|
||||
totalDocs: number
|
||||
}
|
||||
status: number
|
||||
}
|
||||
|
||||
export class RESTClient {
|
||||
private readonly config: Config
|
||||
|
||||
@@ -127,6 +140,32 @@ export class RESTClient {
|
||||
this.defaultSlug = args.defaultSlug
|
||||
}
|
||||
|
||||
async count<T = any>(args?: CountArgs): Promise<CountResponse> {
|
||||
const options = {
|
||||
headers: { ...headers },
|
||||
}
|
||||
|
||||
if (args?.auth !== false && this.token) {
|
||||
options.headers.Authorization = `JWT ${this.token}`
|
||||
}
|
||||
|
||||
const whereQuery = qs.stringify(
|
||||
{
|
||||
...(args?.query ? { where: args.query } : {}),
|
||||
},
|
||||
{
|
||||
addQueryPrefix: true,
|
||||
},
|
||||
)
|
||||
|
||||
const slug = args?.slug || this.defaultSlug
|
||||
const response = await fetch(`${this.serverURL}/api/${slug}/count${whereQuery}`, options)
|
||||
const { status } = response
|
||||
const result = await response.json()
|
||||
if (result.errors) throw new Error(result.errors[0].message)
|
||||
return { result, status }
|
||||
}
|
||||
|
||||
async create<T = any>(args: CreateArgs): Promise<DocResponse<T>> {
|
||||
const options = {
|
||||
body: args.file ? args.data : JSON.stringify(args.data),
|
||||
|
||||
@@ -437,6 +437,7 @@ describe('Versions', () => {
|
||||
collection,
|
||||
data: {
|
||||
title: patchedTitle,
|
||||
_status: 'draft',
|
||||
},
|
||||
draft: true,
|
||||
locale: 'en',
|
||||
@@ -450,6 +451,7 @@ describe('Versions', () => {
|
||||
collection,
|
||||
data: {
|
||||
title: spanishTitle,
|
||||
_status: 'draft',
|
||||
},
|
||||
draft: true,
|
||||
locale: 'es',
|
||||
@@ -529,9 +531,11 @@ describe('Versions', () => {
|
||||
},
|
||||
})
|
||||
|
||||
// bulk publish
|
||||
const updated = await payload.update({
|
||||
collection: draftCollectionSlug,
|
||||
data: {
|
||||
_status: 'published',
|
||||
description: 'updated description',
|
||||
},
|
||||
draft: true,
|
||||
@@ -544,8 +548,20 @@ describe('Versions', () => {
|
||||
|
||||
const updatedDoc = updated.docs?.[0]
|
||||
|
||||
// get the published doc
|
||||
const findResult = await payload.find({
|
||||
collection: draftCollectionSlug,
|
||||
where: {
|
||||
id: { equals: doc.id },
|
||||
},
|
||||
})
|
||||
|
||||
const findDoc = findResult.docs?.[0]
|
||||
|
||||
expect(updatedDoc.description).toStrictEqual('updated description')
|
||||
expect(updatedDoc.title).toStrictEqual('updated title') // probably will fail
|
||||
expect(updatedDoc.title).toStrictEqual('updated title')
|
||||
expect(findDoc.title).toStrictEqual('updated title')
|
||||
expect(findDoc.description).toStrictEqual('updated description')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1236,6 +1252,7 @@ describe('Versions', () => {
|
||||
await payload.updateGlobal({
|
||||
slug: globalSlug,
|
||||
data: {
|
||||
_status: 'draft',
|
||||
title: updatedTitle2,
|
||||
},
|
||||
draft: true,
|
||||
@@ -1245,6 +1262,7 @@ describe('Versions', () => {
|
||||
await payload.updateGlobal({
|
||||
slug: globalSlug,
|
||||
data: {
|
||||
_status: 'draft',
|
||||
title: updatedTitle2,
|
||||
},
|
||||
draft: true,
|
||||
|
||||
Reference in New Issue
Block a user