From 556e857fefe218c1d3c543031c0d6e966092189a Mon Sep 17 00:00:00 2001 From: j-nanda Date: Thu, 28 Oct 2021 15:53:42 +0300 Subject: [PATCH 1/5] Finished ticket 12 --- .eslintrc.js | 3 ++- src/models/User.ts | 24 +++++++++++------------ src/routes/LoginRoute.test.ts | 2 +- src/routes/LoginRoute.ts | 29 ++++++++++++++-------------- src/routes/UpdateMeRoute.test.ts | 2 +- src/routes/UpdateMeRoute.ts | 21 +++++++++++++++++--- tsconfig.json | 33 ++++++++++++++++---------------- 7 files changed, 65 insertions(+), 49 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 8ee479f..d4e1164 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -23,6 +23,7 @@ module.exports = { 'max-lines-per-function': ['error', { max: 75, skipComments: true }], 'no-underscore-dangle': 0, 'react/jsx-props-no-spreading': 0, - 'react/prop-types': 0 + 'react/prop-types': 0, + 'prettier/prettier': ['error',{endOfLine: 'auto'}] } }; diff --git a/src/models/User.ts b/src/models/User.ts index a06e0c2..7110fdf 100644 --- a/src/models/User.ts +++ b/src/models/User.ts @@ -5,7 +5,6 @@ import AuthUtils from '../utils/AuthUtils'; import { Model } from '../utils/constants'; import { AuthTokens, BaseModel } from '../utils/types'; - interface IUser extends BaseModel { email?: string; @@ -64,20 +63,18 @@ export type UserDocument = Document<{}, {}, IUser> & const userSchema: Schema = new Schema( { - - email: { required: false, sparse: true, type: String, unique: true}, - firstName: { required: false, type: String}, - instagramUrl: { required: false, type: String}, - lastName: { required: false, type: String}, - linkedInUrl: { required: false, type: String}, - profilePictureKey: { required: false, type: String}, - phoneNumber : { required: true, type: String, unique: true}, - twitterUrl: { required: false, type: String}, - - + email: { required: false, sparse: true, type: String, unique: true }, + firstName: { required: false, type: String }, + instagramUrl: { required: false, type: String }, + lastName: { required: false, type: String }, + linkedInUrl: { required: false, type: String }, + phoneNumber: { required: true, type: String, unique: true }, + profilePictureKey: { required: false, type: String }, // We shouldn't be returning the refreshToken when fetching a user from // the database, since that is sensitive information. - refreshToken: { required: false, select: false, type: String} + refreshToken: { required: false, select: false, type: String }, + + twitterUrl: { required: false, type: String } }, { timestamps: true, @@ -90,6 +87,7 @@ type TokenArgs = { date: number; id: string; }; + /* * Creates and returns a new access and refresh token for the user. It also * persists the refresh token to the database, so we can associate the token diff --git a/src/routes/LoginRoute.test.ts b/src/routes/LoginRoute.test.ts index 353aaf3..9f9a2cb 100644 --- a/src/routes/LoginRoute.test.ts +++ b/src/routes/LoginRoute.test.ts @@ -10,7 +10,7 @@ import TestUtils from '../utils/TestUtils'; * npm run test LoginRoute * - Delete this comment. */ -describe.skip('POST /login', () => { +describe('POST /login', () => { test('If the phone number is not valid, should return a 400.', async () => { await TestUtils.agent .post('/login') diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 4d37ccb..2c12aa7 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -8,34 +8,28 @@ import BaseRoute from '../utils/BaseRoute'; import { RouteMethod } from '../utils/constants'; import RouteError from '../utils/RouteError'; + type LoginBody = Pick; export default class LoginRoute extends BaseRoute { constructor() { super({ - /** - * TODO: (7.01) - * - Replace null with the correct route type from the RouteMethod enum - * in the constants.ts file. - * - Fill in the path string with the appropriate path to this endpoint. - * - Delete this comment. - */ - method: null, - path: '/' + method: RouteMethod.POST, + path: '/login' }); } - /** - * Validate the following inputs: - * - body.phoneNumber - */ middleware() { /** * TODO: (7.02) * - Add a validation the returned array ensureing that the phoneNumber * field in the body is a valid US phone number. */ - return []; + return [ + body('phoneNumber') + .isMobilePhone('en-US') + .withMessage('This is not a valid phone number.') + ]; } /** @@ -56,16 +50,23 @@ export default class LoginRoute extends BaseRoute { * - Send a text to the user with the code. */ // TODO: (7.03) Get the phone number from the request body. + const { phoneNumber } = req.body; + // TODO: (7.03) We should delete all codes that previously existed for the // user. + await AuthCode.deleteMany({ phoneNumber }); // TODO: (7.03) Create a new AuthCode document in the database. + const authCode: AuthCodeDocument = await AuthCode.create({ phoneNumber }); + // TODO: (7.03) Send a text to the user. + // TODO: (7.03) If the text was not sent, throw a new RouteError with status // code 500. + return true; } diff --git a/src/routes/UpdateMeRoute.test.ts b/src/routes/UpdateMeRoute.test.ts index dfb149c..1f05a30 100644 --- a/src/routes/UpdateMeRoute.test.ts +++ b/src/routes/UpdateMeRoute.test.ts @@ -9,7 +9,7 @@ import TestUtils from '../utils/TestUtils'; * npm run test UpdateMe * - Delete this comment. */ -describe.skip('PATCH /me', () => { +describe('PATCH /me', () => { test('If the user is not authenticated, should return a 401.', async () => { await TestUtils.agent.patch('/me').expect(401); }); diff --git a/src/routes/UpdateMeRoute.ts b/src/routes/UpdateMeRoute.ts index b51dd0f..560d15e 100644 --- a/src/routes/UpdateMeRoute.ts +++ b/src/routes/UpdateMeRoute.ts @@ -25,9 +25,9 @@ export default class UpdateMeRoute extends BaseRoute { * - Fill in the path string with the appropriate path to this endpoint. * - Delete this comment. */ - authenticated: false, - method: null, - path: '/' + authenticated: true, + method: RouteMethod.PATCH, + path: '/me' }); } @@ -54,6 +54,21 @@ export default class UpdateMeRoute extends BaseRoute { .isURL() .withMessage('The Instagram URL must be a valid URL.'), + body('linkedInUrl') + .if((value: string) => Utils.isDefined(value) && !!value.length) + .isURL() + .withMessage('The LinkedIn URL must be a valid URL.'), + + body('twitterUrl') + .if((value: string) => Utils.isDefined(value) && !!value.length) + .isURL() + .withMessage('The Twitter URL must be a valid URL.'), + + body('lastName') + .if((value: string) => Utils.isDefined(value)) + .isLength({ min: 1 }) + .withMessage('Last name cannot be empty.'), + multer().single('profilePicture') ]; } diff --git a/tsconfig.json b/tsconfig.json index d84c6a5..11796c1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,17 @@ -{ - "compilerOptions": { - "esModuleInterop": true, - "module": "commonjs", - "outDir": "./dist", - "target": "es2018" - }, - "include": ["src/**/*.ts"], - "exclude": [ - "node_modules", - "src/**/*.test.ts", - "./*.config.ts", - "./jest.*.ts" - ], - "typeRoots": ["./node_modules/@types"] -} +{ + "compilerOptions": { + "esModuleInterop": true, + "module": "commonjs", + "outDir": "./dist", + "target": "es2018" + + }, + "include": ["src/**/*.ts"], + "exclude": [ + "node_modules", + "src/**/*.test.ts", + "./*.config.ts", + "./jest.*.ts" + ], + "typeRoots": ["./node_modules/@types"] +} From 6e5110115bf42b66b958ba3446b115f2e90f3513 Mon Sep 17 00:00:00 2001 From: j-nanda Date: Thu, 28 Oct 2021 16:03:50 +0300 Subject: [PATCH 2/5] Finished ticket 12 --- src/routes/LoginRoute.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 2c12aa7..2d2392a 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -8,7 +8,6 @@ import BaseRoute from '../utils/BaseRoute'; import { RouteMethod } from '../utils/constants'; import RouteError from '../utils/RouteError'; - type LoginBody = Pick; export default class LoginRoute extends BaseRoute { @@ -52,7 +51,6 @@ export default class LoginRoute extends BaseRoute { // TODO: (7.03) Get the phone number from the request body. const { phoneNumber } = req.body; - // TODO: (7.03) We should delete all codes that previously existed for the // user. await AuthCode.deleteMany({ phoneNumber }); @@ -60,13 +58,10 @@ export default class LoginRoute extends BaseRoute { // TODO: (7.03) Create a new AuthCode document in the database. const authCode: AuthCodeDocument = await AuthCode.create({ phoneNumber }); - // TODO: (7.03) Send a text to the user. - // TODO: (7.03) If the text was not sent, throw a new RouteError with status // code 500. - return true; } From 7062a8471995e7b742d28ebb23df958790d028f7 Mon Sep 17 00:00:00 2001 From: j-nanda Date: Thu, 28 Oct 2021 16:17:23 +0300 Subject: [PATCH 3/5] Done with ticket 12 --- src/routes/LoginRoute.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 2d2392a..9db6126 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -49,14 +49,11 @@ export default class LoginRoute extends BaseRoute { * - Send a text to the user with the code. */ // TODO: (7.03) Get the phone number from the request body. - const { phoneNumber } = req.body; // TODO: (7.03) We should delete all codes that previously existed for the // user. - await AuthCode.deleteMany({ phoneNumber }); // TODO: (7.03) Create a new AuthCode document in the database. - const authCode: AuthCodeDocument = await AuthCode.create({ phoneNumber }); // TODO: (7.03) Send a text to the user. From 39be708ca398dbf8f078214c2c4c6b01cd8a218b Mon Sep 17 00:00:00 2001 From: j-nanda Date: Sun, 31 Oct 2021 21:42:57 +0300 Subject: [PATCH 4/5] Ticket 12 passed finally --- .eslintrc.js | 4 +++- src/models/AuthCode.ts | 3 ++- src/routes/LoginRoute.ts | 16 +++++++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index d4e1164..a33f9c8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -24,6 +24,8 @@ module.exports = { 'no-underscore-dangle': 0, 'react/jsx-props-no-spreading': 0, 'react/prop-types': 0, - 'prettier/prettier': ['error',{endOfLine: 'auto'}] + 'no-unused-vars': 'off', + '@typescipt-eslint/no-unused-vars': ['error'], + 'prettier/prettier': ['error', { endOfLine: 'auto' }] } }; diff --git a/src/models/AuthCode.ts b/src/models/AuthCode.ts index 3c566a9..a646cd0 100644 --- a/src/models/AuthCode.ts +++ b/src/models/AuthCode.ts @@ -39,7 +39,8 @@ const authCodeSchema: Schema = new Schema( */ { // Here's an example of how to add a field to the schema. - exampleField: { required: true, type: String, unique: false } + phoneNumber: { required: true, type: String, unique: true }, + value: { default: AuthUtils.generateOTP, required: true, type: Number } }, { timestamps: true } ); diff --git a/src/routes/LoginRoute.ts b/src/routes/LoginRoute.ts index 9db6126..b1d85ea 100644 --- a/src/routes/LoginRoute.ts +++ b/src/routes/LoginRoute.ts @@ -27,7 +27,7 @@ export default class LoginRoute extends BaseRoute { return [ body('phoneNumber') .isMobilePhone('en-US') - .withMessage('This is not a valid phone number.') + .withMessage('This is not a valid phone number') ]; } @@ -49,16 +49,30 @@ export default class LoginRoute extends BaseRoute { * - Send a text to the user with the code. */ // TODO: (7.03) Get the phone number from the request body. + const { phoneNumber } = req.body; // TODO: (7.03) We should delete all codes that previously existed for the // user. + await AuthCode.deleteMany({ phoneNumber }); // TODO: (7.03) Create a new AuthCode document in the database. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const authCode: AuthCodeDocument = await AuthCode.create({ phoneNumber }); // TODO: (7.03) Send a text to the user. + const wasTextSent: boolean = await TextService.sendText({ + message: 'Your OTP code: $(authCode.value)', + to: phoneNumber + }); // TODO: (7.03) If the text was not sent, throw a new RouteError with status // code 500. + if (!wasTextSent) { + throw new RouteError({ + message: 'Failed to send OTP code text, please try again', + statusCode: 500 + }); + } return true; } From 6b94d774dfb6bedff3215bda971dbab2f3482c3b Mon Sep 17 00:00:00 2001 From: j-nanda Date: Sun, 31 Oct 2021 22:59:56 +0300 Subject: [PATCH 5/5] Ticket 12 done --- .eslintrc.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index a33f9c8..cb00900 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -25,7 +25,7 @@ module.exports = { 'react/jsx-props-no-spreading': 0, 'react/prop-types': 0, 'no-unused-vars': 'off', - '@typescipt-eslint/no-unused-vars': ['error'], + //'@typescipt-eslint/no-unused-vars': ['error'], 'prettier/prettier': ['error', { endOfLine: 'auto' }] } };