feat: add forceSelect collection / global config property (#11627)
### What?
Adds a new property to collection / global config `forceSelect` which
can be used to ensure that some fields are always selected, regardless
of the `select` query.
### Why?
This can be beneficial for hooks and access control, for example imagine
you need the value of `data.slug` in your hook.
With the following query it would be `undefined`:
`?select[title]=true`
Now, to solve this you can specify
```
forceSelect: {
slug: true
}
```
### How?
Every operation now merges the incoming `select` with
`collectionConfig.forceSelect`.
This commit is contained in:
@@ -79,6 +79,7 @@ The following options are available:
|
|||||||
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
|
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#collection-config). |
|
||||||
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
|
| `defaultPopulate` | Specify which fields to select when this Collection is populated from another document. [More Details](../queries/select#defaultpopulate-collection-config-property). |
|
||||||
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
|
| `indexes` | Define compound indexes for this collection. This can be used to either speed up querying/sorting by 2 or more fields at the same time or to ensure uniqueness between several fields. |
|
||||||
|
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
|
||||||
|
|
||||||
_* An asterisk denotes that a property is required._
|
_* An asterisk denotes that a property is required._
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export const Nav: GlobalConfig = {
|
|||||||
The following options are available:
|
The following options are available:
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `access` | Provide Access Control functions to define exactly who should be able to do what with this Global. [More details](../access-control/globals). |
|
| `access` | Provide Access Control functions to define exactly who should be able to do what with this Global. [More details](../access-control/globals). |
|
||||||
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
|
| `admin` | The configuration options for the Admin Panel. [More details](#admin-options). |
|
||||||
| `custom` | Extension point for adding custom data (e.g. for plugins) |
|
| `custom` | Extension point for adding custom data (e.g. for plugins) |
|
||||||
@@ -81,6 +81,7 @@ The following options are available:
|
|||||||
| `slug` * | Unique, URL-friendly string that will act as an identifier for this Global. |
|
| `slug` * | Unique, URL-friendly string that will act as an identifier for this Global. |
|
||||||
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
| `typescript` | An object with property `interface` as the text used in schema generation. Auto-generated from slug if not defined. |
|
||||||
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#global-config). |
|
| `versions` | Set to true to enable default options, or configure with object properties. [More details](../versions/overview#global-config). |
|
||||||
|
| `forceSelect` | Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks |
|
||||||
|
|
||||||
_* An asterisk denotes that a property is required._
|
_* An asterisk denotes that a property is required._
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const getPosts = async (payload: Payload) => {
|
|||||||
<Banner type="warning">
|
<Banner type="warning">
|
||||||
**Important:**
|
**Important:**
|
||||||
To perform querying with `select` efficiently, Payload implements your `select` query on the database level. Because of that, your `beforeRead` and `afterRead` hooks may not receive the full `doc`.
|
To perform querying with `select` efficiently, Payload implements your `select` query on the database level. Because of that, your `beforeRead` and `afterRead` hooks may not receive the full `doc`.
|
||||||
|
To ensure that some fields are always selected for your hooks / access control, regardless of the `select` query you can use `forceSelect` collection config property.
|
||||||
</Banner>
|
</Banner>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import type {
|
|||||||
} from '../../index.js'
|
} from '../../index.js'
|
||||||
import type {
|
import type {
|
||||||
PayloadRequest,
|
PayloadRequest,
|
||||||
|
SelectIncludeType,
|
||||||
SelectType,
|
SelectType,
|
||||||
Sort,
|
Sort,
|
||||||
TransformCollectionWithSelect,
|
TransformCollectionWithSelect,
|
||||||
@@ -424,6 +425,12 @@ export type CollectionConfig<TSlug extends CollectionSlug = any> = {
|
|||||||
*/
|
*/
|
||||||
endpoints?: false | Omit<Endpoint, 'root'>[]
|
endpoints?: false | Omit<Endpoint, 'root'>[]
|
||||||
fields: Field[]
|
fields: Field[]
|
||||||
|
/**
|
||||||
|
* Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks
|
||||||
|
*/
|
||||||
|
forceSelect?: IsAny<SelectFromCollectionSlug<TSlug>> extends true
|
||||||
|
? SelectIncludeType
|
||||||
|
: SelectFromCollectionSlug<TSlug>
|
||||||
/**
|
/**
|
||||||
* GraphQL configuration
|
* GraphQL configuration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
|
|||||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { saveVersion } from '../../versions/saveVersion.js'
|
import { saveVersion } from '../../versions/saveVersion.js'
|
||||||
import { buildAfterOperation } from './utils.js'
|
import { buildAfterOperation } from './utils.js'
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ export const createOperation = async <
|
|||||||
payload: { config },
|
payload: { config },
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -245,6 +246,11 @@ export const createOperation = async <
|
|||||||
|
|
||||||
let doc
|
let doc
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
if (collectionConfig.auth && !collectionConfig.auth.disableLocalStrategy) {
|
if (collectionConfig.auth && !collectionConfig.auth.disableLocalStrategy) {
|
||||||
if (collectionConfig.auth.verify) {
|
if (collectionConfig.auth.verify) {
|
||||||
resultWithLocales._verified = Boolean(resultWithLocales._verified) || false
|
resultWithLocales._verified = Boolean(resultWithLocales._verified) || false
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { checkDocumentLockStatus } from '../../utilities/checkDocumentLockStatus
|
|||||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions.js'
|
import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions.js'
|
||||||
import { deleteScheduledPublishJobs } from '../../versions/deleteScheduledPublishJobs.js'
|
import { deleteScheduledPublishJobs } from '../../versions/deleteScheduledPublishJobs.js'
|
||||||
import { buildAfterOperation } from './utils.js'
|
import { buildAfterOperation } from './utils.js'
|
||||||
@@ -80,7 +81,7 @@ export const deleteOperation = async <
|
|||||||
payload,
|
payload,
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
where,
|
where,
|
||||||
} = args
|
} = args
|
||||||
@@ -108,6 +109,11 @@ export const deleteOperation = async <
|
|||||||
|
|
||||||
const fullWhere = combineQueries(where, accessResult)
|
const fullWhere = combineQueries(where, accessResult)
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Retrieve documents
|
// Retrieve documents
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { checkDocumentLockStatus } from '../../utilities/checkDocumentLockStatus
|
|||||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions.js'
|
import { deleteCollectionVersions } from '../../versions/deleteCollectionVersions.js'
|
||||||
import { deleteScheduledPublishJobs } from '../../versions/deleteScheduledPublishJobs.js'
|
import { deleteScheduledPublishJobs } from '../../versions/deleteScheduledPublishJobs.js'
|
||||||
import { buildAfterOperation } from './utils.js'
|
import { buildAfterOperation } from './utils.js'
|
||||||
@@ -75,7 +76,7 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
|||||||
payload,
|
payload,
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -166,6 +167,11 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Delete document
|
// Delete document
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { sanitizeJoinQuery } from '../../database/sanitizeJoinQuery.js'
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
||||||
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
|
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
|
||||||
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
@@ -93,12 +94,17 @@ export const findOperation = async <
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
sort,
|
sort,
|
||||||
where,
|
where,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Access
|
// Access
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
|||||||
import { validateQueryPaths } from '../../index.js'
|
import { validateQueryPaths } from '../../index.js'
|
||||||
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import replaceWithDraftIfAvailable from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
import replaceWithDraftIfAvailable from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
||||||
import { buildAfterOperation } from './utils.js'
|
import { buildAfterOperation } from './utils.js'
|
||||||
|
|
||||||
@@ -81,10 +82,15 @@ export const findByIDOperation = async <
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale, t },
|
req: { fallbackLocale, locale, t },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Access
|
// Access
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { combineQueries } from '../../database/combineQueries.js'
|
|||||||
import { APIError, Forbidden, NotFound } from '../../errors/index.js'
|
import { APIError, Forbidden, NotFound } from '../../errors/index.js'
|
||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
collection: Collection
|
collection: Collection
|
||||||
@@ -37,7 +39,7 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -67,6 +69,11 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
|||||||
// Find by ID
|
// Find by ID
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: getQueryDraftsSelect({ select: collectionConfig.forceSelect }),
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
const versionsQuery = await payload.db.findVersions<TData>({
|
const versionsQuery = await payload.db.findVersions<TData>({
|
||||||
collection: collectionConfig.slug,
|
collection: collectionConfig.slug,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import { validateQueryPaths } from '../../database/queryValidation/validateQuery
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
||||||
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
collection: Collection
|
collection: Collection
|
||||||
@@ -40,7 +42,7 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
sort,
|
sort,
|
||||||
where,
|
where,
|
||||||
@@ -69,6 +71,11 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
|||||||
|
|
||||||
const fullWhere = combineQueries(where, accessResults)
|
const fullWhere = combineQueries(where, accessResults)
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: getQueryDraftsSelect({ select: collectionConfig.forceSelect }),
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Find
|
// Find
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { APIError, Forbidden, NotFound } from '../../errors/index.js'
|
|||||||
import { afterChange } from '../../fields/hooks/afterChange/index.js'
|
import { afterChange } from '../../fields/hooks/afterChange/index.js'
|
||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
|
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
@@ -40,7 +41,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
|||||||
populate,
|
populate,
|
||||||
req,
|
req,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -115,6 +116,11 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
|||||||
// Update
|
// Update
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
let result = await req.payload.db.updateOne({
|
let result = await req.payload.db.updateOne({
|
||||||
id: parentDocID,
|
id: parentDocID,
|
||||||
collection: collectionConfig.slug,
|
collection: collectionConfig.slug,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { unlinkTempFiles } from '../../uploads/unlinkTempFiles.js'
|
|||||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
||||||
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
|
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
|
||||||
import { updateDocument } from './utilities/update.js'
|
import { updateDocument } from './utilities/update.js'
|
||||||
@@ -93,7 +94,7 @@ export const updateOperation = async <
|
|||||||
payload,
|
payload,
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
where,
|
where,
|
||||||
} = args
|
} = args
|
||||||
@@ -183,6 +184,11 @@ export const updateOperation = async <
|
|||||||
const { id } = docWithLocales
|
const { id } = docWithLocales
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// ///////////////////////////////////////////////
|
// ///////////////////////////////////////////////
|
||||||
// Update document, runs all document level hooks
|
// Update document, runs all document level hooks
|
||||||
// ///////////////////////////////////////////////
|
// ///////////////////////////////////////////////
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { unlinkTempFiles } from '../../uploads/unlinkTempFiles.js'
|
|||||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
|
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
|
||||||
import { updateDocument } from './utilities/update.js'
|
import { updateDocument } from './utilities/update.js'
|
||||||
import { buildAfterOperation } from './utils.js'
|
import { buildAfterOperation } from './utils.js'
|
||||||
@@ -100,7 +101,7 @@ export const updateByIDOperation = async <
|
|||||||
payload,
|
payload,
|
||||||
},
|
},
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -159,6 +160,11 @@ export const updateByIDOperation = async <
|
|||||||
throwOnMissingFile: false,
|
throwOnMissingFile: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: collectionConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// ///////////////////////////////////////////////
|
// ///////////////////////////////////////////////
|
||||||
// Update document, runs all document level hooks
|
// Update document, runs all document level hooks
|
||||||
// ///////////////////////////////////////////////
|
// ///////////////////////////////////////////////
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { GraphQLNonNull, GraphQLObjectType } from 'graphql'
|
import type { GraphQLNonNull, GraphQLObjectType } from 'graphql'
|
||||||
import type { DeepRequired } from 'ts-essentials'
|
import type { DeepRequired, IsAny } from 'ts-essentials'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
CustomPreviewButton,
|
CustomPreviewButton,
|
||||||
@@ -22,7 +22,7 @@ import type {
|
|||||||
import type { DBIdentifierName } from '../../database/types.js'
|
import type { DBIdentifierName } from '../../database/types.js'
|
||||||
import type { Field, FlattenedField } from '../../fields/config/types.js'
|
import type { Field, FlattenedField } from '../../fields/config/types.js'
|
||||||
import type { GlobalSlug, RequestContext, TypedGlobal, TypedGlobalSelect } from '../../index.js'
|
import type { GlobalSlug, RequestContext, TypedGlobal, TypedGlobalSelect } from '../../index.js'
|
||||||
import type { PayloadRequest, Where } from '../../types/index.js'
|
import type { PayloadRequest, SelectIncludeType, Where } from '../../types/index.js'
|
||||||
import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types.js'
|
import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types.js'
|
||||||
|
|
||||||
export type DataFromGlobalSlug<TSlug extends GlobalSlug> = TypedGlobal[TSlug]
|
export type DataFromGlobalSlug<TSlug extends GlobalSlug> = TypedGlobal[TSlug]
|
||||||
@@ -142,7 +142,7 @@ export type GlobalAdminOptions = {
|
|||||||
preview?: GeneratePreviewURL
|
preview?: GeneratePreviewURL
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GlobalConfig = {
|
export type GlobalConfig<TSlug extends GlobalSlug = any> = {
|
||||||
/**
|
/**
|
||||||
* Do not set this property manually. This is set to true during sanitization, to avoid
|
* Do not set this property manually. This is set to true during sanitization, to avoid
|
||||||
* sanitizing the same global multiple times.
|
* sanitizing the same global multiple times.
|
||||||
@@ -163,6 +163,12 @@ export type GlobalConfig = {
|
|||||||
dbName?: DBIdentifierName
|
dbName?: DBIdentifierName
|
||||||
endpoints?: false | Omit<Endpoint, 'root'>[]
|
endpoints?: false | Omit<Endpoint, 'root'>[]
|
||||||
fields: Field[]
|
fields: Field[]
|
||||||
|
/**
|
||||||
|
* Specify which fields should be selected always, regardless of the `select` query which can be useful that the field exists for access control / hooks
|
||||||
|
*/
|
||||||
|
forceSelect?: IsAny<SelectFromGlobalSlug<TSlug>> extends true
|
||||||
|
? SelectIncludeType
|
||||||
|
: SelectFromGlobalSlug<TSlug>
|
||||||
graphQL?:
|
graphQL?:
|
||||||
| {
|
| {
|
||||||
disableMutations?: true
|
disableMutations?: true
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
|||||||
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
import { lockedDocumentsCollectionSlug } from '../../locked-documents/config.js'
|
||||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import replaceWithDraftIfAvailable from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
import replaceWithDraftIfAvailable from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
||||||
|
|
||||||
type Args = {
|
type Args = {
|
||||||
@@ -36,7 +37,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale },
|
req: { fallbackLocale, locale },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -51,6 +52,11 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
|||||||
accessResult = await executeAccess({ req }, globalConfig.access.read)
|
accessResult = await executeAccess({ req }, globalConfig.access.read)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: globalConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Perform database operation
|
// Perform database operation
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { Forbidden, NotFound } from '../../errors/index.js'
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { deepCopyObjectSimple } from '../../utilities/deepCopyObject.js'
|
import { deepCopyObjectSimple } from '../../utilities/deepCopyObject.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
currentDepth?: number
|
currentDepth?: number
|
||||||
@@ -37,7 +39,7 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -57,6 +59,11 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
|||||||
|
|
||||||
const hasWhereAccess = typeof accessResults === 'object'
|
const hasWhereAccess = typeof accessResults === 'object'
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: getQueryDraftsSelect({ select: globalConfig.forceSelect }),
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
const findGlobalVersionsArgs: FindGlobalVersionsArgs = {
|
const findGlobalVersionsArgs: FindGlobalVersionsArgs = {
|
||||||
global: globalConfig.slug,
|
global: globalConfig.slug,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import { validateQueryPaths } from '../../database/queryValidation/validateQuery
|
|||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { buildVersionGlobalFields } from '../../versions/buildGlobalFields.js'
|
import { buildVersionGlobalFields } from '../../versions/buildGlobalFields.js'
|
||||||
|
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||||
|
|
||||||
export type Arguments = {
|
export type Arguments = {
|
||||||
depth?: number
|
depth?: number
|
||||||
@@ -40,7 +42,7 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
|||||||
populate,
|
populate,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
sort,
|
sort,
|
||||||
where,
|
where,
|
||||||
@@ -67,6 +69,11 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
|||||||
|
|
||||||
const fullWhere = combineQueries(where, accessResults)
|
const fullWhere = combineQueries(where, accessResults)
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: getQueryDraftsSelect({ select: globalConfig.forceSelect }),
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
// Find
|
// Find
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
|
|||||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||||
|
import { sanitizeSelect } from '../../utilities/sanitizeSelect.js'
|
||||||
import { getLatestGlobalVersion } from '../../versions/getLatestGlobalVersion.js'
|
import { getLatestGlobalVersion } from '../../versions/getLatestGlobalVersion.js'
|
||||||
import { saveVersion } from '../../versions/saveVersion.js'
|
import { saveVersion } from '../../versions/saveVersion.js'
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ export const updateOperation = async <
|
|||||||
publishSpecificLocale,
|
publishSpecificLocale,
|
||||||
req: { fallbackLocale, locale, payload },
|
req: { fallbackLocale, locale, payload },
|
||||||
req,
|
req,
|
||||||
select,
|
select: incomingSelect,
|
||||||
showHiddenFields,
|
showHiddenFields,
|
||||||
} = args
|
} = args
|
||||||
|
|
||||||
@@ -244,6 +245,11 @@ export const updateOperation = async <
|
|||||||
// Update
|
// Update
|
||||||
// /////////////////////////////////////
|
// /////////////////////////////////////
|
||||||
|
|
||||||
|
const select = sanitizeSelect({
|
||||||
|
forceSelect: globalConfig.forceSelect,
|
||||||
|
select: incomingSelect,
|
||||||
|
})
|
||||||
|
|
||||||
if (!shouldSaveDraft) {
|
if (!shouldSaveDraft) {
|
||||||
// Ensure global has createdAt
|
// Ensure global has createdAt
|
||||||
if (!result.createdAt) {
|
if (!result.createdAt) {
|
||||||
|
|||||||
25
packages/payload/src/utilities/sanitizeSelect.ts
Normal file
25
packages/payload/src/utilities/sanitizeSelect.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { deepMergeSimple } from '@payloadcms/translations/utilities'
|
||||||
|
|
||||||
|
import type { SelectType } from '../types/index.js'
|
||||||
|
|
||||||
|
import { getSelectMode } from './getSelectMode.js'
|
||||||
|
|
||||||
|
export const sanitizeSelect = ({
|
||||||
|
forceSelect,
|
||||||
|
select,
|
||||||
|
}: {
|
||||||
|
forceSelect?: SelectType
|
||||||
|
select?: SelectType
|
||||||
|
}): SelectType | undefined => {
|
||||||
|
if (!forceSelect || !select) {
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectMode = getSelectMode(select)
|
||||||
|
|
||||||
|
if (selectMode === 'exclude') {
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
|
return deepMergeSimple(select, forceSelect)
|
||||||
|
}
|
||||||
@@ -83,7 +83,7 @@ export interface Config {
|
|||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
};
|
};
|
||||||
db: {
|
db: {
|
||||||
defaultIDType: string;
|
defaultIDType: number;
|
||||||
};
|
};
|
||||||
globals: {
|
globals: {
|
||||||
menu: Menu;
|
menu: Menu;
|
||||||
@@ -123,7 +123,7 @@ export interface UserAuthOperations {
|
|||||||
* via the `definition` "posts".
|
* via the `definition` "posts".
|
||||||
*/
|
*/
|
||||||
export interface Post {
|
export interface Post {
|
||||||
id: string;
|
id: number;
|
||||||
title?: string | null;
|
title?: string | null;
|
||||||
content?: {
|
content?: {
|
||||||
root: {
|
root: {
|
||||||
@@ -148,7 +148,7 @@ export interface Post {
|
|||||||
* via the `definition` "media".
|
* via the `definition` "media".
|
||||||
*/
|
*/
|
||||||
export interface Media {
|
export interface Media {
|
||||||
id: string;
|
id: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
url?: string | null;
|
url?: string | null;
|
||||||
@@ -192,7 +192,7 @@ export interface Media {
|
|||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
*/
|
*/
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string;
|
id: number;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -209,24 +209,24 @@ export interface User {
|
|||||||
* via the `definition` "payload-locked-documents".
|
* via the `definition` "payload-locked-documents".
|
||||||
*/
|
*/
|
||||||
export interface PayloadLockedDocument {
|
export interface PayloadLockedDocument {
|
||||||
id: string;
|
id: number;
|
||||||
document?:
|
document?:
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'posts';
|
relationTo: 'posts';
|
||||||
value: string | Post;
|
value: number | Post;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'media';
|
relationTo: 'media';
|
||||||
value: string | Media;
|
value: number | Media;
|
||||||
} | null)
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
@@ -236,10 +236,10 @@ export interface PayloadLockedDocument {
|
|||||||
* via the `definition` "payload-preferences".
|
* via the `definition` "payload-preferences".
|
||||||
*/
|
*/
|
||||||
export interface PayloadPreference {
|
export interface PayloadPreference {
|
||||||
id: string;
|
id: number;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: number | User;
|
||||||
};
|
};
|
||||||
key?: string | null;
|
key?: string | null;
|
||||||
value?:
|
value?:
|
||||||
@@ -259,7 +259,7 @@ export interface PayloadPreference {
|
|||||||
* via the `definition` "payload-migrations".
|
* via the `definition` "payload-migrations".
|
||||||
*/
|
*/
|
||||||
export interface PayloadMigration {
|
export interface PayloadMigration {
|
||||||
id: string;
|
id: number;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
batch?: number | null;
|
batch?: number | null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
@@ -378,7 +378,7 @@ export interface PayloadMigrationsSelect<T extends boolean = true> {
|
|||||||
* via the `definition` "menu".
|
* via the `definition` "menu".
|
||||||
*/
|
*/
|
||||||
export interface Menu {
|
export interface Menu {
|
||||||
id: string;
|
id: number;
|
||||||
globalText?: string | null;
|
globalText?: string | null;
|
||||||
updatedAt?: string | null;
|
updatedAt?: string | null;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
|
|||||||
26
test/select/collections/ForceSelect/index.tsx
Normal file
26
test/select/collections/ForceSelect/index.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import type { CollectionConfig } from 'payload'
|
||||||
|
|
||||||
|
export const ForceSelect: CollectionConfig<'force-select'> = {
|
||||||
|
slug: 'force-select',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'forceSelected',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'array',
|
||||||
|
type: 'array',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'forceSelected',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
forceSelect: { array: { forceSelected: true }, forceSelected: true },
|
||||||
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
|
import type { GlobalConfig } from 'payload'
|
||||||
|
|
||||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||||
import { fileURLToPath } from 'node:url'
|
import { fileURLToPath } from 'node:url'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
|
import type { Post } from './payload-types.js'
|
||||||
|
|
||||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
|
||||||
import { devUser } from '../credentials.js'
|
import { devUser } from '../credentials.js'
|
||||||
import { CustomID } from './collections/CustomID/index.js'
|
import { CustomID } from './collections/CustomID/index.js'
|
||||||
import { DeepPostsCollection } from './collections/DeepPosts/index.js'
|
import { DeepPostsCollection } from './collections/DeepPosts/index.js'
|
||||||
|
import { ForceSelect } from './collections/ForceSelect/index.js'
|
||||||
import { LocalizedPostsCollection } from './collections/LocalizedPosts/index.js'
|
import { LocalizedPostsCollection } from './collections/LocalizedPosts/index.js'
|
||||||
import { Pages } from './collections/Pages/index.js'
|
import { Pages } from './collections/Pages/index.js'
|
||||||
import { Points } from './collections/Points/index.js'
|
import { Points } from './collections/Points/index.js'
|
||||||
@@ -24,6 +29,7 @@ export default buildConfigWithDefaults({
|
|||||||
DeepPostsCollection,
|
DeepPostsCollection,
|
||||||
Pages,
|
Pages,
|
||||||
Points,
|
Points,
|
||||||
|
ForceSelect,
|
||||||
{
|
{
|
||||||
slug: 'upload',
|
slug: 'upload',
|
||||||
fields: [],
|
fields: [],
|
||||||
@@ -51,6 +57,30 @@ export default buildConfigWithDefaults({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
slug: 'force-select-global',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'forceSelected',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'array',
|
||||||
|
type: 'array',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'forceSelected',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
forceSelect: { array: { forceSelected: true }, forceSelected: true },
|
||||||
|
} satisfies GlobalConfig<'force-select-global'>,
|
||||||
],
|
],
|
||||||
admin: {
|
admin: {
|
||||||
importMap: {
|
importMap: {
|
||||||
|
|||||||
@@ -2336,6 +2336,53 @@ describe('Select', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should force collection select fields with forceSelect', async () => {
|
||||||
|
const { id, text, array, forceSelected } = await payload.create({
|
||||||
|
collection: 'force-select',
|
||||||
|
data: {
|
||||||
|
array: [{ forceSelected: 'text' }],
|
||||||
|
text: 'some-text',
|
||||||
|
forceSelected: 'force-selected',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await payload.findByID({
|
||||||
|
collection: 'force-select',
|
||||||
|
id,
|
||||||
|
select: { text: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response).toStrictEqual({
|
||||||
|
id,
|
||||||
|
forceSelected,
|
||||||
|
text,
|
||||||
|
array,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should force global select fields with forceSelect', async () => {
|
||||||
|
const { forceSelected, id, array, text } = await payload.updateGlobal({
|
||||||
|
slug: 'force-select-global',
|
||||||
|
data: {
|
||||||
|
array: [{ forceSelected: 'text' }],
|
||||||
|
text: 'some-text',
|
||||||
|
forceSelected: 'force-selected',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await payload.findGlobal({
|
||||||
|
slug: 'force-select-global',
|
||||||
|
select: { text: true },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response).toStrictEqual({
|
||||||
|
id,
|
||||||
|
forceSelected,
|
||||||
|
text,
|
||||||
|
array,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function createPost() {
|
async function createPost() {
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ export interface Config {
|
|||||||
'deep-posts': DeepPost;
|
'deep-posts': DeepPost;
|
||||||
pages: Page;
|
pages: Page;
|
||||||
points: Point;
|
points: Point;
|
||||||
|
'force-select': ForceSelect;
|
||||||
upload: Upload;
|
upload: Upload;
|
||||||
rels: Rel;
|
rels: Rel;
|
||||||
'custom-ids': CustomId;
|
'custom-ids': CustomId;
|
||||||
@@ -88,6 +89,7 @@ export interface Config {
|
|||||||
'deep-posts': DeepPostsSelect<false> | DeepPostsSelect<true>;
|
'deep-posts': DeepPostsSelect<false> | DeepPostsSelect<true>;
|
||||||
pages: PagesSelect<false> | PagesSelect<true>;
|
pages: PagesSelect<false> | PagesSelect<true>;
|
||||||
points: PointsSelect<false> | PointsSelect<true>;
|
points: PointsSelect<false> | PointsSelect<true>;
|
||||||
|
'force-select': ForceSelectSelect<false> | ForceSelectSelect<true>;
|
||||||
upload: UploadSelect<false> | UploadSelect<true>;
|
upload: UploadSelect<false> | UploadSelect<true>;
|
||||||
rels: RelsSelect<false> | RelsSelect<true>;
|
rels: RelsSelect<false> | RelsSelect<true>;
|
||||||
'custom-ids': CustomIdsSelect<false> | CustomIdsSelect<true>;
|
'custom-ids': CustomIdsSelect<false> | CustomIdsSelect<true>;
|
||||||
@@ -101,9 +103,11 @@ export interface Config {
|
|||||||
};
|
};
|
||||||
globals: {
|
globals: {
|
||||||
'global-post': GlobalPost;
|
'global-post': GlobalPost;
|
||||||
|
'force-select-global': ForceSelectGlobal;
|
||||||
};
|
};
|
||||||
globalsSelect: {
|
globalsSelect: {
|
||||||
'global-post': GlobalPostSelect<false> | GlobalPostSelect<true>;
|
'global-post': GlobalPostSelect<false> | GlobalPostSelect<true>;
|
||||||
|
'force-select-global': ForceSelectGlobalSelect<false> | ForceSelectGlobalSelect<true>;
|
||||||
};
|
};
|
||||||
locale: 'en' | 'de';
|
locale: 'en' | 'de';
|
||||||
user: User & {
|
user: User & {
|
||||||
@@ -445,6 +449,23 @@ export interface Point {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "force-select".
|
||||||
|
*/
|
||||||
|
export interface ForceSelect {
|
||||||
|
id: string;
|
||||||
|
text?: string | null;
|
||||||
|
forceSelected?: string | null;
|
||||||
|
array?:
|
||||||
|
| {
|
||||||
|
forceSelected?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "custom-ids".
|
* via the `definition` "custom-ids".
|
||||||
@@ -503,6 +524,10 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'points';
|
relationTo: 'points';
|
||||||
value: string | Point;
|
value: string | Point;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'force-select';
|
||||||
|
value: string | ForceSelect;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'upload';
|
relationTo: 'upload';
|
||||||
value: string | Upload;
|
value: string | Upload;
|
||||||
@@ -835,6 +860,22 @@ export interface PointsSelect<T extends boolean = true> {
|
|||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "force-select_select".
|
||||||
|
*/
|
||||||
|
export interface ForceSelectSelect<T extends boolean = true> {
|
||||||
|
text?: T;
|
||||||
|
forceSelected?: T;
|
||||||
|
array?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
forceSelected?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "upload_select".
|
* via the `definition` "upload_select".
|
||||||
@@ -928,6 +969,23 @@ export interface GlobalPost {
|
|||||||
updatedAt?: string | null;
|
updatedAt?: string | null;
|
||||||
createdAt?: string | null;
|
createdAt?: string | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "force-select-global".
|
||||||
|
*/
|
||||||
|
export interface ForceSelectGlobal {
|
||||||
|
id: string;
|
||||||
|
text?: string | null;
|
||||||
|
forceSelected?: string | null;
|
||||||
|
array?:
|
||||||
|
| {
|
||||||
|
forceSelected?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt?: string | null;
|
||||||
|
createdAt?: string | null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "global-post_select".
|
* via the `definition` "global-post_select".
|
||||||
@@ -939,6 +997,23 @@ export interface GlobalPostSelect<T extends boolean = true> {
|
|||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
globalType?: T;
|
globalType?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "force-select-global_select".
|
||||||
|
*/
|
||||||
|
export interface ForceSelectGlobalSelect<T extends boolean = true> {
|
||||||
|
text?: T;
|
||||||
|
forceSelected?: T;
|
||||||
|
array?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
forceSelected?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
globalType?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "auth".
|
* via the `definition` "auth".
|
||||||
|
|||||||
Reference in New Issue
Block a user