Skip to content

Commit

Permalink
Merge pull request #9 from omar-dulaimi:namespaced-routers-support
Browse files Browse the repository at this point in the history
Namespaced routers support
  • Loading branch information
omar-dulaimi authored Jan 13, 2023
2 parents 73397cd + 7545912 commit 7621b58
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 41 deletions.
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const permissions = shield<Context>({
mutation: {
addFruitToBasket: isAuthenticated,
},
})
});

export const t = trpc.initTRPC.context<Context>().create();

Expand All @@ -128,7 +128,32 @@ export const shieldedProcedure = t.procedure.use(permissionsMiddleware);
For a fully working example, [go here](https://github.com/omar-dulaimi/trpc-shield/tree/master/example).
## Documentation

### `shield(rules?, options?)`
### Namespaced routers

```ts
export const permissions = shield<Context>({
user: {
query: {
aggregateUser: allow,
findFirstUser: allow,
findManyUser: isAuthenticated,
findUniqueUser: allow,
groupByUser: allow,
},
mutation: {
createOneUser: isAuthenticated,
deleteManyUser: allow,
deleteOneUser: allow,
updateManyUser: allow,
updateOneUser: allow,
upsertOneUser: allow,
},
},
});
```

### API
#### `shield(rules?, options?)`

> Generates tRPC Middleware layer from your rules.
Expand Down
Binary file modified example/prisma/db.sqlite
Binary file not shown.
10 changes: 5 additions & 5 deletions example/prisma/shield/shield.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { allow, rule, shield } from '../../../dist'
import { Context } from '../../src/context'
import { allow, rule, shield } from '../../../dist';
import { Context } from '../../src/context';

const isAuthenticated = rule<Context>()(async (ctx, type, path, input, rawInput) => {
return ctx.user !== null
})
return ctx.user !== null;
});

export const permissions = shield<Context>({
query: {
Expand All @@ -21,4 +21,4 @@ export const permissions = shield<Context>({
updateOneUser: allow,
upsertOneUser: allow,
},
})
});
14 changes: 7 additions & 7 deletions example/src/context.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { PrismaClient } from '@prisma/client'
import * as trpc from '@trpc/server'
import { PrismaClient } from '@prisma/client';
import * as trpc from '@trpc/server';

export const createContext = () => {
const prisma = new PrismaClient()
const prisma = new PrismaClient();
return {
prisma,
user: null
}
}
user: null,
};
};

export type Context = trpc.inferAsyncReturnType<typeof createContext>
export type Context = trpc.inferAsyncReturnType<typeof createContext>;
20 changes: 10 additions & 10 deletions example/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import * as trpcExpress from '@trpc/server/adapters/express'
import express from 'express'
import { appRouter } from '../prisma/trpc/routers/index'
import { createContext } from './context'
import * as trpcExpress from '@trpc/server/adapters/express';
import express from 'express';
import { appRouter } from '../prisma/trpc/routers/index';
import { createContext } from './context';

const PORT = 3001
const PORT = 3001;

const app = express()
const app = express();

app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
}),
)
app.get('/', (req, res) => res.send('Express + Prisma + tRPC + tRPC Shield'))
);
app.get('/', (req, res) => res.send('Express + Prisma + tRPC + tRPC Shield'));

app.listen(PORT, () => {
console.log(`server listening at http://localhost:${PORT}`)
})
console.log(`server listening at http://localhost:${PORT}`);
});
2 changes: 1 addition & 1 deletion prettier.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
printWidth: 130,
semi: false,
semi: true,
singleQuote: true,
trailingComma: 'all',
}
42 changes: 27 additions & 15 deletions src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IOptions, IRules } from './types'
import { IOptions, IRules } from './types';

/**
*
Expand All @@ -20,23 +20,35 @@ export function generateMiddlewareFromRuleTree<TContext extends Record<string, u
input,
rawInput,
}: {
next: Function
ctx: { [name: string]: any }
type: string
path: string
input: { [name: string]: any }
rawInput: unknown
next: Function;
ctx: { [name: string]: any };
type: string;
path: string;
input: { [name: string]: any };
rawInput: unknown;
}) => {
const opWithPath: Array<string> = path.split('.')
const opName: string = opWithPath[opWithPath.length - 1]
const rule = ruleTree?.[type]?.[opName] || options.fallbackRule
const opWithPath: Array<string> = path.split('.');
const opName: string = opWithPath[opWithPath.length - 1];
const keys = Object.keys(ruleTree);
let rule;
if (keys.includes('query') || keys.includes('mutation')) {
rule = ruleTree?.[type]?.[opName] || options.fallbackRule;
} else {
for (const key of keys) {
const namespace = ruleTree[key];
if (namespace?.[type]?.[opName]) {
rule = namespace?.[type]?.[opName] || options.fallbackRule;
break;
}
}
}

if (rule) {
return rule?.resolve(ctx, type, path, input, rawInput, options).then((result: any) => {
if (!result) throw options.fallbackError
return next()
})
if (!result) throw options.fallbackError;
return next();
});
}
return next()
}
return next();
};
}
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface IRuleConstructorOptions {}
// Rules Definition Tree

export interface IRuleTypeMap<TContext> {
[key: string]: ShieldRule<TContext> | IRuleFieldMap<TContext>
[key: string]: ShieldRule<TContext> | IRuleFieldMap<TContext> | IRuleTypeMap<TContext>
}

export interface IRuleFieldMap<TContext> {
Expand Down

0 comments on commit 7621b58

Please sign in to comment.