diff --git a/data-serving/data-service/src/controllers/case.ts b/data-serving/data-service/src/controllers/case.ts index 9ecb20cb5..f90663ee2 100644 --- a/data-serving/data-service/src/controllers/case.ts +++ b/data-serving/data-service/src/controllers/case.ts @@ -6,7 +6,7 @@ import { CuratorsDocument, } from '../model/day0-case'; import caseFields from '../model/fields.json'; -import { CaseStatus } from '../types/enums'; +import { CaseStatus, Role } from '../types/enums'; import { Error, Query } from 'mongoose'; import { ObjectId } from 'mongodb'; import { GeocodeOptions, Geocoder, Resolution } from '../geocoding/geocoder'; @@ -59,14 +59,24 @@ const caseFromDTO = async (receivedCase: CaseDTO) => { const user = await User.findOne({ email: receivedCase.curator?.email }); if (user) { - if (user.roles.includes('junior curator')) { + logger.info(`User: ${JSON.stringify(user)}`) + if (user.roles.includes(Role.JuniorCurator)) { aCase.curators = { - createdBy: user, + createdBy: { + name: user.name || '', + email: user.email + }, }; - } else { + } else if (user.roles.includes(Role.Curator) || user.roles.includes(Role.Admin)) { aCase.curators = { - createdBy: user, - verifiedBy: user, + createdBy: { + name: user.name || '', + email: user.email + }, + verifiedBy: { + name: user.name || '', + email: user.email + }, }; } } @@ -88,40 +98,43 @@ const dtoFromCase = async (storedCase: CaseDocument) => { }); } - if (ageRange && creator) { - if (verifier) { - dto = { - ...dto, - demographics: { - ...dto.demographics!, - ageRange, - }, - curators: { - createdBy: { - email: creator.email, - name: creator.name, - }, - verifiedBy: { - email: verifier.email, - name: verifier.name, - }, - } as CuratorsDocument, - }; - } else { - dto = { - ...dto, - demographics: { - ...dto.demographics!, - ageRange, - }, - curators: { - createdBy: { - email: creator.email, - name: creator.name, - }, - } as CuratorsDocument, - }; + if (ageRange) { + if(creator) { + if (verifier) { + dto = { + ...dto, + curators: { + createdBy: { + email: creator.email, + name: creator.name, + }, + verifiedBy: { + email: verifier.email, + name: verifier.name, + }, + } as CuratorsDocument, + }; + } else { + dto = { + ...dto, + curators: { + createdBy: { + email: creator.email, + name: creator.name, + }, + } as CuratorsDocument, + }; + } } + dto = { + ...dto, + demographics: { + ...dto.demographics!, + ageRange, + } + }; + + // although the type system can't see it, there's an ageBuckets property on the demographics DTO now delete ((dto as unknown) as { @@ -242,7 +255,7 @@ export class CasesController { return; } - logger.info( + logger.debug( `Streaming case data in format ${req.body.format} matching query ${req.body.query} for correlation ID ${req.body.correlationId}`, ); @@ -255,7 +268,7 @@ export class CasesController { // Stream from mongoose directly into response // Use provided query and limit, if provided if (req.body.query) { - logger.info('Request body with query'); + logger.debug('Request body with query'); cursor = casesMatchingSearchQuery({ searchQuery: req.body.query as string, count: false, @@ -264,7 +277,7 @@ export class CasesController { .collation({ locale: 'en_US', strength: 2 }) .cursor(); } else if (req.body.caseIds && queryLimit) { - logger.info('Request body with case IDs and limit'); + logger.debug('Request body with case IDs and limit'); cursor = Day0Case.find({ _id: { $in: req.body.caseIds }, }) @@ -276,7 +289,7 @@ export class CasesController { }) .cursor(); } else if (req.body.caseIds) { - logger.info('Request body with case IDs and no limit'); + logger.debug('Request body with case IDs and no limit'); cursor = Day0Case.find({ _id: { $in: req.body.caseIds }, }) @@ -287,7 +300,7 @@ export class CasesController { }) .cursor(); } else if (queryLimit) { - logger.info('Request body with limit and no case IDs'); + logger.debug('Request body with limit and no case IDs'); cursor = Day0Case.find() .lean() .limit(queryLimit) @@ -297,7 +310,7 @@ export class CasesController { }) .cursor(); } else { - logger.info('Request body with no query, limit, or case IDs'); + logger.debug('Request body with no query, limit, or case IDs'); cursor = Day0Case.find() .lean() .collation({ @@ -392,7 +405,7 @@ export class CasesController { * Handles HTTP GET /api/cases. */ list = async (req: Request, res: Response): Promise => { - logger.info('List method entrypoint'); + logger.debug('List method entrypoint'); const page = Number(req.query.page) || 1; const limit = Number(req.query.limit) || 10; const countLimit = Number(req.query.count_limit) || 10000; @@ -401,7 +414,7 @@ export class CasesController { const verificationStatus = Boolean(req.query.verification_status) || undefined; - logger.info('Got query params'); + logger.debug('Got query params'); if (page < 1) { res.status(422).json({ message: 'page must be > 0' }); @@ -420,7 +433,7 @@ export class CasesController { return; } - logger.info('Got past 422s'); + logger.debug('Got past 422s'); try { const casesQuery = casesMatchingSearchQuery({ searchQuery: req.query.q || '', @@ -457,7 +470,7 @@ export class CasesController { ]); const dtos = await Promise.all(docs.map(dtoFromCase)); - logger.info('got results'); + logger.debug('got results'); // total is actually stored in a count index in mongo, so the query is fast. // however to maintain existing behaviour, only return the count limit const reportedTotal = Math.min(total, countLimit); @@ -469,11 +482,11 @@ export class CasesController { nextPage: page + 1, total: reportedTotal, }); - logger.info('Got multiple pages of results'); + logger.debug('Got multiple pages of results'); return; } // If we fetched all available data, just return it. - logger.info('Got one page of results'); + logger.debug('Got one page of results'); res.json({ cases: dtos, total: reportedTotal }); } catch (e) { if (e instanceof ParsingError) { @@ -495,7 +508,7 @@ export class CasesController { * Handles HTTP GET /api/cases/countryData. */ countryData = async (req: Request, res: Response): Promise => { - logger.info('Get cases by country method entrypoint'); + logger.debug('Get cases by country method entrypoint'); try { // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/data-serving/data-service/src/model/day0-case.ts b/data-serving/data-service/src/model/day0-case.ts index 6134bd855..122cc0d9f 100644 --- a/data-serving/data-service/src/model/day0-case.ts +++ b/data-serving/data-service/src/model/day0-case.ts @@ -27,6 +27,7 @@ import { RevisionMetadataDocument, revisionMetadataSchema, } from './revision-metadata'; +import { userSchema } from '../model/user'; /* * There are separate types for case for data storage (the mongoose document) and @@ -60,8 +61,8 @@ export const sourceSchema = new mongoose.Schema({ export const curatorsSchema = new mongoose.Schema( { - verifiedBy: { type: mongoose.Schema.Types.ObjectId, ref: 'user' }, - createdBy: { type: mongoose.Schema.Types.ObjectId, ref: 'user' }, + verifiedBy: { type: userSchema }, + createdBy: { type: userSchema }, }, { _id: false }, ); diff --git a/data-serving/data-service/src/model/user.ts b/data-serving/data-service/src/model/user.ts index bd28092f2..5584e8e8d 100644 --- a/data-serving/data-service/src/model/user.ts +++ b/data-serving/data-service/src/model/user.ts @@ -13,6 +13,7 @@ export type UserDocument = mongoose.Document & }; export const userSchema = new mongoose.Schema({ + name: String, email: String, roles: [String], }); diff --git a/data-serving/data-service/src/types/enums.ts b/data-serving/data-service/src/types/enums.ts index 8526df416..56d5ef12c 100644 --- a/data-serving/data-service/src/types/enums.ts +++ b/data-serving/data-service/src/types/enums.ts @@ -10,3 +10,9 @@ export enum YesNo { N = 'N', NA = 'NA', } + +export enum Role { + Admin = 'admin', + Curator = 'curator', + JuniorCurator = 'junior curator', +} diff --git a/data-serving/data-service/test/controllers/case.test.ts b/data-serving/data-service/test/controllers/case.test.ts index 985d524cc..619a7d9fa 100644 --- a/data-serving/data-service/test/controllers/case.test.ts +++ b/data-serving/data-service/test/controllers/case.test.ts @@ -18,10 +18,18 @@ import { import fs from 'fs'; import { AgeBucket } from '../../src/model/age-bucket'; import { User, UserDocument } from '../../src/model/user'; +import { Role } from '../../src/types/enums'; let mongoServer: MongoMemoryServer; +const curatorName = 'Casey Curatorio'; const curatorUserEmail = 'case_curator@global.health'; +// const curatorMetadata = { +// curator: { +// name: curatorName, +// email: curatorUserEmail +// } +// }; const curatorMetadata = { curator: { email: curatorUserEmail } }; const minimalRequest = { @@ -69,10 +77,16 @@ beforeAll(async () => { mockLocationServer.listen(); mongoServer = new MongoMemoryServer(); await createAgeBuckets(); - curator = await User.create({ email: curatorUserEmail }); + curator = await User.create( + { + name: curatorName, + email: curatorUserEmail, + roles: [Role.Curator] + } + ); minimalDay0CaseData = { ...minimalDay0CaseData, - ...{ curators: { createdBy: curator._id } }, + ...{ curators: { createdBy: curatorMetadata.curator } }, ...{ revisionMetadata: { revisionNumber: 0, @@ -84,7 +98,7 @@ beforeAll(async () => { }; fullDay0CaseData = { ...fullCase, - ...{ curators: { createdBy: curator._id } }, + ...{ curators: { createdBy: curatorMetadata.curator } }, ...{ revisionMetadata: { revisionNumber: 0, @@ -1000,7 +1014,7 @@ describe('POST', () => { .post('/api/cases/batchUpsert') .send({ cases: [ - { ...minimalCase, curators: { createdBy: curator._id } }, + minimalDay0CaseData, invalidRequest, ], ...curatorMetadata,