Skip to content

Commit

Permalink
docs: add more details to the doc
Browse files Browse the repository at this point in the history
Add more details to the doc and improve code

Signed-off-by: Hage Yaapa <hage.yaapa@in.ibm.com>
Signed-off-by: jannyHou <juehou@ca.ibm.com>
Signed-off-by: Nora <nora.abdelgadir@ibm.com>
Co-authored-by: Janny <juehou@ca.ibm.com>
  • Loading branch information
2 people authored and nabdelgadir committed Jun 28, 2019
1 parent 49f80bb commit e230ba3
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 13 deletions.
102 changes: 102 additions & 0 deletions README.md
Expand Up @@ -20,6 +20,108 @@ npm i
npm start
```

The main app will be running at `http://[::1]:3000`.

You will also see `Recommendation server is running at http://127.0.0.1:3001.`,
it is the server to which the `services/recommender.service` service will
connect to get the recommendations for a user.

## Usage

This app is intended to be interacted with using the API Explorer located at
http://[::1]:3000/explorer/.

## Models

This app has five models:

1. `User` - representing the users of the system.
2. `Product` - a model which is mapped to a remote service by
`services/recommender.service`.
3. `ShoppingCartItem` - a model for representing purchases.
4. `ShoppingCart` - a model to represent a user's shopping cart, can contain
many items (`items`) of the type `ShoppingCartItem`.
5. `Order` - a model to represent an order by user, can have many products
(`products`) of the type `ShoppingCartItem`.

`ShoppingCart` and `Order` are marked as belonging to the `User` model by the
use of the `@belongsTo` model decorator. Correspondingly, the `User` model is
marked as having many `Order`s using the `@hasMany` model decorator. Although
possible, a `hasMany` relation for `User` to `ShoppingCart` has not be created
in this particular app to limit the scope of the example.

## Controllers

Controllers expose API endpoints for interacting with the models and more.

In this app, there are four controllers:

1. `ping` - a simple controller to checking the status of the app.
2. `user` - controller for creating user, fetching user info, updating user
info, and logging in.
3. `shopping-cart` - controller for creating, updating, deleting shopping carts,
and getting the details about a shopping cart.
4. `user-order` - controller for creating, updating, deleting orders, and
getting the details about an order.

## Services

Services are modular components that can be plugged into a LoopBack application
in various locations to contribute additional capabilities and features to the
application.

This app has five services:

1. `services/recommender.service` - responsible for connecting to a "remote"
server and getting recommendations for a user. The API endpoint at
`GET /users​/{userId}​/recommend`, is made possible by this service.
2. `services/user-service` - responsible for verifying if user exists and the
submitted password matches that of the existing user.
3. `services/hash.password.bcryptjs` - responsible for generating and comparing
password hashes.
4. `services/validator` - responsible for validating email and password when a
new user is created.
5. `services/jwt-service` - responsible for generating and verifying JSON Web
Token.

## Authentication

_Note: This app contains a `login` endpoint for the purpose of spike and demo,
the authentication for the CRUD operations and navigational endpoints of model
User is still in progress._

### Login

The endpoint for logging in a user is a `POST` request to `/users/login`.

Once the credentials are extracted, the logging-in implementation at the
controller level is just a four step process. This level of simplicity is made
possible by the use of the `UserService` service provided by
`@loopback/authentication`.

1. `const user = await this.userService.verifyCredentials(credentials)` - verify
the credentials.
2. `const userProfile = this.userService.convertToUserProfile(user)` - generate
user profile object.
3. `const token = await this.jwtService.generateToken(userProfile)` - generate
JWT based on the user profile object.
4. `return {token}` - send the JWT.

You can see the details in
[`packages/shopping/src/controllers/user.controller.ts`](https://github.com/strongloop/loopback4-example-shopping/blob/master/packages/shopping/src/controllers/user.controller.ts).

### Tutorial

There is a tutorial which shows how to apply the JWT strategy to secure your
endpoint with `@loopback/authentication@2.x`. You can check more details in
https://loopback.io/doc/en/lb4/Authentication-Tutorial.html

### Trying It Out

Please check the
[try it out](https://loopback.io/doc/en/lb4/Authentication-Tutorial.html#try-it-out)
section in the tutorial.

## Contributing

This project uses [DCO](https://developercertificate.org/). Be sure to sign off
Expand Down
17 changes: 17 additions & 0 deletions packages/shopping/package-lock.json

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

1 change: 1 addition & 0 deletions packages/shopping/package.json
Expand Up @@ -49,6 +49,7 @@
"@loopback/openapi-v3": "1.6.4",
"@loopback/repository": "1.8.1",
"@loopback/rest": "1.16.2",
"@loopback/rest-explorer": "^1.2.4",
"@loopback/service-proxy": "1.2.4",
"@types/jsonwebtoken": "8.3.2",
"@types/passport": "1.0.0",
Expand Down
10 changes: 10 additions & 0 deletions packages/shopping/src/application.ts
Expand Up @@ -9,6 +9,10 @@ import {RepositoryMixin} from '@loopback/repository';
import {RestApplication} from '@loopback/rest';
import {ServiceMixin} from '@loopback/service-proxy';
import {MyAuthenticationSequence} from './sequence';
import {
RestExplorerBindings,
RestExplorerComponent,
} from '@loopback/rest-explorer';
import {
TokenServiceBindings,
UserServiceBindings,
Expand Down Expand Up @@ -57,6 +61,12 @@ export class ShoppingApplication extends BootMixin(
// Set up default home page
this.static('/', path.join(__dirname, '../public'));

// Customize @loopback/rest-explorer configuration here
this.bind(RestExplorerBindings.CONFIG).to({
path: '/explorer',
});
this.component(RestExplorerComponent);

this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
Expand Down
10 changes: 4 additions & 6 deletions packages/shopping/src/models/order.model.ts
Expand Up @@ -3,8 +3,9 @@
// 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 {Entity, model, property, belongsTo} from '@loopback/repository';
import {ShoppingCartItem} from './shopping-cart-item.model';
import {User} from './user.model';

@model()
export class Order extends Entity {
Expand All @@ -14,11 +15,8 @@ export class Order extends Entity {
})
orderId?: string;

// TODO(virkt25): This should be a belongsTo once it is available
@property({
type: 'string',
required: true,
})
// Each order belongs to a user, indentified by its id (userId)
@belongsTo(() => User)
userId: string;

@property({
Expand Down
11 changes: 4 additions & 7 deletions packages/shopping/src/models/shopping-cart.model.ts
Expand Up @@ -3,19 +3,16 @@
// 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 {Entity, model, property, belongsTo} from '@loopback/repository';
import {ShoppingCartItem} from './shopping-cart-item.model';
import {User} from './user.model';

@model()
export class ShoppingCart extends Entity {
/**
* Each user has a unique shopping cart keyed by the user id
* Each shopping cart belongs to a user, indentified by its id (userId)
*/
@property({
type: 'string',
id: true,
required: true,
})
@belongsTo(() => User)
userId: string;

/**
Expand Down

0 comments on commit e230ba3

Please sign in to comment.