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,90 @@ 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 , resolvedPreset , {
178+ node : { req, res } ,
179+ expressv4 : { req, res } ,
180+ } ) ;
170181
171182 // Because we're connected as the database owner, we should manually switch to
172183 // the authenticator role
173- if ( ! pgSettings . role ) {
174- pgSettings . role = process . env . DATABASE_AUTHENTICATOR ;
184+ const context = args . contextValue as Grafast . Context ;
185+ if ( ! context . pgSettings ?. role ) {
186+ context . pgSettings = context . pgSettings ?? { } ;
187+ context . pgSettings . role = process . env . DATABASE_AUTHENTICATOR as string ;
175188 }
176189
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- } ) ;
190+ const pgClient = await rootPgPool . connect ( ) ;
191+ try {
192+ await pgClient . query ( "begin" ) ;
193+
194+ // Override withPgClient with a transactional version for the tests
195+ const withPgClient = makeWithPgClientViaPgClientAlreadyInTransaction (
196+ pgClient ,
197+ true
198+ ) ;
199+ context . withPgClient = withPgClient ;
200+
201+ const result = ( await execute ( args , resolvedPreset ) ) as ExecutionResult ;
202+ // Expand errors
203+ if ( result . errors ) {
204+ if ( resolvedPreset . grafserv ?. maskError ) {
205+ result . errors = result . errors . map ( resolvedPreset . grafserv . maskError ) ;
206+ } else {
207+ // This does a similar transform that PostGraphile does to errors.
208+ // It's not the same. Sorry.
209+ result . errors = result . errors . map ( ( rawErr ) => {
210+ const e = Object . create ( rawErr ) ;
211+ Object . defineProperty ( e , "originalError" , {
212+ value : rawErr . originalError ,
213+ enumerable : false ,
214+ } ) ;
215+
216+ if ( e . originalError ) {
217+ Object . keys ( e . originalError ) . forEach ( ( k ) => {
218+ try {
219+ e [ k ] = e . originalError [ k ] ;
220+ } catch ( err ) {
221+ // Meh.
225222 }
226- return e ;
227223 } ) ;
228224 }
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,
225+ return e ;
237226 } ) ;
227+ }
228+ }
238229
239- // You don't have to keep this, I just like knowing when things change!
240- expect ( sanitize ( result ) ) . toMatchSnapshot ( ) ;
230+ // This is were we call the `checker` so you can do your assertions.
231+ // Also note that we pass the `replacementPgClient` so that you can
232+ // query the data in the database from within the transaction before it
233+ // gets rolled back.
234+ checkResult = await checker ( result , {
235+ pgClient,
236+ } ) ;
241237
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- }
238+ // You don't have to keep this, I just like knowing when things change!
239+ expect ( sanitize ( result ) ) . toMatchSnapshot ( ) ;
240+
241+ return checkResult == null ? result : checkResult ;
242+ } finally {
243+ try {
244+ await pgClient . query ( "rollback" ) ;
245+ } finally {
246+ pgClient . release ( ) ;
248247 }
249- ) ;
248+ }
250249} ;
0 commit comments