Skip to content

Commit

Permalink
Support enhancing resolvers with decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
MichalLytek committed Nov 11, 2020
1 parent 6d4460f commit 4f93194
Show file tree
Hide file tree
Showing 16 changed files with 630 additions and 77 deletions.
54 changes: 54 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,60 @@ export class CustomUserResolver {
}
```

#### Additional decorators for Prisma schema resolvers

When you need to apply some decorators like `@Authorized`, `@UseMiddleware` or `@Extensions` on the generated resolvers methods, you don't need to modify the generated source files.

To support this, `typegraphql-prisma` generates two things: `applyResolversEnhanceMap` function and a `ResolversEnhanceMap` type. All you need to do is to create a config object, where you put the decorator functions (without `@`) in an array, and then call that function with that config, eg.:

```ts
import {
ResolversEnhanceMap,
applyResolversEnhanceMap,
} from "@generated/type-graphql";
import { Authorized } from "type-graphql";

const resolversEnhanceMap: ResolversEnhanceMap = {
Category: {
createCategory: [Authorized(Role.ADMIN)],
},
};

applyResolversEnhanceMap(resolversEnhanceMap);
```

This way, when you call `createCategory` GraphQL mutation, it will trigger the `type-graphql` `authChecker` function, providing a `Role.ADMIN` role, just like you would put the `@Authorized` on top of the resolver method.

Also, if you have a large schema and you need to provide plenty of decorators, you can split the config definition into multiple smaller objects placed in different files.
To accomplish this, just import the generic `ResolverActionsConfig` type and define the resolvers config separately for every Prisma schema model, e.g:

```ts
import {
ResolversEnhanceMap,
ResolverActionsConfig,
applyResolversEnhanceMap,
} from "@generated/type-graphql";
import { Authorized, Extensions } from "type-graphql";

// define the decorators config using generic ResolverActionsConfig<TModelName> type
const categoryActionsConfig: ResolverActionsConfig<"Category"> = {
deleteCategory: [
Authorized(Role.ADMIN),
Extensions({ logMessage: "Danger zone", logLevel: LogLevel.WARN }),
],
};
const problemActionsConfig: ResolverActionsConfig<"Problem"> = {
createProblem: [Authorized()],
};

