11import { createServer } from "node:http" ;
22import { and , eq } from "drizzle-orm" ;
33import { drizzle } from "drizzle-orm/node-postgres" ;
4- import { rumble } from "../../lib" ;
54import {
65 assertFindFirstExists ,
76 assertFirstEntryExists ,
8- } from "../../lib/helpers/helper" ;
9- import type {
10- DrizzleQueryFunction ,
11- DrizzleQueryFunctionInput ,
12- InternalDrizzleInstance ,
13- } from "../../lib/types/drizzleInstanceType" ;
7+ mapNullFieldsToUndefined ,
8+ rumble ,
9+ } from "../../lib" ;
10+ import { mergeFilters } from "../../lib/helpers/mergeFilters" ;
1411import { relations } from "./db/relations" ;
1512import * as schema from "./db/schema" ;
1613
@@ -32,26 +29,6 @@ export const db = drizzle(
3229 } ,
3330) ;
3431
35- const d = rumble ( {
36- // here we pass the db instance from above
37- db,
38- // this is how we can define a context callback
39- // it takes a request object as an argument and returns the objects you want in the request context
40- // similar to the context callback in express or similar frameworks
41- // the type of the request parameter may vary based on the HTTP library you are using
42- context ( _request ) {
43- return {
44- // for our usecase we simply mock a user ID to be set in the context
45- // this will allow us to perform permission checks based on who the user is
46- userId : 2 ,
47- } ;
48- } ,
49- // in case you want to allow searching via string in the helper implementations
50- // search: {
51- // enabled: true,
52- // },
53- } ) ;
54-
5532/*
5633
5734 Next, we can create a rumble instance. The creator returns a set of functions which you
@@ -99,8 +76,6 @@ const {
9976
10077*/
10178
102- abilityBuilder . users . allow ( [ "read" , "update" , "delete" ] ) . when ( ) ;
103-
10479// users can edit themselves
10580abilityBuilder . users . allow ( [ "read" , "update" , "delete" ] ) . when ( ( { userId } ) => ( {
10681 where : {
@@ -111,6 +86,13 @@ abilityBuilder.users.allow(["read", "update", "delete"]).when(({ userId }) => ({
11186// everyone can read posts
11287abilityBuilder . posts . allow ( "read" ) ;
11388
89+ // or define a static condition
90+ // abilityBuilder.posts.allow("read").when({
91+ // where: {
92+ // published: true,
93+ // },
94+ // });
95+
11496// only the author can update posts
11597abilityBuilder . posts . allow ( [ "update" , "delete" ] ) . when ( ( { userId } ) => ( {
11698 where : {
@@ -157,7 +139,11 @@ abilityBuilder.posts.filter("read").by(({ context: _context, entities }) => {
157139const PostRef = schemaBuilder . drizzleObject ( "posts" , {
158140 name : "Post" ,
159141 // this is how you can apply application level filter in manual object definitions
160- applyFilters : abilityBuilder . _ . registeredFilters . posts . read ,
142+ // the helpers will do this for you automatically
143+ applyFilters : abilityBuilder . _ . registeredFilters ( {
144+ action : "read" ,
145+ table : "posts" ,
146+ } ) ,
161147 fields : ( t ) => ( {
162148 id : t . exposeInt ( "id" ) ,
163149 content : t . exposeString ( "content" , { nullable : false } ) ,
@@ -245,20 +231,15 @@ schemaBuilder.queryFields((t) => {
245231 where : t . arg ( { type : PostWhere } ) ,
246232 } ,
247233 resolve : ( query , _root , args , ctx , _info ) => {
234+ // a helper to map null fields to undefined in any object
235+ const mappedArgs = mapNullFieldsToUndefined ( args ) ;
248236 return db . query . posts . findMany (
249237 query (
250- ctx . abilities . posts . filter ( "read" , {
251- // this additional object offers temporarily injecting additional filters to our existing ability filters
252- // the inject field allows for temp, this time only filters to be added to our ability filters.
253- // They will only be applied for this specific call. This is a helper which exists because of the old
254- // filter API, and enabled easier handling. It is convenient since it provides proper typings and takes
255- // some boilerplate off your shoulders and will stay in the API. Where conditions which are injected
256- // will be applied with an AND rather than an OR so the injected filter will further restrict the
257- // existing restrictions rather than expanding them.
258- inject : {
259- where : args . where ?? undefined ,
260- } ,
261- } ) . query . many ,
238+ // a helper to merge multiple filter objects into one
239+ // so we can easily apply both the permissions filter and the user provided filter
240+ mergeFilters ( ctx . abilities . posts . filter ( "read" ) . query . many , {
241+ where : mappedArgs . where ,
242+ } ) ,
262243 ) ,
263244 ) ;
264245 } ,
@@ -333,11 +314,9 @@ schemaBuilder.mutationFields((t) => {
333314 db . query . users
334315 . findFirst (
335316 query (
336- ctx . abilities . users . filter ( "read" , {
337- inject : {
338- where : { id : args . userId } ,
339- } ,
340- } ) . query . single ,
317+ mergeFilters ( ctx . abilities . users . filter ( "read" ) . query . single , {
318+ where : { id : args . userId } ,
319+ } ) ,
341320 ) ,
342321 )
343322 // this maps the db response to a graphql response
@@ -373,17 +352,25 @@ schemaBuilder.mutationFields((t) => {
373352 // this notifies all subscribers that a user has been added
374353 createdUser ( ) ;
375354
376- return db . query . users
377- . findFirst (
378- query (
379- ctx . abilities . users . filter ( "read" , {
380- inject : {
381- where : { id : newUser . id } ,
382- } ,
383- } ) . query . single ,
384- ) ,
385- )
386- . then ( assertFindFirstExists ) ;
355+ return (
356+ db . query . users
357+ // run the db query
358+ . findFirst (
359+ // inject all graphql selections which ought to be queried from the db
360+ query (
361+ // merge multiple filter objects which should be applied to the query
362+ mergeFilters (
363+ {
364+ // only retrieve the newly created user
365+ where : { id : newUser . id } ,
366+ } ,
367+ // only retrieve the user if the caller is allowed to read it
368+ ctx . abilities . users . filter ( "read" ) . query . single ,
369+ ) ,
370+ ) ,
371+ )
372+ . then ( assertFindFirstExists )
373+ ) ;
387374 } ,
388375 } ) ,
389376 } ;
0 commit comments