-
Notifications
You must be signed in to change notification settings - Fork 0
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
API Endpoint to Download TSV Files for donors by Donor ID #1062
Conversation
export type PaginatedClinicalQuery = ClinicalDonorEntityQuery & { | ||
export type PaginationQuery = { | ||
page: number; | ||
pageSize?: number; | ||
sort: string; | ||
}; | ||
|
||
export type PaginatedClinicalQuery = ClinicalDonorEntityQuery & PaginationQuery; |
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 had a need to separate the a worker thread task to accept Pagination parameters separate from the Clinical Donor Entity query data, so we can separate out that data for reference to its own type PaginationQuery
.
const taskToRun = WorkerTasks.ExtractEntityDataFromDonors; | ||
const taskArgs = [donors as Donor[], totalDonors, allSchemasWithFields, query]; | ||
const taskArgs = [donors as Donor[], totalDonors, allSchemasWithFields, query.entityTypes, query]; |
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.
the ExtractEntityDataFromDonors
task was updated to require the PaginatedQuery
in place of the ClinicalDonorEntityQuery
since the new request by DonorId doesn't include much of that request data. The only additional information used from that query type was the list of entities to include in the request, which is now passed as a separate argument query.entityTypes
.
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.
I see the type for query implies the property (entityTypes
) is guaranteed to be present.
is it safe to assume its actual presence is validated on runtime elsewhere?
const date = currentDateFormatted(); | ||
const fileName = `filename=Donor_Clinical_Data_${date}.zip`; |
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.
This is sharing the naming convention used for for downloads requested by program submitters. I'm open to suggestions to improve the provided filename. Perhaps a timestamp is a good idea in addition to the current date.
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.
aside from the filename format itself, would it make sense to abstract this into an env variable?
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.
aside from the filename format itself, would it make sense to abstract this into an env variable?
I don't see a reason to have different filenames per env, what's your reason? If you want filename constanta centralized into a constants file I can do that.
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.
Tbh, I'm just used to abstracting constants like this one, because that makes it easier for these values to be updated, without having to rebuild the code "just for a string change".
If you believe this doesn't need to be abstracted, I of course trust your insight.
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.
Good reason. will make the change.
} from './types'; | ||
import { loggerFor } from '../../logger'; | ||
const L = loggerFor(__filename); |
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.
Could Log
, or Logger
do the same job, while being a bit more declarative than a single capital letter l
?
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.
yes and it would be better. I'm being consistent with the existing pattern in this repo. I would - recommend a separate action to rename them all.
|
||
const zip = createClinicalZipFile(donorEntityData); | ||
|
||
res.send(zip.toBuffer()); |
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.
funny, I'd have imagined you'd get an error/warning here saying that you had already sent a response in line 170.
obviously I must be mistaken.
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.
previous use of res
didn't send data, just set some headers and status.
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.
yeah... I was under the impression res.status
sends a response.
time for me to do some reading and update my understanding.
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.
follow up: https://expressjs.com/en/api.html#res.status
looks like I've been away from coding for too long, starting to forget how to 😢
...I miss creating things
Co-authored-by: Anders Richardsson <[email protected]>
return data; | ||
}; | ||
|
||
export const getDonorEntityData = async (donorIds: number[]) => { | ||
const allSchemasWithFields = await dictionaryManager.instance().getSchemasWithFields(); |
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.
no need to do anything about this one, just felt the need to acknowledge out loud how nervous it makes me to have these long chains without ?.
😆
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.
long optional chaining makes me nervous too.
I actually really like how dictionaryManager
does this - throws an error if there's no instance - so you could actually just catch the error and return ... empty array of donorIds in that case?
that node pattern of throw/catch actual errors vs just retuning nulls/undefined is used a lot in this repo and I think it works well.
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.
re ?
part of this.... getSchemaWIthFields
will never actually be run on undefined, an error will be thrown instead
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.
welp! sounds like that requires a lot of "insider" knowledge on that dictionaryManager
.
try+catch sure does sound desirable, but I guess my non-evident point is that this line is a bit "code golf"ey, and could use being broken into separate lines 🤷♂️
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.
don't need insider knowledge... the type system informs us of what the output will be. if we can trust the type system then we're safe here.
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.
oh fun! didn't realise TS (intellisense) was visible in PR reviews at Github.com
must get myself that browser extension!
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.
sarcasm acknowledged. agreed that the lack of introspection in the browser code review is limiting.
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.
to be fair, we could review PRs by testing them locally... like we used to back in the day.
Modern tooling has become a crutch for what could be more declarative; which proves hard to keep up in the review process. How do you suggest we improve that?
@@ -392,7 +393,17 @@ export const donorDao: DonorRepository = { | |||
submitterId: { $in: submitterIds }, | |||
programId: programId, | |||
}); | |||
const mapped = result.map((d: DonorDocument) => { | |||
const mapped = result.map(d => { | |||
return MongooseUtils.toPojo(d) as Donor; |
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.
you, using as
? le wut!
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.
Old code pattern in the db repo module, there are better TS ways to do this now using mongoose. Following convention of the file instead of investing time in a re-write of the DB management.
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.
screenshotted to use without context in the future (context provider is undefineed) :p
Co-authored-by: Anders Richardsson <[email protected]>
const mapped = result.map(d => { | ||
return MongooseUtils.toPojo(d) as Donor; | ||
}); | ||
return F(mapped); |
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.
what on earth is F
!?
...I know it's just the established pattern, and it's obviously a function, but what does it do!?
without having to change the whole app, at least the import could be renamed in this file.
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.
rewrites the type as DeepReadonly<T>
. Again, following the service convention, I do not recommend adopting this pattern elsewhere.
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.
import { F as Freeze }
should help... but even then, what's freezing
? 😮💨
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't update/extend properties dynamically
if I remember correctly it's a very light lib around the native Object.freeze() https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
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.
LGTM (except for the deeper "pattern" issues pointed out in the comments, which are beyond the scope of this PR)
Description of changes
This adds a new API endpoint at
POST /clinical/donors/tsv
which accepts in the request body an array of donor IDs. It will collect all entity data for the requested donors and return that data in a Zip file with TSVs of each clinical entity.This endpoint is restricted to Full Read Access only, requiring service level read access.
This will be used by the UI/API for users to request Clinical Data for files/donors that they have embargo level access to. Additional authorization restrictions for this request are managed by the Platform API Gateway.
Type of Change
Checklist before requesting review:
develop
not master)validationDependency
in meta tag for Argo Dictionary fields used in code