Skip to content

feat: download cache for assets and asset eodag:download_link creation#1929

Draft
anesson-cs wants to merge 12 commits intodevelopfrom
downloadlink-property-to-asset
Draft

feat: download cache for assets and asset eodag:download_link creation#1929
anesson-cs wants to merge 12 commits intodevelopfrom
downloadlink-property-to-asset

Conversation

@anesson-cs
Copy link
Copy Markdown
Collaborator

@anesson-cs anesson-cs commented Nov 18, 2025

Fixes #1619

The purpose of this PR is to handle individual asset downloads. This leads to these sub-goals:

  • add cache to assets downloads in their own record filename

    • add Asset.location and Asset.remote_location attributes
    • remove EOProduct.location and EOProduct.remote_location attributes
    • in base download method _prepare_download(), use asset instead of product in parameters
  • only use assets to download a product

    • add asset download_link for providers which do not have any asset or contain a "true" download link. Use search plugin attribute assets_mapping to do it.
    • add role archive to asset download_link, and that will be a way to know if an asset is a full product
    • remove mapping of metadata eodag:download_link
    • make method Asset.download() is called in EOProduct.download() instead of the opposite. Then, in method EOProduct.download():
      • if parameter asset is set, download matching asset(s) or only asset download_link if exists among these matching assets
      • if parameter asset is not set, download asset download_link if exists or all other assets
      • use asset instead of product in parameters of methods _prepare_download() and _finalize()
      • if all assets except download_link are kept, with plugin AwsDownload, set build_safe to True
      • if there are several assets, use _finalize(asset, flatten_top_dirs=False) first, then use a new method _finalize_product(product) which handle flatten_top_dirs and build_safe. Otherwise, use only _finalize(asset)
    • Do not add attributes Asset.downloader and Asset.downloader_auth and method Asset.register_downloader(), use the ones of the product to which the asset belongs to
    • merge methods HTTPDownload._download_assets() and HTTPDownload.download() into HTTPDownload.download() which implies:
      • implement order mechanism at asset level
    • merge methods HTTPDownload._stream_download_assets() and HTTPDownload._stream_download() into HTTPDownload._stream_download()
  • outside of these goals:

    • at the initialization of an EOProduct, update attribute assets of EOProduct with get_assets_from_mapping() and remove the ones which have the value of href set to NOT_AVAILABLE
    • [?] add key description to asset download_link
    • [?] some elements of asset download_link may be set directly in the code if they are common to all of providers to reduce assets_mapping size in providers.yml (e.g. role=["data", "archive"])

What must be corrected:

  • in base download method _prepare_download(), do not return a dictionary of record filenames with assets name as keys
  • do not pop assets from EOProduct properties to its attributes at the initialization of an EOProduct
  • product property download_link has been removed if it existed after the search query, undo it as it will not exist anymore

What is missing?

  • attributes Asset.location and Asset.remote_location have been added to the class, but they are not applied in the code

Issues to create:

  • create assets for quicklooks called overview and for thumbnails called thumbnail
  • add attributes Asset.downloader and Asset.downloader_auth and method Asset.register_downloader() when there will be different authentication and download plugins between assets of a same product
  • call assets which are not "true" download links (e.g. for usgs) with a different name than download_link to not create confusion. With role archive, it should be possible.
  • try to refactor the way assets got in properties are put into attribute EOProduct.assets

@anesson-cs anesson-cs force-pushed the downloadlink-property-to-asset branch from 0e1d1db to 9bb0439 Compare February 25, 2026 18:07
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 25, 2026

Test Results

  2 files   -     2  2 suites   - 2   49s ⏱️ - 2m 26s
111 tests  -   573  0 ✅  -   683  0 💤  - 1  0 ❌ ±0  111 🔥 +111 
218 runs   - 2 560  0 ✅  - 2 772  0 💤  - 6  0 ❌ ±0  218 🔥 +218 

For more details on these errors, see this check.

Results for commit ae07435. ± Comparison against base commit da1039b.

