fix: transactionID isolation for GraphQL (#4095)

This commit is contained in:
Take Weiland
2023-11-14 22:07:10 +01:00
committed by GitHub
parent 4bc5fa7086
commit 195a952c43
33 changed files with 309 additions and 40 deletions

View File

@@ -0,0 +1,59 @@
import { commitTransaction } from '../../packages/payload/src/utilities/commitTransaction'
import { initTransaction } from '../../packages/payload/src/utilities/initTransaction'
import { killTransaction } from '../../packages/payload/src/utilities/killTransaction'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
import { devUser } from '../credentials'
const resolveTransactionId = async (_obj, _args, context) => {
try {
const shouldCommit = await initTransaction(context.req)
const transactionID = context.req.transactionID
if (shouldCommit) {
await commitTransaction(context.req)
}
return transactionID ? String(transactionID) : null
} catch (e) {
await killTransaction(context.req)
throw e
}
}
export default buildConfigWithDefaults({
collections: [],
globals: [],
graphQL: {
queries: (GraphQL) => {
return {
TransactionID1: {
type: GraphQL.GraphQLString,
resolve: resolveTransactionId,
},
TransactionID2: {
type: GraphQL.GraphQLString,
resolve: resolveTransactionId,
},
}
},
mutations: (GraphQL) => {
return {
MutateTransactionID1: {
type: GraphQL.GraphQLString,
resolve: resolveTransactionId,
},
MutateTransactionID2: {
type: GraphQL.GraphQLString,
resolve: resolveTransactionId,
},
}
},
},
onInit: async (payload) => {
await payload.create({
collection: 'users',
data: {
email: devUser.email,
password: devUser.password,
},
})
},
})

View File

@@ -0,0 +1,42 @@
import { GraphQLClient } from 'graphql-request'
import { initPayloadTest } from '../helpers/configHelpers'
import configPromise from './config'
let client: GraphQLClient
describe('Custom GraphQL', () => {
beforeAll(async () => {
const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } })
const config = await configPromise
const url = `${serverURL}${config.routes.api}${config.routes.graphQL}`
client = new GraphQLClient(url)
})
describe('Isolated Transaction ID', () => {
it('should isolate transaction IDs between queries in the same request', async () => {
const query = `query {
TransactionID1
TransactionID2
}`
const response = await client.request(query)
// either no transactions at all or they are different
expect(
(response.TransactionID2 === null && response.TransactionID1 === null) ||
response.TransactionID2 !== response.TransactionID1,
).toBe(true)
})
it('should isolate transaction IDs between mutations in the same request', async () => {
const query = `mutation {
MutateTransactionID1
MutateTransactionID2
}`
const response = await client.request(query)
// either no transactions at all or they are different
expect(
(response.MutateTransactionID2 === null && response.MutateTransactionID1 === null) ||
response.MutateTransactionID2 !== response.MutateTransactionID1,
).toBe(true)
})
})
})

View File

@@ -0,0 +1,59 @@
/* 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 {
collections: {
users: User
'payload-preferences': PayloadPreference
'payload-migrations': PayloadMigration
}
globals: {}
}
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
}
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
}
export interface PayloadMigration {
id: string
name?: string | null
batch?: number | null
updatedAt: string
createdAt: string
}
declare module 'payload' {
export interface GeneratedTypes extends Config {}
}