1- import { Request , Response } from "express" ;
2- import { ExecutionResult , graphql , GraphQLSchema } from "graphql" ;
3- import { Pool , PoolClient } from "pg" ;
1+ import { makeWithPgClientViaPgClientAlreadyInTransaction } from "@dataplan/pg/adaptors/pg" ;
2+ import { execute , hookArgs } from "grafast" ;
43import {
5- createPostGraphileSchema ,
6- PostGraphileOptions ,
7- withPostGraphileContext ,
8- } from "postgraphile" ;
4+ ExecutionArgs ,
5+ ExecutionResult ,
6+ GraphQLSchema ,
7+ parse ,
8+ validate ,
9+ } from "graphql" ;
10+ import { Pool , PoolClient } from "pg" ;
11+ import { postgraphile , PostGraphileInstance } from "postgraphile" ;
912
1013import {
1114 createSession ,
1215 createUsers ,
1316 poolFromUrl ,
1417} from "../../__tests__/helpers" ;
15- import { getPostGraphileOptions } from "../src/graphile.config" ;
18+ import { getPreset } from "../src/graphile.config" ;
1619
1720export * from "../../__tests__/helpers" ;
1821
@@ -96,7 +99,7 @@ export function sanitize(json: any): any {
9699// Contains the PostGraphile schema and rootPgPool
97100interface ICtx {
98101 rootPgPool : Pool ;
99- options : PostGraphileOptions < Request , Response > ;
102+ pgl : PostGraphileInstance ;
100103 schema : GraphQLSchema ;
101104}
102105let ctx : ICtx | null = null ;
@@ -106,17 +109,14 @@ export const setup = async () => {
106109 connectionString : process . env . TEST_DATABASE_URL ,
107110 } ) ;
108111
109- const options = getPostGraphileOptions ( { rootPgPool } ) ;
110- const schema = await createPostGraphileSchema (
111- rootPgPool ,
112- "app_public" ,
113- options
114- ) ;
112+ const preset = getPreset ( { rootPgPool, authPgPool : rootPgPool } ) ;
113+ const pgl = postgraphile ( preset ) ;
114+ const schema = await pgl . getSchema ( ) ;
115115
116116 // Store the context
117117 ctx = {
118118 rootPgPool,
119- options ,
119+ pgl ,
120120 schema,
121121 } ;
122122} ;
@@ -146,9 +146,10 @@ export const runGraphQLQuery = async function runGraphQLQuery(
146146 ) => void | ExecutionResult | Promise < void | ExecutionResult > = ( ) => { } // Place test assertions in this function
147147) {
148148 if ( ! ctx ) throw new Error ( "No ctx!" ) ;
149- const { schema, rootPgPool, options } = ctx ;
149+ const { schema, rootPgPool, pgl } = ctx ;
150+ const resolvedPreset = pgl . getResolvedPreset ( ) ;
150151 const req = new MockReq ( {
151- url : options . graphqlRoute || "/graphql" ,
152+ url : resolvedPreset . grafserv ?. graphqlPath || "/graphql" ,
152153 method : "POST" ,
153154 headers : {
154155 Accept : "application/json" ,
@@ -159,92 +160,87 @@ export const runGraphQLQuery = async function runGraphQLQuery(
159160 const res : any = { req } ;
160161 req . res = res ;
161162
162- const {
163- pgSettings : pgSettingsGenerator ,
164- additionalGraphQLContextFromRequest,
165- } = options ;
166- const pgSettings =
167- ( typeof pgSettingsGenerator === "function"
168- ? await pgSettingsGenerator ( req )
169- : pgSettingsGenerator ) || { } ;
163+ let checkResult : ExecutionResult | void ;
164+ const document = parse ( query ) ;
165+ const errors = validate ( schema , document ) ;
166+ if ( errors . length > 0 ) {
167+ throw errors [ 0 ] ;
168+ }
169+ const args : ExecutionArgs = {
170+ schema,
171+ document,
172+ contextValue : {
173+ __TESTING : true ,
174+ } ,
175+ variableValues : variables ,
176+ } ;
177+ await hookArgs ( args , { node : { req, res } } , resolvedPreset ) ;
170178
171179 // Because we're connected as the database owner, we should manually switch to
172180 // the authenticator role
173- if ( ! pgSettings . role ) {
174- pgSettings . role = process . env . DATABASE_AUTHENTICATOR ;
181+ const context = args . contextValue as Grafast . Context ;
182+ if ( ! context . pgSettings ?. role ) {
183+ context . pgSettings = context . pgSettings ?? { } ;
184+ context . pgSettings . role = process . env . DATABASE_AUTHENTICATOR as string ;
175185 }
176186
177- await withPostGraphileContext (
178- {
179- ...options ,
180- pgPool : rootPgPool ,
181- pgSettings,
182- pgForceTransaction : true ,
183- } ,
184- async ( context ) => {
185- let checkResult ;
186- const { pgClient } = context ;
187- try {
188- // This runs our GraphQL query, passing the replacement client
189- const additionalContext = additionalGraphQLContextFromRequest
190- ? await additionalGraphQLContextFromRequest ( req , res )
191- : null ;
192- const result = await graphql (
193- schema ,
194- query ,
195- null ,
196- {
197- ...context ,
198- ...additionalContext ,
199- __TESTING : true ,
200- } ,
201- variables
202- ) ;
203- // Expand errors
204- if ( result . errors ) {
205- if ( options . handleErrors ) {
206- result . errors = options . handleErrors ( result . errors ) ;
207- } else {
208- // This does a similar transform that PostGraphile does to errors.
209- // It's not the same. Sorry.
210- result . errors = result . errors . map ( ( rawErr ) => {
211- const e = Object . create ( rawErr ) ;
212- Object . defineProperty ( e , "originalError" , {
213- value : rawErr . originalError ,
214- enumerable : false ,
215- } ) ;
216-
217- if ( e . originalError ) {
218- Object . keys ( e . originalError ) . forEach ( ( k ) => {
219- try {
220- e [ k ] = e . originalError [ k ] ;
221- } catch ( err ) {
222- // Meh.
223- }
224- } ) ;
187+ const pgClient = await rootPgPool . connect ( ) ;
188+ try {
189+ await pgClient . query ( "begin" ) ;
190+
191+ // Override withPgClient with a transactional version for the tests
192+ const withPgClient = makeWithPgClientViaPgClientAlreadyInTransaction (
193+ pgClient ,
194+ true
195+ ) ;
196+ context . withPgClient = withPgClient ;
197+
198+ const result = ( await execute ( args , resolvedPreset ) ) as ExecutionResult ;
199+ // Expand errors
200+ if ( result . errors ) {
201+ if ( resolvedPreset . grafserv ?. maskError ) {
202+ result . errors = result . errors . map ( resolvedPreset . grafserv . maskError ) ;
203+ } else {
204+ // This does a similar transform that PostGraphile does to errors.
205+ // It's not the same. Sorry.
206+ result . errors = result . errors . map ( ( rawErr ) => {
207+ const e = Object . create ( rawErr ) ;
208+ Object . defineProperty ( e , "originalError" , {
209+ value : rawErr . originalError ,
210+ enumerable : false ,
211+ } ) ;
212+
213+ if ( e . originalError ) {
214+ Object . keys ( e . originalError ) . forEach ( ( k ) => {
215+ try {
216+ e [ k ] = e . originalError [ k ] ;
217+ } catch ( err ) {
218+ // Meh.
225219 }
226- return e ;
227220 } ) ;
228221 }
229- }
230-
231- // This is were we call the `checker` so you can do your assertions.
232- // Also note that we pass the `replacementPgClient` so that you can
233- // query the data in the database from within the transaction before it
234- // gets rolled back.
235- checkResult = await checker ( result , {
236- pgClient,
222+ return e ;
237223 } ) ;
224+ }
225+ }
238226
239- // You don't have to keep this, I just like knowing when things change!
240- expect ( sanitize ( result ) ) . toMatchSnapshot ( ) ;
227+ // This is were we call the `checker` so you can do your assertions.
228+ // Also note that we pass the `replacementPgClient` so that you can
229+ // query the data in the database from within the transaction before it
230+ // gets rolled back.
231+ checkResult = await checker ( result , {
232+ pgClient,
233+ } ) ;
241234
242- return checkResult == null ? result : checkResult ;
243- } finally {
244- // Rollback the transaction so no changes are written to the DB - this
245- // makes our tests fairly deterministic.
246- await pgClient . query ( "rollback" ) ;
247- }
235+ // You don't have to keep this, I just like knowing when things change!
236+ expect ( sanitize ( result ) ) . toMatchSnapshot ( ) ;
237+
238+ return checkResult == null ? result : checkResult ;
239+ } finally {
240+ try {
241+ await pgClient . query ( "rollback" ) ;
242+ } finally {
243+ pgClient . release ( ) ;
248244 }
249- ) ;
245+ }
250246} ;
0 commit comments