Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New schema generation pipeline #304

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require("ts-node/register/transpile-only");
// require("./examples/query-complexity/index.ts");
// require("./examples/redis-subscriptions/index.ts");
// require("./examples/resolvers-inheritance/index.ts");
require("./examples/multiple-schemas/index.ts");
// require("./examples/simple-subscriptions/index.ts");
// require("./examples/simple-usage/index.ts");
// require("./examples/typegoose/index.ts");
Expand Down
41 changes: 41 additions & 0 deletions examples/multiple-schemas/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import "reflect-metadata";
import { ApolloServer } from "apollo-server";
import { Container } from "typedi";
import { buildSchema } from "../../src";

import { RecipeResolver } from "./recipe/recipe.resolver";
import { Recipe } from "./recipe/recipe.type";
import { PersonResolver } from "./person/person.resolver";
import { Person } from "./person/person.type";
import { GetAllArgs } from "./resource/resource.resolver";
import * as path from "path";

async function bootstrap() {
// build TypeGraphQL executable schema
const schemaRecipe = await buildSchema({
resolvers: [RecipeResolver],
types: [Recipe, GetAllArgs],
container: Container,
emitSchemaFile: path.resolve(__dirname, "schemaRecipe.gql"),
});

const schemaPerson = await buildSchema({
resolvers: [PersonResolver],
types: [Person, GetAllArgs],
container: Container,
emitSchemaFile: path.resolve(__dirname, "schemaPerson.gql"),
});

// Create GraphQL server
const serverRecipe = new ApolloServer({ schema: schemaRecipe });
const serverPerson = new ApolloServer({ schema: schemaPerson });

// Start the server
const { url: recipeUrl } = await serverRecipe.listen(4000);
console.log(`Server recipe is running, GraphQL Playground available at ${recipeUrl}`);

const { url: personUrl } = await serverPerson.listen(4001);
console.log(`Server recipe is running, GraphQL Playground available at ${personUrl}`);
}

bootstrap();
42 changes: 42 additions & 0 deletions examples/multiple-schemas/person/person.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Resolver, Arg, Int, Mutation } from "../../../src";

import { ResourceResolver } from "../resource/resource.resolver";
import { Person } from "./person.type";
import { PersonRole } from "./person.role";

const persons: Person[] = [
{
id: 1,
name: "Person 1",
age: 23,
role: PersonRole.Normal,
},
{
id: 2,
name: "Person 2",
age: 48,
role: PersonRole.Admin,
},
];

@Resolver()
export class PersonResolver extends ResourceResolver(Person, persons) {
// here you can add resource-specific operations

@Mutation()
promote(@Arg("personId", type => Int) personId: number): boolean {
// you have full access to base resolver class fields and methods

const person = this.resourceService.getOne(personId);
if (!person) {
throw new Error("Person not found!");
}

if (person.role === PersonRole.Normal) {
person.role = PersonRole.Pro;
return true;
}

return false;
}
}
9 changes: 9 additions & 0 deletions examples/multiple-schemas/person/person.role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { registerEnumType } from "../../../src";

export enum PersonRole {
Normal,
Pro,
Admin,
}

registerEnumType(PersonRole, { name: "PersonRole" });
19 changes: 19 additions & 0 deletions examples/multiple-schemas/person/person.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ObjectType, Field, Int } from "../../../src";

import { Resource } from "../resource/resource";
import { PersonRole } from "./person.role";

@ObjectType()
export class Person implements Resource {
@Field()
id: number;

@Field()
name: string;

@Field(type => Int)
age: number;

@Field(type => PersonRole)
role: PersonRole;
}
21 changes: 21 additions & 0 deletions examples/multiple-schemas/queries.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
query AllPersons {
persons {
id
name
age
role
}
}

query OneRecipe {
recipe(id: 1) {
uuid
title
ratings
averageRating
}
}

mutation PromotePeronsOne {
promote(personId: 1)
}
22 changes: 22 additions & 0 deletions examples/multiple-schemas/recipe/recipe.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Resolver, FieldResolver, Root } from "../../../src";

