Dictionary Migration - Get migration endpoints#200
Dictionary Migration - Get migration endpoints#200leoraba wants to merge 39 commits intofeat/dictionary_migrationfrom
Conversation
joneubank
left a comment
There was a problem hiding this comment.
Minor changes here. Good work.
| retries: | ||
| type: number | ||
| description: Number of retries of the migration |
There was a problem hiding this comment.
Do we explain "retries" anywhere? this is a confusing data point.
There was a problem hiding this comment.
I've updated the description for this field "Number of times the migration has run. When a migration fails, it can be retried by registering again the same dictionary."
However, a proper documentation is added in the following PR, where the "retry" functionality is implemented
| const defaultPage = 1; | ||
| const defaultPageSize = 20; |
There was a problem hiding this comment.
Maybe should be global constants defined with the pagination types?
There was a problem hiding this comment.
moved this constants to a new shared file.
| try { | ||
| const migrationId = Number(req.params.migrationId); | ||
|
|
||
| logger.info(LOG_MODULE, `Request Migration id '${migrationId}'`); |
There was a problem hiding this comment.
should probably be debug level
There was a problem hiding this comment.
This comment applies this to all similar request detail logging in this controller.
There was a problem hiding this comment.
we add a info log for every request in every controller because we're not using express request logger.
| if (!submission) { | ||
| throw new NotFound('Submission not found'); |
There was a problem hiding this comment.
should we be logging that the migration was not found?
There was a problem hiding this comment.
👍 code changed as suggested
| if (submissions.result.length === 0) { | ||
| throw new NotFound('Submissions not found'); | ||
| } |
There was a problem hiding this comment.
If the category ID exists, this should return an empty list.
If the category ID does not exist, then it should return NotFound.
There was a problem hiding this comment.
code changed to return NotFound when category does not exists, also changed to return same error in other endpoints of this controller
| } | ||
| }; | ||
|
|
||
| const getMigrationById = async (migrationId: number): Promise<MigrationRecordWithRelations | undefined> => { |
There was a problem hiding this comment.
Similar comment as previous... this needs to explain why it sometimes returns undefined instead of the returned type.
While I prefer using undefined for missing values, the rest of the Service functions return null instead. It is confusing to have both values used in an ad-hoc way. Let's consistently use either null or undefined
There was a problem hiding this comment.
for consistency, will return null when migration is not found, and will update tsdocs
| const getMigrationsByCategoryId = async ( | ||
| categoryId: number, | ||
| paginationOptions: PaginationOptions, | ||
| ): Promise<{ metadata: { totalRecords: number; errorMessage?: string }; result: MigrationRecordWithRelations[] }> => { |
There was a problem hiding this comment.
Might be useful to create a type alias for this return and export it for use where this function is consumed.
There was a problem hiding this comment.
added a type PaginatedResult<T> to be used in this and other functions
| } catch (error) { | ||
| logger.error(LOG_MODULE, `Error retrieving migrations for categoryId '${categoryId}'`, error); | ||
| throw error; | ||
| } |
There was a problem hiding this comment.
This can be a troubling pattern to fall into, where every function that could throw an error just logs the error then throws it. You can get a long log where every function in the call stack logs the same error with no new information added. Generally, we want to log errors where we catch and handle them.
We could instead make a design decision here, or in any function within this call stack, that the caught error can be handled here instead of thrown again. We can replace our return value either with void return type if there is exactly only one expected failure condition, or replace the return type with a Result that has a list of known failure conditions.
There was a problem hiding this comment.
Removed the try-catch here as the error is unhandled in this function, it should be handled by the upper function (controller) that uses this function, repeated this logic in other functions in this Service.
| organizations?: string | string[]; | ||
| isInvalid?: boolean; | ||
| }, | ||
| ): Promise<{ result: AuditDataResponse[]; metadata: { totalRecords: number; errorMessage?: string } }> => { |
There was a problem hiding this comment.
Another place for a helpful type alias.
| * @param migration | ||
| * @returns | ||
| */ | ||
| export const formatMigrationSummary = (migration: MigrationSummary): MigrationSummary => ({ |
There was a problem hiding this comment.
I assume this is to make the return type from the route nicely formatted for presentation. If this is important to enforce, there should be a unit test for this on the route.
Don't spend much time on this next comment, but: this may be a good use for branded types. You can create a Branded type MigrationSummaryFormatted and have the controller require that it returns this branded type, that will enforce that this function is called before returning. This will enforce the rule through the type system, but since its only used for the route return type there's not a lot gained for the amount of effort here, so its not worth doin. Writing a test is the best way to enforce this.
There was a problem hiding this comment.
for the purpose of this PR, I added unit test for this function to verify it, I would considerate using branded types as suggested in future PRs
There was a problem hiding this comment.
unit tests added for migration formatter functions
|
PR has been updated based on the feedback received, it is now ready for code review. |
| records: migrationsResult.result.map(formatMigrationSummary), | ||
| }; | ||
|
|
||
| return res.send(response); |
There was a problem hiding this comment.
We should send a status 200
| * @param {string} filterOptions.systemId | ||
| * and additional filters | ||
| * @param {number} categoryId Category ID to filter the Audit records | ||
| * @param {object} filterOptions Additional filters and pagination options |
There was a problem hiding this comment.
Instead of {object} AuditFilterOptions might be better for the tsDocs
| status: Extract<MigrationStatus, 'COMPLETED' | 'FAILED'>; | ||
| userName: string; | ||
| }): Promise<Result<null, string>> => { | ||
| }): AsyncResult<null, string> => { |
There was a problem hiding this comment.
Should we use the AsyncResult pattern for the remainder of the services in this file? Or is this only used for create/update actions?
|
|
||
| /** | ||
| * Order the properties of the Migration Summary Record | ||
| * @param migration |
There was a problem hiding this comment.
| * @param migration | |
| * @param {MigrationSummary} migration - The migration summary to format. |
Description
This PR creates 3 new GET endpoints to retrieve migrations information.
Related tickets:
This PR depends on: