Skip to content
This repository was archived by the owner on Jan 26, 2018. It is now read-only.

Commit 175706c

Browse files
authored
feat: repository integration (#23)
Add a User model, an in-memory datasource, and a repository which ties them together. Also add unit tests for HelloWorldController, and an acceptance test for persistence. Lastly, refactor repo to make use of rest package.
1 parent d66f267 commit 175706c

16 files changed

+371
-92
lines changed

package.json

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,20 @@
2121
"author": "IBM",
2222
"license": "MIT",
2323
"devDependencies": {
24-
"@loopback/openapi-spec": "^4.0.0-alpha.8",
25-
"@loopback/testlab": "^4.0.0-alpha.6",
24+
"@loopback/testlab": "^4.0.0-alpha.7",
2625
"@types/mocha": "^2.2.41",
2726
"dredd": "^4.3.0",
2827
"mocha": "^3.5.0",
2928
"ts-node": "^3.3.0",
3029
"typescript": "^2.4.2"
3130
},
3231
"dependencies": {
33-
"@loopback/authentication": "^4.0.0-alpha.5",
34-
"@loopback/context": "^4.0.0-alpha.11",
35-
"@loopback/core": "^4.0.0-alpha.11",
32+
"@loopback/authentication": "^4.0.0-alpha.10",
33+
"@loopback/context": "^4.0.0-alpha.14",
34+
"@loopback/core": "^4.0.0-alpha.16",
35+
"@loopback/openapi-spec": "^4.0.0-alpha.10",
36+
"@loopback/repository": "^4.0.0-alpha.8",
37+
"@loopback/rest": "^4.0.0-alpha.3",
3638
"@types/passport": "^0.3.3",
3739
"@types/passport-http": "^0.3.2",
3840
"passport": "^0.3.2",

src/apidefs/hello-world.api.ts

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright IBM Corp. 2017. All Rights Reserved.
2+
// Node module: loopback-next-hello-world
3+
// This file is licensed under the MIT License.
4+
// License text available at https://opensource.org/licenses/MIT
5+
'use strict';
6+
7+
export const helloWorldApi = {
8+
paths: {
9+
'/helloworld': {
10+
get: {
11+
'x-operation-name': 'helloWorld',
12+
'x-controller-name': 'HelloWorldController',
13+
parameters: [
14+
{
15+
name: 'name',
16+
in: 'query',
17+
description: 'Your name.',
18+
required: false,
19+
type: 'string',
20+
'x-example': 'Ted',
21+
},
22+
],
23+
responses: {
24+
'200': {
25+
description: 'Returns a hello world with your (optional) name.',
26+
examples: {
27+
'text/plain': 'Hello world Ted {"username":"a","password":"a"}',
28+
},
29+
},
30+
},
31+
},
32+
},
33+
'/users': {
34+
get: {
35+
'x-operation-name': 'getUsers',
36+
'x-controller-name': 'HelloWorldController',
37+
parameters: [
38+
{
39+
name: 'name',
40+
in: 'query',
41+
description: 'The name for the user instance.',
42+
required: false,
43+
type: 'string',
44+
'x-example': 'Ted',
45+
},
46+
],
47+
responses: {
48+
'200': {
49+
description: 'An array of persisted user instances.',
50+
schema: {
51+
type: 'array',
52+
items: {
53+
$ref: '#/definitions/user',
54+
},
55+
},
56+
examples: {
57+
'application/json': '[{username:"Ted", id: 1}]',
58+
},
59+
},
60+
},
61+
},
62+
post: {
63+
'x-operation-name': 'createUser',
64+
'x-controller-name': 'HelloWorldController',
65+
consumes: ['application/json'],
66+
produces: ['application/json'],
67+
parameters: [
68+
{
69+
name: 'userInfo',
70+
in: 'body',
71+
description: 'The user model instance to create.',
72+
required: false,
73+
schema: {
74+
$ref: '#/definitions/user',
75+
},
76+
},
77+
],
78+
responses: {
79+
'200': {
80+
description: 'The created user instance.',
81+
schema: {
82+
$ref: '#/definitions/user',
83+
},
84+
examples: {
85+
'application/json': '{"id": 1,"username": "Ted"}',
86+
},
87+
},
88+
},
89+
},
90+
},
91+
},
92+
};

src/apidefs/swagger.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as lbOpenApiSpec from '@loopback/openapi-spec';
2+
import {OpenApiSpec} from '@loopback/openapi-spec';
3+
import {helloWorldApi} from './hello-world.api';
4+
5+
export let spec = lbOpenApiSpec.createEmptyApiSpec();
6+
spec.info = {
7+
title: 'Hello World API',
8+
version: '1.0',
9+
};
10+
spec.swagger = '2.0';
11+
spec.basePath = '/';
12+
spec.definitions = {
13+
user: {
14+
type: 'object',
15+
properties: {
16+
id: {
17+
type: 'number',
18+
description: 'The ID for a user instance.',
19+
},
20+
username: {
21+
type: 'string',
22+
description: 'The username for a user instance.',
23+
},
24+
},
25+
required: ['username'],
26+
example: {
27+
username: 'Ted',
28+
},
29+
},
30+
};
31+
32+
spec = Object.assign({}, spec, helloWorldApi);

src/application.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,40 @@
33
// This file is licensed under the MIT License.
44
// License text available at https://opensource.org/licenses/MIT
55
import {Application} from '@loopback/core';
6-
import {AuthenticationComponent, AuthenticationBindings} from '@loopback/authentication';
7-
import {validateApiSpec} from '@loopback/testlab'
6+
import {
7+
AuthenticationComponent,
8+
AuthenticationBindings,
9+
} from '@loopback/authentication';
10+
import {validateApiSpec} from '@loopback/testlab';
811
import {MyAuthStrategyProvider} from './providers/auth-strategy';
912
import {HelloWorldController} from './controllers/hello-world';
1013
import {MySequence} from './sequence';
14+
import {UserRepository} from './repositories/user.repository';
15+
import {Class, Repository, RepositoryMixin} from '@loopback/repository';
16+
import {RestComponent, RestServer, RestBindings} from '@loopback/rest';
17+
import {spec} from './apidefs/swagger';
1118

12-
export class HelloWorldApp extends Application {
19+
export class HelloWorldApp extends RepositoryMixin(Application) {
1320
constructor() {
1421
super({
15-
components: [AuthenticationComponent],
16-
sequence: MySequence,
22+
components: [AuthenticationComponent, RestComponent],
23+
repositories: [UserRepository],
1724
});
18-
this.bind(AuthenticationBindings.STRATEGY)
19-
.toProvider(MyAuthStrategyProvider);
2025
this.controller(HelloWorldController);
2126
}
27+
28+
async start() {
29+
const server = await this.getServer(RestServer);
30+
server.sequence(MySequence);
31+
server.api(spec);
32+
server
33+
.bind(AuthenticationBindings.STRATEGY)
34+
.toProvider(MyAuthStrategyProvider);
35+
await super.start();
36+
let port = await server.get(RestBindings.PORT);
37+
console.log(`HTTP server listening on port ${port}`);
38+
console.log(
39+
`Run \'curl localhost:${port}/helloworld?name=YOUR_NAME\' to try it out`,
40+
);
41+
}
2242
}

src/controllers/hello-world.api.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/controllers/hello-world.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,38 @@
44
// License text available at https://opensource.org/licenses/MIT
55
'use strict';
66

7-
import {api, inject} from '@loopback/core';
8-
import {controllerSpec} from './hello-world.api';
9-
import {authenticate, AuthenticationBindings, UserProfile} from '@loopback/authentication';
7+
import {inject} from '@loopback/core';
8+
import {api} from '@loopback/rest';
9+
import {helloWorldApi} from '../apidefs/hello-world.api';
10+
import {
11+
authenticate,
12+
AuthenticationMetadata,
13+
AuthenticationBindings,
14+
UserProfile,
15+
} from '@loopback/authentication';
16+
import {UserRepository} from '../repositories/user.repository';
17+
import {User} from '../models/user.model';
1018

11-
@api(controllerSpec)
19+
@api(helloWorldApi)
1220
export class HelloWorldController {
13-
constructor(@inject(AuthenticationBindings.CURRENT_USER) private user: UserProfile) {}
21+
constructor(
22+
@inject(AuthenticationBindings.CURRENT_USER) private user: UserProfile,
23+
@inject('repositories.UserRepository') private repository: UserRepository,
24+
) {}
1425

1526
@authenticate('BasicStrategy')
16-
helloWorld(name: string) {
17-
return `Hello world ${name} ` + JSON.stringify(this.user);
27+
async helloWorld(name?: string) {
28+
return `Hello world ${name} ` + JSON.stringify(this.user);
29+
}
30+
31+
@authenticate('BasicStrategy')
32+
async getUsers(name?: string): Promise<User[]> {
33+
let filter = name ? {where: {username: name}} : {};
34+
return await this.repository.find(filter);
35+
}
36+
37+
@authenticate('BasicStrategy')
38+
async createUser(userInfo: Partial<User>) {
39+
return await this.repository.create(new User(userInfo));
1840
}
1941
}

src/datasources/db.datasource.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import {juggler, DataSourceConstructor} from '@loopback/repository';
2+
3+
export const db = new DataSourceConstructor({
4+
name: 'db',
5+
connector: 'memory',
6+
});

src/index.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,12 @@
55
import {CoreBindings} from '@loopback/core';
66
import {HelloWorldApp} from './application';
77
export {HelloWorldApp};
8+
import {RestServer, RestBindings} from '@loopback/rest';
89

910
if (require.main === module.parent) {
1011
// executed from the console
1112
const app = new HelloWorldApp();
12-
13-
app.start().then(
14-
() => {
15-
let port = app.getSync(CoreBindings.HTTP_PORT);
16-
console.log(`HTTP server listening on port ${port}`);
17-
console.log(`Run \'curl localhost:${port}/helloworld?name=YOUR_NAME\' to try it out`);
18-
},
19-
err => {
20-
console.error('Cannot start the application', err);
21-
process.exit(1);
22-
});
23-
}
13+
(async function main() {
14+
await app.start();
15+
})();
16+
}

src/models/user.model.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {Entity, model, property} from '@loopback/repository';
2+
3+
@model()
4+
export class User extends Entity {
5+
@property({
6+
description: 'The ID for a user.',
7+
id: true,
8+
generated: true,
9+
type: 'number',
10+
})
11+
id: number;
12+
13+
@property({
14+
description: 'The username for a user.',
15+
required: true,
16+
default: 'Anonymous User',
17+
type: 'string',
18+
})
19+
username: string;
20+
}

src/providers/auth-strategy.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,11 @@
22
// Node module: loopback-next-hello-world
33
// This file is licensed under the MIT License.
44
// License text available at https://opensource.org/licenses/MIT
5-
import {
6-
inject,
7-
Provider,
8-
ValueOrPromise,
9-
} from '@loopback/context';
5+
import {inject, Provider, ValueOrPromise} from '@loopback/context';
106
import {
117
AuthenticationBindings,
128
AuthenticationMetadata,
139
} from '@loopback/authentication';
14-
1510
import {Strategy} from 'passport';
1611
import {BasicStrategy} from 'passport-http';
1712

@@ -21,8 +16,7 @@ export class MyAuthStrategyProvider implements Provider<Strategy> {
2116
private metadata: AuthenticationMetadata,
2217
) {}
2318

24-
25-
value() : ValueOrPromise<Strategy> {
19+
value(): ValueOrPromise<Strategy> {
2620
const name = this.metadata.strategy;
2721
if (name === 'BasicStrategy') {
2822
return new BasicStrategy(this.verify);

0 commit comments

Comments
 (0)