Skip to content

Commit cb1cc88

Browse files
committed
🚧 wip: ret type merge helper typings
1 parent 4171f2f commit cb1cc88

23 files changed

Lines changed: 642 additions & 543 deletions

‎biome.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"$schema": "https://biomejs.dev/schemas/2.1.1/schema.json",
2+
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
33
"vcs": {
44
"enabled": true,
55
"clientKind": "git",

‎bun.lock‎

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎example/src/main.ts‎

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { createServer } from "node:http";
22
import { and, eq } from "drizzle-orm";
33
import { drizzle } from "drizzle-orm/node-postgres";
4-
import { rumble } from "../../lib";
54
import {
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";
1411
import { relations } from "./db/relations";
1512
import * 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
10580
abilityBuilder.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
11287
abilityBuilder.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
11597
abilityBuilder.posts.allow(["update", "delete"]).when(({ userId }) => ({
11698
where: {
@@ -157,7 +139,11 @@ abilityBuilder.posts.filter("read").by(({ context: _context, entities }) => {
157139
const 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

Comments
 (0)