This pull request removes 622 and adds 49 tests. Note that renamed tests count towards both.
eodag.api.product._assets ‑ eodag.api.product._assets.Asset
eodag.api.product._assets ‑ eodag.api.product._assets.AssetsDict
eodag.api.search_result ‑ eodag.api.search_result.SearchResult.next_page
tests.integration.test_config_ext_plugin.TestExternalPluginConfig ‑ test_update_providers_from_ext_plugin
tests.integration.test_core_config.TestCoreCollectionsConfig ‑ test_core_discover_collections_auth
tests.integration.test_core_config.TestCoreProvidersConfig ‑ test_core_providers_add
tests.integration.test_core_config.TestCoreProvidersConfig ‑ test_core_providers_add_update
tests.integration.test_core_config.TestCoreProvidersConfig ‑ test_core_providers_config_update
tests.integration.test_core_config.TestCoreProvidersConfig ‑ test_core_providers_shared_credentials
tests.integration.test_core_search.TestCoreSearch ‑ test_core_search_auths_matching
…
eodag.api.__init__
eodag.api.core
eodag.api.product.__init__
eodag.api.product._assets
eodag.api.product._product
eodag.api.product.drivers.__init__
eodag.api.product.drivers.base
eodag.api.product.drivers.generic
eodag.api.product.drivers.sentinel1
eodag.api.product.drivers.sentinel2
…

♻️ This comment has been updated with latest results.

@eodag-bot
Copy link
Copy Markdown
Collaborator

eodag-bot commented Feb 25, 2026

badge

Code Coverage (Ubuntu)

