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,91 @@ 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 (
178+ args ,
179+ { node : { req, res } , expressv4 : { req, res } } ,
180+ resolvedPreset
181+ ) ;
170182
171183 // Because we're connected as the database owner, we should manually switch to
172184 // the authenticator role
173- if ( ! pgSettings . role ) {
174- pgSettings . role = process . env . DATABASE_AUTHENTICATOR ;
185+ const context = args . contextValue as Grafast . Context ;
186+ if ( ! context . pgSettings ?. role ) {
187+ context . pgSettings = context . pgSettings ?? { } ;
188+ context . pgSettings . role = process . env . DATABASE_AUTHENTICATOR as string ;
175189 }
176190
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- } ) ;
191+ const pgClient = await rootPgPool . connect ( ) ;
192+ try {
193+ await pgClient . query ( "begin" ) ;
194+
195+ // Override withPgClient with a transactional version for the tests
196+ const withPgClient = makeWithPgClientViaPgClientAlreadyInTransaction (
197+ pgClient ,
198+ true
199+ ) ;
200+ context . withPgClient = withPgClient ;
201+
202+ const result = ( await execute ( args , resolvedPreset ) ) as ExecutionResult ;
203+ // Expand errors
204+ if ( result . errors ) {
205+ if ( resolvedPreset . grafserv ?. maskError ) {
206+ result . errors = result . errors . map ( resolvedPreset . grafserv . maskError ) ;
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.
225223 }
226- return e ;
227224 } ) ;
228225 }
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,
226+ return e ;
237227 } ) ;
228+ }
229+ }
238230
239- // You don't have to keep this, I just like knowing when things change!
240- expect ( sanitize ( result ) ) . toMatchSnapshot ( ) ;
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,
237+ } ) ;
241238
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- }
239+ // You don't have to keep this, I just like knowing when things change!
240+ expect ( sanitize ( result ) ) . toMatchSnapshot ( ) ;
241+
242+ return checkResult == null ? result : checkResult ;
243+ } finally {
244+ try {
245+ await pgClient . query ( "rollback" ) ;
246+ } finally {
247+ pgClient . release ( ) ;
248248 }
249- ) ;
249+ }
250250} ;
0 commit comments