feat: add upsert to database interface and adapters (#8397)
- Adds the upsert method to the database interface - Adds a mongodb specific option to extend the updateOne to accept mongoDB Query Options (to pass `upsert: true`) - Added upsert method to all database adapters - Uses db.upsert in the payload preferences update operation Includes a test using payload-preferences
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { CollationOptions, TransactionOptions } from 'mongodb'
|
||||
import type { MongoMemoryReplSet } from 'mongodb-memory-server'
|
||||
import type { ClientSession, Connection, ConnectOptions } from 'mongoose'
|
||||
import type { BaseDatabaseAdapter, DatabaseAdapterObj, Payload } from 'payload'
|
||||
import type { ClientSession, Connection, ConnectOptions, QueryOptions } from 'mongoose'
|
||||
import type { BaseDatabaseAdapter, DatabaseAdapterObj, Payload, UpdateOneArgs } from 'payload'
|
||||
|
||||
import fs from 'fs'
|
||||
import mongoose from 'mongoose'
|
||||
@@ -36,6 +36,7 @@ import { updateGlobal } from './updateGlobal.js'
|
||||
import { updateGlobalVersion } from './updateGlobalVersion.js'
|
||||
import { updateOne } from './updateOne.js'
|
||||
import { updateVersion } from './updateVersion.js'
|
||||
import { upsert } from './upsert.js'
|
||||
|
||||
export type { MigrateDownArgs, MigrateUpArgs } from './types.js'
|
||||
|
||||
@@ -124,6 +125,7 @@ declare module 'payload' {
|
||||
}[]
|
||||
sessions: Record<number | string, ClientSession>
|
||||
transactionOptions: TransactionOptions
|
||||
updateOne: (args: { options?: QueryOptions } & UpdateOneArgs) => Promise<Document>
|
||||
versions: {
|
||||
[slug: string]: CollectionModel
|
||||
}
|
||||
@@ -191,6 +193,7 @@ export function mongooseAdapter({
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { QueryOptions } from 'mongoose'
|
||||
import type { PayloadRequest, UpdateOne } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
@@ -9,11 +10,20 @@ import { withSession } from './withSession.js'
|
||||
|
||||
export const updateOne: UpdateOne = async function updateOne(
|
||||
this: MongooseAdapter,
|
||||
{ id, collection, data, locale, req = {} as PayloadRequest, where: whereArg },
|
||||
{
|
||||
id,
|
||||
collection,
|
||||
data,
|
||||
locale,
|
||||
options: optionsArgs = {},
|
||||
req = {} as PayloadRequest,
|
||||
where: whereArg,
|
||||
},
|
||||
) {
|
||||
const where = id ? { id: { equals: id } } : whereArg
|
||||
const Model = this.collections[collection]
|
||||
const options = {
|
||||
const options: QueryOptions = {
|
||||
...optionsArgs,
|
||||
...(await withSession(this, req)),
|
||||
lean: true,
|
||||
new: true,
|
||||
|
||||
10
packages/db-mongodb/src/upsert.ts
Normal file
10
packages/db-mongodb/src/upsert.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { PayloadRequest, Upsert } from 'payload'
|
||||
|
||||
import type { MongooseAdapter } from './index.js'
|
||||
|
||||
export const upsert: Upsert = async function upsert(
|
||||
this: MongooseAdapter,
|
||||
{ collection, data, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
return this.updateOne({ collection, data, locale, options: { upsert: true }, req, where })
|
||||
}
|
||||
@@ -150,6 +150,7 @@ export function postgresAdapter(args: Args): DatabaseAdapterObj<PostgresAdapter>
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert: updateOne,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +151,7 @@ export function sqliteAdapter(args: Args): DatabaseAdapterObj<SQLiteAdapter> {
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert: updateOne,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -150,6 +150,7 @@ export function vercelPostgresAdapter(args: Args = {}): DatabaseAdapterObj<Verce
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
upsert: updateOne,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,8 @@ export interface BaseDatabaseAdapter {
|
||||
updateOne: UpdateOne
|
||||
|
||||
updateVersion: UpdateVersion
|
||||
|
||||
upsert: Upsert
|
||||
}
|
||||
|
||||
export type Init = () => Promise<void> | void
|
||||
@@ -380,6 +382,10 @@ export type UpdateOneArgs = {
|
||||
draft?: boolean
|
||||
joins?: JoinQuery
|
||||
locale?: string
|
||||
/**
|
||||
* Additional database adapter specific options to pass to the query
|
||||
*/
|
||||
options?: Record<string, unknown>
|
||||
req: PayloadRequest
|
||||
} & (
|
||||
| {
|
||||
@@ -394,6 +400,17 @@ export type UpdateOneArgs = {
|
||||
|
||||
export type UpdateOne = (args: UpdateOneArgs) => Promise<Document>
|
||||
|
||||
export type UpsertArgs = {
|
||||
collection: string
|
||||
data: Record<string, unknown>
|
||||
joins?: JoinQuery
|
||||
locale?: string
|
||||
req: PayloadRequest
|
||||
where: Where
|
||||
}
|
||||
|
||||
export type Upsert = (args: UpsertArgs) => Promise<Document>
|
||||
|
||||
export type DeleteOneArgs = {
|
||||
collection: string
|
||||
joins?: JoinQuery
|
||||
|
||||
@@ -821,6 +821,7 @@ export type {
|
||||
UpdateOneArgs,
|
||||
UpdateVersion,
|
||||
UpdateVersionArgs,
|
||||
Upsert,
|
||||
} from './database/types.js'
|
||||
export type { EmailAdapter as PayloadEmailAdapter, SendEmailOptions } from './email/types.js'
|
||||
export {
|
||||
|
||||
@@ -35,23 +35,10 @@ export async function update(args: PreferenceUpdateRequest) {
|
||||
value,
|
||||
}
|
||||
|
||||
let result
|
||||
|
||||
try {
|
||||
// try/catch because we attempt to update without first reading to check if it exists first to save on db calls
|
||||
result = await payload.db.updateOne({
|
||||
collection,
|
||||
data: preference,
|
||||
req,
|
||||
where,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
result = await payload.db.create({
|
||||
collection,
|
||||
data: preference,
|
||||
req,
|
||||
})
|
||||
}
|
||||
|
||||
return result
|
||||
return await payload.db.upsert({
|
||||
collection,
|
||||
data: preference,
|
||||
req,
|
||||
where,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ describe('Auth', () => {
|
||||
|
||||
describe('logged in', () => {
|
||||
let token: string | undefined
|
||||
let loggedInUser: User | undefined
|
||||
let loggedInUser: undefined | User
|
||||
|
||||
beforeAll(async () => {
|
||||
const response = await restClient.POST(`/${slug}/login`, {
|
||||
@@ -396,6 +396,52 @@ describe('Auth', () => {
|
||||
expect(result.docs).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should only have one preference per user per key', async () => {
|
||||
await restClient.POST(`/payload-preferences/${key}`, {
|
||||
body: JSON.stringify({
|
||||
value: { property: 'test', property2: 'test' },
|
||||
}),
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
},
|
||||
})
|
||||
await restClient.POST(`/payload-preferences/${key}`, {
|
||||
body: JSON.stringify({
|
||||
value: { property: 'updated', property2: 'updated' },
|
||||
}),
|
||||
headers: {
|
||||
Authorization: `JWT ${token}`,
|
||||
},
|
||||
})
|
||||
|
||||
const result = await payload.find({
|
||||
collection: 'payload-preferences',
|
||||
depth: 0,
|
||||
where: {
|
||||
and: [
|
||||
{
|
||||
key: { equals: key },
|
||||
},
|
||||
{
|
||||
'user.relationTo': {
|
||||
equals: 'users',
|
||||
},
|
||||
},
|
||||
{
|
||||
'user.value': {
|
||||
equals: loggedInUser.id,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(result.docs[0].value.property).toStrictEqual('updated')
|
||||
expect(result.docs[0].value.property2).toStrictEqual('updated')
|
||||
|
||||
expect(result.docs).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should delete', async () => {
|
||||
const response = await restClient.DELETE(`/payload-preferences/${key}`, {
|
||||
headers: {
|
||||
|
||||
Reference in New Issue
Block a user