Skip to content

Commit

Permalink
feat(graphql): use RecipeRepository to handle CRUD
Browse files Browse the repository at this point in the history
  • Loading branch information
raymondfeng committed May 25, 2020
1 parent ecf0de0 commit 0f0ef30
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 120 deletions.
90 changes: 40 additions & 50 deletions extensions/graphql/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions extensions/graphql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"apollo-server-express": "^2.13.1",
"debug": "^4.0.1",
"express": "^4.17.1",
"graphql": "^14.6.0",
"type-graphql": "^0.17.6"
"graphql": "^15.0.0",
"type-graphql": "^1.0.0-rc.2"
},
"devDependencies": {
"@loopback/build": "^5.4.1",
Expand Down
12 changes: 10 additions & 2 deletions extensions/graphql/src/__tests__/acceptance/graphql.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createBindingFromClass} from '@loopback/core';
import {supertest} from '@loopback/testlab';
import {GraphQLServer} from '../..';
import {RecipesDataSource} from '../fixtures/graphql-test/src/datasources';
import {RecipeResolver} from '../fixtures/graphql-test/src/graphql-resolvers/recipe-resolver';
import {RecipeRepository} from '../fixtures/graphql-test/src/repositories';
import {sampleRecipes} from '../fixtures/graphql-test/src/sample-recipes';
import {RecipeService} from '../fixtures/graphql-test/src/services/recipe.service';
import {runTests} from './graphql-tests';
Expand All @@ -21,10 +24,15 @@ describe('GraphQL integration', () => {
async function givenServer() {
server = new GraphQLServer(undefined, {host: '127.0.0.1', port: 0});
server.resolver(RecipeResolver);
await server.start();

server.bind('recipes').to([...sampleRecipes]);
server.bind('services.RecipeService').toClass(RecipeService);
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
server.add(createBindingFromClass(RecipeService));
await server.start();
const repo = await server.get<RecipeRepository>(repoBinding.key);
await repo.start();
}

async function stopServer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

export * from './receipes.datasource';
export * from './recipes.datasource';
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {inject, lifeCycleObserver, LifeCycleObserver} from '@loopback/core';
import {juggler} from '@loopback/repository';
import {
ContextTags,
inject,
lifeCycleObserver,
LifeCycleObserver,
} from '@loopback/core';
import {juggler, RepositoryBindings} from '@loopback/repository';

const config = {
name: 'receipes',
name: 'recipes',
connector: 'memory',
localStorage: '',
file: '',
Expand All @@ -17,14 +22,19 @@ const config = {
// application is stopped. This allows the application to be shut down
// gracefully. The `stop()` method is inherited from `juggler.DataSource`.
// Learn more at https://loopback.io/doc/en/lb4/Life-cycle.html
@lifeCycleObserver('datasource')
export class ReceipesDataSource extends juggler.DataSource
@lifeCycleObserver('datasource', {
tags: {
[ContextTags.NAME]: 'recipes',
[ContextTags.NAMESPACE]: RepositoryBindings.DATASOURCES,
},
})
export class RecipesDataSource extends juggler.DataSource
implements LifeCycleObserver {
static dataSourceName = 'receipes';
static dataSourceName = 'recipes';
static readonly defaultConfig = config;

constructor(
@inject('datasources.config.receipes', {optional: true})
@inject('datasources.config.recipes', {optional: true})
dsConfig: object = config,
) {
super(dsConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// License text available at https://opensource.org/licenses/MIT

import {service} from '@loopback/core';
import {repository} from '@loopback/repository';
import {ResolverInterface} from 'type-graphql';
import {
arg,
Expand All @@ -16,34 +17,36 @@ import {
} from '../../../../..';
import {RecipeInput} from '../graphql-types/recipe-input';
import {Recipe} from '../graphql-types/recipe-type';
import {RecipeRepository} from '../repositories';
import {RecipeService} from '../services/recipe.service';

@resolver(of => Recipe)
export class RecipeResolver implements ResolverInterface<Recipe> {
constructor(
// constructor injection of service
@service()
private readonly recipeService: RecipeService,
@repository('RecipeRepository')
private readonly recipeRepo: RecipeRepository,
@service(RecipeService) private readonly recipeService: RecipeService,
) {}

@query(returns => Recipe, {nullable: true})
async recipe(@arg('recipeId') recipeId: string) {
return this.recipeService.getOne(recipeId);
return this.recipeRepo.getOne(recipeId);
}

@query(returns => [Recipe])
async recipes(): Promise<Recipe[]> {
return this.recipeService.getAll();
return this.recipeRepo.getAll();
}

@mutation(returns => Recipe)
async addRecipe(@arg('recipe') recipe: RecipeInput): Promise<Recipe> {
return this.recipeService.add(recipe);
return this.recipeRepo.add(recipe);
}

@fieldResolver()
async numberInCollection(@root() recipe: Recipe): Promise<number> {
const index = await this.recipeService.findIndex(recipe);
const index = await this.recipeRepo.findIndex(recipe);
return index + 1;
}

Expand All @@ -53,6 +56,6 @@ export class RecipeResolver implements ResolverInterface<Recipe> {
@arg('minRate', type => Int, {defaultValue: 0.0})
minRate: number,
): number {
return recipe.ratings.filter(rating => rating >= minRate).length;
return this.recipeService.ratingsCount(recipe, minRate);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {Entity, model, property} from '@loopback/repository';
import {field, Float, ID, Int, objectType} from '../../../../..';

@objectType({description: 'Object representing cooking recipe'})
export class Recipe {
@model({settings: {strict: false}})
export class Recipe extends Entity {
@field(type => ID)
@property({id: true})
id: string;

@field()
@property()
title: string;

@field(type => String, {
Expand All @@ -25,12 +29,14 @@ export class Recipe {
nullable: true,
description: 'The recipe description with preparation info',
})
@property()
description?: string;

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

@field()
@property()
creationDate: Date;

@field(type => Int)
Expand Down
Loading

0 comments on commit 0f0ef30

Please sign in to comment.