feat: select fields (#8550)
Adds `select` which is used to specify the field projection for local and rest API calls. This is available as an optimization to reduce the payload's of requests and make the database queries more efficient. Includes: - [x] generate types for the `select` property - [x] infer the return type by `select` with 2 modes - include (`field: true`) and exclude (`field: false`) - [x] lots of integration tests, including deep fields / localization etc - [x] implement the property in db adapters - [x] implement the property in the local api for most operations - [x] implement the property in the rest api - [x] docs --------- Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
@@ -78,11 +78,12 @@ Both options function in exactly the same way outside of one having HMR support
|
||||
You can specify more options within the Local API vs. REST or GraphQL due to the server-only context that they are executed in.
|
||||
|
||||
| Local Option | Description |
|
||||
|-----------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `collection` | Required for Collection operations. Specifies the Collection slug to operate against. |
|
||||
| `data` | The data to use within the operation. Required for `create`, `update`. |
|
||||
| `depth` | [Control auto-population](../queries/depth) of nested relationship and upload fields. |
|
||||
| `locale` | Specify [locale](/docs/configuration/localization) for any returned documents. |
|
||||
| `select` | Specify [select](../queries/select) to control which fields to include to the result. |
|
||||
| `fallbackLocale` | Specify a [fallback locale](/docs/configuration/localization) to use for any returned documents. |
|
||||
| `overrideAccess` | Skip access control. By default, this property is set to true within all Local API operations. |
|
||||
| `overrideLock` | By default, document locks are ignored (`true`). Set to `false` to enforce locks and prevent operations when a document is locked by another user. [More details](../admin/locked-documents). |
|
||||
|
||||
99
docs/queries/select.mdx
Normal file
99
docs/queries/select.mdx
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Select
|
||||
label: Select
|
||||
order: 30
|
||||
desc: Payload select determines which fields are selected to the result.
|
||||
keywords: query, documents, pagination, documentation, Content Management System, cms, headless, javascript, node, react, nextjs
|
||||
---
|
||||
|
||||
You may not need the full data from your Local API / REST queries, but only some specific fields. The select fields API can help you to optimize those cases.
|
||||
|
||||
## Local API
|
||||
|
||||
To specify select in the [Local API](../local-api/overview), you can use the `select` option in your query:
|
||||
|
||||
```ts
|
||||
// Include mode
|
||||
const getPosts = async () => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
select: {
|
||||
text: true,
|
||||
// select a specific field from group
|
||||
group: {
|
||||
number: true
|
||||
},
|
||||
// select all fields from array
|
||||
array: true,
|
||||
}, // highlight-line
|
||||
})
|
||||
|
||||
return posts
|
||||
}
|
||||
|
||||
// Exclude mode
|
||||
const getPosts = async () => {
|
||||
const posts = await payload.find({
|
||||
collection: 'posts',
|
||||
// Select everything except for array and group.number
|
||||
select: {
|
||||
array: false,
|
||||
group: {
|
||||
number: false
|
||||
}
|
||||
}, // highlight-line
|
||||
})
|
||||
|
||||
return posts
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<Banner type="warning">
|
||||
<strong>Important:</strong>
|
||||
To perform querying with `select` efficiently, it works on the database level. Because of that, your `beforeRead` and `afterRead` hooks may not receive the full `doc`.
|
||||
</Banner>
|
||||
|
||||
|
||||
## REST API
|
||||
|
||||
To specify select in the [REST API](../rest-api/overview), you can use the `select` parameter in your query:
|
||||
|
||||
```ts
|
||||
fetch('https://localhost:3000/api/posts?select[color]=true&select[group][number]=true') // highlight-line
|
||||
.then((res) => res.json())
|
||||
.then((data) => console.log(data))
|
||||
```
|
||||
|
||||
To understand the syntax, you need to understand that complex URL search strings are parsed into a JSON object. This one isn't too bad, but more complex queries get unavoidably more difficult to write.
|
||||
|
||||
For this reason, we recommend to use the extremely helpful and ubiquitous [`qs`](https://www.npmjs.com/package/qs) package to parse your JSON / object-formatted queries into query strings:
|
||||
|
||||
```ts
|
||||
import { stringify } from 'qs-esm'
|
||||
|
||||
const select = {
|
||||
text: true,
|
||||
group: {
|
||||
number: true
|
||||
}
|
||||
// This query could be much more complex
|
||||
// and QS would handle it beautifully
|
||||
}
|
||||
|
||||
const getPosts = async () => {
|
||||
const stringifiedQuery = stringify(
|
||||
{
|
||||
select, // ensure that `qs` adds the `select` property, too!
|
||||
},
|
||||
{ addQueryPrefix: true },
|
||||
)
|
||||
|
||||
const response = await fetch(`http://localhost:3000/api/posts${stringifiedQuery}`)
|
||||
// Continue to handle the response below...
|
||||
}
|
||||
|
||||
<Banner type="info">
|
||||
<strong>Reminder:</strong>
|
||||
This is the same for [Globals](../configuration/globals) using the `/api/globals` endpoint.
|
||||
</Banner>
|
||||
@@ -18,6 +18,7 @@ All Payload API routes are mounted and prefixed to your config's `routes.api` UR
|
||||
- [depth](../queries/depth) - automatically populates relationships and uploads
|
||||
- [locale](/docs/configuration/localization#retrieving-localized-docs) - retrieves document(s) in a specific locale
|
||||
- [fallback-locale](/docs/configuration/localization#retrieving-localized-docs) - specifies a fallback locale if no locale value exists
|
||||
- [select](../queries/select) - speicifes which fields to include to the result
|
||||
|
||||
## Collections
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ import type { DeleteOne, Document, PayloadRequest } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: MongooseAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
{ collection, req = {} as PayloadRequest, select, where },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const options = await withSession(this, req)
|
||||
@@ -17,7 +18,14 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
where,
|
||||
})
|
||||
|
||||
const doc = await Model.findOneAndDelete(query, options).lean()
|
||||
const doc = await Model.findOneAndDelete(query, {
|
||||
...options,
|
||||
projection: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
select,
|
||||
}),
|
||||
}).lean()
|
||||
|
||||
let result: Document = JSON.parse(JSON.stringify(doc))
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
@@ -21,6 +22,7 @@ export const find: Find = async function find(
|
||||
pagination,
|
||||
projection,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
@@ -67,6 +69,14 @@ export const find: Find = async function find(
|
||||
useEstimatedCount,
|
||||
}
|
||||
|
||||
if (select) {
|
||||
paginationOptions.projection = buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: collectionConfig.fields,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.collation) {
|
||||
const defaultLocale = 'en'
|
||||
paginationOptions.collation = {
|
||||
|
||||
@@ -4,17 +4,23 @@ import { combineQueries } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ slug, locale, req = {} as PayloadRequest, where },
|
||||
{ slug, locale, req = {} as PayloadRequest, select, where },
|
||||
) {
|
||||
const Model = this.globals
|
||||
const options = {
|
||||
...(await withSession(this, req)),
|
||||
lean: true,
|
||||
select: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: this.payload.globals.config.find((each) => each.slug === slug).fields,
|
||||
select,
|
||||
}),
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
|
||||
@@ -6,6 +6,7 @@ import { buildVersionGlobalFields, flattenWhereToOperators } from 'payload'
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
@@ -18,6 +19,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
@@ -69,6 +71,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
options,
|
||||
page,
|
||||
pagination,
|
||||
projection: buildProjectionFromSelect({ adapter: this, fields: versionFields, select }),
|
||||
sort,
|
||||
useEstimatedCount,
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import type { MongooseQueryOptions } from 'mongoose'
|
||||
import type { MongooseQueryOptions, QueryOptions } from 'mongoose'
|
||||
import type { Document, FindOne, PayloadRequest } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
export const findOne: FindOne = async function findOne(
|
||||
this: MongooseAdapter,
|
||||
{ collection, joins, locale, req = {} as PayloadRequest, where },
|
||||
{ collection, joins, locale, req = {} as PayloadRequest, select, where },
|
||||
) {
|
||||
const Model = this.collections[collection]
|
||||
const collectionConfig = this.payload.collections[collection].config
|
||||
@@ -24,6 +25,12 @@ export const findOne: FindOne = async function findOne(
|
||||
where,
|
||||
})
|
||||
|
||||
const projection = buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: collectionConfig.fields,
|
||||
select,
|
||||
})
|
||||
|
||||
const aggregate = await buildJoinAggregation({
|
||||
adapter: this,
|
||||
collection,
|
||||
@@ -31,6 +38,7 @@ export const findOne: FindOne = async function findOne(
|
||||
joins,
|
||||
limit: 1,
|
||||
locale,
|
||||
projection,
|
||||
query,
|
||||
})
|
||||
|
||||
@@ -38,6 +46,7 @@ export const findOne: FindOne = async function findOne(
|
||||
if (aggregate) {
|
||||
;[doc] = await Model.aggregate(aggregate, options)
|
||||
} else {
|
||||
;(options as Record<string, unknown>).projection = projection
|
||||
doc = await Model.findOne(query, {}, options)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { FindVersions, PayloadRequest } from 'payload'
|
||||
|
||||
import { flattenWhereToOperators } from 'payload'
|
||||
import { buildVersionCollectionFields, flattenWhereToOperators } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
@@ -18,6 +19,7 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
@@ -65,6 +67,11 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
options,
|
||||
page,
|
||||
pagination,
|
||||
projection: buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: buildVersionCollectionFields(this.payload.config, collectionConfig),
|
||||
select,
|
||||
}),
|
||||
sort,
|
||||
useEstimatedCount,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { PaginateOptions } from 'mongoose'
|
||||
import type { PayloadRequest, QueryDrafts } from 'payload'
|
||||
|
||||
import { combineQueries, flattenWhereToOperators } from 'payload'
|
||||
import { buildVersionCollectionFields, combineQueries, flattenWhereToOperators } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildSortParam } from './queries/buildSortParam.js'
|
||||
import { buildJoinAggregation } from './utilities/buildJoinAggregation.js'
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
@@ -20,6 +21,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
@@ -54,6 +56,11 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
where: combinedWhere,
|
||||
})
|
||||
|
||||
const projection = buildProjectionFromSelect({
|
||||
adapter: this,
|
||||
fields: buildVersionCollectionFields(this.payload.config, collectionConfig),
|
||||
select,
|
||||
})
|
||||
// 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 || !versionQuery || Object.keys(versionQuery).length === 0
|
||||
@@ -64,6 +71,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
options,
|
||||
page,
|
||||
pagination,
|
||||
projection,
|
||||
sort,
|
||||
useEstimatedCount,
|
||||
}
|
||||
@@ -109,6 +117,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
projection,
|
||||
query: versionQuery,
|
||||
versions: true,
|
||||
})
|
||||
|
||||
@@ -2,19 +2,23 @@ import type { PayloadRequest, UpdateGlobal } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
export const updateGlobal: UpdateGlobal = async function updateGlobal(
|
||||
this: MongooseAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest },
|
||||
{ slug, data, req = {} as PayloadRequest, select },
|
||||
) {
|
||||
const Model = this.globals
|
||||
const fields = this.payload.config.globals.find((global) => global.slug === slug).fields
|
||||
|
||||
const options = {
|
||||
...(await withSession(this, req)),
|
||||
lean: true,
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({ adapter: this, fields, select }),
|
||||
}
|
||||
|
||||
let result
|
||||
@@ -22,7 +26,7 @@ export const updateGlobal: UpdateGlobal = async function updateGlobal(
|
||||
const sanitizedData = sanitizeRelationshipIDs({
|
||||
config: this.payload.config,
|
||||
data,
|
||||
fields: this.payload.config.globals.find((global) => global.slug === slug).fields,
|
||||
fields,
|
||||
})
|
||||
|
||||
result = await Model.findOneAndUpdate({ globalType: slug }, sanitizedData, options)
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
@@ -17,16 +18,23 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
global: globalSlug,
|
||||
locale,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
versionData,
|
||||
where,
|
||||
}: UpdateGlobalVersionArgs<T>,
|
||||
) {
|
||||
const VersionModel = this.versions[globalSlug]
|
||||
const whereToUse = where || { id: { equals: id } }
|
||||
const fields = buildVersionGlobalFields(
|
||||
this.payload.config,
|
||||
this.payload.config.globals.find((global) => global.slug === globalSlug),
|
||||
)
|
||||
|
||||
const options = {
|
||||
...(await withSession(this, req)),
|
||||
lean: true,
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({ adapter: this, fields, select }),
|
||||
}
|
||||
|
||||
const query = await VersionModel.buildQuery({
|
||||
@@ -38,10 +46,7 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
const sanitizedData = sanitizeRelationshipIDs({
|
||||
config: this.payload.config,
|
||||
data: versionData,
|
||||
fields: buildVersionGlobalFields(
|
||||
this.payload.config,
|
||||
this.payload.config.globals.find((global) => global.slug === globalSlug),
|
||||
),
|
||||
fields,
|
||||
})
|
||||
|
||||
const doc = await VersionModel.findOneAndUpdate(query, sanitizedData, options)
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { PayloadRequest, UpdateOne } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { handleError } from './utilities/handleError.js'
|
||||
import { sanitizeInternalFields } from './utilities/sanitizeInternalFields.js'
|
||||
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
|
||||
@@ -17,16 +18,19 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
locale,
|
||||
options: optionsArgs = {},
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
where: whereArg,
|
||||
},
|
||||
) {
|
||||
const where = id ? { id: { equals: id } } : whereArg
|
||||
const Model = this.collections[collection]
|
||||
const fields = this.payload.collections[collection].config.fields
|
||||
const options: QueryOptions = {
|
||||
...optionsArgs,
|
||||
...(await withSession(this, req)),
|
||||
lean: true,
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({ adapter: this, fields, select }),
|
||||
}
|
||||
|
||||
const query = await Model.buildQuery({
|
||||
@@ -40,7 +44,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
const sanitizedData = sanitizeRelationshipIDs({
|
||||
config: this.payload.config,
|
||||
data,
|
||||
fields: this.payload.collections[collection].config.fields,
|
||||
fields,
|
||||
})
|
||||
|
||||
try {
|
||||
|
||||
@@ -2,19 +2,26 @@ import { buildVersionCollectionFields, type PayloadRequest, type UpdateVersion }
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
import { buildProjectionFromSelect } from './utilities/buildProjectionFromSelect.js'
|
||||
import { sanitizeRelationshipIDs } from './utilities/sanitizeRelationshipIDs.js'
|
||||
import { withSession } from './withSession.js'
|
||||
|
||||
export const updateVersion: UpdateVersion = async function updateVersion(
|
||||
this: MongooseAdapter,
|
||||
{ id, collection, locale, req = {} as PayloadRequest, versionData, where },
|
||||
{ id, collection, locale, req = {} as PayloadRequest, select, versionData, where },
|
||||
) {
|
||||
const VersionModel = this.versions[collection]
|
||||
const whereToUse = where || { id: { equals: id } }
|
||||
const fields = buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collection].config,
|
||||
)
|
||||
|
||||
const options = {
|
||||
...(await withSession(this, req)),
|
||||
lean: true,
|
||||
new: true,
|
||||
projection: buildProjectionFromSelect({ adapter: this, fields, select }),
|
||||
}
|
||||
|
||||
const query = await VersionModel.buildQuery({
|
||||
@@ -26,10 +33,7 @@ export const updateVersion: UpdateVersion = async function updateVersion(
|
||||
const sanitizedData = sanitizeRelationshipIDs({
|
||||
config: this.payload.config,
|
||||
data: versionData,
|
||||
fields: buildVersionCollectionFields(
|
||||
this.payload.config,
|
||||
this.payload.collections[collection].config,
|
||||
),
|
||||
fields,
|
||||
})
|
||||
|
||||
const doc = await VersionModel.findOneAndUpdate(query, sanitizedData, options)
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { MongooseAdapter } from './index.js'
|
||||
|
||||
export const upsert: Upsert = async function upsert(
|
||||
this: MongooseAdapter,
|
||||
{ collection, data, locale, req = {} as PayloadRequest, where },
|
||||
{ collection, data, locale, req = {} as PayloadRequest, select, where },
|
||||
) {
|
||||
return this.updateOne({ collection, data, locale, options: { upsert: true }, req, where })
|
||||
return this.updateOne({ collection, data, locale, options: { upsert: true }, req, select, where })
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ type BuildJoinAggregationArgs = {
|
||||
// the number of docs to get at the top collection level
|
||||
limit?: number
|
||||
locale: string
|
||||
projection?: Record<string, true>
|
||||
// the where clause for the top collection
|
||||
query?: Where
|
||||
/** whether the query is from drafts */
|
||||
@@ -26,6 +27,7 @@ export const buildJoinAggregation = async ({
|
||||
joins,
|
||||
limit,
|
||||
locale,
|
||||
projection,
|
||||
query,
|
||||
versions,
|
||||
}: BuildJoinAggregationArgs): Promise<PipelineStage[] | undefined> => {
|
||||
@@ -56,6 +58,10 @@ export const buildJoinAggregation = async ({
|
||||
for (const join of joinConfig[slug]) {
|
||||
const joinModel = adapter.collections[join.field.collection]
|
||||
|
||||
if (projection && !projection[join.schemaPath]) {
|
||||
continue
|
||||
}
|
||||
|
||||
const {
|
||||
limit: limitJoin = join.field.defaultLimit ?? 10,
|
||||
sort: sortJoin = join.field.defaultSort || collectionConfig.defaultSort,
|
||||
@@ -174,5 +180,9 @@ export const buildJoinAggregation = async ({
|
||||
}
|
||||
}
|
||||
|
||||
if (projection) {
|
||||
aggregate.push({ $project: projection })
|
||||
}
|
||||
|
||||
return aggregate
|
||||
}
|
||||
|
||||
234
packages/db-mongodb/src/utilities/buildProjectionFromSelect.ts
Normal file
234
packages/db-mongodb/src/utilities/buildProjectionFromSelect.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
import {
|
||||
deepCopyObjectSimple,
|
||||
type Field,
|
||||
type FieldAffectingData,
|
||||
type SelectMode,
|
||||
type SelectType,
|
||||
type TabAsField,
|
||||
} from 'payload'
|
||||
import { fieldAffectsData, getSelectMode } from 'payload/shared'
|
||||
|
||||
import type { MongooseAdapter } from '../index.js'
|
||||
|
||||
const addFieldToProjection = ({
|
||||
adapter,
|
||||
databaseSchemaPath,
|
||||
field,
|
||||
projection,
|
||||
withinLocalizedField,
|
||||
}: {
|
||||
adapter: MongooseAdapter
|
||||
databaseSchemaPath: string
|
||||
field: FieldAffectingData
|
||||
projection: Record<string, true>
|
||||
withinLocalizedField: boolean
|
||||
}) => {
|
||||
const { config } = adapter.payload
|
||||
|
||||
if (withinLocalizedField && config.localization) {
|
||||
for (const locale of config.localization.localeCodes) {
|
||||
const localeDatabaseSchemaPath = databaseSchemaPath.replace('<locale>', locale)
|
||||
projection[`${localeDatabaseSchemaPath}${field.name}`] = true
|
||||
}
|
||||
} else {
|
||||
projection[`${databaseSchemaPath}${field.name}`] = true
|
||||
}
|
||||
}
|
||||
|
||||
const traverseFields = ({
|
||||
adapter,
|
||||
databaseSchemaPath = '',
|
||||
fields,
|
||||
projection,
|
||||
select,
|
||||
selectAllOnCurrentLevel = false,
|
||||
selectMode,
|
||||
withinLocalizedField = false,
|
||||
}: {
|
||||
adapter: MongooseAdapter
|
||||
databaseSchemaPath?: string
|
||||
fields: (Field | TabAsField)[]
|
||||
projection: Record<string, true>
|
||||
select: SelectType
|
||||
selectAllOnCurrentLevel?: boolean
|
||||
selectMode: SelectMode
|
||||
withinLocalizedField?: boolean
|
||||
}) => {
|
||||
for (const field of fields) {
|
||||
if (fieldAffectsData(field)) {
|
||||
if (selectMode === 'include') {
|
||||
if (select[field.name] === true || selectAllOnCurrentLevel) {
|
||||
addFieldToProjection({
|
||||
adapter,
|
||||
databaseSchemaPath,
|
||||
field,
|
||||
projection,
|
||||
withinLocalizedField,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if (!select[field.name]) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (selectMode === 'exclude') {
|
||||
if (typeof select[field.name] === 'undefined') {
|
||||
addFieldToProjection({
|
||||
adapter,
|
||||
databaseSchemaPath,
|
||||
field,
|
||||
projection,
|
||||
withinLocalizedField,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if (select[field.name] === false) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let fieldDatabaseSchemaPath = databaseSchemaPath
|
||||
let fieldWithinLocalizedField = withinLocalizedField
|
||||
|
||||
if (fieldAffectsData(field)) {
|
||||
fieldDatabaseSchemaPath = `${databaseSchemaPath}${field.name}.`
|
||||
|
||||
if (field.localized) {
|
||||
fieldDatabaseSchemaPath = `${fieldDatabaseSchemaPath}<locale>.`
|
||||
fieldWithinLocalizedField = true
|
||||
}
|
||||
}
|
||||
|
||||
switch (field.type) {
|
||||
case 'collapsible':
|
||||
case 'row':
|
||||
traverseFields({
|
||||
adapter,
|
||||
databaseSchemaPath,
|
||||
fields: field.fields,
|
||||
projection,
|
||||
select,
|
||||
selectMode,
|
||||
withinLocalizedField,
|
||||
})
|
||||
break
|
||||
|
||||
case 'tabs':
|
||||
traverseFields({
|
||||
adapter,
|
||||
databaseSchemaPath,
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
projection,
|
||||
select,
|
||||
selectMode,
|
||||
withinLocalizedField,
|
||||
})
|
||||
break
|
||||
|
||||
case 'group':
|
||||
case 'tab':
|
||||
case 'array':
|
||||
if (field.type === 'array' && selectMode === 'include') {
|
||||
select[field.name]['id'] = true
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
adapter,
|
||||
databaseSchemaPath: fieldDatabaseSchemaPath,
|
||||
fields: field.fields,
|
||||
projection,
|
||||
select: select[field.name] as SelectType,
|
||||
selectMode,
|
||||
withinLocalizedField: fieldWithinLocalizedField,
|
||||
})
|
||||
|
||||
break
|
||||
|
||||
case 'blocks': {
|
||||
const blocksSelect = select[field.name] as SelectType
|
||||
|
||||
for (const block of field.blocks) {
|
||||
if (
|
||||
(selectMode === 'include' && blocksSelect[block.slug] === true) ||
|
||||
(selectMode === 'exclude' && typeof blocksSelect[block.slug] === 'undefined')
|
||||
) {
|
||||
traverseFields({
|
||||
adapter,
|
||||
databaseSchemaPath: fieldDatabaseSchemaPath,
|
||||
fields: block.fields,
|
||||
projection,
|
||||
select: {},
|
||||
selectAllOnCurrentLevel: true,
|
||||
selectMode: 'include',
|
||||
withinLocalizedField: fieldWithinLocalizedField,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
let blockSelectMode = selectMode
|
||||
|
||||
if (selectMode === 'exclude' && blocksSelect[block.slug] === false) {
|
||||
blockSelectMode = 'include'
|
||||
}
|
||||
|
||||
if (typeof blocksSelect[block.slug] !== 'object') {
|
||||
blocksSelect[block.slug] = {}
|
||||
}
|
||||
|
||||
if (blockSelectMode === 'include') {
|
||||
blocksSelect[block.slug]['id'] = true
|
||||
blocksSelect[block.slug]['blockType'] = true
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
adapter,
|
||||
databaseSchemaPath: fieldDatabaseSchemaPath,
|
||||
fields: block.fields,
|
||||
projection,
|
||||
select: blocksSelect[block.slug] as SelectType,
|
||||
selectMode: blockSelectMode,
|
||||
withinLocalizedField: fieldWithinLocalizedField,
|
||||
})
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const buildProjectionFromSelect = ({
|
||||
adapter,
|
||||
fields,
|
||||
select,
|
||||
}: {
|
||||
adapter: MongooseAdapter
|
||||
fields: Field[]
|
||||
select?: SelectType
|
||||
}): Record<string, true> | undefined => {
|
||||
if (!select) {
|
||||
return
|
||||
}
|
||||
|
||||
const projection: Record<string, true> = {
|
||||
_id: true,
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
adapter,
|
||||
fields,
|
||||
projection,
|
||||
// Clone to safely mutate it later
|
||||
select: deepCopyObjectSimple(select),
|
||||
selectMode: getSelectMode(select),
|
||||
})
|
||||
|
||||
return projection
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export const create: Create = async function create(
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, data, req },
|
||||
{ collection: collectionSlug, data, req, select },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
@@ -22,6 +22,7 @@ export const create: Create = async function create(
|
||||
fields: collection.fields,
|
||||
operation: 'create',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
globalSlug,
|
||||
publishedLocale,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
snapshot,
|
||||
updatedAt,
|
||||
versionData,
|
||||
@@ -41,6 +42,7 @@ export async function createGlobalVersion<T extends TypeWithID>(
|
||||
fields: buildVersionGlobalFields(this.payload.config, global),
|
||||
operation: 'create',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
parent,
|
||||
publishedLocale,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
snapshot,
|
||||
updatedAt,
|
||||
versionData,
|
||||
@@ -51,6 +52,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
fields: buildVersionCollectionFields(this.payload.config, collection),
|
||||
operation: 'create',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -12,7 +12,13 @@ import { transform } from './transform/read/index.js'
|
||||
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: DrizzleAdapter,
|
||||
{ collection: collectionSlug, joins: joinQuery, req = {} as PayloadRequest, where: whereArg },
|
||||
{
|
||||
collection: collectionSlug,
|
||||
joins: joinQuery,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
where: whereArg,
|
||||
},
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
@@ -49,6 +55,7 @@ export const deleteOne: DeleteOne = async function deleteOne(
|
||||
depth: 0,
|
||||
fields: collection.fields,
|
||||
joinQuery,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export const find: Find = async function find(
|
||||
page = 1,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
@@ -34,6 +35,7 @@ export const find: Find = async function find(
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
sort,
|
||||
tableName,
|
||||
where,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import type { DBQueryConfig } from 'drizzle-orm'
|
||||
import type { Field, JoinQuery } from 'payload'
|
||||
import type { Field, JoinQuery, SelectType } from 'payload'
|
||||
|
||||
import { getSelectMode } from 'payload/shared'
|
||||
|
||||
import type { BuildQueryJoinAliases, DrizzleAdapter } from '../types.js'
|
||||
|
||||
@@ -15,6 +17,7 @@ type BuildFindQueryArgs = {
|
||||
*/
|
||||
joins?: BuildQueryJoinAliases
|
||||
locale?: string
|
||||
select?: SelectType
|
||||
tableName: string
|
||||
versions?: boolean
|
||||
}
|
||||
@@ -34,6 +37,7 @@ export const buildFindManyArgs = ({
|
||||
joinQuery,
|
||||
joins = [],
|
||||
locale,
|
||||
select,
|
||||
tableName,
|
||||
versions,
|
||||
}: BuildFindQueryArgs): Record<string, unknown> => {
|
||||
@@ -42,8 +46,16 @@ export const buildFindManyArgs = ({
|
||||
with: {},
|
||||
}
|
||||
|
||||
if (select) {
|
||||
result.columns = {
|
||||
id: true,
|
||||
}
|
||||
}
|
||||
|
||||
const _locales: Result = {
|
||||
columns: {
|
||||
columns: select
|
||||
? { _locale: true }
|
||||
: {
|
||||
id: false,
|
||||
_parentID: false,
|
||||
},
|
||||
@@ -51,38 +63,12 @@ export const buildFindManyArgs = ({
|
||||
with: {},
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}_texts`]) {
|
||||
result.with._texts = {
|
||||
columns: {
|
||||
id: false,
|
||||
parent: false,
|
||||
},
|
||||
orderBy: ({ order }, { asc: ASC }) => [ASC(order)],
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}_numbers`]) {
|
||||
result.with._numbers = {
|
||||
columns: {
|
||||
id: false,
|
||||
parent: false,
|
||||
},
|
||||
orderBy: ({ order }, { asc: ASC }) => [ASC(order)],
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}${adapter.relationshipsSuffix}`]) {
|
||||
result.with._rels = {
|
||||
columns: {
|
||||
id: false,
|
||||
parent: false,
|
||||
},
|
||||
orderBy: ({ order }, { asc: ASC }) => [ASC(order)],
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}${adapter.localesSuffix}`]) {
|
||||
result.with._locales = _locales
|
||||
const withTabledFields = select
|
||||
? {}
|
||||
: {
|
||||
numbers: true,
|
||||
rels: true,
|
||||
texts: true,
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
@@ -96,11 +82,51 @@ export const buildFindManyArgs = ({
|
||||
joins,
|
||||
locale,
|
||||
path: '',
|
||||
select,
|
||||
selectMode: select ? getSelectMode(select) : undefined,
|
||||
tablePath: '',
|
||||
topLevelArgs: result,
|
||||
topLevelTableName: tableName,
|
||||
versions,
|
||||
withTabledFields,
|
||||
})
|
||||
|
||||
if (adapter.tables[`${tableName}_texts`] && withTabledFields.texts) {
|
||||
result.with._texts = {
|
||||
columns: {
|
||||
id: false,
|
||||
parent: false,
|
||||
},
|
||||
orderBy: ({ order }, { asc: ASC }) => [ASC(order)],
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}_numbers`] && withTabledFields.numbers) {
|
||||
result.with._numbers = {
|
||||
columns: {
|
||||
id: false,
|
||||
parent: false,
|
||||
},
|
||||
orderBy: ({ order }, { asc: ASC }) => [ASC(order)],
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}${adapter.relationshipsSuffix}`] && withTabledFields.rels) {
|
||||
result.with._rels = {
|
||||
columns: {
|
||||
id: false,
|
||||
parent: false,
|
||||
},
|
||||
orderBy: ({ order }, { asc: ASC }) => [ASC(order)],
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
adapter.tables[`${tableName}${adapter.localesSuffix}`] &&
|
||||
(!select || Object.keys(_locales.columns).length > 1)
|
||||
) {
|
||||
result.with._locales = _locales
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ export const findMany = async function find({
|
||||
page = 1,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
skip,
|
||||
sort,
|
||||
tableName,
|
||||
@@ -72,6 +73,7 @@ export const findMany = async function find({
|
||||
fields,
|
||||
joinQuery,
|
||||
joins,
|
||||
select,
|
||||
tableName,
|
||||
versions,
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { LibSQLDatabase } from 'drizzle-orm/libsql'
|
||||
import type { Field, JoinQuery } from 'payload'
|
||||
import type { Field, JoinQuery, SelectMode, SelectType } from 'payload'
|
||||
|
||||
import { and, eq, sql } from 'drizzle-orm'
|
||||
import { fieldAffectsData, fieldIsVirtual, tabHasName } from 'payload/shared'
|
||||
@@ -22,10 +22,19 @@ type TraverseFieldArgs = {
|
||||
joins?: BuildQueryJoinAliases
|
||||
locale?: string
|
||||
path: string
|
||||
select?: SelectType
|
||||
selectAllOnCurrentLevel?: boolean
|
||||
selectMode?: SelectMode
|
||||
tablePath: string
|
||||
topLevelArgs: Record<string, unknown>
|
||||
topLevelTableName: string
|
||||
versions?: boolean
|
||||
withinLocalizedField?: boolean
|
||||
withTabledFields: {
|
||||
numbers?: boolean
|
||||
rels?: boolean
|
||||
texts?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const traverseFields = ({
|
||||
@@ -39,10 +48,15 @@ export const traverseFields = ({
|
||||
joins,
|
||||
locale,
|
||||
path,
|
||||
select,
|
||||
selectAllOnCurrentLevel = false,
|
||||
selectMode,
|
||||
tablePath,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
versions,
|
||||
withinLocalizedField = false,
|
||||
withTabledFields,
|
||||
}: TraverseFieldArgs) => {
|
||||
fields.forEach((field) => {
|
||||
if (fieldIsVirtual(field)) {
|
||||
@@ -74,9 +88,12 @@ export const traverseFields = ({
|
||||
joinQuery,
|
||||
joins,
|
||||
path,
|
||||
select,
|
||||
selectMode,
|
||||
tablePath,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
withTabledFields,
|
||||
})
|
||||
|
||||
return
|
||||
@@ -87,6 +104,20 @@ export const traverseFields = ({
|
||||
const tabPath = tabHasName(tab) ? `${path}${tab.name}_` : path
|
||||
const tabTablePath = tabHasName(tab) ? `${tablePath}${toSnakeCase(tab.name)}_` : tablePath
|
||||
|
||||
const tabSelect = tabHasName(tab) ? select?.[tab.name] : select
|
||||
|
||||
if (tabSelect === false) {
|
||||
return
|
||||
}
|
||||
|
||||
let tabSelectAllOnCurrentLevel = selectAllOnCurrentLevel
|
||||
|
||||
if (tabHasName(tab) && select && !tabSelectAllOnCurrentLevel) {
|
||||
tabSelectAllOnCurrentLevel =
|
||||
select[tab.name] === true ||
|
||||
(selectMode === 'exclude' && typeof select[tab.name] === 'undefined')
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
_locales,
|
||||
adapter,
|
||||
@@ -97,10 +128,14 @@ export const traverseFields = ({
|
||||
joinQuery,
|
||||
joins,
|
||||
path: tabPath,
|
||||
select: typeof tabSelect === 'object' ? tabSelect : undefined,
|
||||
selectAllOnCurrentLevel: tabSelectAllOnCurrentLevel,
|
||||
selectMode,
|
||||
tablePath: tabTablePath,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
versions,
|
||||
withTabledFields,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -110,8 +145,25 @@ export const traverseFields = ({
|
||||
if (fieldAffectsData(field)) {
|
||||
switch (field.type) {
|
||||
case 'array': {
|
||||
const arraySelect = selectAllOnCurrentLevel ? true : select?.[field.name]
|
||||
|
||||
if (select) {
|
||||
if (
|
||||
(selectMode === 'include' && typeof arraySelect === 'undefined') ||
|
||||
(selectMode === 'exclude' && arraySelect === false)
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const withArray: Result = {
|
||||
columns: {
|
||||
columns:
|
||||
typeof arraySelect === 'object'
|
||||
? {
|
||||
id: true,
|
||||
_order: true,
|
||||
}
|
||||
: {
|
||||
_parentID: false,
|
||||
},
|
||||
orderBy: ({ _order }, { asc }) => [asc(_order)],
|
||||
@@ -122,17 +174,33 @@ export const traverseFields = ({
|
||||
`${currentTableName}_${tablePath}${toSnakeCase(field.name)}`,
|
||||
)
|
||||
|
||||
if (typeof arraySelect === 'object') {
|
||||
if (adapter.tables[arrayTableName]._locale) {
|
||||
withArray.columns._locale = true
|
||||
}
|
||||
|
||||
if (adapter.tables[arrayTableName]._uuid) {
|
||||
withArray.columns._uuid = true
|
||||
}
|
||||
}
|
||||
|
||||
const arrayTableNameWithLocales = `${arrayTableName}${adapter.localesSuffix}`
|
||||
|
||||
if (adapter.tables[arrayTableNameWithLocales]) {
|
||||
withArray.with._locales = {
|
||||
columns: {
|
||||
columns:
|
||||
typeof arraySelect === 'object'
|
||||
? {
|
||||
_locale: true,
|
||||
}
|
||||
: {
|
||||
id: false,
|
||||
_parentID: false,
|
||||
},
|
||||
with: {},
|
||||
}
|
||||
}
|
||||
|
||||
currentArgs.with[`${path}${field.name}`] = withArray
|
||||
|
||||
traverseFields({
|
||||
@@ -144,16 +212,37 @@ export const traverseFields = ({
|
||||
fields: field.fields,
|
||||
joinQuery,
|
||||
path: '',
|
||||
select: typeof arraySelect === 'object' ? arraySelect : undefined,
|
||||
selectMode,
|
||||
tablePath: '',
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
withinLocalizedField: withinLocalizedField || field.localized,
|
||||
withTabledFields,
|
||||
})
|
||||
|
||||
if (
|
||||
typeof arraySelect === 'object' &&
|
||||
withArray.with._locales &&
|
||||
Object.keys(withArray.with._locales).length === 1
|
||||
) {
|
||||
delete withArray.with._locales
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'select': {
|
||||
if (field.hasMany) {
|
||||
if (select) {
|
||||
if (
|
||||
(selectMode === 'include' && !select[field.name]) ||
|
||||
(selectMode === 'exclude' && select[field.name] === false)
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const withSelect: Result = {
|
||||
columns: {
|
||||
id: false,
|
||||
@@ -169,13 +258,53 @@ export const traverseFields = ({
|
||||
break
|
||||
}
|
||||
|
||||
case 'blocks':
|
||||
case 'blocks': {
|
||||
const blocksSelect = selectAllOnCurrentLevel ? true : select?.[field.name]
|
||||
|
||||
if (select) {
|
||||
if (
|
||||
(selectMode === 'include' && !blocksSelect) ||
|
||||
(selectMode === 'exclude' && blocksSelect === false)
|
||||
) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
field.blocks.forEach((block) => {
|
||||
const blockKey = `_blocks_${block.slug}`
|
||||
|
||||
let blockSelect: boolean | SelectType | undefined
|
||||
|
||||
let blockSelectMode = selectMode
|
||||
|
||||
if (selectMode === 'include' && blocksSelect === true) {
|
||||
blockSelect = true
|
||||
}
|
||||
|
||||
if (typeof blocksSelect === 'object') {
|
||||
if (typeof blocksSelect[block.slug] === 'object') {
|
||||
blockSelect = blocksSelect[block.slug]
|
||||
} else if (
|
||||
(selectMode === 'include' && typeof blocksSelect[block.slug] === 'undefined') ||
|
||||
(selectMode === 'exclude' && blocksSelect[block.slug] === false)
|
||||
) {
|
||||
blockSelect = {}
|
||||
blockSelectMode = 'include'
|
||||
} else if (selectMode === 'include' && blocksSelect[block.slug] === true) {
|
||||
blockSelect = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!topLevelArgs[blockKey]) {
|
||||
const withBlock: Result = {
|
||||
columns: {
|
||||
columns:
|
||||
typeof blockSelect === 'object'
|
||||
? {
|
||||
id: true,
|
||||
_order: true,
|
||||
_path: true,
|
||||
}
|
||||
: {
|
||||
_parentID: false,
|
||||
},
|
||||
orderBy: ({ _order }, { asc }) => [asc(_order)],
|
||||
@@ -186,10 +315,26 @@ export const traverseFields = ({
|
||||
`${topLevelTableName}_blocks_${toSnakeCase(block.slug)}`,
|
||||
)
|
||||
|
||||
if (typeof blockSelect === 'object') {
|
||||
if (adapter.tables[tableName]._locale) {
|
||||
withBlock.columns._locale = true
|
||||
}
|
||||
|
||||
if (adapter.tables[tableName]._uuid) {
|
||||
withBlock.columns._uuid = true
|
||||
}
|
||||
}
|
||||
|
||||
if (adapter.tables[`${tableName}${adapter.localesSuffix}`]) {
|
||||
withBlock.with._locales = {
|
||||
with: {},
|
||||
}
|
||||
|
||||
if (typeof blockSelect === 'object') {
|
||||
withBlock.with._locales.columns = {
|
||||
_locale: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
topLevelArgs.with[blockKey] = withBlock
|
||||
|
||||
@@ -202,16 +347,35 @@ export const traverseFields = ({
|
||||
fields: block.fields,
|
||||
joinQuery,
|
||||
path: '',
|
||||
select: typeof blockSelect === 'object' ? blockSelect : undefined,
|
||||
selectMode: blockSelectMode,
|
||||
tablePath: '',
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
withinLocalizedField: withinLocalizedField || field.localized,
|
||||
withTabledFields,
|
||||
})
|
||||
|
||||
if (
|
||||
typeof blockSelect === 'object' &&
|
||||
withBlock.with._locales &&
|
||||
Object.keys(withBlock.with._locales.columns).length === 1
|
||||
) {
|
||||
delete withBlock.with._locales
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
case 'group': {
|
||||
const groupSelect = select?.[field.name]
|
||||
|
||||
if (groupSelect === false) {
|
||||
break
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
_locales,
|
||||
adapter,
|
||||
@@ -222,10 +386,18 @@ export const traverseFields = ({
|
||||
joinQuery,
|
||||
joins,
|
||||
path: `${path}${field.name}_`,
|
||||
select: typeof groupSelect === 'object' ? groupSelect : undefined,
|
||||
selectAllOnCurrentLevel:
|
||||
selectAllOnCurrentLevel ||
|
||||
groupSelect === true ||
|
||||
(selectMode === 'exclude' && typeof groupSelect === 'undefined'),
|
||||
selectMode,
|
||||
tablePath: `${tablePath}${toSnakeCase(field.name)}_`,
|
||||
topLevelArgs,
|
||||
topLevelTableName,
|
||||
versions,
|
||||
withinLocalizedField: withinLocalizedField || field.localized,
|
||||
withTabledFields,
|
||||
})
|
||||
|
||||
break
|
||||
@@ -237,6 +409,13 @@ export const traverseFields = ({
|
||||
break
|
||||
}
|
||||
|
||||
if (
|
||||
(select && selectMode === 'include' && !select[field.name]) ||
|
||||
(selectMode === 'exclude' && select[field.name] === false)
|
||||
) {
|
||||
break
|
||||
}
|
||||
|
||||
const {
|
||||
limit: limitArg = field.defaultLimit ?? 10,
|
||||
sort = field.defaultSort,
|
||||
@@ -410,6 +589,40 @@ export const traverseFields = ({
|
||||
}
|
||||
|
||||
default: {
|
||||
if (!select && !selectAllOnCurrentLevel) {
|
||||
break
|
||||
}
|
||||
|
||||
if (
|
||||
selectAllOnCurrentLevel ||
|
||||
(selectMode === 'include' && select[field.name] === true) ||
|
||||
(selectMode === 'exclude' && typeof select[field.name] === 'undefined')
|
||||
) {
|
||||
const fieldPath = `${path}${field.name}`
|
||||
|
||||
if ((field.localized || withinLocalizedField) && _locales) {
|
||||
_locales.columns[fieldPath] = true
|
||||
} else if (adapter.tables[currentTableName]?.[fieldPath]) {
|
||||
currentArgs.columns[fieldPath] = true
|
||||
}
|
||||
|
||||
if (
|
||||
!withTabledFields.rels &&
|
||||
field.type === 'relationship' &&
|
||||
(field.hasMany || Array.isArray(field.relationTo))
|
||||
) {
|
||||
withTabledFields.rels = true
|
||||
}
|
||||
|
||||
if (!withTabledFields.numbers && field.type === 'number' && field.hasMany) {
|
||||
withTabledFields.numbers = true
|
||||
}
|
||||
|
||||
if (!withTabledFields.texts && field.type === 'text' && field.hasMany) {
|
||||
withTabledFields.texts = true
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { findMany } from './find/findMany.js'
|
||||
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: DrizzleAdapter,
|
||||
{ slug, locale, req, where },
|
||||
{ slug, locale, req, select, where },
|
||||
) {
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
|
||||
@@ -23,6 +23,7 @@ export const findGlobal: FindGlobal = async function findGlobal(
|
||||
locale,
|
||||
pagination: false,
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -16,6 +16,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
@@ -40,6 +41,7 @@ export const findGlobalVersions: FindGlobalVersions = async function findGlobalV
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
skip,
|
||||
sort,
|
||||
tableName,
|
||||
|
||||
@@ -8,7 +8,7 @@ import { findMany } from './find/findMany.js'
|
||||
|
||||
export async function findOne<T extends TypeWithID>(
|
||||
this: DrizzleAdapter,
|
||||
{ collection, joins, locale, req = {} as PayloadRequest, where }: FindOneArgs,
|
||||
{ collection, joins, locale, req = {} as PayloadRequest, select, where }: FindOneArgs,
|
||||
): Promise<T> {
|
||||
const collectionConfig: SanitizedCollectionConfig = this.payload.collections[collection].config
|
||||
|
||||
@@ -23,6 +23,7 @@ export async function findOne<T extends TypeWithID>(
|
||||
page: 1,
|
||||
pagination: false,
|
||||
req,
|
||||
select,
|
||||
sort: undefined,
|
||||
tableName,
|
||||
where,
|
||||
|
||||
@@ -16,6 +16,7 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
@@ -38,6 +39,7 @@ export const findVersions: FindVersions = async function findVersions(
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
skip,
|
||||
sort,
|
||||
tableName,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { JoinQuery, PayloadRequest, QueryDrafts, SanitizedCollectionConfig } from 'payload'
|
||||
import type { PayloadRequest, QueryDrafts, SanitizedCollectionConfig } from 'payload'
|
||||
|
||||
import { buildVersionCollectionFields, combineQueries } from 'payload'
|
||||
import toSnakeCase from 'to-snake-case'
|
||||
@@ -17,6 +17,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
page = 1,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
sort,
|
||||
where,
|
||||
},
|
||||
@@ -38,6 +39,7 @@ export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
page,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
sort,
|
||||
tableName,
|
||||
versions: true,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export const updateOne: UpdateOne = async function updateOne(
|
||||
this: DrizzleAdapter,
|
||||
{ id, collection: collectionSlug, data, draft, joins: joinQuery, locale, req, where: whereArg },
|
||||
{ id, collection: collectionSlug, data, joins: joinQuery, locale, req, select, where: whereArg },
|
||||
) {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const collection = this.payload.collections[collectionSlug].config
|
||||
@@ -49,6 +49,7 @@ export const updateOne: UpdateOne = async function updateOne(
|
||||
joinQuery,
|
||||
operation: 'update',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { upsertRow } from './upsertRow/index.js'
|
||||
|
||||
export async function updateGlobal<T extends Record<string, unknown>>(
|
||||
this: DrizzleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest }: UpdateGlobalArgs,
|
||||
{ slug, data, req = {} as PayloadRequest, select }: UpdateGlobalArgs,
|
||||
): Promise<T> {
|
||||
const db = this.sessions[await req?.transactionID]?.db || this.drizzle
|
||||
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
|
||||
@@ -23,6 +23,7 @@ export async function updateGlobal<T extends Record<string, unknown>>(
|
||||
db,
|
||||
fields: globalConfig.fields,
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
global,
|
||||
locale,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
versionData,
|
||||
where: whereArg,
|
||||
}: UpdateGlobalVersionArgs<T>,
|
||||
@@ -53,6 +54,7 @@ export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
fields,
|
||||
operation: 'update',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ export async function updateVersion<T extends TypeWithID>(
|
||||
collection,
|
||||
locale,
|
||||
req = {} as PayloadRequest,
|
||||
select,
|
||||
versionData,
|
||||
where: whereArg,
|
||||
}: UpdateVersionArgs<T>,
|
||||
@@ -50,6 +51,7 @@ export async function updateVersion<T extends TypeWithID>(
|
||||
fields,
|
||||
operation: 'update',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -24,6 +24,7 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
|
||||
operation,
|
||||
path = '',
|
||||
req,
|
||||
select,
|
||||
tableName,
|
||||
upsertTarget,
|
||||
where,
|
||||
@@ -415,6 +416,7 @@ export const upsertRow = async <T extends Record<string, unknown> | TypeWithID>(
|
||||
depth: 0,
|
||||
fields,
|
||||
joinQuery,
|
||||
select,
|
||||
tableName,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SQL } from 'drizzle-orm'
|
||||
import type { Field, JoinQuery, PayloadRequest } from 'payload'
|
||||
import type { Field, JoinQuery, PayloadRequest, SelectType } from 'payload'
|
||||
|
||||
import type { DrizzleAdapter, DrizzleTransaction, GenericColumn } from '../types.js'
|
||||
|
||||
@@ -23,6 +23,7 @@ type CreateArgs = {
|
||||
id?: never
|
||||
joinQuery?: never
|
||||
operation: 'create'
|
||||
select?: SelectType
|
||||
upsertTarget?: never
|
||||
where?: never
|
||||
} & BaseArgs
|
||||
@@ -31,6 +32,7 @@ type UpdateArgs = {
|
||||
id?: number | string
|
||||
joinQuery?: JoinQuery
|
||||
operation: 'update'
|
||||
select?: SelectType
|
||||
upsertTarget?: GenericColumn
|
||||
where?: SQL<unknown>
|
||||
} & BaseArgs
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { DataFromGlobalSlug, GlobalSlug, PayloadRequest, SanitizedGlobalConfig } from 'payload'
|
||||
import type {
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
PayloadRequest,
|
||||
SanitizedGlobalConfig,
|
||||
SelectType,
|
||||
} from 'payload'
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import { isolateObjectProperty, updateOperationGlobal } from 'payload'
|
||||
@@ -40,7 +46,7 @@ export function update<TSlug extends GlobalSlug>(
|
||||
req: isolateObjectProperty(context.req, 'transactionID'),
|
||||
}
|
||||
|
||||
const result = await updateOperationGlobal<TSlug>(options)
|
||||
const result = await updateOperationGlobal<TSlug, SelectType>(options)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { isNumber } from 'payload/shared'
|
||||
import type { CollectionRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const create: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
const { searchParams } = req
|
||||
@@ -20,6 +21,7 @@ export const create: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
depth: isNumber(depth) ? depth : undefined,
|
||||
draft,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
return Response.json(
|
||||
|
||||
@@ -8,11 +8,13 @@ import { isNumber } from 'payload/shared'
|
||||
import type { CollectionRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
const { depth, overrideLock, where } = req.query as {
|
||||
const { depth, overrideLock, select, where } = req.query as {
|
||||
depth?: string
|
||||
overrideLock?: string
|
||||
select?: Record<string, unknown>
|
||||
where?: Where
|
||||
}
|
||||
|
||||
@@ -21,6 +23,7 @@ export const deleteDoc: CollectionRouteHandler = async ({ collection, req }) =>
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
overrideLock: Boolean(overrideLock === 'true'),
|
||||
req,
|
||||
select: sanitizeSelect(select),
|
||||
where,
|
||||
})
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const deleteByID: CollectionRouteHandlerWithID = async ({
|
||||
id: incomingID,
|
||||
@@ -28,6 +29,7 @@ export const deleteByID: CollectionRouteHandlerWithID = async ({
|
||||
depth: isNumber(depth) ? depth : undefined,
|
||||
overrideLock: Boolean(overrideLock === 'true'),
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
const headers = headersWithCors({
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const duplicate: CollectionRouteHandlerWithID = async ({
|
||||
id: incomingID,
|
||||
@@ -30,6 +31,7 @@ export const duplicate: CollectionRouteHandlerWithID = async ({
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
draft,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
const message = req.t('general:successfullyDuplicated', {
|
||||
|
||||
@@ -8,14 +8,16 @@ import type { CollectionRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeJoinParams } from '../utilities/sanitizeJoinParams.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const find: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
const { depth, draft, joins, limit, page, sort, where } = req.query as {
|
||||
const { depth, draft, joins, limit, page, select, sort, where } = req.query as {
|
||||
depth?: string
|
||||
draft?: string
|
||||
joins?: JoinQuery
|
||||
limit?: string
|
||||
page?: string
|
||||
select?: Record<string, unknown>
|
||||
sort?: string
|
||||
where?: Where
|
||||
}
|
||||
@@ -28,6 +30,7 @@ export const find: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
limit: isNumber(limit) ? Number(limit) : undefined,
|
||||
page: isNumber(page) ? Number(page) : undefined,
|
||||
req,
|
||||
select: sanitizeSelect(select),
|
||||
sort: typeof sort === 'string' ? sort.split(',') : undefined,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
|
||||
import { sanitizeJoinParams } from '../utilities/sanitizeJoinParams.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const findByID: CollectionRouteHandlerWithID = async ({
|
||||
id: incomingID,
|
||||
@@ -31,6 +32,7 @@ export const findByID: CollectionRouteHandlerWithID = async ({
|
||||
draft: searchParams.get('draft') === 'true',
|
||||
joins: sanitizeJoinParams(req.query.joins as JoinQuery),
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
return Response.json(result, {
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const findVersionByID: CollectionRouteHandlerWithID = async ({
|
||||
id: incomingID,
|
||||
@@ -26,6 +27,7 @@ export const findVersionByID: CollectionRouteHandlerWithID = async ({
|
||||
collection,
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
return Response.json(result, {
|
||||
|
||||
@@ -7,12 +7,14 @@ import { isNumber } from 'payload/shared'
|
||||
import type { CollectionRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const findVersions: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
const { depth, limit, page, sort, where } = req.query as {
|
||||
const { depth, limit, page, select, sort, where } = req.query as {
|
||||
depth?: string
|
||||
limit?: string
|
||||
page?: string
|
||||
select?: Record<string, unknown>
|
||||
sort?: string
|
||||
where?: Where
|
||||
}
|
||||
@@ -23,6 +25,7 @@ export const findVersions: CollectionRouteHandler = async ({ collection, req })
|
||||
limit: isNumber(limit) ? Number(limit) : undefined,
|
||||
page: isNumber(page) ? Number(page) : undefined,
|
||||
req,
|
||||
select: sanitizeSelect(select),
|
||||
sort: typeof sort === 'string' ? sort.split(',') : undefined,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -8,13 +8,15 @@ import { isNumber } from 'payload/shared'
|
||||
import type { CollectionRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const update: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
const { depth, draft, limit, overrideLock, where } = req.query as {
|
||||
const { depth, draft, limit, overrideLock, select, where } = req.query as {
|
||||
depth?: string
|
||||
draft?: string
|
||||
limit?: string
|
||||
overrideLock?: string
|
||||
select?: Record<string, unknown>
|
||||
where?: Where
|
||||
}
|
||||
|
||||
@@ -26,6 +28,7 @@ export const update: CollectionRouteHandler = async ({ collection, req }) => {
|
||||
limit: isNumber(limit) ? Number(limit) : undefined,
|
||||
overrideLock: Boolean(overrideLock === 'true'),
|
||||
req,
|
||||
select: sanitizeSelect(select),
|
||||
where,
|
||||
})
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { CollectionRouteHandlerWithID } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeCollectionID } from '../utilities/sanitizeCollectionID.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const updateByID: CollectionRouteHandlerWithID = async ({
|
||||
id: incomingID,
|
||||
@@ -35,6 +36,7 @@ export const updateByID: CollectionRouteHandlerWithID = async ({
|
||||
overrideLock: Boolean(overrideLock === 'true'),
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
let message = req.t('general:updatedSuccessfully')
|
||||
|
||||
@@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
|
||||
import type { GlobalRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const findOne: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||
const { searchParams } = req
|
||||
@@ -16,6 +17,7 @@ export const findOne: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||
draft: searchParams.get('draft') === 'true',
|
||||
globalConfig,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
return Response.json(result, {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
|
||||
import type { GlobalRouteHandlerWithID } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const findVersionByID: GlobalRouteHandlerWithID = async ({ id, globalConfig, req }) => {
|
||||
const { searchParams } = req
|
||||
@@ -15,6 +16,7 @@ export const findVersionByID: GlobalRouteHandlerWithID = async ({ id, globalConf
|
||||
depth: isNumber(depth) ? Number(depth) : undefined,
|
||||
globalConfig,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
return Response.json(result, {
|
||||
|
||||
@@ -7,12 +7,14 @@ import { isNumber } from 'payload/shared'
|
||||
import type { GlobalRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const findVersions: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||
const { depth, limit, page, sort, where } = req.query as {
|
||||
const { depth, limit, page, select, sort, where } = req.query as {
|
||||
depth?: string
|
||||
limit?: string
|
||||
page?: string
|
||||
select?: Record<string, unknown>
|
||||
sort?: string
|
||||
where?: Where
|
||||
}
|
||||
@@ -23,6 +25,7 @@ export const findVersions: GlobalRouteHandler = async ({ globalConfig, req }) =>
|
||||
limit: isNumber(limit) ? Number(limit) : undefined,
|
||||
page: isNumber(page) ? Number(page) : undefined,
|
||||
req,
|
||||
select: sanitizeSelect(select),
|
||||
sort: typeof sort === 'string' ? sort.split(',') : undefined,
|
||||
where,
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@ import { isNumber } from 'payload/shared'
|
||||
import type { GlobalRouteHandler } from '../types.js'
|
||||
|
||||
import { headersWithCors } from '../../../utilities/headersWithCors.js'
|
||||
import { sanitizeSelect } from '../utilities/sanitizeSelect.js'
|
||||
|
||||
export const update: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||
const { searchParams } = req
|
||||
@@ -22,6 +23,7 @@ export const update: GlobalRouteHandler = async ({ globalConfig, req }) => {
|
||||
globalConfig,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
select: sanitizeSelect(req.query.select),
|
||||
})
|
||||
|
||||
let message = req.t('general:updatedSuccessfully')
|
||||
|
||||
20
packages/next/src/routes/rest/utilities/sanitizeSelect.ts
Normal file
20
packages/next/src/routes/rest/utilities/sanitizeSelect.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { SelectType } from 'payload'
|
||||
|
||||
/**
|
||||
* Sanitizes REST select query to SelectType
|
||||
*/
|
||||
export const sanitizeSelect = (unsanitizedSelect: unknown): SelectType | undefined => {
|
||||
if (unsanitizedSelect && typeof unsanitizedSelect === 'object') {
|
||||
for (const k in unsanitizedSelect) {
|
||||
if (unsanitizedSelect[k] === 'true') {
|
||||
unsanitizedSelect[k] = true
|
||||
} else if (unsanitizedSelect[k] === 'false') {
|
||||
unsanitizedSelect[k] = false
|
||||
} else if (typeof unsanitizedSelect[k] === 'object') {
|
||||
sanitizeSelect(unsanitizedSelect[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return unsanitizedSelect as SelectType
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
RequiredDataFromCollectionSlug,
|
||||
} from '../../collections/config/types.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType } from '../../types/index.js'
|
||||
|
||||
import { Forbidden } from '../../errors/index.js'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||
@@ -66,7 +66,7 @@ export const registerFirstUserOperation = async <TSlug extends CollectionSlug>(
|
||||
// Register first user
|
||||
// /////////////////////////////////////
|
||||
|
||||
const result = await payload.create<TSlug>({
|
||||
const result = await payload.create<TSlug, SelectType>({
|
||||
collection: slug as TSlug,
|
||||
data,
|
||||
overrideAccess: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { SanitizedCollectionConfig } from '../../../collections/config/types.js'
|
||||
import type { JsonObject, Payload } from '../../../index.js'
|
||||
import type { PayloadRequest, Where } from '../../../types/index.js'
|
||||
import type { PayloadRequest, SelectType, Where } from '../../../types/index.js'
|
||||
|
||||
import { ValidationError } from '../../../errors/index.js'
|
||||
import { generatePasswordSaltHash } from './generatePasswordSaltHash.js'
|
||||
@@ -11,6 +11,7 @@ type Args = {
|
||||
password: string
|
||||
payload: Payload
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
}
|
||||
|
||||
export const registerLocalStrategy = async ({
|
||||
@@ -19,6 +20,7 @@ export const registerLocalStrategy = async ({
|
||||
password,
|
||||
payload,
|
||||
req,
|
||||
select,
|
||||
}: Args): Promise<Record<string, unknown>> => {
|
||||
const loginWithUsername = collection?.auth?.loginWithUsername
|
||||
|
||||
@@ -90,5 +92,6 @@ export const registerLocalStrategy = async ({
|
||||
salt,
|
||||
},
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { compile } from 'json-schema-to-typescript'
|
||||
|
||||
import type { SanitizedConfig } from '../config/types.js'
|
||||
|
||||
import { addSelectGenericsToGeneratedTypes } from '../utilities/addSelectGenericsToGeneretedTypes.js'
|
||||
import { configToJSONSchema } from '../utilities/configToJSONSchema.js'
|
||||
import { getLogger } from '../utilities/logger.js'
|
||||
|
||||
@@ -36,6 +37,8 @@ export async function generateTypes(
|
||||
unreachableDefinitions: true,
|
||||
})
|
||||
|
||||
compiled = addSelectGenericsToGeneratedTypes({ compiledGeneratedTypes: compiled })
|
||||
|
||||
if (config.typescript.declare !== false) {
|
||||
if (config.typescript.declare?.ignoreTSError) {
|
||||
compiled += `\n\n${declareWithTSIgnoreError}`
|
||||
|
||||
@@ -37,8 +37,15 @@ import type {
|
||||
JsonObject,
|
||||
TypedAuthOperations,
|
||||
TypedCollection,
|
||||
TypedCollectionSelect,
|
||||
} from '../../index.js'
|
||||
import type { PayloadRequest, RequestContext, Sort } from '../../types/index.js'
|
||||
import type {
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
Sort,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../types/index.js'
|
||||
import type { SanitizedUploadConfig, UploadConfig } from '../../uploads/types.js'
|
||||
import type {
|
||||
IncomingCollectionVersions,
|
||||
@@ -47,6 +54,9 @@ import type {
|
||||
import type { AfterOperationArg, AfterOperationMap } from '../operations/utils.js'
|
||||
|
||||
export type DataFromCollectionSlug<TSlug extends CollectionSlug> = TypedCollection[TSlug]
|
||||
|
||||
export type SelectFromCollectionSlug<TSlug extends CollectionSlug> = TypedCollectionSelect[TSlug]
|
||||
|
||||
export type AuthOperationsFromCollectionSlug<TSlug extends CollectionSlug> =
|
||||
TypedAuthOperations[TSlug]
|
||||
|
||||
@@ -523,8 +533,8 @@ export type Collection = {
|
||||
}
|
||||
}
|
||||
|
||||
export type BulkOperationResult<TSlug extends CollectionSlug> = {
|
||||
docs: DataFromCollectionSlug<TSlug>[]
|
||||
export type BulkOperationResult<TSlug extends CollectionSlug, TSelect extends SelectType> = {
|
||||
docs: TransformCollectionWithSelect<TSlug, TSelect>[]
|
||||
errors: {
|
||||
id: DataFromCollectionSlug<TSlug>['id']
|
||||
message: string
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import crypto from 'crypto'
|
||||
|
||||
import type { CollectionSlug, JsonObject } from '../../index.js'
|
||||
import type { Document, PayloadRequest } from '../../types/index.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../types/index.js'
|
||||
import type {
|
||||
AfterChangeHook,
|
||||
BeforeOperationHook,
|
||||
BeforeValidateHook,
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
RequiredDataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import { ensureUsernameOrEmail } from '../../auth/ensureUsernameOrEmail.js'
|
||||
@@ -40,12 +45,16 @@ export type Arguments<TSlug extends CollectionSlug> = {
|
||||
overrideAccess?: boolean
|
||||
overwriteExistingFiles?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export const createOperation = async <TSlug extends CollectionSlug>(
|
||||
export const createOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -95,6 +104,7 @@ export const createOperation = async <TSlug extends CollectionSlug>(
|
||||
payload: { config, email },
|
||||
},
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -235,12 +245,14 @@ export const createOperation = async <TSlug extends CollectionSlug>(
|
||||
password: data.password as string,
|
||||
payload: req.payload,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
} else {
|
||||
doc = await payload.db.create({
|
||||
collection: collectionConfig.slug,
|
||||
data: resultWithLocales,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -293,6 +305,7 @@ export const createOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -2,8 +2,14 @@ import httpStatus from 'http-status'
|
||||
|
||||
import type { AccessResult } from '../../config/types.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest, Where } from '../../types/index.js'
|
||||
import type { BeforeOperationHook, Collection, DataFromCollectionSlug } from '../config/types.js'
|
||||
import type { PayloadRequest, SelectType, Where } from '../../types/index.js'
|
||||
import type {
|
||||
BeforeOperationHook,
|
||||
BulkOperationResult,
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
import { combineQueries } from '../../database/combineQueries.js'
|
||||
@@ -26,19 +32,17 @@ export type Arguments = {
|
||||
overrideAccess?: boolean
|
||||
overrideLock?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
where: Where
|
||||
}
|
||||
|
||||
export const deleteOperation = async <TSlug extends CollectionSlug>(
|
||||
export const deleteOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments,
|
||||
): Promise<{
|
||||
docs: DataFromCollectionSlug<TSlug>[]
|
||||
errors: {
|
||||
id: DataFromCollectionSlug<TSlug>['id']
|
||||
message: string
|
||||
}[]
|
||||
}> => {
|
||||
): Promise<BulkOperationResult<TSlug, TSelect>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -75,6 +79,7 @@ export const deleteOperation = async <TSlug extends CollectionSlug>(
|
||||
payload,
|
||||
},
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = args
|
||||
@@ -110,6 +115,7 @@ export const deleteOperation = async <TSlug extends CollectionSlug>(
|
||||
collection: collectionConfig.slug,
|
||||
locale,
|
||||
req,
|
||||
select,
|
||||
where: fullWhere,
|
||||
})
|
||||
|
||||
@@ -198,6 +204,7 @@ export const deleteOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type {
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../types/index.js'
|
||||
import type { BeforeOperationHook, Collection, DataFromCollectionSlug } from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
@@ -24,12 +28,13 @@ export type Arguments = {
|
||||
overrideAccess?: boolean
|
||||
overrideLock?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export const deleteByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
export const deleteByIDOperation = async <TSlug extends CollectionSlug, TSelect extends SelectType>(
|
||||
incomingArgs: Arguments,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -68,6 +73,7 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
payload,
|
||||
},
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -153,6 +159,7 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
let result: DataFromCollectionSlug<TSlug> = await req.payload.db.deleteOne({
|
||||
collection: collectionConfig.slug,
|
||||
req,
|
||||
select,
|
||||
where: { id: { equals: id } },
|
||||
})
|
||||
|
||||
@@ -182,6 +189,7 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
@@ -237,7 +245,7 @@ export const deleteByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
await commitTransaction(req)
|
||||
}
|
||||
|
||||
return result
|
||||
return result as TransformCollectionWithSelect<TSlug, TSelect>
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
throw error
|
||||
|
||||
@@ -4,8 +4,16 @@ import httpStatus from 'http-status'
|
||||
|
||||
import type { FindOneArgs } from '../../database/types.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { Collection, DataFromCollectionSlug } from '../config/types.js'
|
||||
import type {
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../types/index.js'
|
||||
import type {
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
import { hasWhereAccessResult } from '../../auth/types.js'
|
||||
@@ -21,6 +29,7 @@ import { uploadFiles } from '../../uploads/uploadFiles.js'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
||||
import { getLatestCollectionVersion } from '../../versions/getLatestCollectionVersion.js'
|
||||
import { saveVersion } from '../../versions/saveVersion.js'
|
||||
import { buildAfterOperation } from './utils.js'
|
||||
@@ -33,12 +42,16 @@ export type Arguments = {
|
||||
id: number | string
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export const duplicateOperation = async <TSlug extends CollectionSlug>(
|
||||
export const duplicateOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
|
||||
let args = incomingArgs
|
||||
const operation = 'create'
|
||||
|
||||
@@ -70,6 +83,7 @@ export const duplicateOperation = async <TSlug extends CollectionSlug>(
|
||||
overrideAccess,
|
||||
req: { fallbackLocale, locale: localeArg, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -254,12 +268,15 @@ export const duplicateOperation = async <TSlug extends CollectionSlug>(
|
||||
await uploadFiles(payload, filesToUpload, req)
|
||||
}
|
||||
|
||||
const versionDoc = await payload.db.create({
|
||||
let versionDoc = await payload.db.create({
|
||||
collection: collectionConfig.slug,
|
||||
data: result,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
|
||||
versionDoc = sanitizeInternalFields(versionDoc)
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Create version
|
||||
// /////////////////////////////////////
|
||||
@@ -290,6 +307,7 @@ export const duplicateOperation = async <TSlug extends CollectionSlug>(
|
||||
locale: localeArg,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import type { AccessResult } from '../../config/types.js'
|
||||
import type { PaginatedDocs } from '../../database/types.js'
|
||||
import type { CollectionSlug, JoinQuery } from '../../index.js'
|
||||
import type { PayloadRequest, Sort, Where } from '../../types/index.js'
|
||||
import type { Collection, DataFromCollectionSlug } from '../config/types.js'
|
||||
import type {
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
Sort,
|
||||
TransformCollectionWithSelect,
|
||||
Where,
|
||||
} from '../../types/index.js'
|
||||
import type {
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
import { combineQueries } from '../../database/combineQueries.js'
|
||||
@@ -11,6 +21,7 @@ import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import { buildVersionCollectionFields } from '../../versions/buildCollectionFields.js'
|
||||
import { appendVersionToQueryKey } from '../../versions/drafts/appendVersionToQueryKey.js'
|
||||
import { getQueryDraftsSelect } from '../../versions/drafts/getQueryDraftsSelect.js'
|
||||
import { getQueryDraftsSort } from '../../versions/drafts/getQueryDraftsSort.js'
|
||||
import { buildAfterOperation } from './utils.js'
|
||||
|
||||
@@ -27,14 +38,18 @@ export type Arguments = {
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export const findOperation = async <TSlug extends CollectionSlug>(
|
||||
export const findOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments,
|
||||
): Promise<PaginatedDocs<DataFromCollectionSlug<TSlug>>> => {
|
||||
): Promise<PaginatedDocs<TransformCollectionWithSelect<TSlug, TSelect>>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -70,6 +85,7 @@ export const findOperation = async <TSlug extends CollectionSlug>(
|
||||
pagination = true,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
@@ -132,6 +148,7 @@ export const findOperation = async <TSlug extends CollectionSlug>(
|
||||
page: sanitizedPage,
|
||||
pagination: usePagination,
|
||||
req,
|
||||
select: getQueryDraftsSelect({ select }),
|
||||
sort: getQueryDraftsSort({ collectionConfig, sort }),
|
||||
where: fullWhere,
|
||||
})
|
||||
@@ -151,6 +168,7 @@ export const findOperation = async <TSlug extends CollectionSlug>(
|
||||
page: sanitizedPage,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
sort,
|
||||
where: fullWhere,
|
||||
})
|
||||
@@ -268,6 +286,7 @@ export const findOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
}),
|
||||
),
|
||||
@@ -318,7 +337,7 @@ export const findOperation = async <TSlug extends CollectionSlug>(
|
||||
// Return results
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result
|
||||
return result as PaginatedDocs<TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
throw error
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import type { FindOneArgs } from '../../database/types.js'
|
||||
import type { CollectionSlug, JoinQuery } from '../../index.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { Collection, DataFromCollectionSlug } from '../config/types.js'
|
||||
import type {
|
||||
ApplyDisableErrors,
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../types/index.js'
|
||||
import type {
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
import { combineQueries } from '../../database/combineQueries.js'
|
||||
@@ -22,12 +31,17 @@ export type Arguments = {
|
||||
joins?: JoinQuery
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export const findByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
export const findByIDOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<TSlug, TSelect>, TDisableErrors>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -60,6 +74,7 @@ export const findByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
overrideAccess = false,
|
||||
req: { fallbackLocale, locale, t },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -83,6 +98,7 @@ export const findByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
req: {
|
||||
transactionID: req.transactionID,
|
||||
} as PayloadRequest,
|
||||
select,
|
||||
where: combineQueries({ id: { equals: id } }, accessResult),
|
||||
}
|
||||
|
||||
@@ -170,6 +186,7 @@ export const findByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
entityType: 'collection',
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -206,6 +223,7 @@ export const findByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
@@ -241,7 +259,10 @@ export const findByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
// Return results
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result
|
||||
return result as ApplyDisableErrors<
|
||||
TransformCollectionWithSelect<TSlug, TSelect>,
|
||||
TDisableErrors
|
||||
>
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
throw error
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import httpStatus from 'http-status'
|
||||
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType } from '../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../versions/types.js'
|
||||
import type { Collection, TypeWithID } from '../config/types.js'
|
||||
|
||||
@@ -18,6 +18,7 @@ export type Arguments = {
|
||||
id: number | string
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
@@ -33,6 +34,7 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
||||
overrideAccess,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -68,6 +70,7 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
||||
locale,
|
||||
pagination: false,
|
||||
req,
|
||||
select,
|
||||
where: fullWhere,
|
||||
})
|
||||
|
||||
@@ -119,6 +122,7 @@ export const findVersionByIDOperation = async <TData extends TypeWithID = any>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select: typeof select?.version === 'object' ? select.version : undefined,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PaginatedDocs } from '../../database/types.js'
|
||||
import type { PayloadRequest, Sort, Where } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType, Sort, Where } from '../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../versions/types.js'
|
||||
import type { Collection } from '../config/types.js'
|
||||
|
||||
@@ -19,6 +19,7 @@ export type Arguments = {
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
@@ -36,6 +37,7 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
||||
pagination = true,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
@@ -75,6 +77,7 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
||||
page: page || 1,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
sort,
|
||||
where: fullWhere,
|
||||
})
|
||||
@@ -127,6 +130,7 @@ export const findVersionsOperation = async <TData extends TypeWithVersion<TData>
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select: typeof select?.version === 'object' ? select.version : undefined,
|
||||
showHiddenFields,
|
||||
}),
|
||||
})),
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import type { CollectionSlug, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../../types/index.js'
|
||||
import type { File } from '../../../uploads/types.js'
|
||||
import type { DataFromCollectionSlug, RequiredDataFromCollectionSlug } from '../../config/types.js'
|
||||
import type {
|
||||
RequiredDataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { getFileByPath } from '../../../uploads/getFileByPath.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { createOperation } from '../create.js'
|
||||
|
||||
export type Options<TSlug extends CollectionSlug> = {
|
||||
export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> = {
|
||||
collection: TSlug
|
||||
/**
|
||||
* context, which will then be passed to req.context, which can be read by hooks
|
||||
@@ -26,15 +35,19 @@ export type Options<TSlug extends CollectionSlug> = {
|
||||
overrideAccess?: boolean
|
||||
overwriteExistingFiles?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-restricted-exports
|
||||
export default async function createLocal<TSlug extends CollectionSlug>(
|
||||
export default async function createLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
data,
|
||||
@@ -46,6 +59,7 @@ export default async function createLocal<TSlug extends CollectionSlug>(
|
||||
filePath,
|
||||
overrideAccess = true,
|
||||
overwriteExistingFiles = false,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
const collection = payload.collections[collectionSlug]
|
||||
@@ -59,7 +73,7 @@ export default async function createLocal<TSlug extends CollectionSlug>(
|
||||
const req = await createLocalReq(options, payload)
|
||||
req.file = file ?? (await getFileByPath(filePath))
|
||||
|
||||
return createOperation<TSlug>({
|
||||
return createOperation<TSlug, TSelect>({
|
||||
collection,
|
||||
data,
|
||||
depth,
|
||||
@@ -69,6 +83,7 @@ export default async function createLocal<TSlug extends CollectionSlug>(
|
||||
overrideAccess,
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
import type { CollectionSlug, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext, Where } from '../../../types/index.js'
|
||||
import type { BulkOperationResult, DataFromCollectionSlug } from '../../config/types.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
Where,
|
||||
} from '../../../types/index.js'
|
||||
import type { BulkOperationResult, SelectFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { deleteOperation } from '../delete.js'
|
||||
import { deleteByIDOperation } from '../deleteByID.js'
|
||||
|
||||
export type BaseOptions<TSlug extends CollectionSlug> = {
|
||||
export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType> = {
|
||||
collection: TSlug
|
||||
/**
|
||||
* context, which will then be passed to req.context, which can be read by hooks
|
||||
@@ -20,38 +27,60 @@ export type BaseOptions<TSlug extends CollectionSlug> = {
|
||||
overrideAccess?: boolean
|
||||
overrideLock?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export type ByIDOptions<TSlug extends CollectionSlug> = {
|
||||
export type ByIDOptions<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
> = {
|
||||
id: number | string
|
||||
where?: never
|
||||
} & BaseOptions<TSlug>
|
||||
} & BaseOptions<TSlug, TSelect>
|
||||
|
||||
export type ManyOptions<TSlug extends CollectionSlug> = {
|
||||
export type ManyOptions<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
> = {
|
||||
id?: never
|
||||
where: Where
|
||||
} & BaseOptions<TSlug>
|
||||
} & BaseOptions<TSlug, TSelect>
|
||||
|
||||
export type Options<TSlug extends CollectionSlug> = ByIDOptions<TSlug> | ManyOptions<TSlug>
|
||||
export type Options<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
> = ByIDOptions<TSlug, TSelect> | ManyOptions<TSlug, TSelect>
|
||||
|
||||
async function deleteLocal<TSlug extends CollectionSlug>(
|
||||
async function deleteLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: ByIDOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>>
|
||||
async function deleteLocal<TSlug extends CollectionSlug>(
|
||||
options: ByIDOptions<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
async function deleteLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: ManyOptions<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug>>
|
||||
async function deleteLocal<TSlug extends CollectionSlug>(
|
||||
options: ManyOptions<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect>>
|
||||
async function deleteLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug> | DataFromCollectionSlug<TSlug>>
|
||||
async function deleteLocal<TSlug extends CollectionSlug>(
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
async function deleteLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug> | DataFromCollectionSlug<TSlug>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
@@ -59,6 +88,7 @@ async function deleteLocal<TSlug extends CollectionSlug>(
|
||||
disableTransaction,
|
||||
overrideAccess = true,
|
||||
overrideLock,
|
||||
select,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = options
|
||||
@@ -79,14 +109,15 @@ async function deleteLocal<TSlug extends CollectionSlug>(
|
||||
overrideAccess,
|
||||
overrideLock,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
where,
|
||||
}
|
||||
|
||||
if (options.id) {
|
||||
return deleteByIDOperation<TSlug>(args)
|
||||
return deleteByIDOperation<TSlug, TSelect>(args)
|
||||
}
|
||||
return deleteOperation<TSlug>(args)
|
||||
return deleteOperation<TSlug, TSelect>(args)
|
||||
}
|
||||
|
||||
export default deleteLocal
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
import type { CollectionSlug, TypedLocale } from '../../..//index.js'
|
||||
import type { Payload } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type { DataFromCollectionSlug } from '../../config/types.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../../types/index.js'
|
||||
import type { DataFromCollectionSlug, SelectFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { duplicateOperation } from '../duplicate.js'
|
||||
|
||||
export type Options<TSlug extends CollectionSlug> = {
|
||||
export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> = {
|
||||
collection: TSlug
|
||||
/**
|
||||
* context, which will then be passed to req.context, which can be read by hooks
|
||||
@@ -21,14 +27,18 @@ export type Options<TSlug extends CollectionSlug> = {
|
||||
locale?: TypedLocale
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export async function duplicate<TSlug extends CollectionSlug>(
|
||||
export async function duplicate<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> {
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
@@ -36,6 +46,7 @@ export async function duplicate<TSlug extends CollectionSlug>(
|
||||
disableTransaction,
|
||||
draft,
|
||||
overrideAccess = true,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
const collection = payload.collections[collectionSlug]
|
||||
@@ -55,7 +66,7 @@ export async function duplicate<TSlug extends CollectionSlug>(
|
||||
|
||||
const req = await createLocalReq(options, payload)
|
||||
|
||||
return duplicateOperation<TSlug>({
|
||||
return duplicateOperation<TSlug, TSelect>({
|
||||
id,
|
||||
collection,
|
||||
depth,
|
||||
@@ -63,6 +74,7 @@ export async function duplicate<TSlug extends CollectionSlug>(
|
||||
draft,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import type { PaginatedDocs } from '../../../database/types.js'
|
||||
import type { CollectionSlug, JoinQuery, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext, Sort, Where } from '../../../types/index.js'
|
||||
import type { DataFromCollectionSlug } from '../../config/types.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
Sort,
|
||||
TransformCollectionWithSelect,
|
||||
Where,
|
||||
} from '../../../types/index.js'
|
||||
import type { SelectFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { findOperation } from '../find.js'
|
||||
|
||||
export type Options<TSlug extends CollectionSlug> = {
|
||||
export type Options<TSlug extends CollectionSlug, TSelect extends SelectType> = {
|
||||
collection: TSlug
|
||||
/**
|
||||
* context, which will then be passed to req.context, which can be read by hooks
|
||||
@@ -26,16 +34,20 @@ export type Options<TSlug extends CollectionSlug> = {
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
sort?: Sort
|
||||
user?: Document
|
||||
where?: Where
|
||||
}
|
||||
|
||||
export async function findLocal<TSlug extends CollectionSlug>(
|
||||
export async function findLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<PaginatedDocs<DataFromCollectionSlug<TSlug>>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<PaginatedDocs<TransformCollectionWithSelect<TSlug, TSelect>>> {
|
||||
const {
|
||||
collection: collectionSlug,
|
||||
currentDepth,
|
||||
@@ -48,6 +60,8 @@ export async function findLocal<TSlug extends CollectionSlug>(
|
||||
overrideAccess = true,
|
||||
page,
|
||||
pagination = true,
|
||||
select,
|
||||
// select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
@@ -61,7 +75,7 @@ export async function findLocal<TSlug extends CollectionSlug>(
|
||||
)
|
||||
}
|
||||
|
||||
return findOperation<TSlug>({
|
||||
return findOperation<TSlug, TSelect>({
|
||||
collection,
|
||||
currentDepth,
|
||||
depth,
|
||||
@@ -74,6 +88,7 @@ export async function findLocal<TSlug extends CollectionSlug>(
|
||||
page,
|
||||
pagination,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
import type { CollectionSlug, JoinQuery, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type { DataFromCollectionSlug } from '../../config/types.js'
|
||||
/* eslint-disable no-restricted-exports */
|
||||
import type { CollectionSlug, JoinQuery, Payload, SelectType, TypedLocale } from '../../../index.js'
|
||||
import type {
|
||||
ApplyDisableErrors,
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../../types/index.js'
|
||||
import type { SelectFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { findByIDOperation } from '../findByID.js'
|
||||
|
||||
export type Options<TSlug extends CollectionSlug = CollectionSlug> = {
|
||||
export type Options<
|
||||
TSlug extends CollectionSlug,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectType,
|
||||
> = {
|
||||
collection: TSlug
|
||||
/**
|
||||
* context, which will then be passed to req.context, which can be read by hooks
|
||||
@@ -14,7 +25,7 @@ export type Options<TSlug extends CollectionSlug = CollectionSlug> = {
|
||||
context?: RequestContext
|
||||
currentDepth?: number
|
||||
depth?: number
|
||||
disableErrors?: boolean
|
||||
disableErrors?: TDisableErrors
|
||||
draft?: boolean
|
||||
fallbackLocale?: TypedLocale
|
||||
id: number | string
|
||||
@@ -23,18 +34,19 @@ export type Options<TSlug extends CollectionSlug = CollectionSlug> = {
|
||||
locale?: 'all' | TypedLocale
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export default async function findByIDLocal<TOptions extends Options>(
|
||||
export default async function findByIDLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: TOptions,
|
||||
): Promise<
|
||||
TOptions['disableErrors'] extends true
|
||||
? DataFromCollectionSlug<TOptions['collection']> | null
|
||||
: DataFromCollectionSlug<TOptions['collection']>
|
||||
> {
|
||||
options: Options<TSlug, TDisableErrors, TSelect>,
|
||||
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<TSlug, TSelect>, TDisableErrors>> {
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
@@ -45,6 +57,7 @@ export default async function findByIDLocal<TOptions extends Options>(
|
||||
includeLockStatus,
|
||||
joins,
|
||||
overrideAccess = true,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
|
||||
@@ -56,7 +69,7 @@ export default async function findByIDLocal<TOptions extends Options>(
|
||||
)
|
||||
}
|
||||
|
||||
return findByIDOperation<TOptions['collection']>({
|
||||
return findByIDOperation<TSlug, TDisableErrors, TSelect>({
|
||||
id,
|
||||
collection,
|
||||
currentDepth,
|
||||
@@ -67,6 +80,7 @@ export default async function findByIDLocal<TOptions extends Options>(
|
||||
joins,
|
||||
overrideAccess,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CollectionSlug, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type { Document, PayloadRequest, RequestContext, SelectType } from '../../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../../versions/types.js'
|
||||
import type { DataFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
@@ -21,6 +21,7 @@ export type Options<TSlug extends CollectionSlug> = {
|
||||
locale?: 'all' | TypedLocale
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
@@ -35,6 +36,7 @@ export default async function findVersionByIDLocal<TSlug extends CollectionSlug>
|
||||
depth,
|
||||
disableErrors = false,
|
||||
overrideAccess = true,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
|
||||
@@ -55,6 +57,7 @@ export default async function findVersionByIDLocal<TSlug extends CollectionSlug>
|
||||
disableErrors,
|
||||
overrideAccess,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import type { PaginatedDocs } from '../../../database/types.js'
|
||||
import type { CollectionSlug, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext, Sort, Where } from '../../../types/index.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
Sort,
|
||||
Where,
|
||||
} from '../../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../../versions/types.js'
|
||||
import type { DataFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
@@ -22,6 +29,7 @@ export type Options<TSlug extends CollectionSlug> = {
|
||||
overrideAccess?: boolean
|
||||
page?: number
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
sort?: Sort
|
||||
user?: Document
|
||||
@@ -38,6 +46,7 @@ export default async function findVersionsLocal<TSlug extends CollectionSlug>(
|
||||
limit,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
@@ -58,6 +67,7 @@ export default async function findVersionsLocal<TSlug extends CollectionSlug>(
|
||||
overrideAccess,
|
||||
page,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-restricted-exports */
|
||||
import auth from '../../../auth/operations/local/index.js'
|
||||
import count from './count.js'
|
||||
import create from './create.js'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CollectionSlug, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type { Document, PayloadRequest, RequestContext, SelectType } from '../../../types/index.js'
|
||||
import type { DataFromCollectionSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
@@ -19,6 +19,7 @@ export type Options<TSlug extends CollectionSlug> = {
|
||||
locale?: TypedLocale
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
@@ -27,7 +28,14 @@ export default async function restoreVersionLocal<TSlug extends CollectionSlug>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> {
|
||||
const { id, collection: collectionSlug, depth, overrideAccess = true, showHiddenFields } = options
|
||||
const {
|
||||
id,
|
||||
collection: collectionSlug,
|
||||
depth,
|
||||
overrideAccess = true,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
|
||||
const collection = payload.collections[collectionSlug]
|
||||
|
||||
@@ -46,6 +54,7 @@ export default async function restoreVersionLocal<TSlug extends CollectionSlug>(
|
||||
overrideAccess,
|
||||
payload,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import type { CollectionSlug, Payload, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, RequestContext, Where } from '../../../types/index.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
Where,
|
||||
} from '../../../types/index.js'
|
||||
import type { File } from '../../../uploads/types.js'
|
||||
import type {
|
||||
BulkOperationResult,
|
||||
DataFromCollectionSlug,
|
||||
RequiredDataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
@@ -15,7 +22,7 @@ import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { updateOperation } from '../update.js'
|
||||
import { updateByIDOperation } from '../updateByID.js'
|
||||
|
||||
export type BaseOptions<TSlug extends CollectionSlug> = {
|
||||
export type BaseOptions<TSlug extends CollectionSlug, TSelect extends SelectType> = {
|
||||
autosave?: boolean
|
||||
collection: TSlug
|
||||
/**
|
||||
@@ -35,40 +42,62 @@ export type BaseOptions<TSlug extends CollectionSlug> = {
|
||||
overwriteExistingFiles?: boolean
|
||||
publishSpecificLocale?: string
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export type ByIDOptions<TSlug extends CollectionSlug> = {
|
||||
export type ByIDOptions<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
> = {
|
||||
id: number | string
|
||||
limit?: never
|
||||
where?: never
|
||||
} & BaseOptions<TSlug>
|
||||
} & BaseOptions<TSlug, TSelect>
|
||||
|
||||
export type ManyOptions<TSlug extends CollectionSlug> = {
|
||||
export type ManyOptions<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
> = {
|
||||
id?: never
|
||||
limit?: number
|
||||
where: Where
|
||||
} & BaseOptions<TSlug>
|
||||
} & BaseOptions<TSlug, TSelect>
|
||||
|
||||
export type Options<TSlug extends CollectionSlug> = ByIDOptions<TSlug> | ManyOptions<TSlug>
|
||||
export type Options<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
> = ByIDOptions<TSlug, TSelect> | ManyOptions<TSlug, TSelect>
|
||||
|
||||
async function updateLocal<TSlug extends CollectionSlug>(
|
||||
async function updateLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: ByIDOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>>
|
||||
async function updateLocal<TSlug extends CollectionSlug>(
|
||||
options: ByIDOptions<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
async function updateLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: ManyOptions<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug>>
|
||||
async function updateLocal<TSlug extends CollectionSlug>(
|
||||
options: ManyOptions<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect>>
|
||||
async function updateLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug> | DataFromCollectionSlug<TSlug>>
|
||||
async function updateLocal<TSlug extends CollectionSlug>(
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
async function updateLocal<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug> | DataFromCollectionSlug<TSlug>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
|
||||
const {
|
||||
id,
|
||||
autosave,
|
||||
@@ -84,6 +113,7 @@ async function updateLocal<TSlug extends CollectionSlug>(
|
||||
overrideLock,
|
||||
overwriteExistingFiles = false,
|
||||
publishSpecificLocale,
|
||||
select,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = options
|
||||
@@ -114,14 +144,15 @@ async function updateLocal<TSlug extends CollectionSlug>(
|
||||
payload,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
where,
|
||||
}
|
||||
|
||||
if (options.id) {
|
||||
return updateByIDOperation<TSlug>(args)
|
||||
return updateByIDOperation<TSlug, TSelect>(args)
|
||||
}
|
||||
return updateOperation<TSlug>(args)
|
||||
return updateOperation<TSlug, TSelect>(args)
|
||||
}
|
||||
|
||||
export default updateLocal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import httpStatus from 'http-status'
|
||||
|
||||
import type { FindOneArgs } from '../../database/types.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType } from '../../types/index.js'
|
||||
import type { Collection, TypeWithID } from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
@@ -22,6 +22,7 @@ export type Arguments = {
|
||||
id: number | string
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
@@ -36,6 +37,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
overrideAccess = false,
|
||||
req,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -115,6 +117,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
collection: collectionConfig.slug,
|
||||
data: rawVersion.version,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
|
||||
// /////////////////////////////////////
|
||||
@@ -150,6 +153,7 @@ export const restoreVersionOperation = async <TData extends TypeWithID = any>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -4,12 +4,13 @@ import httpStatus from 'http-status'
|
||||
|
||||
import type { AccessResult } from '../../config/types.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest, Where } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType, Where } from '../../types/index.js'
|
||||
import type {
|
||||
BulkOperationResult,
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
RequiredDataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import { ensureUsernameOrEmail } from '../../auth/ensureUsernameOrEmail.js'
|
||||
@@ -46,13 +47,17 @@ export type Arguments<TSlug extends CollectionSlug> = {
|
||||
overrideLock?: boolean
|
||||
overwriteExistingFiles?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
where: Where
|
||||
}
|
||||
|
||||
export const updateOperation = async <TSlug extends CollectionSlug>(
|
||||
export const updateOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
incomingArgs: Arguments<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug>> => {
|
||||
): Promise<BulkOperationResult<TSlug, TSelect>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -91,6 +96,7 @@ export const updateOperation = async <TSlug extends CollectionSlug>(
|
||||
payload,
|
||||
},
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
where,
|
||||
} = args
|
||||
@@ -322,6 +328,7 @@ export const updateOperation = async <TSlug extends CollectionSlug>(
|
||||
data: result,
|
||||
locale,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -336,6 +343,7 @@ export const updateOperation = async <TSlug extends CollectionSlug>(
|
||||
docWithLocales: result,
|
||||
payload,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -354,6 +362,7 @@ export const updateOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -5,11 +5,16 @@ import httpStatus from 'http-status'
|
||||
import type { FindOneArgs } from '../../database/types.js'
|
||||
import type { Args } from '../../fields/hooks/beforeChange/index.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type {
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
} from '../../types/index.js'
|
||||
import type {
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
RequiredDataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import { ensureUsernameOrEmail } from '../../auth/ensureUsernameOrEmail.js'
|
||||
@@ -48,12 +53,16 @@ export type Arguments<TSlug extends CollectionSlug> = {
|
||||
overwriteExistingFiles?: boolean
|
||||
publishSpecificLocale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
export const updateByIDOperation = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug> = SelectType,
|
||||
>(
|
||||
incomingArgs: Arguments<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
|
||||
let args = incomingArgs
|
||||
|
||||
try {
|
||||
@@ -98,6 +107,7 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
payload,
|
||||
},
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -345,6 +355,7 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
data: dataToUpdate,
|
||||
locale,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -362,6 +373,7 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
payload,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
select,
|
||||
snapshot: versionSnapshotResult,
|
||||
})
|
||||
}
|
||||
@@ -381,6 +393,7 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
@@ -458,7 +471,7 @@ export const updateByIDOperation = async <TSlug extends CollectionSlug>(
|
||||
await commitTransaction(req)
|
||||
}
|
||||
|
||||
return result
|
||||
return result as TransformCollectionWithSelect<TSlug, TSelect>
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(args.req)
|
||||
throw error
|
||||
|
||||
@@ -3,7 +3,11 @@ import type { loginOperation } from '../../auth/operations/login.js'
|
||||
import type { refreshOperation } from '../../auth/operations/refresh.js'
|
||||
import type { CollectionSlug } from '../../index.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { AfterOperationHook, SanitizedCollectionConfig } from '../config/types.js'
|
||||
import type {
|
||||
AfterOperationHook,
|
||||
SanitizedCollectionConfig,
|
||||
SelectFromCollectionSlug,
|
||||
} from '../config/types.js'
|
||||
import type { countOperation } from './count.js'
|
||||
import type { createOperation } from './create.js'
|
||||
import type { deleteOperation } from './delete.js'
|
||||
@@ -15,16 +19,26 @@ import type { updateByIDOperation } from './updateByID.js'
|
||||
|
||||
export type AfterOperationMap<TOperationGeneric extends CollectionSlug> = {
|
||||
count: typeof countOperation<TOperationGeneric>
|
||||
create: typeof createOperation<TOperationGeneric>
|
||||
delete: typeof deleteOperation<TOperationGeneric>
|
||||
deleteByID: typeof deleteByIDOperation<TOperationGeneric>
|
||||
find: typeof findOperation<TOperationGeneric>
|
||||
findByID: typeof findByIDOperation<TOperationGeneric>
|
||||
create: typeof createOperation<TOperationGeneric, SelectFromCollectionSlug<TOperationGeneric>>
|
||||
delete: typeof deleteOperation<TOperationGeneric, SelectFromCollectionSlug<TOperationGeneric>>
|
||||
deleteByID: typeof deleteByIDOperation<
|
||||
TOperationGeneric,
|
||||
SelectFromCollectionSlug<TOperationGeneric>
|
||||
>
|
||||
find: typeof findOperation<TOperationGeneric, SelectFromCollectionSlug<TOperationGeneric>>
|
||||
findByID: typeof findByIDOperation<
|
||||
TOperationGeneric,
|
||||
boolean,
|
||||
SelectFromCollectionSlug<TOperationGeneric>
|
||||
>
|
||||
forgotPassword: typeof forgotPasswordOperation
|
||||
login: typeof loginOperation<TOperationGeneric>
|
||||
refresh: typeof refreshOperation
|
||||
update: typeof updateOperation<TOperationGeneric>
|
||||
updateByID: typeof updateByIDOperation<TOperationGeneric>
|
||||
update: typeof updateOperation<TOperationGeneric, SelectFromCollectionSlug<TOperationGeneric>>
|
||||
updateByID: typeof updateByIDOperation<
|
||||
TOperationGeneric,
|
||||
SelectFromCollectionSlug<TOperationGeneric>
|
||||
>
|
||||
}
|
||||
|
||||
export type AfterOperationArg<TOperationGeneric extends CollectionSlug> = {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import type { TypeWithID } from '../collections/config/types.js'
|
||||
import type { Document, JoinQuery, Payload, PayloadRequest, Sort, Where } from '../types/index.js'
|
||||
import type {
|
||||
Document,
|
||||
JoinQuery,
|
||||
Payload,
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
Sort,
|
||||
Where,
|
||||
} from '../types/index.js'
|
||||
import type { TypeWithVersion } from '../versions/types.js'
|
||||
|
||||
export type { TypeWithVersion }
|
||||
@@ -180,6 +188,7 @@ export type QueryDraftsArgs = {
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
}
|
||||
@@ -191,6 +200,7 @@ export type FindOneArgs = {
|
||||
joins?: JoinQuery
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
where?: Where
|
||||
}
|
||||
|
||||
@@ -206,6 +216,7 @@ export type FindArgs = {
|
||||
pagination?: boolean
|
||||
projection?: Record<string, unknown>
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
skip?: number
|
||||
sort?: Sort
|
||||
versions?: boolean
|
||||
@@ -229,6 +240,7 @@ type BaseVersionArgs = {
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
skip?: number
|
||||
sort?: Sort
|
||||
versions?: boolean
|
||||
@@ -250,6 +262,7 @@ export type FindGlobalVersionsArgs = {
|
||||
export type FindGlobalArgs = {
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
slug: string
|
||||
where?: Where
|
||||
}
|
||||
@@ -258,6 +271,7 @@ export type UpdateGlobalVersionArgs<T = TypeWithID> = {
|
||||
global: string
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
versionData: T
|
||||
} & (
|
||||
| {
|
||||
@@ -290,6 +304,7 @@ export type CreateGlobal = <T extends Record<string, unknown> = any>(
|
||||
export type UpdateGlobalArgs<T extends Record<string, unknown> = any> = {
|
||||
data: T
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
slug: string
|
||||
}
|
||||
export type UpdateGlobal = <T extends Record<string, unknown> = any>(
|
||||
@@ -319,6 +334,7 @@ export type CreateVersionArgs<T = TypeWithID> = {
|
||||
parent: number | string
|
||||
publishedLocale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
snapshot?: true
|
||||
updatedAt: string
|
||||
versionData: T
|
||||
@@ -336,6 +352,7 @@ export type CreateGlobalVersionArgs<T = TypeWithID> = {
|
||||
parent: number | string
|
||||
publishedLocale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
snapshot?: true
|
||||
updatedAt: string
|
||||
versionData: T
|
||||
@@ -351,6 +368,7 @@ export type UpdateVersionArgs<T = TypeWithID> = {
|
||||
collection: string
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
versionData: T
|
||||
} & (
|
||||
| {
|
||||
@@ -373,6 +391,7 @@ export type CreateArgs = {
|
||||
draft?: boolean
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
}
|
||||
|
||||
export type Create = (args: CreateArgs) => Promise<Document>
|
||||
@@ -388,6 +407,7 @@ export type UpdateOneArgs = {
|
||||
*/
|
||||
options?: Record<string, unknown>
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
} & (
|
||||
| {
|
||||
id: number | string
|
||||
@@ -407,6 +427,7 @@ export type UpsertArgs = {
|
||||
joins?: JoinQuery
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
where: Where
|
||||
}
|
||||
|
||||
@@ -416,6 +437,7 @@ export type DeleteOneArgs = {
|
||||
collection: string
|
||||
joins?: JoinQuery
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
where: Where
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,8 @@ export {
|
||||
export { fieldSchemaToJSON } from '../utilities/fieldSchemaToJSON.js'
|
||||
export { getDataByPath } from '../utilities/getDataByPath.js'
|
||||
|
||||
export { getSelectMode } from '../utilities/getSelectMode.js'
|
||||
|
||||
export { getSiblingData } from '../utilities/getSiblingData.js'
|
||||
|
||||
export { getUniqueListBy } from '../utilities/getUniqueListBy.js'
|
||||
@@ -70,9 +72,7 @@ export { setsAreEqual } from '../utilities/setsAreEqual.js'
|
||||
export { default as toKebabCase } from '../utilities/toKebabCase.js'
|
||||
|
||||
export { unflatten } from '../utilities/unflatten.js'
|
||||
|
||||
export { wait } from '../utilities/wait.js'
|
||||
|
||||
export { default as wordBoundariesRegex } from '../utilities/wordBoundariesRegex.js'
|
||||
export { versionDefaults } from '../versions/defaults.js'
|
||||
export { deepMergeSimple } from '@payloadcms/translations/utilities'
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import type { SanitizedCollectionConfig } from '../../../collections/config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
|
||||
import type { JsonObject, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type {
|
||||
JsonObject,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectType,
|
||||
} from '../../../types/index.js'
|
||||
|
||||
import { deepCopyObjectSimple } from '../../../utilities/deepCopyObject.js'
|
||||
import { getSelectMode } from '../../../utilities/getSelectMode.js'
|
||||
import { traverseFields } from './traverseFields.js'
|
||||
|
||||
type Args<T extends JsonObject> = {
|
||||
@@ -19,6 +25,7 @@ type Args<T extends JsonObject> = {
|
||||
locale: string
|
||||
overrideAccess: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields: boolean
|
||||
}
|
||||
|
||||
@@ -47,6 +54,7 @@ export async function afterRead<T extends JsonObject>(args: Args<T>): Promise<T>
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -83,6 +91,8 @@ export async function afterRead<T extends JsonObject>(args: Args<T>): Promise<T>
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: [],
|
||||
select,
|
||||
selectMode: select ? getSelectMode(select) : undefined,
|
||||
showHiddenFields,
|
||||
siblingDoc: doc,
|
||||
})
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import type { RichTextAdapter } from '../../../admin/RichText.js'
|
||||
import type { SanitizedCollectionConfig } from '../../../collections/config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
|
||||
import type { JsonObject, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type {
|
||||
JsonObject,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectMode,
|
||||
SelectType,
|
||||
} from '../../../types/index.js'
|
||||
import type { Field, TabAsField } from '../../config/types.js'
|
||||
|
||||
import { MissingEditorProp } from '../../../errors/index.js'
|
||||
@@ -39,6 +45,8 @@ type Args = {
|
||||
parentSchemaPath: string[]
|
||||
populationPromises: Promise<void>[]
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
selectMode?: SelectMode
|
||||
showHiddenFields: boolean
|
||||
siblingDoc: JsonObject
|
||||
triggerAccessControl?: boolean
|
||||
@@ -72,6 +80,8 @@ export const promise = async ({
|
||||
parentSchemaPath,
|
||||
populationPromises,
|
||||
req,
|
||||
select,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
triggerAccessControl = true,
|
||||
@@ -92,6 +102,22 @@ export const promise = async ({
|
||||
delete siblingDoc[field.name]
|
||||
}
|
||||
|
||||
if (fieldAffectsData(field) && select && selectMode) {
|
||||
if (selectMode === 'include') {
|
||||
if (!select[field.name]) {
|
||||
delete siblingDoc[field.name]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (selectMode === 'exclude') {
|
||||
if (select[field.name] === false) {
|
||||
delete siblingDoc[field.name]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shouldHoistLocalizedValue =
|
||||
flattenLocales &&
|
||||
fieldAffectsData(field) &&
|
||||
@@ -317,6 +343,8 @@ export const promise = async ({
|
||||
groupDoc = {}
|
||||
}
|
||||
|
||||
const groupSelect = select?.[field.name]
|
||||
|
||||
traverseFields({
|
||||
collection,
|
||||
context,
|
||||
@@ -336,6 +364,8 @@ export const promise = async ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: fieldSchemaPath,
|
||||
select: typeof groupSelect === 'object' ? groupSelect : undefined,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc: groupDoc,
|
||||
triggerAccessControl,
|
||||
@@ -348,6 +378,12 @@ export const promise = async ({
|
||||
case 'array': {
|
||||
const rows = siblingDoc[field.name] as JsonObject
|
||||
|
||||
const arraySelect = select?.[field.name]
|
||||
|
||||
if (selectMode === 'include' && typeof arraySelect === 'object') {
|
||||
arraySelect.id = true
|
||||
}
|
||||
|
||||
if (Array.isArray(rows)) {
|
||||
rows.forEach((row, i) => {
|
||||
traverseFields({
|
||||
@@ -369,6 +405,8 @@ export const promise = async ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: fieldSchemaPath,
|
||||
select: typeof arraySelect === 'object' ? arraySelect : undefined,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc: row || {},
|
||||
triggerAccessControl,
|
||||
@@ -415,12 +453,38 @@ export const promise = async ({
|
||||
case 'blocks': {
|
||||
const rows = siblingDoc[field.name]
|
||||
|
||||
const blocksSelect = select?.[field.name]
|
||||
|
||||
if (Array.isArray(rows)) {
|
||||
rows.forEach((row, i) => {
|
||||
const block = field.blocks.find(
|
||||
(blockType) => blockType.slug === (row as JsonObject).blockType,
|
||||
)
|
||||
|
||||
let blockSelectMode = selectMode
|
||||
|
||||
if (typeof blocksSelect === 'object') {
|
||||
// sanitize blocks: {cta: false} to blocks: {cta: {id: true, blockType: true}}
|
||||
if (selectMode === 'exclude' && blocksSelect[block.slug] === false) {
|
||||
blockSelectMode = 'include'
|
||||
blocksSelect[block.slug] = {
|
||||
id: true,
|
||||
blockType: true,
|
||||
}
|
||||
} else if (selectMode === 'include') {
|
||||
if (!blocksSelect[block.slug]) {
|
||||
blocksSelect[block.slug] = {}
|
||||
}
|
||||
|
||||
if (typeof blocksSelect[block.slug] === 'object') {
|
||||
blocksSelect[block.slug]['id'] = true
|
||||
blocksSelect[block.slug]['blockType'] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const blockSelect = blocksSelect?.[block.slug]
|
||||
|
||||
if (block) {
|
||||
traverseFields({
|
||||
collection,
|
||||
@@ -441,6 +505,8 @@ export const promise = async ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: fieldSchemaPath,
|
||||
select: typeof blockSelect === 'object' ? blockSelect : undefined,
|
||||
selectMode: blockSelectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc: (row as JsonObject) || {},
|
||||
triggerAccessControl,
|
||||
@@ -513,6 +579,8 @@ export const promise = async ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: fieldSchemaPath,
|
||||
select,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
triggerAccessControl,
|
||||
@@ -524,11 +592,18 @@ export const promise = async ({
|
||||
|
||||
case 'tab': {
|
||||
let tabDoc = siblingDoc
|
||||
let tabSelect: SelectType | undefined
|
||||
if (tabHasName(field)) {
|
||||
tabDoc = siblingDoc[field.name] as JsonObject
|
||||
if (typeof siblingDoc[field.name] !== 'object') {
|
||||
tabDoc = {}
|
||||
}
|
||||
|
||||
if (typeof select?.[field.name] === 'object') {
|
||||
tabSelect = select?.[field.name] as SelectType
|
||||
}
|
||||
} else {
|
||||
tabSelect = select
|
||||
}
|
||||
|
||||
traverseFields({
|
||||
@@ -550,6 +625,8 @@ export const promise = async ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: fieldSchemaPath,
|
||||
select: tabSelect,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc: tabDoc,
|
||||
triggerAccessControl,
|
||||
@@ -579,6 +656,8 @@ export const promise = async ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath: fieldSchemaPath,
|
||||
select,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
triggerAccessControl,
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { SanitizedCollectionConfig } from '../../../collections/config/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../../../globals/config/types.js'
|
||||
import type { JsonObject, PayloadRequest, RequestContext } from '../../../types/index.js'
|
||||
import type {
|
||||
JsonObject,
|
||||
PayloadRequest,
|
||||
RequestContext,
|
||||
SelectMode,
|
||||
SelectType,
|
||||
} from '../../../types/index.js'
|
||||
import type { Field, TabAsField } from '../../config/types.js'
|
||||
|
||||
import { promise } from './promise.js'
|
||||
@@ -27,6 +33,8 @@ type Args = {
|
||||
populationPromises: Promise<void>[]
|
||||
req: PayloadRequest
|
||||
schemaPath: string[]
|
||||
select?: SelectType
|
||||
selectMode?: SelectMode
|
||||
showHiddenFields: boolean
|
||||
siblingDoc: JsonObject
|
||||
triggerAccessControl?: boolean
|
||||
@@ -52,6 +60,8 @@ export const traverseFields = ({
|
||||
populationPromises,
|
||||
req,
|
||||
schemaPath,
|
||||
select,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
triggerAccessControl = true,
|
||||
@@ -78,6 +88,8 @@ export const traverseFields = ({
|
||||
parentSchemaPath: schemaPath,
|
||||
populationPromises,
|
||||
req,
|
||||
select,
|
||||
selectMode,
|
||||
showHiddenFields,
|
||||
siblingDoc,
|
||||
triggerAccessControl,
|
||||
|
||||
@@ -19,12 +19,14 @@ import type {
|
||||
} from '../../config/types.js'
|
||||
import type { DBIdentifierName } from '../../database/types.js'
|
||||
import type { Field } from '../../fields/config/types.js'
|
||||
import type { GlobalSlug, TypedGlobal } from '../../index.js'
|
||||
import type { GlobalSlug, TypedGlobal, TypedGlobalSelect } from '../../index.js'
|
||||
import type { PayloadRequest, RequestContext, Where } from '../../types/index.js'
|
||||
import type { IncomingGlobalVersions, SanitizedGlobalVersions } from '../../versions/types.js'
|
||||
|
||||
export type DataFromGlobalSlug<TSlug extends GlobalSlug> = TypedGlobal[TSlug]
|
||||
|
||||
export type SelectFromGlobalSlug<TSlug extends GlobalSlug> = TypedGlobalSelect[TSlug]
|
||||
|
||||
export type BeforeValidateHook = (args: {
|
||||
context: RequestContext
|
||||
data?: any
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { AccessResult } from '../../config/types.js'
|
||||
import type { PayloadRequest, Where } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType, Where } from '../../types/index.js'
|
||||
import type { SanitizedGlobalConfig } from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import replaceWithDraftIfAvailable from '../../versions/drafts/replaceWithDraftIfAvailable.js'
|
||||
|
||||
@@ -14,6 +15,7 @@ type Args = {
|
||||
includeLockStatus?: boolean
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
slug: string
|
||||
}
|
||||
@@ -30,6 +32,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
overrideAccess = false,
|
||||
req: { fallbackLocale, locale },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -52,6 +55,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
slug,
|
||||
locale,
|
||||
req,
|
||||
select,
|
||||
where: overrideAccess ? undefined : (accessResult as Where),
|
||||
})
|
||||
if (!doc) {
|
||||
@@ -103,6 +107,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
entityType: 'global',
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -122,6 +127,19 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
})) || doc
|
||||
}, Promise.resolve())
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Execute globalType field if not selected
|
||||
// /////////////////////////////////////
|
||||
if (select && doc.globalType) {
|
||||
const selectMode = getSelectMode(select)
|
||||
if (
|
||||
(selectMode === 'include' && !select['globalType']) ||
|
||||
(selectMode === 'exclude' && select['globalType'] === false)
|
||||
) {
|
||||
delete doc['globalType']
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
@@ -137,6 +155,7 @@ export const findOneOperation = async <T extends Record<string, unknown>>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { FindGlobalVersionsArgs } from '../../database/types.js'
|
||||
import type { PayloadRequest } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType } from '../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../versions/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../config/types.js'
|
||||
|
||||
@@ -8,6 +8,7 @@ import { combineQueries } from '../../database/combineQueries.js'
|
||||
import { Forbidden, NotFound } from '../../errors/index.js'
|
||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||
import { deepCopyObjectSimple } from '../../utilities/deepCopyObject.js'
|
||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
|
||||
export type Arguments = {
|
||||
@@ -18,6 +19,7 @@ export type Arguments = {
|
||||
id: number | string
|
||||
overrideAccess?: boolean
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
}
|
||||
|
||||
@@ -33,6 +35,7 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
||||
overrideAccess,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -57,6 +60,7 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
||||
limit: 1,
|
||||
locale,
|
||||
req,
|
||||
select,
|
||||
where: combineQueries({ id: { equals: id } }, accessResults),
|
||||
}
|
||||
|
||||
@@ -120,6 +124,7 @@ export const findVersionByIDOperation = async <T extends TypeWithVersion<T> = an
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select: typeof select?.version === 'object' ? select.version : undefined,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { PaginatedDocs } from '../../database/types.js'
|
||||
import type { PayloadRequest, Sort, Where } from '../../types/index.js'
|
||||
import type { PayloadRequest, SelectType, Sort, Where } from '../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../versions/types.js'
|
||||
import type { SanitizedGlobalConfig } from '../config/types.js'
|
||||
|
||||
@@ -19,6 +19,7 @@ export type Arguments = {
|
||||
page?: number
|
||||
pagination?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
sort?: Sort
|
||||
where?: Where
|
||||
@@ -36,6 +37,7 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
pagination = true,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
@@ -73,6 +75,7 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
page: page || 1,
|
||||
pagination,
|
||||
req,
|
||||
select,
|
||||
sort,
|
||||
where: fullWhere,
|
||||
})
|
||||
@@ -84,7 +87,8 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
let result = {
|
||||
...paginatedDocs,
|
||||
docs: await Promise.all(
|
||||
paginatedDocs.docs.map(async (data) => ({
|
||||
paginatedDocs.docs.map(async (data) => {
|
||||
return {
|
||||
...data,
|
||||
version: await afterRead<T>({
|
||||
collection: null,
|
||||
@@ -102,9 +106,11 @@ export const findVersionsOperation = async <T extends TypeWithVersion<T>>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}),
|
||||
),
|
||||
} as PaginatedDocs<T>
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest } from '../../../types/index.js'
|
||||
import type { DataFromGlobalSlug } from '../../config/types.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformGlobalWithSelect,
|
||||
} from '../../../types/index.js'
|
||||
import type { SelectFromGlobalSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { findOneOperation } from '../findOne.js'
|
||||
|
||||
export type Options<TSlug extends GlobalSlug> = {
|
||||
export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
|
||||
context?: RequestContext
|
||||
depth?: number
|
||||
draft?: boolean
|
||||
@@ -15,21 +20,26 @@ export type Options<TSlug extends GlobalSlug> = {
|
||||
locale?: 'all' | TypedLocale
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
slug: TSlug
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export default async function findOneLocal<TSlug extends GlobalSlug>(
|
||||
export default async function findOneLocal<
|
||||
TSlug extends GlobalSlug,
|
||||
TSelect extends SelectFromGlobalSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<DataFromGlobalSlug<TSlug>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> {
|
||||
const {
|
||||
slug: globalSlug,
|
||||
depth,
|
||||
draft = false,
|
||||
includeLockStatus,
|
||||
overrideAccess = true,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
|
||||
@@ -47,6 +57,7 @@ export default async function findOneLocal<TSlug extends GlobalSlug>(
|
||||
includeLockStatus,
|
||||
overrideAccess,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest } from '../../../types/index.js'
|
||||
import type { Document, PayloadRequest, SelectType } from '../../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../../versions/types.js'
|
||||
import type { DataFromGlobalSlug } from '../../config/types.js'
|
||||
|
||||
@@ -16,6 +16,7 @@ export type Options<TSlug extends GlobalSlug> = {
|
||||
locale?: 'all' | TypedLocale
|
||||
overrideAccess?: boolean
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
slug: TSlug
|
||||
user?: Document
|
||||
@@ -32,6 +33,7 @@ export default async function findVersionByIDLocal<TSlug extends GlobalSlug>(
|
||||
depth,
|
||||
disableErrors = false,
|
||||
overrideAccess = true,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
|
||||
@@ -48,6 +50,7 @@ export default async function findVersionByIDLocal<TSlug extends GlobalSlug>(
|
||||
globalConfig,
|
||||
overrideAccess,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-restricted-exports */
|
||||
import type { PaginatedDocs } from '../../../database/types.js'
|
||||
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest, Sort, Where } from '../../../types/index.js'
|
||||
import type { Document, PayloadRequest, SelectType, Sort, Where } from '../../../types/index.js'
|
||||
import type { TypeWithVersion } from '../../../versions/types.js'
|
||||
import type { DataFromGlobalSlug } from '../../config/types.js'
|
||||
|
||||
@@ -17,6 +18,7 @@ export type Options<TSlug extends GlobalSlug> = {
|
||||
overrideAccess?: boolean
|
||||
page?: number
|
||||
req?: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
slug: TSlug
|
||||
sort?: Sort
|
||||
@@ -34,6 +36,7 @@ export default async function findVersionsLocal<TSlug extends GlobalSlug>(
|
||||
limit,
|
||||
overrideAccess = true,
|
||||
page,
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
@@ -52,6 +55,7 @@ export default async function findVersionsLocal<TSlug extends GlobalSlug>(
|
||||
overrideAccess,
|
||||
page,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
sort,
|
||||
where,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-restricted-exports */
|
||||
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest } from '../../../types/index.js'
|
||||
import type { DataFromGlobalSlug } from '../../config/types.js'
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import type { GlobalSlug, Payload, RequestContext, TypedLocale } from '../../../index.js'
|
||||
import type { Document, PayloadRequest } from '../../../types/index.js'
|
||||
import type { DataFromGlobalSlug } from '../../config/types.js'
|
||||
import type {
|
||||
Document,
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformGlobalWithSelect,
|
||||
} from '../../../types/index.js'
|
||||
import type { DataFromGlobalSlug, SelectFromGlobalSlug } from '../../config/types.js'
|
||||
|
||||
import { APIError } from '../../../errors/index.js'
|
||||
import { createLocalReq } from '../../../utilities/createLocalReq.js'
|
||||
import { updateOperation } from '../update.js'
|
||||
|
||||
export type Options<TSlug extends GlobalSlug> = {
|
||||
export type Options<TSlug extends GlobalSlug, TSelect extends SelectType> = {
|
||||
context?: RequestContext
|
||||
data: DeepPartial<Omit<DataFromGlobalSlug<TSlug>, 'id'>>
|
||||
depth?: number
|
||||
@@ -19,15 +24,19 @@ export type Options<TSlug extends GlobalSlug> = {
|
||||
overrideLock?: boolean
|
||||
publishSpecificLocale?: TypedLocale
|
||||
req?: PayloadRequest
|
||||
select?: TSelect
|
||||
showHiddenFields?: boolean
|
||||
slug: TSlug
|
||||
user?: Document
|
||||
}
|
||||
|
||||
export default async function updateLocal<TSlug extends GlobalSlug>(
|
||||
export default async function updateLocal<
|
||||
TSlug extends GlobalSlug,
|
||||
TSelect extends SelectFromGlobalSlug<TSlug>,
|
||||
>(
|
||||
payload: Payload,
|
||||
options: Options<TSlug>,
|
||||
): Promise<DataFromGlobalSlug<TSlug>> {
|
||||
options: Options<TSlug, TSelect>,
|
||||
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> {
|
||||
const {
|
||||
slug: globalSlug,
|
||||
data,
|
||||
@@ -36,6 +45,7 @@ export default async function updateLocal<TSlug extends GlobalSlug>(
|
||||
overrideAccess = true,
|
||||
overrideLock,
|
||||
publishSpecificLocale,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = options
|
||||
|
||||
@@ -45,7 +55,7 @@ export default async function updateLocal<TSlug extends GlobalSlug>(
|
||||
throw new APIError(`The global with slug ${String(globalSlug)} can't be found.`)
|
||||
}
|
||||
|
||||
return updateOperation<TSlug>({
|
||||
return updateOperation<TSlug, TSelect>({
|
||||
slug: globalSlug as string,
|
||||
data,
|
||||
depth,
|
||||
@@ -55,6 +65,7 @@ export default async function updateLocal<TSlug extends GlobalSlug>(
|
||||
overrideLock,
|
||||
publishSpecificLocale,
|
||||
req: await createLocalReq(options, payload),
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
import type { DeepPartial } from 'ts-essentials'
|
||||
|
||||
import type { GlobalSlug, JsonObject } from '../../index.js'
|
||||
import type { Operation, PayloadRequest, Where } from '../../types/index.js'
|
||||
import type { DataFromGlobalSlug, SanitizedGlobalConfig } from '../config/types.js'
|
||||
import type {
|
||||
Operation,
|
||||
PayloadRequest,
|
||||
SelectType,
|
||||
TransformGlobalWithSelect,
|
||||
Where,
|
||||
} from '../../types/index.js'
|
||||
import type {
|
||||
DataFromGlobalSlug,
|
||||
SanitizedGlobalConfig,
|
||||
SelectFromGlobalSlug,
|
||||
} from '../config/types.js'
|
||||
|
||||
import executeAccess from '../../auth/executeAccess.js'
|
||||
import { afterChange } from '../../fields/hooks/afterChange/index.js'
|
||||
@@ -12,6 +22,7 @@ import { beforeValidate } from '../../fields/hooks/beforeValidate/index.js'
|
||||
import { deepCopyObjectSimple } from '../../index.js'
|
||||
import { checkDocumentLockStatus } from '../../utilities/checkDocumentLockStatus.js'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import { getLatestGlobalVersion } from '../../versions/getLatestGlobalVersion.js'
|
||||
@@ -28,13 +39,17 @@ type Args<TSlug extends GlobalSlug> = {
|
||||
overrideLock?: boolean
|
||||
publishSpecificLocale?: string
|
||||
req: PayloadRequest
|
||||
select?: SelectType
|
||||
showHiddenFields?: boolean
|
||||
slug: string
|
||||
}
|
||||
|
||||
export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
export const updateOperation = async <
|
||||
TSlug extends GlobalSlug,
|
||||
TSelect extends SelectFromGlobalSlug<TSlug>,
|
||||
>(
|
||||
args: Args<TSlug>,
|
||||
): Promise<DataFromGlobalSlug<TSlug>> => {
|
||||
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> => {
|
||||
if (args.publishSpecificLocale) {
|
||||
args.req.locale = args.publishSpecificLocale
|
||||
}
|
||||
@@ -51,6 +66,7 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
publishSpecificLocale,
|
||||
req: { fallbackLocale, locale, payload },
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
} = args
|
||||
|
||||
@@ -230,6 +246,7 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
slug,
|
||||
data: result,
|
||||
req,
|
||||
select,
|
||||
})
|
||||
} else {
|
||||
result = await payload.db.createGlobal({
|
||||
@@ -253,6 +270,7 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
payload,
|
||||
publishSpecificLocale,
|
||||
req,
|
||||
select,
|
||||
snapshot: versionSnapshotResult,
|
||||
})
|
||||
|
||||
@@ -262,6 +280,19 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// Execute globalType field if not selected
|
||||
// /////////////////////////////////////
|
||||
if (select && result.globalType) {
|
||||
const selectMode = getSelectMode(select)
|
||||
if (
|
||||
(selectMode === 'include' && !select['globalType']) ||
|
||||
(selectMode === 'exclude' && select['globalType'] === false)
|
||||
) {
|
||||
delete result['globalType']
|
||||
}
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// afterRead - Fields
|
||||
// /////////////////////////////////////
|
||||
@@ -277,6 +308,7 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
locale,
|
||||
overrideAccess,
|
||||
req,
|
||||
select,
|
||||
showHiddenFields,
|
||||
})
|
||||
|
||||
@@ -336,7 +368,7 @@ export const updateOperation = async <TSlug extends GlobalSlug>(
|
||||
await commitTransaction(req)
|
||||
}
|
||||
|
||||
return result
|
||||
return result as TransformGlobalWithSelect<TSlug, TSelect>
|
||||
} catch (error: unknown) {
|
||||
await killTransaction(req)
|
||||
throw error
|
||||
|
||||
@@ -22,6 +22,7 @@ import type {
|
||||
BulkOperationResult,
|
||||
Collection,
|
||||
DataFromCollectionSlug,
|
||||
SelectFromCollectionSlug,
|
||||
TypeWithID,
|
||||
} from './collections/config/types.js'
|
||||
export type * from './admin/types.js'
|
||||
@@ -33,6 +34,7 @@ import type {
|
||||
Options as DeleteOptions,
|
||||
} from './collections/operations/local/delete.js'
|
||||
export type { MappedView } from './admin/views/types.js'
|
||||
|
||||
import type { Options as DuplicateOptions } from './collections/operations/local/duplicate.js'
|
||||
import type { Options as FindOptions } from './collections/operations/local/find.js'
|
||||
import type { Options as FindByIDOptions } from './collections/operations/local/findByID.js'
|
||||
@@ -47,13 +49,19 @@ import type {
|
||||
import type { InitOptions, SanitizedConfig } from './config/types.js'
|
||||
import type { BaseDatabaseAdapter, PaginatedDocs } from './database/types.js'
|
||||
import type { InitializedEmailAdapter } from './email/types.js'
|
||||
import type { DataFromGlobalSlug, Globals } from './globals/config/types.js'
|
||||
import type { DataFromGlobalSlug, Globals, SelectFromGlobalSlug } from './globals/config/types.js'
|
||||
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne.js'
|
||||
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID.js'
|
||||
import type { Options as FindGlobalVersionsOptions } from './globals/operations/local/findVersions.js'
|
||||
import type { Options as RestoreGlobalVersionOptions } from './globals/operations/local/restoreVersion.js'
|
||||
import type { Options as UpdateGlobalOptions } from './globals/operations/local/update.js'
|
||||
import type { JsonObject } from './types/index.js'
|
||||
import type {
|
||||
ApplyDisableErrors,
|
||||
JsonObject,
|
||||
SelectType,
|
||||
TransformCollectionWithSelect,
|
||||
TransformGlobalWithSelect,
|
||||
} from './types/index.js'
|
||||
import type { TraverseFieldsCallback } from './utilities/traverseFields.js'
|
||||
import type { TypeWithVersion } from './versions/types.js'
|
||||
|
||||
@@ -88,12 +96,20 @@ export interface GeneratedTypes {
|
||||
}
|
||||
}
|
||||
}
|
||||
collectionsSelectUntyped: {
|
||||
[slug: string]: SelectType
|
||||
}
|
||||
|
||||
collectionsUntyped: {
|
||||
[slug: string]: JsonObject & TypeWithID
|
||||
}
|
||||
dbUntyped: {
|
||||
defaultIDType: number | string
|
||||
}
|
||||
globalsSelectUntyped: {
|
||||
[slug: string]: SelectType
|
||||
}
|
||||
|
||||
globalsUntyped: {
|
||||
[slug: string]: JsonObject
|
||||
}
|
||||
@@ -106,13 +122,29 @@ type ResolveCollectionType<T> = 'collections' extends keyof T
|
||||
? T['collections']
|
||||
: // @ts-expect-error
|
||||
T['collectionsUntyped']
|
||||
// @ts-expect-error
|
||||
type ResolveGlobalType<T> = 'globals' extends keyof T ? T['globals'] : T['globalsUntyped']
|
||||
|
||||
type ResolveCollectionSelectType<T> = 'collectionsSelect' extends keyof T
|
||||
? T['collectionsSelect']
|
||||
: // @ts-expect-error
|
||||
T['collectionsSelectUntyped']
|
||||
type ResolveGlobalType<T> = 'globals' extends keyof T
|
||||
? T['globals']
|
||||
: // @ts-expect-error
|
||||
T['globalsUntyped']
|
||||
|
||||
type ResolveGlobalSelectType<T> = 'globalsSelect' extends keyof T
|
||||
? T['globalsSelect']
|
||||
: // @ts-expect-error
|
||||
T['globalsSelectUntyped']
|
||||
|
||||
// Applying helper types to GeneratedTypes
|
||||
export type TypedCollection = ResolveCollectionType<GeneratedTypes>
|
||||
|
||||
export type TypedCollectionSelect = ResolveCollectionSelectType<GeneratedTypes>
|
||||
export type TypedGlobal = ResolveGlobalType<GeneratedTypes>
|
||||
|
||||
export type TypedGlobalSelect = ResolveGlobalSelectType<GeneratedTypes>
|
||||
|
||||
// Extract string keys from the type
|
||||
type StringKeyOf<T> = Extract<keyof T, string>
|
||||
|
||||
@@ -184,21 +216,21 @@ export class BasePayload {
|
||||
* @param options
|
||||
* @returns created document
|
||||
*/
|
||||
create = async <TSlug extends CollectionSlug>(
|
||||
options: CreateOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
create = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: CreateOptions<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
|
||||
const { create } = localOperations
|
||||
return create<TSlug>(this, options)
|
||||
return create<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
db: DatabaseAdapter
|
||||
decrypt = decrypt
|
||||
|
||||
duplicate = async <TSlug extends CollectionSlug>(
|
||||
options: DuplicateOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>> => {
|
||||
duplicate = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: DuplicateOptions<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>> => {
|
||||
const { duplicate } = localOperations
|
||||
return duplicate<TSlug>(this, options)
|
||||
return duplicate<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
email: InitializedEmailAdapter
|
||||
@@ -219,11 +251,11 @@ export class BasePayload {
|
||||
* @param options
|
||||
* @returns documents satisfying query
|
||||
*/
|
||||
find = async <TSlug extends CollectionSlug>(
|
||||
options: FindOptions<TSlug>,
|
||||
): Promise<PaginatedDocs<DataFromCollectionSlug<TSlug>>> => {
|
||||
find = async <TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: FindOptions<TSlug, TSelect>,
|
||||
): Promise<PaginatedDocs<TransformCollectionWithSelect<TSlug, TSelect>>> => {
|
||||
const { find } = localOperations
|
||||
return find<TSlug>(this, options)
|
||||
return find<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -231,22 +263,22 @@ export class BasePayload {
|
||||
* @param options
|
||||
* @returns document with specified ID
|
||||
*/
|
||||
findByID = async <TOptions extends FindByIDOptions>(
|
||||
options: TOptions,
|
||||
): Promise<
|
||||
TOptions['disableErrors'] extends true
|
||||
? DataFromCollectionSlug<TOptions['collection']> | null
|
||||
: DataFromCollectionSlug<TOptions['collection']>
|
||||
> => {
|
||||
findByID = async <
|
||||
TSlug extends CollectionSlug,
|
||||
TDisableErrors extends boolean,
|
||||
TSelect extends SelectFromCollectionSlug<TSlug>,
|
||||
>(
|
||||
options: FindByIDOptions<TSlug, TDisableErrors, TSelect>,
|
||||
): Promise<ApplyDisableErrors<TransformCollectionWithSelect<TSlug, TSelect>, TDisableErrors>> => {
|
||||
const { findByID } = localOperations
|
||||
return findByID<TOptions>(this, options)
|
||||
return findByID<TSlug, TDisableErrors, TSelect>(this, options)
|
||||
}
|
||||
|
||||
findGlobal = async <TSlug extends GlobalSlug>(
|
||||
options: FindGlobalOptions<TSlug>,
|
||||
): Promise<DataFromGlobalSlug<TSlug>> => {
|
||||
findGlobal = async <TSlug extends GlobalSlug, TSelect extends SelectFromGlobalSlug<TSlug>>(
|
||||
options: FindGlobalOptions<TSlug, TSelect>,
|
||||
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> => {
|
||||
const { findOne } = localGlobalOperations
|
||||
return findOne<TSlug>(this, options)
|
||||
return findOne<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,11 +407,11 @@ export class BasePayload {
|
||||
return unlock<TSlug>(this, options)
|
||||
}
|
||||
|
||||
updateGlobal = async <TSlug extends GlobalSlug>(
|
||||
options: UpdateGlobalOptions<TSlug>,
|
||||
): Promise<DataFromGlobalSlug<TSlug>> => {
|
||||
updateGlobal = async <TSlug extends GlobalSlug, TSelect extends SelectFromGlobalSlug<TSlug>>(
|
||||
options: UpdateGlobalOptions<TSlug, TSelect>,
|
||||
): Promise<TransformGlobalWithSelect<TSlug, TSelect>> => {
|
||||
const { update } = localGlobalOperations
|
||||
return update<TSlug>(this, options)
|
||||
return update<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
validationRules: (args: OperationArgs<any>) => ValidationRule[]
|
||||
@@ -425,19 +457,19 @@ export class BasePayload {
|
||||
* @param options
|
||||
* @returns Updated document(s)
|
||||
*/
|
||||
delete<TSlug extends CollectionSlug>(
|
||||
options: DeleteByIDOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>>
|
||||
delete<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: DeleteByIDOptions<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
|
||||
delete<TSlug extends CollectionSlug>(
|
||||
options: DeleteManyOptions<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug>>
|
||||
delete<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: DeleteManyOptions<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect>>
|
||||
|
||||
delete<TSlug extends CollectionSlug>(
|
||||
options: DeleteOptions<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug> | DataFromCollectionSlug<TSlug>> {
|
||||
delete<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: DeleteOptions<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
|
||||
const { deleteLocal } = localOperations
|
||||
return deleteLocal<TSlug>(this, options)
|
||||
return deleteLocal<TSlug, TSelect>(this, options)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -593,24 +625,24 @@ export class BasePayload {
|
||||
return this
|
||||
}
|
||||
|
||||
update<TSlug extends CollectionSlug>(
|
||||
options: UpdateManyOptions<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug>>
|
||||
update<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: UpdateManyOptions<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect>>
|
||||
|
||||
/**
|
||||
* @description Update one or more documents
|
||||
* @param options
|
||||
* @returns Updated document(s)
|
||||
*/
|
||||
update<TSlug extends CollectionSlug>(
|
||||
options: UpdateByIDOptions<TSlug>,
|
||||
): Promise<DataFromCollectionSlug<TSlug>>
|
||||
update<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: UpdateByIDOptions<TSlug, TSelect>,
|
||||
): Promise<TransformCollectionWithSelect<TSlug, TSelect>>
|
||||
|
||||
update<TSlug extends CollectionSlug>(
|
||||
options: UpdateOptions<TSlug>,
|
||||
): Promise<BulkOperationResult<TSlug> | DataFromCollectionSlug<TSlug>> {
|
||||
update<TSlug extends CollectionSlug, TSelect extends SelectFromCollectionSlug<TSlug>>(
|
||||
options: UpdateOptions<TSlug, TSelect>,
|
||||
): Promise<BulkOperationResult<TSlug, TSelect> | TransformCollectionWithSelect<TSlug, TSelect>> {
|
||||
const { update } = localOperations
|
||||
return update<TSlug>(this, options)
|
||||
return update<TSlug, TSelect>(this, options)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,19 @@ import type { I18n, TFunction } from '@payloadcms/translations'
|
||||
import type DataLoader from 'dataloader'
|
||||
import type { URL } from 'url'
|
||||
|
||||
import type { TypeWithID, TypeWithTimestamps } from '../collections/config/types.js'
|
||||
import type {
|
||||
DataFromCollectionSlug,
|
||||
TypeWithID,
|
||||
TypeWithTimestamps,
|
||||
} from '../collections/config/types.js'
|
||||
import type payload from '../index.js'
|
||||
import type { TypedLocale, TypedUser } from '../index.js'
|
||||
import type {
|
||||
CollectionSlug,
|
||||
DataFromGlobalSlug,
|
||||
GlobalSlug,
|
||||
TypedLocale,
|
||||
TypedUser,
|
||||
} from '../index.js'
|
||||
import type { validOperators } from './constants.js'
|
||||
export type { Payload as Payload } from '../index.js'
|
||||
|
||||
@@ -140,3 +150,73 @@ export function docHasTimestamps(doc: any): doc is TypeWithTimestamps {
|
||||
export type IfAny<T, Y, N> = 0 extends 1 & T ? Y : N // This is a commonly used trick to detect 'any'
|
||||
export type IsAny<T> = IfAny<T, true, false>
|
||||
export type ReplaceAny<T, DefaultType> = IsAny<T> extends true ? DefaultType : T
|
||||
|
||||
export type SelectIncludeType = {
|
||||
[k: string]: SelectIncludeType | true
|
||||
}
|
||||
|
||||
export type SelectExcludeType = {
|
||||
[k: string]: false | SelectExcludeType
|
||||
}
|
||||
|
||||
export type SelectMode = 'exclude' | 'include'
|
||||
|
||||
export type SelectType = SelectExcludeType | SelectIncludeType
|
||||
|
||||
export type ApplyDisableErrors<T, DisableErrors extends boolean> = DisableErrors extends true
|
||||
? null | T
|
||||
: T
|
||||
|
||||
export type TransformDataWithSelect<
|
||||
Data extends Record<string, any>,
|
||||
Select extends SelectType,
|
||||
> = Select extends never
|
||||
? Data
|
||||
: string extends keyof Select
|
||||
? Data
|
||||
: // START Handle types when they aren't generated
|
||||
// For example in any package in this repository outside of tests / plugins
|
||||
// This stil gives us autocomplete when using include select mode, i.e select: {title :true} returns type {title: any, id: string | number}
|
||||
string extends keyof Omit<Data, 'id'>
|
||||
? Select extends SelectIncludeType
|
||||
? {
|
||||
[K in Data extends TypeWithID ? 'id' | keyof Select : keyof Select]: K extends 'id'
|
||||
? number | string
|
||||
: unknown
|
||||
}
|
||||
: Data
|
||||
: // END Handle types when they aren't generated
|
||||
// Handle include mode
|
||||
Select extends SelectIncludeType
|
||||
? {
|
||||
[K in keyof Data as K extends keyof Select
|
||||
? Select[K] extends object | true
|
||||
? K
|
||||
: never
|
||||
: // select 'id' always
|
||||
K extends 'id'
|
||||
? K
|
||||
: never]: Data[K]
|
||||
}
|
||||
: // Handle exclude mode
|
||||
{
|
||||
[K in keyof Data as K extends keyof Select
|
||||
? Select[K] extends object | undefined
|
||||
? K
|
||||
: never
|
||||
: K]: Data[K]
|
||||
}
|
||||
|
||||
export type TransformCollectionWithSelect<
|
||||
TSlug extends CollectionSlug,
|
||||
TSelect extends SelectType,
|
||||
> = TSelect extends SelectType
|
||||
? TransformDataWithSelect<DataFromCollectionSlug<TSlug>, TSelect>
|
||||
: DataFromCollectionSlug<TSlug>
|
||||
|
||||
export type TransformGlobalWithSelect<
|
||||
TSlug extends GlobalSlug,
|
||||
TSelect extends SelectType,
|
||||
> = TSelect extends SelectType
|
||||
? TransformDataWithSelect<DataFromGlobalSlug<TSlug>, TSelect>
|
||||
: DataFromGlobalSlug<TSlug>
|
||||
|
||||
@@ -0,0 +1,564 @@
|
||||
import { addSelectGenericsToGeneratedTypes } from './addSelectGenericsToGeneretedTypes.js'
|
||||
|
||||
const INPUT_AND_OUTPUT = [
|
||||
{
|
||||
input: `
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
* and re-run \`payload generate:types\` to regenerate this file.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
collections: {
|
||||
posts: Post;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
collectionsSelect?: {
|
||||
posts: PostsSelect;
|
||||
users: UsersSelect;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect;
|
||||
'payload-preferences': PayloadPreferencesSelect;
|
||||
'payload-migrations': PayloadMigrationsSelect;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {};
|
||||
globalsSelect?: {};
|
||||
locale: null;
|
||||
user: User & {
|
||||
collection: 'users';
|
||||
};
|
||||
}
|
||||
export interface UserAuthOperations {
|
||||
forgotPassword: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
login: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: string;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
group?: {
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
};
|
||||
array?:
|
||||
| {
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
blocks?:
|
||||
| (
|
||||
| {
|
||||
text?: string | null;
|
||||
introText?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'intro';
|
||||
}
|
||||
| {
|
||||
text?: string | null;
|
||||
ctaText?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
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;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect {
|
||||
text?: boolean;
|
||||
number?: boolean;
|
||||
group?:
|
||||
| boolean
|
||||
| {
|
||||
text?: boolean;
|
||||
number?: boolean;
|
||||
};
|
||||
array?:
|
||||
| boolean
|
||||
| {
|
||||
text?: boolean;
|
||||
number?: boolean;
|
||||
id?: boolean;
|
||||
};
|
||||
blocks?:
|
||||
| boolean
|
||||
| {
|
||||
intro?:
|
||||
| boolean
|
||||
| {
|
||||
text?: boolean;
|
||||
introText?: boolean;
|
||||
id?: boolean;
|
||||
blockName?: boolean;
|
||||
};
|
||||
cta?:
|
||||
| boolean
|
||||
| {
|
||||
text?: boolean;
|
||||
ctaText?: boolean;
|
||||
id?: boolean;
|
||||
blockName?: boolean;
|
||||
};
|
||||
};
|
||||
updatedAt?: boolean;
|
||||
createdAt?: boolean;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "users_select".
|
||||
*/
|
||||
export interface UsersSelect {
|
||||
updatedAt?: boolean;
|
||||
createdAt?: boolean;
|
||||
email?: boolean;
|
||||
resetPasswordToken?: boolean;
|
||||
resetPasswordExpiration?: boolean;
|
||||
salt?: boolean;
|
||||
hash?: boolean;
|
||||
loginAttempts?: boolean;
|
||||
lockUntil?: boolean;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-locked-documents_select".
|
||||
*/
|
||||
export interface PayloadLockedDocumentsSelect {
|
||||
document?: boolean;
|
||||
globalSlug?: boolean;
|
||||
user?: boolean;
|
||||
updatedAt?: boolean;
|
||||
createdAt?: boolean;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-preferences_select".
|
||||
*/
|
||||
export interface PayloadPreferencesSelect {
|
||||
user?: boolean;
|
||||
key?: boolean;
|
||||
value?: boolean;
|
||||
updatedAt?: boolean;
|
||||
createdAt?: boolean;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-migrations_select".
|
||||
*/
|
||||
export interface PayloadMigrationsSelect {
|
||||
name?: boolean;
|
||||
batch?: boolean;
|
||||
updatedAt?: boolean;
|
||||
createdAt?: boolean;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "auth".
|
||||
*/
|
||||
export interface Auth {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
declare module 'payload' {
|
||||
// @ts-ignore
|
||||
export interface GeneratedTypes extends Config {}
|
||||
}
|
||||
`,
|
||||
output: `
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* This file was automatically generated by Payload.
|
||||
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
|
||||
* and re-run \`payload generate:types\` to regenerate this file.
|
||||
*/
|
||||
|
||||
export interface Config {
|
||||
auth: {
|
||||
users: UserAuthOperations;
|
||||
};
|
||||
collections: {
|
||||
posts: Post;
|
||||
users: User;
|
||||
'payload-locked-documents': PayloadLockedDocument;
|
||||
'payload-preferences': PayloadPreference;
|
||||
'payload-migrations': PayloadMigration;
|
||||
};
|
||||
collectionsSelect?: {
|
||||
posts: PostsSelect<false> | PostsSelect<true>;
|
||||
users: UsersSelect<false> | UsersSelect<true>;
|
||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||
};
|
||||
db: {
|
||||
defaultIDType: string;
|
||||
};
|
||||
globals: {};
|
||||
globalsSelect?: {};
|
||||
locale: null;
|
||||
user: User & {
|
||||
collection: 'users';
|
||||
};
|
||||
}
|
||||
export interface UserAuthOperations {
|
||||
forgotPassword: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
login: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
registerFirstUser: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
unlock: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "posts".
|
||||
*/
|
||||
export interface Post {
|
||||
id: string;
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
group?: {
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
};
|
||||
array?:
|
||||
| {
|
||||
text?: string | null;
|
||||
number?: number | null;
|
||||
id?: string | null;
|
||||
}[]
|
||||
| null;
|
||||
blocks?:
|
||||
| (
|
||||
| {
|
||||
text?: string | null;
|
||||
introText?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'intro';
|
||||
}
|
||||
| {
|
||||
text?: string | null;
|
||||
ctaText?: string | null;
|
||||
id?: string | null;
|
||||
blockName?: string | null;
|
||||
blockType: 'cta';
|
||||
}
|
||||
)[]
|
||||
| null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "users".
|
||||
*/
|
||||
export interface User {
|
||||
id: string;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
email: string;
|
||||
resetPasswordToken?: string | null;
|
||||
resetPasswordExpiration?: string | null;
|
||||
salt?: string | null;
|
||||
hash?: string | null;
|
||||
loginAttempts?: number | null;
|
||||
lockUntil?: string | null;
|
||||
password?: string | null;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-locked-documents".
|
||||
*/
|
||||
export interface PayloadLockedDocument {
|
||||
id: string;
|
||||
document?:
|
||||
| ({
|
||||
relationTo: 'posts';
|
||||
value: string | Post;
|
||||
} | null)
|
||||
| ({
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
} | null);
|
||||
globalSlug?: string | null;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-preferences".
|
||||
*/
|
||||
export interface PayloadPreference {
|
||||
id: string;
|
||||
user: {
|
||||
relationTo: 'users';
|
||||
value: string | User;
|
||||
};
|
||||
key?: string | null;
|
||||
value?:
|
||||
| {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
| unknown[]
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null;
|
||||
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;
|
||||
batch?: number | null;
|
||||
updatedAt: string;
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "posts_select".
|
||||
*/
|
||||
export interface PostsSelect<T extends boolean = true> {
|
||||
text?: T;
|
||||
number?: T;
|
||||
group?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
number?: T;
|
||||
};
|
||||
array?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
number?: T;
|
||||
id?: T;
|
||||
};
|
||||
blocks?:
|
||||
| T
|
||||
| {
|
||||
intro?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
introText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
cta?:
|
||||
| T
|
||||
| {
|
||||
text?: T;
|
||||
ctaText?: T;
|
||||
id?: T;
|
||||
blockName?: T;
|
||||
};
|
||||
};
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "users_select".
|
||||
*/
|
||||
export interface UsersSelect<T extends boolean = true> {
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
email?: T;
|
||||
resetPasswordToken?: T;
|
||||
resetPasswordExpiration?: T;
|
||||
salt?: T;
|
||||
hash?: T;
|
||||
loginAttempts?: T;
|
||||
lockUntil?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-locked-documents_select".
|
||||
*/
|
||||
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
|
||||
document?: T;
|
||||
globalSlug?: T;
|
||||
user?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-preferences_select".
|
||||
*/
|
||||
export interface PayloadPreferencesSelect<T extends boolean = true> {
|
||||
user?: T;
|
||||
key?: T;
|
||||
value?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "payload-migrations_select".
|
||||
*/
|
||||
export interface PayloadMigrationsSelect<T extends boolean = true> {
|
||||
name?: T;
|
||||
batch?: T;
|
||||
updatedAt?: T;
|
||||
createdAt?: T;
|
||||
}
|
||||
/**
|
||||
* This interface was referenced by \`Config\`'s JSON-Schema
|
||||
* via the \`definition\` "auth".
|
||||
*/
|
||||
export interface Auth {
|
||||
[k: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
declare module 'payload' {
|
||||
// @ts-ignore
|
||||
export interface GeneratedTypes extends Config {}
|
||||
}
|
||||
`,
|
||||
},
|
||||
]
|
||||
|
||||
describe('addSelectGenericsToGeneratedTypes', () => {
|
||||
it('should match return of given input with output', () => {
|
||||
for (const { input, output } of INPUT_AND_OUTPUT) {
|
||||
expect(
|
||||
addSelectGenericsToGeneratedTypes({
|
||||
compiledGeneratedTypes: input,
|
||||
}),
|
||||
).toStrictEqual(output)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,51 @@
|
||||
export const addSelectGenericsToGeneratedTypes = ({
|
||||
compiledGeneratedTypes,
|
||||
}: {
|
||||
compiledGeneratedTypes: string
|
||||
}) => {
|
||||
const modifiedLines = []
|
||||
|
||||
let isCollectionsSelectToken = false
|
||||
let isSelectTypeToken = false
|
||||
|
||||
for (const line of compiledGeneratedTypes.split('\n')) {
|
||||
let newLine = line
|
||||
if (line === ` collectionsSelect?: {` || line === ` globalsSelect?: {`) {
|
||||
isCollectionsSelectToken = true
|
||||
}
|
||||
|
||||
if (isCollectionsSelectToken) {
|
||||
if (line === ' };') {
|
||||
isCollectionsSelectToken = false
|
||||
} else {
|
||||
// replace <posts: PostsSelect;> with <posts: PostsSelect<true> | PostsSelect<false;>
|
||||
newLine = line.replace(/(['"]?\w+['"]?):\s*(\w+);/g, (_, variable, type) => {
|
||||
return `${variable}: ${type}<false> | ${type}<true>;`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line regexp/no-unused-capturing-group
|
||||
if (line.match(/via the `definition` "([\w-]+_select)"/g)) {
|
||||
isSelectTypeToken = true
|
||||
}
|
||||
|
||||
if (isSelectTypeToken) {
|
||||
if (line.startsWith('export interface')) {
|
||||
// add generic to the interface
|
||||
newLine = line.replace(/(export interface\s+\w+)(\s*\{)/g, '$1<T extends boolean = true>$2')
|
||||
} else {
|
||||
// replace booleans with T on the line
|
||||
newLine = line.replace(/(?<!\?)\bboolean\b/g, 'T')
|
||||
|
||||
if (line === '}') {
|
||||
isSelectTypeToken = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
modifiedLines.push(newLine)
|
||||
}
|
||||
|
||||
return modifiedLines.join('\n')
|
||||
}
|
||||
@@ -73,6 +73,25 @@ function generateEntitySchemas(
|
||||
}
|
||||
}
|
||||
|
||||
function generateEntitySelectSchemas(
|
||||
entities: (SanitizedCollectionConfig | SanitizedGlobalConfig)[],
|
||||
): JSONSchema4 {
|
||||
const properties = [...entities].reduce((acc, { slug }) => {
|
||||
acc[slug] = {
|
||||
$ref: `#/definitions/${slug}_select`,
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties,
|
||||
required: Object.keys(properties),
|
||||
}
|
||||
}
|
||||
|
||||
function generateLocaleEntitySchemas(localization: SanitizedConfig['localization']): JSONSchema4 {
|
||||
if (localization && 'locales' in localization && localization?.locales) {
|
||||
const localesFromConfig = localization?.locales
|
||||
@@ -633,6 +652,98 @@ export function entityToJSONSchema(
|
||||
}
|
||||
}
|
||||
|
||||
export function fieldsToSelectJSONSchema({ fields }: { fields: Field[] }): JSONSchema4 {
|
||||
const schema: JSONSchema4 = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
}
|
||||
|
||||
for (const field of fields) {
|
||||
switch (field.type) {
|
||||
case 'row':
|
||||
case 'collapsible':
|
||||
schema.properties = {
|
||||
...schema.properties,
|
||||
...fieldsToSelectJSONSchema({ fields: field.fields }).properties,
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
case 'array':
|
||||
case 'group':
|
||||
schema.properties[field.name] = {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
fieldsToSelectJSONSchema({ fields: field.fields }),
|
||||
],
|
||||
}
|
||||
break
|
||||
|
||||
case 'tabs':
|
||||
for (const tab of field.tabs) {
|
||||
if (tabHasName(tab)) {
|
||||
schema.properties[tab.name] = {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
fieldsToSelectJSONSchema({ fields: tab.fields }),
|
||||
],
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
schema.properties = {
|
||||
...schema.properties,
|
||||
...fieldsToSelectJSONSchema({ fields: tab.fields }).properties,
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
case 'blocks': {
|
||||
const blocksSchema: JSONSchema4 = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {},
|
||||
}
|
||||
|
||||
for (const block of field.blocks) {
|
||||
blocksSchema.properties[block.slug] = {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
fieldsToSelectJSONSchema({ fields: block.fields }),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
schema.properties[field.name] = {
|
||||
oneOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
},
|
||||
blocksSchema,
|
||||
],
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
schema.properties[field.name] = {
|
||||
type: 'boolean',
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
const fieldType: JSONSchema4 = {
|
||||
type: 'string',
|
||||
required: false,
|
||||
@@ -803,13 +914,39 @@ export function configToJSONSchema(
|
||||
|
||||
// Collections and Globals have to be moved to the top-level definitions as well. Reason: The top-level type will be the `Config` type - we don't want all collection and global
|
||||
// types to be inlined inside the `Config` type
|
||||
const entityDefinitions: { [k: string]: JSONSchema4 } = [
|
||||
...config.globals,
|
||||
...config.collections,
|
||||
].reduce((acc, entity) => {
|
||||
|
||||
const entities: {
|
||||
entity: SanitizedCollectionConfig | SanitizedGlobalConfig
|
||||
type: 'collection' | 'global'
|
||||
}[] = [
|
||||
...config.globals.map((global) => ({ type: 'global' as const, entity: global })),
|
||||
...config.collections.map((collection) => ({
|
||||
type: 'collection' as const,
|
||||
entity: collection,
|
||||
})),
|
||||
]
|
||||
|
||||
const entityDefinitions: { [k: string]: JSONSchema4 } = entities.reduce(
|
||||
(acc, { type, entity }) => {
|
||||
acc[entity.slug] = entityToJSONSchema(config, entity, interfaceNameDefinitions, defaultIDType)
|
||||
const select = fieldsToSelectJSONSchema({ fields: entity.fields })
|
||||
|
||||
if (type === 'global') {
|
||||
select.properties.globalType = {
|
||||
type: 'boolean',
|
||||
}
|
||||
}
|
||||
|
||||
acc[`${entity.slug}_select`] = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
...select,
|
||||
}
|
||||
|
||||
return acc
|
||||
}, {})
|
||||
},
|
||||
{},
|
||||
)
|
||||
|
||||
const authOperationDefinitions = [...config.collections]
|
||||
.filter(({ auth }) => Boolean(auth))
|
||||
@@ -833,8 +970,10 @@ export function configToJSONSchema(
|
||||
properties: {
|
||||
auth: generateAuthOperationSchemas(config.collections),
|
||||
collections: generateEntitySchemas(config.collections || []),
|
||||
collectionsSelect: generateEntitySelectSchemas(config.collections || []),
|
||||
db: generateDbEntitySchema(config),
|
||||
globals: generateEntitySchemas(config.globals || []),
|
||||
globalsSelect: generateEntitySelectSchemas(config.globals || []),
|
||||
locale: generateLocaleEntitySchemas(config.localization),
|
||||
user: generateAuthEntitySchemas(config.collections),
|
||||
},
|
||||
|
||||
16
packages/payload/src/utilities/getSelectMode.ts
Normal file
16
packages/payload/src/utilities/getSelectMode.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { SelectMode, SelectType } from '../types/index.js'
|
||||
|
||||
export const getSelectMode = (select: SelectType): SelectMode => {
|
||||
for (const key in select) {
|
||||
const selectValue = select[key]
|
||||
if (selectValue === false) {
|
||||
return 'exclude'
|
||||
}
|
||||
|
||||
if (typeof selectValue === 'object') {
|
||||
return getSelectMode(selectValue)
|
||||
}
|
||||
}
|
||||
|
||||
return 'include'
|
||||
}
|
||||
26
packages/payload/src/versions/drafts/getQueryDraftsSelect.ts
Normal file
26
packages/payload/src/versions/drafts/getQueryDraftsSelect.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { SelectType } from '../../types/index.js'
|
||||
|
||||
import { getSelectMode } from '../../utilities/getSelectMode.js'
|
||||
|
||||
export const getQueryDraftsSelect = ({
|
||||
select,
|
||||
}: {
|
||||
select?: SelectType
|
||||
}): SelectType | undefined => {
|
||||
if (!select) {
|
||||
return
|
||||
}
|
||||
|
||||
const mode = getSelectMode(select)
|
||||
|
||||
if (mode === 'include') {
|
||||
return {
|
||||
parent: true,
|
||||
version: select,
|
||||
} as SelectType
|
||||
}
|
||||
|
||||
return {
|
||||
version: select,
|
||||
} as SelectType
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user