Details
Filename                                     Stmts    Miss  Cover    Missing
-----------------------------------------  -------  ------  -------  ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
__init__.py                                      8       6  25.00%   28-38
cli.py                                         247     247  0.00%    40-629
config.py                                      304      76  75.00%   68-72, 75, 78, 81, 85, 89, 93-95, 580-582, 586-587, 591, 596-597, 602-605, 611-612, 621-624, 630-661, 670, 683-687, 698-708, 716-717, 725, 733, 741, 754-781
crunch.py                                        5       5  0.00%    20-24
api/__init__.py                                  0       0  100.00%
api/collection.py                              150      85  43.33%   112, 121-123, 132-137, 142-145, 156-209, 212, 215, 220-232, 240-245, 257-265, 277-285, 292-341, 356-358, 361, 364, 379, 382, 385, 389-400
api/core.py                                    758     736  2.90%    43-2341
api/provider.py                                381     267  29.92%   112-113, 117, 122-127, 132-146, 154-160, 170-201, 209-216, 220-246, 281-290, 294, 298, 302-308, 312, 316-337, 355, 360, 365, 370, 375, 380, 385, 390, 395, 404-412, 417, 422, 430, 446-454, 463-467, 483-515, 519-534, 543-561, 578-580, 590-593, 602-605, 613, 621-631, 664, 673, 684, 693, 704-705, 749-764, 798-807, 817-825, 832-840, 852-855, 866-894, 903-913, 923-976, 989-991
api/search_result.py                           181     172  4.97%    30-548
api/product/__init__.py                         18      13  27.78%   35-63
api/product/_assets.py                          72      66  8.33%    26-248
api/product/_product.py                        252     252  0.00%    18-777
api/product/metadata_mapping.py                797     746  6.40%    122-131, 136, 147, 203-1156, 1177-1322, 1348-1472, 1489-1523, 1533-1639, 1649-1668, 1675-1710, 1721-1780, 1792-1811, 1823-1827, 1842-1853
api/product/drivers/__init__.py                 11      11  0.00%    19-64
api/product/drivers/base.py                     29      29  0.00%    18-95
api/product/drivers/generic.py                  11      11  0.00%    18-88
api/product/drivers/sentinel1.py                33      33  0.00%    18-140
api/product/drivers/sentinel2.py                33      33  0.00%    18-141
plugins/__init__.py                              0       0  100.00%
plugins/base.py                                 22       9  59.09%   48, 52-55, 64-65, 68-69
plugins/manager.py                             173     173  0.00%    18-465
plugins/apis/__init__.py                         0       0  100.00%
plugins/apis/base.py                             4       1  75.00%   24
plugins/apis/ecmwf.py                          102     102  0.00%    18-291
plugins/apis/usgs.py                           191     191  0.00%    18-506
plugins/authentication/__init__.py               6       3  50.00%   26-31
plugins/authentication/aws_auth.py             124     124  0.00%    18-320
plugins/authentication/base.py                  22      22  0.00%    18-95
plugins/authentication/generic.py               16      16  0.00%    18-65
plugins/authentication/header.py                19      19  0.00%    18-115
plugins/authentication/keycloak.py              46      46  0.00%    18-183
plugins/authentication/openid_connect.py       232     232  0.00%    18-608
plugins/authentication/qsauth.py                34      34  0.00%    18-112
plugins/authentication/sas_auth.py              57      57  0.00%    18-160
plugins/authentication/token.py                128     128  0.00%    18-357
plugins/authentication/token_exchange.py        36      36  0.00%    18-121
plugins/crunch/__init__.py                       0       0  100.00%
plugins/crunch/base.py                          10      10  0.00%    18-43
plugins/crunch/filter_date.py                   59      59  0.00%    18-122
plugins/crunch/filter_latest_intersect.py       54      54  0.00%    18-115
plugins/crunch/filter_latest_tpl_name.py        31      31  0.00%    18-95
plugins/crunch/filter_overlap.py                63      63  0.00%    18-163
plugins/crunch/filter_property.py               30      30  0.00%    18-89
plugins/download/__init__.py                     0       0  100.00%
plugins/download/aws.py                        391     391  0.00%    18-1156
plugins/download/base.py                       306     275  10.13%   102-103, 133, 162, 178-306, 319-333, 346-351, 366-486, 528-678, 696-788, 806-812
plugins/download/http.py                       577     577  0.00%    18-1507
plugins/search/__init__.py                      25       0  100.00%
plugins/search/base.py                         191     183  4.19%    33-528
plugins/search/build_search_result.py          504     504  0.00%    18-1643
plugins/search/cop_marine.py                   268     268  0.00%    18-523
plugins/search/creodias_s3.py                   29      29  0.00%    18-93
plugins/search/csw.py                          112     112  0.00%    18-288
plugins/search/qssearch.py                     823     823  0.00%    18-2232
plugins/search/stac_list_assets.py              25      25  0.00%    18-85
plugins/search/static_stac_search.py            84      84  0.00%    18-251
resources/__init__.py                            0       0  100.00%
resources/shp/__init__.py                        0       0  100.00%
types/__init__.py                              167     135  19.16%   57-62, 71-75, 86-98, 114-140, 170-235, 256-337, 356-362, 378-386, 422-436
types/bbox.py                                   39      19  51.28%   46-61, 72-74, 85-87, 99-101, 113-115, 123
types/download_args.py                          10       0  100.00%
types/queryables.py                             88      48  45.45%   70-75, 82-87, 101, 150-203, 223-241, 244-260, 305
types/search_args.py                            70      33  52.86%   60-64, 71-88, 102-133
types/stac_extensions.py                       114       1  99.12%   282
types/stac_metadata.py                         116      73  37.07%   77, 85-93, 102-106, 112-115, 123-124, 132, 140, 147, 160-168, 177-191, 200-207, 218-234, 249-284, 289-312
utils/__init__.py                              608     438  27.96%   71, 204-224, 237-238, 247-273, 276, 289-299, 315-323, 337, 346-352, 357, 371-375, 397-444, 450-454, 465, 483-497, 505-508, 519, 524, 529-534, 572-577, 603-620, 638, 657, 706-742, 773-783, 804-819, 840-855, 875-881, 895-904, 916-925, 949-1005, 1020-1064, 1081-1085, 1097-1106, 1121-1195, 1205-1216, 1228-1232, 1246-1253, 1265-1268, 1272, 1275, 1278, 1281, 1285-1288, 1301-1305, 1317, 1341, 1346-1349, 1358, 1363-1364, 1375, 1387-1407, 1418-1430, 1441-1470, 1484-1486, 1503-1516, 1541-1548, 1557, 1565-1572, 1581, 1589-1593, 1608-1611, 1625, 1636-1643, 1656, 1674, 1688-1695, 1707, 1719, 1728-1749, 1776-1796
utils/cache.py                                  22      16  27.27%   45-68
utils/dates.py                                 113      85  24.78%   69-72, 96-98, 123-137, 160-179, 201-212, 233-240, 259-262, 284-291, 308-333, 355-373, 389, 404
utils/env.py                                     3       1  66.67%   29
utils/exceptions.py                             47      20  57.45%   56, 95-109, 116-117, 122-124, 137-142
utils/free_text_search.py                       65      58  10.77%   41-52, 72-102, 129-166, 227-229
utils/import_system.py                          28      19  32.14%   64-78, 89-99
utils/logging.py                                28      22  21.43%   38-58, 105-120
utils/notebook.py                               44      32  27.27%   25-29, 34-43, 55-64, 69-78, 83-87
utils/repr.py                                   38      27  28.95%   36-39, 50-57, 73-99, 124-131, 155, 169-174
utils/requests.py                               55      41  25.45%   47-68, 85-96, 107-124, 128
utils/s3.py                                    240     227  5.42%    34-790
utils/stac_reader.py                           113      89  21.24%   43-46, 51-59, 63-85, 90-102, 124-138, 148-185, 206-216, 226-256
TOTAL                                        10092    8883  11.98%

Diff against develop

