Skip to content

BDMS 55: asset coverage#97

Merged
jirhiker merged 53 commits into
pre-productionfrom
jab-api-coverage-asset
Aug 25, 2025
Merged

BDMS 55: asset coverage#97
jirhiker merged 53 commits into
pre-productionfrom
jab-api-coverage-asset

Conversation

@jacob-a-brown

@jacob-a-brown jacob-a-brown commented Aug 22, 2025

Copy link
Copy Markdown
Contributor

Why

This PR addresses the following problem / context:

  • The asset router should be fully covered with GET, GET by ID, POST, PATCH, and DELETE endpoints
  • tests impacted by fixtures needed to be updated to use those fixtures
  • proper authentication and permissions need to be vetted at endpoints

How

Implementation summary - the following was changed / added / removed:

  • Added PATCH endpoint
    • the only fields that can be updated are name and label. Is this correct? If they're updated in the database does anything need to change in GCS?
  • Added DELETE endpoint
    • DELETE the asset from the database
    • DELETE the asset from GCS

Notes

Any special considerations, workarounds, or follow-up work to note?

  • Did I implement DELETE /asset/{asset_id}/remove correctly to remove a blob from GCS? I'm using the asset's uri to remove.
  • This is a sequential PR and should NOT be merged until PR BDMS 55: observation coverage & other updates #91 is reviewed, amended, and merged.

the sample table is no longer queried when adding observations, so the
simplified adder is now used
cannot have two foreign key constraints to the same (sample) table
@codecov-commenter

codecov-commenter commented Aug 22, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.57082% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
api/asset.py 97.72% 1 Missing ⚠️
services/observation_helper.py 98.38% 1 Missing ⚠️
tests/test_observation.py 99.69% 1 Missing ⚠️
Files with missing lines Coverage Δ
api/contact.py 100.00% <ø> (+0.82%) ⬆️
api/observation.py 100.00% <100.00%> (+2.63%) ⬆️
db/observation.py 100.00% <100.00%> (ø)
schemas/asset.py 100.00% <100.00%> (ø)
schemas/observation.py 98.83% <100.00%> (+1.46%) ⬆️
services/gcs_helper.py 78.72% <100.00%> (+1.45%) ⬆️
tests/conftest.py 100.00% <100.00%> (ø)
tests/test_asset.py 100.00% <100.00%> (ø)
api/asset.py 98.71% <97.72%> (+17.46%) ⬆️
services/observation_helper.py 98.43% <98.38%> (+33.43%) ⬆️
... and 1 more

... and 1 file with indirect coverage changes

Comment thread api/asset.py Outdated
Comment thread api/asset.py Outdated
bucket = get_storage_bucket()
records = [add_signed_url(ai, bucket) for ai in records]

records = [add_signed_url(ai, bucket) for ai in records]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the thing_id guard was there on purpose. I do not want to generate signed urls if the thing_id is not set.

@jacob-a-brown jacob-a-brown Aug 22, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a user sends a GET request and doesn't filter for thing_id, but an asset is associated with a thing, should the response for that record have a signed URL? (and responses for records with no association will not have a signed URL)

@jacob-a-brown jacob-a-brown Aug 22, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essentially

def transformer(records: list[Asset]):
    records = [add_signed_url(ai, bucket) if ai.things != [] else ai for ai in records]
    return records

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented this in my latest push. One of my concerns is that this could be slow because calling ai.things does a lazy load, I think. I imagine this could be improved and made faster, but this is a solution that comes to mind initially.

I am also checking for thing associations in /asset/{asset_id}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no. the signed_url should only be generated when the user specifies a thing_id via the get_assets function. The original code was sufficient

There is no reason to generate signed urls when listing all assets. There is a reason to generate signed urls when listing assets for a given thing_id because this is used by the front end to display a gallery of images all at once. This is the only case in which signer urls should be generated for a list of assets. A signed url is always generated when retrieving assets individually

@jacob-a-brown jacob-a-brown requested a review from jirhiker August 23, 2025 00:07
Comment thread api/asset.py Outdated
records = [add_signed_url(ai, bucket) for ai in records]

records = [
add_signed_url(ai, bucket) if ai.things != [] else ai for ai in records

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not the pythonic way to test for an empty list. typically this would be

add_signed_url(ai, bucket) if ai.things else ai ...

or
add_signed_url(ai, bucket) if ai.things and len(ai.things)>0 else ai ...

in this case however the original code should be used

 def transformer(records: list[Asset]):
        if thing_id is not None:
            bucket = get_storage_bucket()
            records = [add_signed_url(ai, bucket) for ai in records]

        return records

@jacob-a-brown jacob-a-brown requested review from jirhiker and removed request for chasetmartin, jirhiker and ksmuczynski August 25, 2025 15:32
@jacob-a-brown jacob-a-brown requested a review from jirhiker August 25, 2025 15:40

@jirhiker jirhiker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One last small change. Otherwise looking great

Comment thread api/asset.py
@@ -110,7 +151,6 @@ def transformer(records: list[Asset]):
if thing_id is not None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets add a comment explaining this behavior e.g.

Do not generate signed urls when listing ALL assets. There is a reason to generate signed urls when listing assets for a given `thing_id` because this is used by the front end to display a gallery of images all at once. This is the only case in which signed urls should be generated for a list of assets. A signed url is always generated when retrieving assets individually

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note was added at the beginning of the GET section of the file since it applies to both GET /asset and GET /asset/{asset_id}

@jacob-a-brown jacob-a-brown requested a review from jirhiker August 25, 2025 18:10
@jirhiker jirhiker merged commit b7bf3cd into pre-production Aug 25, 2025
3 checks passed
@jirhiker jirhiker deleted the jab-api-coverage-asset branch December 3, 2025 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants