Add GET /users/:identifier endpoint

This commit is contained in:
ricardo 2022-03-27 18:23:59 -04:00
parent 8829684d5b
commit 924773ef5d
5 changed files with 12620 additions and 53790 deletions

66217
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -114,7 +114,7 @@ paths:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/InputValidationError' $ref: '#/components/schemas/InputValidationError'
/users/{userId}: /users/{identifier}:
get: get:
x-eov-operation-id: getUser x-eov-operation-id: getUser
x-eov-operation-handler: routes/Users x-eov-operation-handler: routes/Users
@ -124,7 +124,7 @@ paths:
description: Retrieve a user from your instance. Only available for the instance owner. description: Retrieve a user from your instance. Only available for the instance owner.
operationId: getUser operationId: getUser
parameters: parameters:
- name: userId - name: identifier
in: path in: path
description: The ID or email of the user description: The ID or email of the user
required: true required: true
@ -132,6 +132,14 @@ paths:
explode: false explode: false
schema: schema:
type: string type: string
- name: includeRole
in: query
required: false
style: form
explode: true
schema:
type: string
example: true
responses: responses:
"200": "200":
description: Successful operation description: Successful operation
@ -154,7 +162,7 @@ paths:
description: Deletes a user from your instance. Only available for the instance owner. description: Deletes a user from your instance. Only available for the instance owner.
operationId: deleteUser operationId: deleteUser
parameters: parameters:
- name: userId - name: identifier
in: path in: path
description: The name that needs to be deleted description: The name that needs to be deleted
required: true required: true

View file

@ -1,6 +1,7 @@
import express = require('express'); import express = require('express');
import { getConnection } from 'typeorm'; import { getConnection } from 'typeorm';
import { validate as uuidValidate } from 'uuid';
import { UserRequest } from '../../../../requests'; import { UserRequest } from '../../../../requests';
import { User } from '../../../../databases/entities/User'; import { User } from '../../../../databases/entities/User';
@ -19,7 +20,32 @@ export = {
res.json({ success: true }); res.json({ success: true });
}, },
getUser: async (req: UserRequest.Get, res: express.Response): Promise<void> => { getUser: async (req: UserRequest.Get, res: express.Response): Promise<void> => {
res.json({ success: true }); const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
const { identifier } = req.params;
const query = getConnection(connectionName())
.getRepository(User)
.createQueryBuilder()
.leftJoinAndSelect('User.globalRole', 'Role')
.select(getSelectableProperties('user')?.map((property) => `User.${property}`));
if (includeRole) {
query.addSelect(getSelectableProperties('role')?.map((property) => `Role.${property}`));
}
if (uuidValidate(identifier)) {
query.where({ id: identifier });
} else {
query.where({ email: identifier });
}
const user = await query.getOne();
if (user === undefined) {
res.status(404);
}
res.json(user);
}, },
getUsers: async (req: UserRequest.Get, res: express.Response): Promise<void> => { getUsers: async (req: UserRequest.Get, res: express.Response): Promise<void> => {
let offset = 0; let offset = 0;

View file

@ -204,7 +204,7 @@ export declare namespace UserRequest {
>; >;
export type Get = AuthenticatedRequest< export type Get = AuthenticatedRequest<
{ id: string; email: string }, { id: string; email: string; identifier: string },
{}, {},
{}, {},
{ limit: string; cursor: string; includeRole: string } { limit: string; cursor: string; includeRole: string }

View file

@ -173,6 +173,155 @@ test('GET /users should return all users', async () => {
} }
}); });
test('GET /users/:identifier should fail due to missing API Key', async () => {
const owner = await Db.collections.User!.findOneOrFail();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: false, user: owner });
await testDb.createUser();
const response = await authOwnerAgent.get(`/v1/users/${owner.id}`);
expect(response.statusCode).toBe(401);
});
test('GET /users/:identifier should fail due to invalid API Key', async () => {
const owner = await Db.collections.User!.findOneOrFail();
owner.apiKey = null;
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: false, user: owner });
const response = await authOwnerAgent.get(`/v1/users/${owner.id}`);
expect(response.statusCode).toBe(401);
});
test('GET /users/:identifier should fail due to member trying to access owner only endpoint', async () => {
const member = await testDb.createUser();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: true, user: member });
const response = await authOwnerAgent.get(`/v1/users/${member.id}`);
expect(response.statusCode).toBe(403);
});
test('GET /users/:identifier should fail due no instance owner not setup', async () => {
config.set('userManagement.isInstanceOwnerSetUp', false);
const owner = await Db.collections.User!.findOneOrFail();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: true, user: owner });
const response = await authOwnerAgent.get(`/v1/users/${owner.id}`);
expect(response.statusCode).toBe(400);
});
test('GET /users/:email with unexisting email should return 404', async () => {
const owner = await Db.collections.User!.findOneOrFail();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: true, user: owner });
const response = await authOwnerAgent.get(`/v1/users/jhondoe@gmail.com`);
expect(response.statusCode).toBe(404);
});
test('GET /users/:id with unexisting id should return 404', async () => {
const owner = await Db.collections.User!.findOneOrFail();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: true, user: owner });
const response = await authOwnerAgent.get(`/v1/users/123`);
expect(response.statusCode).toBe(404);
});
test('GET /users/:email should return a user', async () => {
const owner = await Db.collections.User!.findOneOrFail();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: true, user: owner });
const response = await authOwnerAgent.get(`/v1/users/${owner.email}`);
expect(response.statusCode).toBe(200);
const {
id,
email,
firstName,
lastName,
personalizationAnswers,
globalRole,
password,
resetPasswordToken,
isPending,
createdAt,
updatedAt,
} = response.body;
expect(validator.isUUID(id)).toBe(true);
expect(email).toBeDefined();
expect(firstName).toBeDefined();
expect(lastName).toBeDefined();
expect(personalizationAnswers).toBeUndefined();
expect(password).toBeUndefined();
expect(resetPasswordToken).toBeUndefined();
//virtual method not working
//expect(isPending).toBe(false);
expect(globalRole).toBeUndefined();
expect(createdAt).toBeDefined();
expect(updatedAt).toBeDefined();
});
test('GET /users/:id should return a user', async () => {
const owner = await Db.collections.User!.findOneOrFail();
const authOwnerAgent = utils.createAgent(app, { apiPath: 'public', auth: true, user: owner });
const response = await authOwnerAgent.get(`/v1/users/${owner.id}`);
expect(response.statusCode).toBe(200);
const {
id,
email,
firstName,
lastName,
personalizationAnswers,
globalRole,
password,
resetPasswordToken,
isPending,
createdAt,
updatedAt,
} = response.body;
expect(validator.isUUID(id)).toBe(true);
expect(email).toBeDefined();
expect(firstName).toBeDefined();
expect(lastName).toBeDefined();
expect(personalizationAnswers).toBeUndefined();
expect(password).toBeUndefined();
expect(resetPasswordToken).toBeUndefined();
//virtual method not working
//expect(isPending).toBe(false);
expect(globalRole).toBeUndefined();
expect(createdAt).toBeDefined();
expect(updatedAt).toBeDefined();
});
const INITIAL_TEST_USER = { const INITIAL_TEST_USER = {
id: uuid(), id: uuid(),
email: randomEmail(), email: randomEmail(),