Filename                                     Stmts    Miss  Cover
-----------------------------------------  -------  ------  --------
__init__.py                                      0      +6  -75.00%
cli.py                                           0    +237  -95.95%
config.py                                       -1     +52  -17.13%
api/collection.py                                0     +77  -51.34%
api/core.py                                     +1    +688  -90.76%
api/provider.py                                  0    +232  -60.89%
api/search_result.py                             0    +153  -84.53%
api/product/__init__.py                          0     +11  -61.11%
api/product/_assets.py                         +20     +62  -83.98%
api/product/_product.py                         +4    +232  -91.94%
api/product/metadata_mapping.py                 +6    +693  -86.90%
api/product/drivers/__init__.py                  0     +11  -100.00%
api/product/drivers/base.py                      0     +29  -100.00%
api/product/drivers/generic.py                   0     +11  -100.00%
api/product/drivers/sentinel1.py                 0     +33  -100.00%
api/product/drivers/sentinel2.py                 0     +33  -100.00%
plugins/base.py                                  0      +5  -22.73%
plugins/manager.py                               0    +157  -90.75%
plugins/apis/base.py                             0      +1  -25.00%
plugins/apis/ecmwf.py                           +1     +94  -92.08%
plugins/apis/usgs.py                            +9    +166  -86.26%
plugins/authentication/__init__.py               0      +2  -33.33%
plugins/authentication/aws_auth.py               0     +89  -71.77%
plugins/authentication/base.py                   0     +18  -81.82%
plugins/authentication/generic.py                0     +13  -81.25%
plugins/authentication/header.py                 0     +19  -100.00%
plugins/authentication/keycloak.py               0     +39  -84.78%
plugins/authentication/openid_connect.py         0    +204  -87.93%
plugins/authentication/qsauth.py                 0     +33  -97.06%
plugins/authentication/sas_auth.py               0     +54  -94.74%
plugins/authentication/token.py                  0    +119  -92.97%
plugins/authentication/token_exchange.py         0     +22  -61.11%
plugins/crunch/base.py                           0      +9  -90.00%
plugins/crunch/filter_date.py                    0     +45  -76.27%
plugins/crunch/filter_latest_intersect.py        0     +15  -27.78%
plugins/crunch/filter_latest_tpl_name.py         0     +11  -35.48%
plugins/crunch/filter_overlap.py                 0     +38  -60.32%
plugins/crunch/filter_property.py                0     +25  -83.33%
plugins/download/aws.py                        -10    +315  -81.05%
plugins/download/base.py                       +26    +240  -77.37%
plugins/download/http.py                       +15    +499  -86.12%
plugins/search/base.py                           0    +165  -86.39%
plugins/search/build_search_result.py            0    +421  -83.53%
plugins/search/cop_marine.py                     0    +212  -79.10%
plugins/search/creodias_s3.py                    0     +28  -96.55%
plugins/search/csw.py                            0     +25  -22.32%
plugins/search/qssearch.py                       0    +729  -88.58%
plugins/search/stac_list_assets.py               0     +15  -60.00%
plugins/search/static_stac_search.py             0     +66  -78.57%
types/__init__.py                                0     +92  -55.09%
types/queryables.py                              0     +48  -54.55%
types/search_args.py                             0     +15  -21.43%
types/stac_metadata.py                           0     +57  -49.14%
utils/__init__.py                                0    +395  -64.97%
utils/cache.py                                   0     +16  -72.73%
utils/dates.py                                   0     +81  -71.68%
utils/env.py                                     0      +1  -33.33%
utils/exceptions.py                              0     +20  -42.55%
utils/free_text_search.py                        0     +56  -86.15%
utils/logging.py                                 0     +21  -75.00%
utils/notebook.py                                0      +9  -20.46%
utils/repr.py                                    0     +27  -71.05%
utils/requests.py                                0     +12  -21.82%
utils/s3.py                                      0    +215  -89.58%
utils/stac_reader.py                             0     +45  -39.82%
TOTAL                                          +71   +7563  -74.85%

Results for commit: ae07435

Minimum allowed coverage is 70%

♻️ This comment has been updated with latest results

@anesson-cs anesson-cs force-pushed the downloadlink-property-to-asset branch from 7792189 to ae07435 Compare February 26, 2026 14:28
@anesson-cs anesson-cs changed the title feat: create 'downloadLink' asset feat: download cache for assets and asset 'eodag:download_link' creation Feb 26, 2026
@anesson-cs anesson-cs changed the title feat: download cache for assets and asset 'eodag:download_link' creation feat: download cache for assets and asset eodag:download_link creation Feb 26, 2026
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.

individual asset download handling

3 participants