import { ResourceResolver } from "../resource/resource.resolver";
import { Recipe } from "./recipe.type";

const recipes: Recipe[] = [
{
id: 1,
title: "Recipe 1",
ratings: [1, 3, 4],
},
];

@Resolver(of => Recipe)
export class RecipeResolver extends ResourceResolver(Recipe, recipes) {
// here you can add resource-specific operations

@FieldResolver()
averageRating(@Root() recipe: Recipe): number {
return recipe.ratings.reduce((a, b) => a + b, 0) / recipe.ratings.length;
}
}
15 changes: 15 additions & 0 deletions examples/multiple-schemas/recipe/recipe.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ObjectType, Field, Int } from "../../../src";

import { Resource } from "../resource/resource";

@ObjectType()
export class Recipe implements Resource {
@Field()
id: number;

@Field()
title: string;

@Field(type => [Int])
ratings: number[];
}
62 changes: 62 additions & 0 deletions examples/multiple-schemas/resource/resource.resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Service } from "typedi";
import {
Query,
Arg,
Int,
Resolver,
ArgsType,
Field,
Args,
FieldResolver,
Root,
ClassType,
} from "../../../src";

import { Resource } from "./resource";
import { ResourceService, ResourceServiceFactory } from "./resource.service";

@ArgsType()
export class GetAllArgs {
@Field(type => Int)
skip: number = 0;

@Field(type => Int)
take: number = 10;
}

export function ResourceResolver<TResource extends Resource>(
ResourceCls: ClassType,
resources: TResource[],
) {
const resourceName = ResourceCls.name.toLocaleLowerCase();

// `isAbstract` decorator option is mandatory to prevent multiple registering in schema
@Resolver(of => ResourceCls, { isAbstract: true })
@Service()
abstract class ResourceResolverClass {
protected resourceService: ResourceService<TResource>;

constructor(factory: ResourceServiceFactory) {
this.resourceService = factory.create(resources);
}

@Query(returns => ResourceCls, { name: `${resourceName}` })
protected async getOne(@Arg("id", type => Int) id: number) {
return this.resourceService.getOne(id);
}

@Query(returns => [ResourceCls], { name: `${resourceName}s` })
protected async getAll(@Args() { skip, take }: GetAllArgs) {
const all = this.resourceService.getAll(skip, take);
return all;
}

// dynamically created field with resolver for all child resource classes
@FieldResolver({ name: "uuid" })
protected getUuid(@Root() resource: Resource): string {
return `${resourceName}_${resource.id}`;
}
}

return ResourceResolverClass;
}
25 changes: 25 additions & 0 deletions examples/multiple-schemas/resource/resource.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Service } from "typedi";

import { Resource } from "./resource";

// we need to use factory as we need separate instance of service for each generic
@Service()
export class ResourceServiceFactory {
create<TResource extends Resource>(resources?: TResource[]) {
return new ResourceService(resources);
}
}

export class ResourceService<TResource extends Resource> {
constructor(protected resources: TResource[] = []) {}

getOne(id: number): TResource | undefined {
return this.resources.find(res => res.id === id);
}

getAll(skip: number, take: number): TResource[] {
const start: number = skip;
const end: number = skip + take;
return this.resources.slice(start, end);
}
}
3 changes: 3 additions & 0 deletions examples/multiple-schemas/resource/resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface Resource {
id: number;
}
26 changes: 26 additions & 0 deletions examples/multiple-schemas/schemaPerson.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------

type Mutation {
promote(personId: Int!): Boolean!
}

type Person {
id: Float!
name: String!
age: Int!
role: PersonRole!
}

enum PersonRole {
Normal
Pro
Admin
}

type Query {
person(id: Int!): Person!
persons(skip: Int = 0, take: Int = 10): [Person!]!
}
15 changes: 15 additions & 0 deletions examples/multiple-schemas/schemaRecipe.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------

type Query {
recipe(id: Int!): Recipe!
recipes(skip: Int = 0, take: Int = 10): [Recipe!]!
}

type Recipe {
id: Float!
title: String!
ratings: [Int!]!
}
Loading