// join the actions config into a single resolvers enhance object
const resolversEnhanceMap: ResolversEnhanceMap = {
Category: categoryActionsConfig,
Problem: problemActionsConfig,
};
applyResolversEnhanceMap(resolversEnhanceMap);
```

#### Adding fields to model type

If you want to add a field to the generated type like `User`, you have to add a proper `@FieldResolver` for that:
Expand Down
51 changes: 0 additions & 51 deletions experiments/generated-schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------

type AggregateCategory {
avg: CategoryAvgAggregate
count: Int!
max: CategoryMaxAggregate
min: CategoryMinAggregate
sum: CategorySumAggregate
}

type AggregateClient {
avg: ClientAvgAggregate
count: Int!
Expand Down Expand Up @@ -66,52 +58,18 @@ type Category {
slug: String!
}

type CategoryAvgAggregate {
number: Float!
}

input CategoryCreateInput {
name: String!
number: Int!
slug: String!
}

enum CategoryDistinctFieldEnum {
name
number
slug
}

type CategoryMaxAggregate {
number: Int!
}

type CategoryMinAggregate {
number: Int!
}

input CategoryOrderByInput {
name: SortOrder
number: SortOrder
slug: SortOrder
}

type CategorySumAggregate {
number: Int!
}

input CategoryUpdateInput {
name: StringFieldUpdateOperationsInput
number: IntFieldUpdateOperationsInput
slug: StringFieldUpdateOperationsInput
}

input CategoryUpdateManyMutationInput {
name: StringFieldUpdateOperationsInput
number: IntFieldUpdateOperationsInput
slug: StringFieldUpdateOperationsInput
}

input CategoryWhereInput {
AND: [CategoryWhereInput!]
name: StringFilter
Expand Down Expand Up @@ -806,7 +764,6 @@ input MovieWhereUniqueInput {
}

type Mutation {
createCategory(data: CategoryCreateInput!): Category!
createClient(data: ClientCreateInput!): Client!
createCreator(data: CreatorCreateInput!): Creator!
createDirector(data: DirectorCreateInput!): Director!
Expand All @@ -815,11 +772,9 @@ type Mutation {
createPost(data: PostCreateInput!): Post!
createProblem(data: ProblemCreateInput!): Problem!
customCreatePost(data: PostCreateInput!): Post!
deleteCategory(where: CategoryWhereUniqueInput!): Category
deleteClient(where: ClientWhereUniqueInput!): Client
deleteCreator(where: CreatorWhereUniqueInput!): Creator
deleteDirector(where: DirectorWhereUniqueInput!): Director
deleteManyCategory(where: CategoryWhereInput): BatchPayload!
deleteManyClient(where: ClientWhereInput): BatchPayload!
deleteManyCreator(where: CreatorWhereInput): BatchPayload!
deleteManyDirector(where: DirectorWhereInput): BatchPayload!
Expand All @@ -829,11 +784,9 @@ type Mutation {
deleteMovie(where: MovieWhereUniqueInput!): Movie
deletePatient(where: PatientWhereUniqueInput!): Patient
deleteProblem(where: ProblemWhereUniqueInput!): Problem
updateCategory(data: CategoryUpdateInput!, where: CategoryWhereUniqueInput!): Category
updateClient(data: ClientUpdateInput!, where: ClientWhereUniqueInput!): Client
updateCreator(data: CreatorUpdateInput!, where: CreatorWhereUniqueInput!): Creator
updateDirector(data: DirectorUpdateInput!, where: DirectorWhereUniqueInput!): Director
updateManyCategory(data: CategoryUpdateManyMutationInput!, where: CategoryWhereInput): BatchPayload!
updateManyClient(data: ClientUpdateManyMutationInput!, where: ClientWhereInput): BatchPayload!
updateManyCreator(data: CreatorUpdateManyMutationInput!, where: CreatorWhereInput): BatchPayload!
updateManyDirector(data: DirectorUpdateManyMutationInput!, where: DirectorWhereInput): BatchPayload!
Expand All @@ -844,7 +797,6 @@ type Mutation {
updateMovie(data: MovieUpdateInput!, where: MovieWhereUniqueInput!): Movie
updatePatient(data: PatientUpdateInput!, where: PatientWhereUniqueInput!): Patient
updateProblem(data: ProblemUpdateInput!, where: ProblemWhereUniqueInput!): Problem
upsertCategory(create: CategoryCreateInput!, update: CategoryUpdateInput!, where: CategoryWhereUniqueInput!): Category!
upsertClient(create: ClientCreateInput!, update: ClientUpdateInput!, where: ClientWhereUniqueInput!): Client!
upsertCreator(create: CreatorCreateInput!, update: CreatorUpdateInput!, where: CreatorWhereUniqueInput!): Creator!
upsertDirector(create: DirectorCreateInput!, update: DirectorUpdateInput!, where: DirectorWhereUniqueInput!): Director!
Expand Down Expand Up @@ -1434,7 +1386,6 @@ input ProblemWhereUniqueInput {
}

type Query {
aggregateCategory(cursor: CategoryWhereUniqueInput, distinct: [CategoryDistinctFieldEnum!], orderBy: [CategoryOrderByInput!], skip: Int, take: Int, where: CategoryWhereInput): AggregateCategory!
aggregateClient(cursor: ClientWhereUniqueInput, distinct: [ClientDistinctFieldEnum!], orderBy: [ClientOrderByInput!], skip: Int, take: Int, where: ClientWhereInput): AggregateClient!
aggregateCreator(cursor: CreatorWhereUniqueInput, distinct: [CreatorDistinctFieldEnum!], orderBy: [CreatorOrderByInput!], skip: Int, take: Int, where: CreatorWhereInput): AggregateCreator!
aggregateDirector(cursor: DirectorWhereUniqueInput, distinct: [DirectorDistinctFieldEnum!], orderBy: [DirectorOrderByInput!], skip: Int, take: Int, where: DirectorWhereInput): AggregateDirector!
Expand All @@ -1444,15 +1395,13 @@ type Query {
allClients: [Client!]!
allPosts: [Post!]!
categories(cursor: CategoryWhereUniqueInput, distinct: [CategoryDistinctFieldEnum!], orderBy: [CategoryOrderByInput!], skip: Int, take: Int, where: CategoryWhereInput): [Category!]!
category(where: CategoryWhereUniqueInput!): Category
client(where: ClientWhereUniqueInput!): Client
clients(cursor: ClientWhereUniqueInput, distinct: [ClientDistinctFieldEnum!], orderBy: [ClientOrderByInput!], skip: Int, take: Int, where: ClientWhereInput): [Client!]!
creator(where: CreatorWhereUniqueInput!): Creator
creators(cursor: CreatorWhereUniqueInput, distinct: [CreatorDistinctFieldEnum!], orderBy: [CreatorOrderByInput!], skip: Int, take: Int, where: CreatorWhereInput): [Creator!]!
customFindClientsWithArgs(cursor: ClientWhereUniqueInput, distinct: [ClientDistinctFieldEnum!], orderBy: [ClientOrderByInput!], skip: Int, take: Int, where: ClientWhereInput): [Client!]!
director(where: DirectorWhereUniqueInput!): Director
directors(cursor: DirectorWhereUniqueInput, distinct: [DirectorDistinctFieldEnum!], orderBy: [DirectorOrderByInput!], skip: Int, take: Int, where: DirectorWhereInput): [Director!]!
findFirstCategory(cursor: CategoryWhereUniqueInput, distinct: [CategoryDistinctFieldEnum!], orderBy: [CategoryOrderByInput!], skip: Int, take: Int, where: CategoryWhereInput): Category
findFirstClient(cursor: ClientWhereUniqueInput, distinct: [ClientDistinctFieldEnum!], orderBy: [ClientOrderByInput!], skip: Int, take: Int, where: ClientWhereInput): Client
findFirstCreator(cursor: CreatorWhereUniqueInput, distinct: [CreatorDistinctFieldEnum!], orderBy: [CreatorOrderByInput!], skip: Int, take: Int, where: CreatorWhereInput): Creator
findFirstDirector(cursor: DirectorWhereUniqueInput, distinct: [DirectorDistinctFieldEnum!], orderBy: [DirectorOrderByInput!], skip: Int, take: Int, where: DirectorWhereInput): Director
Expand Down
30 changes: 28 additions & 2 deletions experiments/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Ctx,
Args,
Mutation,
Authorized,
} from "type-graphql";
import { ApolloServer } from "apollo-server";
import path from "path";
Expand All @@ -22,7 +23,6 @@ import {
CreatePostResolver,
UpdateManyPostResolver,
// Category,
CategoryCrudResolver,
// Patient,
PatientCrudResolver,
FindManyPostResolver,
Expand All @@ -34,12 +34,29 @@ import {
ProblemRelationsResolver,
CreatorRelationsResolver,
CreatePostArgs,
ResolversEnhanceMap,
applyResolversEnhanceMap,
ResolverActionsConfig,
FindManyCategoryResolver,
} from "./prisma/generated/type-graphql";
import { PrismaClient } from "./prisma/generated/client";
import * as Prisma from "./prisma/generated/client";
import { ProblemCrudResolver } from "./prisma/generated/type-graphql/resolvers/crud/Problem/ProblemCrudResolver";
import { CreatorCrudResolver } from "./prisma/generated/type-graphql/resolvers/crud/Creator/CreatorCrudResolver";

const problemActionsConfig: ResolverActionsConfig<"Problem"> = {
createProblem: [Authorized()],
};

const resolversEnhanceMap: ResolversEnhanceMap = {
Category: {
categories: [Authorized()],
},
Problem: problemActionsConfig,
};

applyResolversEnhanceMap(resolversEnhanceMap);

interface Context {
prisma: PrismaClient;
}
Expand Down Expand Up @@ -92,7 +109,8 @@ async function main() {
FindOnePostResolver,
CreatePostResolver,
UpdateManyPostResolver,
CategoryCrudResolver,
// CategoryCrudResolver,
FindManyCategoryResolver,
PatientCrudResolver,
FindManyPostResolver,
MovieCrudResolver,
Expand All @@ -106,13 +124,21 @@ async function main() {
],
validate: false,
emitSchemaFile: path.resolve(__dirname, "./generated-schema.graphql"),
authChecker: ({ info }) => {
console.log(
`${info.parentType.name}.${info.fieldName} requested, access prohibited`,
);
return false;
},
});

const prisma = new PrismaClient({
// see dataloader for relations in action
log: ["query"],
});

await prisma.$connect();

const server = new ApolloServer({
schema,
playground: true,
Expand Down
Loading

0 comments on commit 4f93194

Please sign in to comment.