-
Notifications
You must be signed in to change notification settings - Fork 208
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
feat: implement basic user component #5
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#!/bin/bash | ||
|
||
CONTAINER_NAME="mongodb_c" | ||
docker rm -f $CONTAINER_NAME | ||
docker pull mongo:latest | ||
docker run --name $CONTAINER_NAME -p 27017:27017 -d mongo:latest |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/bin/bash | ||
set -e | ||
|
||
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then | ||
echo "OSX => UPDATING HOMEBREW" | ||
brew update | ||
echo "OSX => INSTALLING AND STARTING MONGODB" | ||
brew install mongodb | ||
brew services start mongodb | ||
fi | ||
|
||
if [ $TASK = "test" ]; then | ||
echo "TASK => MONGODB STARTUP DELAY" | ||
sleep 10 | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {repository} from '@loopback/repository'; | ||
import {post, param, get, requestBody, HttpErrors} from '@loopback/rest'; | ||
import {User} from '../models'; | ||
import {UserRepository} from '../repositories'; | ||
import {hash} from 'bcryptjs'; | ||
import {promisify} from 'util'; | ||
import * as isemail from 'isemail'; | ||
|
||
const hashAsync = promisify(hash); | ||
|
||
export class UserController { | ||
constructor( | ||
@repository(UserRepository) public userRepository: UserRepository, | ||
) {} | ||
|
||
@post('/users') | ||
async create(@requestBody() user: User): Promise<User> { | ||
// Validate Email | ||
if (!isemail.validate(user.email)) { | ||
throw new HttpErrors.UnprocessableEntity('invalid email'); | ||
} | ||
|
||
// Validate Password Length | ||
if (user.password.length < 8) { | ||
throw new HttpErrors.UnprocessableEntity( | ||
'password must be minimum 8 characters', | ||
); | ||
} | ||
|
||
// Salt + Hash Password | ||
user.password = await hashAsync(user.password, 10); | ||
|
||
// Save & Return Result | ||
const savedUser = await this.userRepository.create(user); | ||
delete savedUser.password; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this is good enough for the initial implementation, it is also very brittle. We should find a more robust way how to allow models to hide certain properties from In the past, I had very good experience with moving the password to a different model (table/collection) and use |
||
return savedUser; | ||
} | ||
|
||
@get('/users/{id}') | ||
async findById(@param.path.string('id') id: string): Promise<User> { | ||
const user = await this.userRepository.findById(id, { | ||
fields: {password: false}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto here. |
||
}); | ||
return user; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
export * from './user.datasource'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"name": "user", | ||
"connector": "mongodb", | ||
"url": "", | ||
"host": "127.0.0.1", | ||
"port": 27017, | ||
"user": "", | ||
"password": "", | ||
"database": "" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {inject} from '@loopback/core'; | ||
import {juggler, AnyObject} from '@loopback/repository'; | ||
const config = require('./user.datasource.json'); | ||
|
||
export class UserDataSource extends juggler.DataSource { | ||
static dataSourceName = 'user'; | ||
|
||
constructor( | ||
@inject('datasources.config.user', {optional: true}) | ||
dsConfig: AnyObject = config, | ||
) { | ||
super(dsConfig); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
export * from './user.model'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {Entity, model, property} from '@loopback/repository'; | ||
|
||
@model() | ||
export class User extends Entity { | ||
@property({ | ||
type: 'string', | ||
id: true, | ||
}) | ||
id: string; | ||
|
||
@property({ | ||
type: 'string', | ||
required: true, | ||
}) | ||
email: string; | ||
|
||
@property({ | ||
type: 'string', | ||
required: true, | ||
}) | ||
password: string; | ||
|
||
@property({ | ||
type: 'string', | ||
}) | ||
firstname?: string; | ||
|
||
@property({ | ||
type: 'string', | ||
}) | ||
surname?: string; | ||
|
||
constructor(data?: Partial<User>) { | ||
super(data); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
export * from './user.repository'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright IBM Corp. 2018. All Rights Reserved. | ||
// Node module: @loopback/example-shopping | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {DefaultCrudRepository, juggler} from '@loopback/repository'; | ||
import {User} from '../models'; | ||
import {inject} from '@loopback/core'; | ||
|
||
export class UserRepository extends DefaultCrudRepository< | ||
User, | ||
typeof User.prototype.id | ||
> { | ||
constructor( | ||
@inject('datasources.user') protected datasource: juggler.DataSource, | ||
) { | ||
super(User, datasource); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ | |
|
||
import {createClientForHandler, supertest} from '@loopback/testlab'; | ||
import {RestServer} from '@loopback/rest'; | ||
import {ShoppingApplication} from '../'; | ||
import {ShoppingApplication} from '../..'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is probably a good reminder for us to change the template generated by our app generator so that this file lives in the acceptance folder There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
describe('PingController', () => { | ||
let app: ShoppingApplication; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a check for mongodb service / process here instead of using sleep?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would have to check and sleep / something if the service isn't up and ready yet anyways. This is the fix recommended by Travis themselves, documented here: https://docs.travis-ci.com/user/database-setup/#mongodb-does-not-immediately-accept-connections