-
Notifications
You must be signed in to change notification settings - Fork 19
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
Feature: DESENG-443/UI implementation #2413
Conversation
* DESENG-452 : Applying pending migrations * Updating unit test * Updated changelog * Fixed lint issue
* DESENG-484: Adding max age for cors (#2377)
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2413 +/- ##
==========================================
+ Coverage 74.65% 74.71% +0.06%
==========================================
Files 537 537
Lines 18555 18557 +2
Branches 1349 1347 -2
==========================================
+ Hits 13852 13865 +13
+ Misses 4496 4485 -11
Partials 207 207
Flags with carried forward coverage won't be shown. Click here to find out more.
|
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.
Great work! It's been a really exciting feature to watch take shape.
Could you please just:
- Remove some non-error console.log calls (maybe just one?)
- Remove some commented-out code, I've left comments indicating where
Optionally, could you consider removing Promise.resolve, reject calls in async functions? I'm pretty sure they're not necessary. Also optionally, could you double-check that we need to add error messages to default context values? I feel like since we're setting those values in the provider right away, they shouldn't be necessary.
@@ -93,9 +101,27 @@ def post(engagement_id: int): | |||
except (ValueError, ValidationError) as err: | |||
return str(err), HTTPStatus.BAD_REQUEST | |||
|
|||
@staticmethod | |||
@cross_origin(origins=allowedorigins()) | |||
@API.doc(security='apikey') |
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 so this was the method to add documentation to the API route? What does the security kwarg do here? Does it just add an additional key-value pair to the outputted json?
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.
Here's a couple screenshots of http://localhost:5000/api/swagger.json
showing the generated output. apikey
and tenant
are auth methods that can be referenced on endpoints at will:
"/engagement_metadata/taxa": {
"patch": {
"responses": { ... },
"summary": "Reorder the tenant's metadata taxa",
"operationId": "patch_metadata_taxa",
"parameters": [ ... ],
"security": [
{
"apikey": []
},
{
"tenant": []
}
],
"tags": [ ... ]
},
They show up in the SecurityDefinitions as header parameters, like so:
"securityDefinitions": {
"apikey": {
"type": "apiKey",
"in": "header",
"name": "Authorization"
},
"tenant": {
"type": "apiKey",
"in": "header",
"name": "tenant-id"
}
},
This is achieved on line ~40 when we use the imported security definitions and apply them to our Namespace
@cross_origin(origins=allowedorigins()) | ||
@API.doc(security='apikey') | ||
@API.expect(metadata_bulk_update_model, validate=True) | ||
@API.marshal_list_with(metadata_return_model) |
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 don't think we're really doing response marshalling anywhere else, right?
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.
Nope :P
|
||
@cors_preflight('GET,PUT,DELETE') | ||
@API.route('/<metadata_id>') # /api/engagements/{engagement.id}/metadata/{metadata.id} |
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.
Thanks for keeping this comment up to date!
@@ -131,7 +132,7 @@ def post(tenant: Tenant): | |||
|
|||
@staticmethod | |||
@cross_origin(origins=allowedorigins()) | |||
@API.expect({'taxon_ids': fields.List(fields.Integer(required=True))}) | |||
@API.expect(taxon_ids_model) |
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.
So many new flast_restx methods to learn! Would you mind explaining what this is doing? Can this handle full-on request validation?
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 basically a shortcut to sidestep a lot of validation I'd be doing manually. This particular endpoint is expecting a list of tenant IDs, and it puts them in the order that they appear in the list.
the @API.expect
part means that the request will be automatically rejected with proper error messages if:
- the mimetype is not
application/json
- the json is invalid
- the json does not have a key
taxon_ids
containing a list of integers (in this case)
I'm not sure exactly what counts as "full-on" validation - this mostly handles data structure, not values. For example, if a valid integer that didn't correspond to a taxon ID was passed in, this decorator would not catch it.
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.
Thanks for breaking that down! I was just curious what level of validation this decorator could provide and how it was being used alongside the Marshmallow or JSON validation we're doing elsewhere
@@ -145,6 +138,37 @@ def update(metadata_id: int, value: str) -> dict: | |||
metadata.value = value | |||
return dict(EngagementMetadataSchema().dump(metadata, many=False)) | |||
|
|||
@staticmethod | |||
@transactional() | |||
def update_by_taxon(engagement_id: int, taxon_id: int, values: List[str]) -> List[dict]: |
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.
Great type annotations and documentation!
</Collapse> | ||
</Grid> | ||
<Grid item container alignItems="center" justifyContent="flex-start"> | ||
<Button variant="outlined" onClick={() => setSelectedTaxonId(-1)}> |
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.
On the subject of accessibility, if you were a non-sighted person using a keyboard, would it be clear what "cancel" on its own means? Is there enough context on this button for someone not relying on sight to understand what it does? If not, we should add an aria-label with more context.
I'm going to speak to Steve about what's expected of the backend of the app in terms of web accessibility. No need to rush to update/change this or anything
}} | ||
> | ||
<Box sx={{ padding: '2.1em', width: '100%' }}> | ||
{/* <MetHeader2>Metadata Management</MetHeader2> */} |
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.
👀
/> | ||
); | ||
})} | ||
{isLoading && [...Array(9)].map(() => <TaxonCardSkeleton />)} |
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.
Love this way of quickly spinning up nine components
trigger: (name?: string | string[] | undefined) => Promise<boolean>; | ||
errors: Partial< | ||
FieldErrorsImpl<{ | ||
[x: string]: any; |
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 should handled with a type or linter exception if needed
const url = replaceUrl(Endpoints.EngagementMetadata.GET_BY_ENG, 'engagement_id', String(engagementId)); | ||
if (!engagementId || isNaN(Number(engagementId))) { | ||
return Promise.reject('Invalid Engagement Id ' + engagementId); | ||
return Promise.reject(new Error('Invalid Engagement Id ' + engagementId)); |
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.
Just an FYI, when the async keyword is appended to a function, it'll automatically return a promise so explicit calls to Promise.resolve
, Promise.reject
shouldn't be necessary. To have the promise resolve, simply return something. To reject, just throw a new Error
Quality Gate passedIssues Measures |
Issue #: https://github.com/bcgov/met-public/issues/
Description of changes:
Replace with "preset values", metadata entries that are not assigned to an engagement.
metadata management to rely on normal authorization check functions.
edit an engagement). 🎟️DESENG-443
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of the met-public license (Apache 2.0).