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

Add Metadata Decorator [WIP] #124 #190

Closed
wants to merge 2 commits into from
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 @@ -14,3 +14,4 @@ require("ts-node/register/transpile-only");
// require("./examples/using-scoped-container/index.ts");
// require("./examples/typeorm-basic-usage/index.ts");
// require("./examples/typeorm-lazy-relations/index.ts");
// require("./examples/using-metadata/index.ts");
28 changes: 28 additions & 0 deletions examples/using-metadata/examples.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
query GetRecipe1 {
recipe(title: "Recipe 1") {
title
description
ratings
creationDate
ratingsCount(minRate: 2)
averageRating
}
}

query GetRecipes {
recipes {
title
description
creationDate
averageRating
}
}

mutation AddRecipe {
addRecipe(recipe: {
title: "New recipe"
description: "Simple description"
}) {
creationDate
}
}
27 changes: 27 additions & 0 deletions examples/using-metadata/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import "reflect-metadata";
import { ApolloServer } from "apollo-server";
import * as path from "path";
import { buildSchema } from "../../src";

import { RecipeResolver } from "./recipe-resolver";

async function bootstrap() {
// build TypeGraphQL executable schema
const schema = await buildSchema({
resolvers: [RecipeResolver],
// automatically create `schema.gql` file with schema definition in current folder
emitSchemaFile: path.resolve(__dirname, "schema.gql"),
});
// Create GraphQL server
const server = new ApolloServer({
schema,
// enable GraphQL Playground
playground: true,
});

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

bootstrap();
13 changes: 13 additions & 0 deletions examples/using-metadata/recipe-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Recipe } from "./recipe-type";
import { InputType, Field, Metadata } from "../../src";

@Metadata({ classTest: "123" })
@InputType()
export class RecipeInput implements Partial<Recipe> {
@Metadata({ test: "123" })
@Field()
title: string;

@Field({ nullable: true })
description?: string;
}
51 changes: 51 additions & 0 deletions examples/using-metadata/recipe-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
Resolver,
Query,
FieldResolver,
Arg,
Root,
Mutation,
Float,
Int,
ResolverInterface,
} from "../../src";
import { plainToClass } from "class-transformer";

import { Recipe } from "./recipe-type";
import { RecipeInput } from "./recipe-input";
import { createRecipeSamples } from "./recipe-samples";

@Resolver(of => Recipe)
export class RecipeResolver implements ResolverInterface<Recipe> {
private readonly items: Recipe[] = createRecipeSamples();

@Query(returns => Recipe, { nullable: true })
async recipe(@Arg("title") title: string): Promise<Recipe | undefined> {
return await this.items.find(recipe => recipe.title === title);
}

@Query(returns => [Recipe], { description: "Get all the recipes from around the world " })
async recipes(): Promise<Recipe[]> {
return await this.items;
}

@Mutation(returns => Recipe)
async addRecipe(@Arg("recipe") recipeInput: RecipeInput): Promise<Recipe> {
const recipe = plainToClass(Recipe, {
description: recipeInput.description,
title: recipeInput.title,
ratings: [],
creationDate: new Date(),
});
await this.items.push(recipe);
return recipe;
}

@FieldResolver()
ratingsCount(
@Root() recipe: Recipe,
@Arg("minRate", type => Int, { nullable: true }) minRate: number = 0.0,
): number {
return recipe.ratings.filter(rating => rating >= minRate).length;
}
}
26 changes: 26 additions & 0 deletions examples/using-metadata/recipe-samples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { plainToClass } from "class-transformer";

import { Recipe } from "./recipe-type";

export function createRecipeSamples() {
return plainToClass(Recipe, [
{
description: "Desc 1",
title: "Recipe 1",
ratings: [0, 3, 1],
creationDate: new Date("2018-04-11"),
},
{
description: "Desc 2",
title: "Recipe 2",
ratings: [4, 2, 3, 1],
creationDate: new Date("2018-04-15"),
},
{
description: "Desc 3",
title: "Recipe 3",
ratings: [5, 4],
creationDate: new Date(),
},
]);
}
34 changes: 34 additions & 0 deletions examples/using-metadata/recipe-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Field, ObjectType, Int, Float } from "../../src";

@ObjectType({ description: "Object representing cooking recipe" })
export class Recipe {
@Field()
title: string;

@Field(type => String, { nullable: true, deprecationReason: "Use `description` field instead" })
get specification(): string | undefined {
return this.description;
}

@Field({ nullable: true, description: "The recipe description with preparation info" })
description?: string;

@Field(type => [Int])
ratings: number[];

@Field()
creationDate: Date;

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

@Field(type => Float, { nullable: true })
get averageRating(): number | null {
const ratingsCount = this.ratings.length;
if (ratingsCount === 0) {
return null;
}
const ratingsSum = this.ratings.reduce((a, b) => a + b, 0);
return ratingsSum / ratingsCount;
}
}
36 changes: 36 additions & 0 deletions examples/using-metadata/schema.gql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -----------------------------------------------
# !!! THIS FILE WAS GENERATED BY TYPE-GRAPHQL !!!
# !!! DO NOT MODIFY THIS FILE BY YOURSELF !!!
# -----------------------------------------------

# The javascript `Date` as string. Type represents date and time as the ISO Date string.
scalar DateTime

type Mutation {
addRecipe(recipe: RecipeInput!): Recipe!
}

type Query {
recipe(title: String!): Recipe

# Get all the recipes from around the world
recipes: [Recipe!]!
}

# Object representing cooking recipe
type Recipe {
title: String!
specification: String @deprecated(reason: "Use `description` field instead")

# The recipe description with preparation info
description: String
ratings: [Int!]!
creationDate: DateTime!
ratingsCount(minRate: Int): Int!
averageRating: Float
}

input RecipeInput {
title: String!
description: String
}
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module.exports = {
transform: {
"^.+\\.tsx?$": "ts-jest",
},
testMatch: ["**/functional/**/*.ts", "**/units/**/*.ts"],
//testMatch: ["**/functional/**/*.ts", "**/units/**/*.ts"],
testMatch: ["**/functional/fields.ts"],
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
rootDir: "./",
roots: ["<rootDir>/tests", "<rootDir>/src"],
Expand Down
Loading