Repository: CS-SI/eodag
Pull request #1643 (2025-04-30 07:55:45)

fix(plugins): StaticStacSearch text opener
Restores custom text opener passed to `pystac` from `StaticStacSearch` that was not used any more.

Also updates documentation to mention that a STAC item can directly be a possible source for `StaticStacSearch` plugin.

eodag: ![badge](https://raw.githubusercontent.com/CS-SI/eodag/_xml_coverage_reports/data/staticstac-fix/./badge.svg)


## Code Coverage (Ubuntu)

<details>


```
Filename                                     Stmts    Miss  Cover    Missing
-----------------------------------------  -------  ------  -------  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
__init__.py                                      8       0  100.00%
cli.py                                         323      64  80.19%   62-63, 87, 674-737, 839-890, 894
config.py                                      433      27  93.76%   80-82, 91, 99, 103-105, 176, 187, 701-703, 817-820, 863-864, 873-874, 979, 1038-1043, 1045
crunch.py                                        5       5  0.00%    20-24
api/__init__.py                                  0       0  100.00%
api/core.py                                    784      73  90.69%   316-317, 367, 657, 701-704, 742, 786, 820, 865-870, 896, 987, 1055, 1278-1290, 1326, 1328, 1356, 1360-1371, 1384-1390, 1473-1476, 1509-1529, 1598-1603, 1615-1618, 1954, 1978-1984, 2235, 2239-2243, 2252-2254, 2298-2299, 2328-2329
api/search_result.py                            56       4  92.86%   80, 89, 96, 110
api/product/__init__.py                          6       0  100.00%
api/product/_assets.py                          52       5  90.38%   97, 183, 191, 194-198
api/product/_product.py                        201      19  90.55%   70-72, 236-237, 312, 339, 440, 458, 482-485, 494-497, 552-555
api/product/metadata_mapping.py                702      63  91.03%   120-122, 220, 252-253, 310-322, 324, 335, 360, 400-401, 438, 459-462, 485, 493-494, 587-588, 612-613, 619-622, 637-638, 787, 838, 991, 1000-1004, 1021-1026, 1153, 1167-1187, 1207, 1212, 1341, 1355, 1380, 1426, 1478, 1501-1502, 1522-1526, 1542, 1550
api/product/drivers/__init__.py                 20       2  90.00%   33-36
api/product/drivers/base.py                     29       1  96.55%   98
api/product/drivers/generic.py                   7       0  100.00%
api/product/drivers/sentinel1.py                15       0  100.00%
api/product/drivers/sentinel2.py                15       0  100.00%
plugins/__init__.py                              0       0  100.00%
plugins/base.py                                 21       2  90.48%   48, 55
plugins/manager.py                             172      16  90.70%   106-111, 161, 199, 221, 225, 249, 288-289, 389-392, 404-405
plugins/apis/__init__.py                         0       0  100.00%
plugins/apis/base.py                             4       0  100.00%
plugins/apis/ecmwf.py                           97      10  89.69%   167-169, 216-217, 243-245, 298-299
plugins/apis/usgs.py                           180      26  85.56%   156, 258, 292, 327-329, 334, 360-361, 366, 396-403, 414-419, 441-447, 478
plugins/authentication/__init__.py               6       1  83.33%   31
plugins/authentication/aws_auth.py              20       0  100.00%
plugins/authentication/base.py                  17       2  88.24%   45, 58
plugins/authentication/generic.py               14       2  85.71%   51, 61
plugins/authentication/header.py                19       0  100.00%
plugins/authentication/keycloak.py              46       7  84.78%   151-154, 175-180
plugins/authentication/oauth.py                 13       7  46.15%   43-45, 49-52
plugins/authentication/openid_connect.py       208      28  86.54%   80-81, 93-111, 149, 155-183, 191, 323-326, 352, 386
plugins/authentication/qsauth.py                34       1  97.06%   91
plugins/authentication/sas_auth.py              47       1  97.87%   76
plugins/authentication/token.py                127       9  92.91%   168, 205, 277-278, 328-332
plugins/authentication/token_exchange.py        36      14  61.11%   75, 92-120
plugins/crunch/__init__.py                       0       0  100.00%
plugins/crunch/base.py                          10       1  90.00%   43
plugins/crunch/filter_date.py                   59      14  76.27%   52-57, 69, 78, 87, 90, 102-104, 113-115, 122
plugins/crunch/filter_latest_intersect.py       47       8  82.98%   52-53, 69, 78-81, 83, 90-93
plugins/crunch/filter_latest_tpl_name.py        31       1  96.77%   83
plugins/crunch/filter_overlap.py                66      18  72.73%   28-30, 66-69, 76-79, 85, 93, 104-120
plugins/crunch/filter_property.py               30       5  83.33%   55-60, 63-64
plugins/download/__init__.py                     0       0  100.00%
plugins/download/aws.py                        528     158  70.08%   272, 285, 365-368, 382-386, 476-478, 482, 515-516, 522-526, 556, 589, 593, 600, 630-638, 642, 677-685, 696-698, 729-810, 828-886, 897-902, 955, 970-972, 975, 985-993, 1001-1015, 1025-1048, 1055-1068, 1106, 1132, 1177-1179, 1399
plugins/download/base.py                       253      42  83.40%   127, 155, 235-238, 291-292, 340-344, 350-351, 393, 396-410, 422, 426, 490-494, 524-525, 550-558, 560-564, 607, 629, 651, 659
plugins/download/creodias_s3.py                 25       9  64.00%   55-69
plugins/download/http.py                       541      93  82.81%   227, 269-272, 333-336, 339, 346-351, 382-384, 401, 416, 470, 505, 519, 533, 541-543, 559-564, 575, 593, 629-632, 653, 663, 667, 687, 764, 783-794, 802-807, 817-834, 852, 882-891, 927, 952-953, 972-977, 986, 1001-1003, 1007, 1010, 1025-1026, 1061, 1123, 1138, 1197-1198, 1204, 1214, 1250, 1286, 1306, 1341-1343
plugins/download/s3rest.py                     116      24  79.31%   119, 153, 160, 195, 222-229, 232-234, 238, 249-255, 263-264, 267-271, 294, 315-318
plugins/search/__init__.py                      22       0  100.00%
plugins/search/base.py                         148      11  92.57%   101, 105, 129-135, 276, 296, 431
plugins/search/build_search_result.py          469      61  86.99%   245-246, 282, 285, 310, 313, 344-346, 553-564, 688, 690, 751, 758, 782, 817, 868, 917-932, 977, 1002, 1005, 1009, 1129-1130, 1139-1148, 1208, 1226, 1245-1254, 1371-1372, 1417, 1426-1428, 1485, 1531
plugins/search/cop_marine.py                   250      47  81.20%   56, 64-66, 76-77, 82, 87-88, 104, 106, 109, 175-176, 232, 238, 242, 246, 259, 270-271, 279, 307, 311, 332, 336, 340, 344, 348-352, 358-361, 364-378, 395, 444-448, 453, 465
plugins/search/creodias_s3.py                   25       1  96.00%   51
plugins/search/csw.py                          105      81  22.86%   98-99, 103-104, 112-160, 166-179, 187-219, 237-278
plugins/search/data_request_search.py          202      69  65.84%   189-192, 208, 219, 223-224, 235, 240, 245, 252, 265-268, 322-323, 327, 337-343, 348, 374-377, 385-396, 413, 415, 422-425, 427-428, 446-450, 483, 493, 504, 517, 523-538, 543
plugins/search/qssearch.py                     699      89  87.27%   450, 508, 522, 526-532, 560-564, 677-689, 734-737, 808-809, 857, 876, 883, 895, 952, 973, 976-977, 986-987, 996-997, 1006-1007, 1034, 1118-1123, 1127-1136, 1170, 1192, 1265-1266, 1272, 1362, 1459-1463, 1523, 1526, 1532-1533, 1554, 1582-1594, 1601, 1633-1635, 1645-1651, 1681, 1704, 1709-1710, 1725, 1741, 1825, 1832, 1843, 1866-1871, 1880, 1889
plugins/search/stac_list_assets.py              25      10  60.00%   44-51, 75-85
plugins/search/static_stac_search.py            79      13  83.54%   100-126, 170, 183
rest/__init__.py                                 4       2  50.00%   21-22
rest/cache.py                                   33       7  78.79%   35-37, 53-55, 59, 68
rest/config.py                                  25       0  100.00%
rest/constants.py                                6       0  100.00%
rest/core.py                                   260      67  74.23%   258, 266, 284-301, 316-352, 446, 483-522, 599, 705, 712-765
rest/errors.py                                  69       5  92.75%   112, 122, 133, 158-159
rest/server.py                                 192      24  87.50%   86, 109-111, 285-290, 318, 514-516, 533-538, 567, 569, 573-574, 578-579
rest/stac.py                                   319      63  80.25%   309, 331, 383-386, 413-440, 471-473, 496, 528-529, 611-651, 673-689, 781-785, 792, 846-847, 908, 998-1000
rest/types/__init__.py                           0       0  100.00%
rest/types/collections_search.py                13      13  0.00%    18-44
rest/types/eodag_search.py                     180       5  97.22%   225-229, 282, 285, 353
rest/types/queryables.py                        64      12  81.25%   61, 71-79, 105-110, 174
rest/types/stac_search.py                      125       7  94.40%   128, 174, 189-191, 199, 203
rest/utils/__init__.py                          94      12  87.23%   101-102, 121-123, 175, 185-199
rest/utils/cql_evaluate.py                      48       5  89.58%   69, 76, 90, 97, 105
rest/utils/rfc3339.py                           23       3  86.96%   48, 60, 62
types/__init__.py                              148      40  72.97%   67, 80-84, 95-107, 135-137, 144-149, 239, 249-265, 270, 272, 294, 299, 307, 317
types/bbox.py                                   43      19  55.81%   46-61, 72-74, 85-87, 99-101, 113-115, 123
types/download_args.py                           9       0  100.00%
types/queryables.py                            110       0  100.00%
types/search_args.py                            70      18  74.29%   60-64, 71-88, 103
types/whoosh.py                                 81      16  80.25%   129-132, 136-143, 155-161, 174-176
utils/__init__.py                              539      40  92.58%   81, 198-199, 208-235, 238, 253, 333-337, 410-414, 433-435, 449, 541, 556, 596-597, 626, 1001-1004, 1012-1013, 1051-1052, 1099-1100, 1240
utils/exceptions.py                             46       0  100.00%
utils/import_system.py                          28      19  32.14%   64-78, 89-99
utils/logging.py                                28       1  96.43%   41
utils/notebook.py                               44      23  47.73%   25-29, 36-41, 58-62, 72-78, 83-87
utils/repr.py                                   38       8  78.95%   51, 53, 57, 98, 122-129
utils/requests.py                               55      29  47.27%   51-52, 64, 85-96, 107-124, 128
utils/rest.py                                   36       1  97.22%   55
utils/s3.py                                     69       3  95.65%   162-163, 226
utils/stac_reader.py                           113      44  61.06%   63-85, 95-97, 101, 138, 154-159, 206-216, 226-256
TOTAL                                        10359    1630  84.26%
```

</details>


### Diff against develop

```
Filename                Stmts    Miss  Cover
--------------------  -------  ------  -------
utils/stac_reader.py       +2      -1  +1.60%
TOTAL                      +2      -1  +0.01%
```


Results for commit: 4097915af6313ac8d4e11d119ca81b1db414f67c


_Minimum allowed coverage is `70%`_


:recycle: This comment has been updated with latest results


<!-- Sticky Pull Request Commentartifacts/unit-test-results-python3.13-ubuntu-latest/coverage.xml -->
eodag: ![badge](https://raw.githubusercontent.com/CS-SI/eodag/_xml_coverage_reports_win/data/staticstac-fix/./badge.svg)


## Code Coverage (Windows)

<details>


```
Filename                                     Stmts    Miss  Cover    Missing
-----------------------------------------  -------  ------  -------  -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
__init__.py                                      8       0  100.00%
cli.py                                         323      64  80.19%   62-63, 87, 674-737, 839-890, 894
config.py                                      433      28  93.53%   80-82, 91, 99, 103-105, 176, 187, 701-703, 817-820, 863-864, 873-874, 979, 1008, 1038-1043, 1045
crunch.py                                        5       5  0.00%    20-24
api/__init__.py                                  0       0  100.00%
api/core.py                                    784      73  90.69%   316-317, 367, 657, 701-704, 742, 786, 820, 865-870, 896, 987, 1055, 1278-1290, 1326, 1328, 1356, 1360-1371, 1384-1390, 1473-1476, 1509-1529, 1598-1603, 1615-1618, 1954, 1978-1984, 2235, 2239-2243, 2252-2254, 2298-2299, 2328-2329
api/search_result.py                            56       4  92.86%   80, 89, 96, 110
api/product/__init__.py                          6       0  100.00%
api/product/_assets.py                          52       5  90.38%   97, 183, 191, 194-198
api/product/_product.py                        201      19  90.55%   70-72, 236-237, 312, 339, 440, 458, 482-485, 494-497, 552-555
api/product/metadata_mapping.py                702      63  91.03%   120-122, 220, 252-253, 310-322, 324, 335, 360, 400-401, 438, 459-462, 485, 493-494, 587-588, 612-613, 619-622, 637-638, 787, 838, 991, 1000-1004, 1021-1026, 1153, 1167-1187, 1207, 1212, 1341, 1355, 1380, 1426, 1478, 1501-1502, 1522-1526, 1542, 1550
api/product/drivers/__init__.py                 20       2  90.00%   33-36
api/product/drivers/base.py                     29       1  96.55%   98
api/product/drivers/generic.py                   7       0  100.00%
api/product/drivers/sentinel1.py                15       0  100.00%
api/product/drivers/sentinel2.py                15       0  100.00%
plugins/__init__.py                              0       0  100.00%
plugins/base.py                                 21       3  85.71%   48, 55, 68
plugins/manager.py                             172      16  90.70%   106-111, 161, 199, 221, 225, 249, 288-289, 389-392, 404-405
plugins/apis/__init__.py                         0       0  100.00%
plugins/apis/base.py                             4       0  100.00%
plugins/apis/ecmwf.py                           97      10  89.69%   167-169, 216-217, 243-245, 298-299
plugins/apis/usgs.py                           180      26  85.56%   156, 258, 292, 327-329, 334, 360-361, 366, 396-403, 414-419, 441-447, 478
plugins/authentication/__init__.py               6       1  83.33%   31
plugins/authentication/aws_auth.py              20       0  100.00%
plugins/authentication/base.py                  17       2  88.24%   45, 58
plugins/authentication/generic.py               14       2  85.71%   51, 61
plugins/authentication/header.py                19       0  100.00%
plugins/authentication/keycloak.py              46       7  84.78%   151-154, 175-180
plugins/authentication/oauth.py                 13       7  46.15%   43-45, 49-52
plugins/authentication/openid_connect.py       208      28  86.54%   80-81, 93-111, 149, 155-183, 191, 323-326, 352, 386
plugins/authentication/qsauth.py                34       1  97.06%   91
plugins/authentication/sas_auth.py              47       1  97.87%   76
plugins/authentication/token.py                127       9  92.91%   168, 205, 277-278, 328-332
plugins/authentication/token_exchange.py        36      14  61.11%   75, 92-120
plugins/crunch/__init__.py                       0       0  100.00%
plugins/crunch/base.py                          10       1  90.00%   43
plugins/crunch/filter_date.py                   59      14  76.27%   52-57, 69, 78, 87, 90, 102-104, 113-115, 122
plugins/crunch/filter_latest_intersect.py       47      33  29.79%   49-54, 67-112
plugins/crunch/filter_latest_tpl_name.py        31       1  96.77%   83
plugins/crunch/filter_overlap.py                66      18  72.73%   28-30, 66-69, 76-79, 85, 93, 104-120
plugins/crunch/filter_property.py               30       5  83.33%   55-60, 63-64
plugins/download/__init__.py                     0       0  100.00%
plugins/download/aws.py                        528     158  70.08%   272, 285, 365-368, 382-386, 476-478, 482, 515-516, 522-526, 556, 589, 593, 600, 630-638, 642, 677-685, 696-698, 729-810, 828-886, 897-902, 955, 970-972, 975, 985-993, 1001-1015, 1025-1048, 1055-1068, 1106, 1132, 1177-1179, 1399
plugins/download/base.py                       253      44  82.61%   127, 155, 222-224, 235-238, 291-292, 340-344, 350-351, 393, 396-410, 422, 426, 490-494, 524-525, 550-558, 560-564, 607, 629, 651, 659
plugins/download/creodias_s3.py                 25       9  64.00%   55-69
plugins/download/http.py                       541      93  82.81%   227, 269-272, 333-336, 339, 346-351, 382-384, 401, 416, 470, 505, 519, 533, 541-543, 559-564, 575, 593, 629-632, 653, 663, 667, 687, 764, 783-794, 802-807, 817-834, 852, 882-891, 927, 952-953, 972-977, 986, 1001-1003, 1007, 1010, 1025-1026, 1061, 1123, 1138, 1197-1198, 1204, 1214, 1250, 1286, 1306, 1341-1343
plugins/download/s3rest.py                     116      24  79.31%   119, 153, 160, 195, 222-229, 232-234, 238, 249-255, 263-264, 267-271, 294, 315-318
plugins/search/__init__.py                      22       0  100.00%
plugins/search/base.py                         148      11  92.57%   101, 105, 129-135, 276, 296, 431
plugins/search/build_search_result.py          469      62  86.78%   245-246, 282, 285, 310, 313, 344-346, 553-564, 688, 690, 751, 758, 782, 817, 868, 883, 917-932, 977, 1002, 1005, 1009, 1129-1130, 1139-1148, 1208, 1226, 1245-1254, 1371-1372, 1417, 1426-1428, 1485, 1531
plugins/search/cop_marine.py                   250      47  81.20%   56, 64-66, 76-77, 82, 87-88, 104, 106, 109, 175-176, 232, 238, 242, 246, 259, 270-271, 279, 307, 311, 332, 336, 340, 344, 348-352, 358-361, 364-378, 395, 444-448, 453, 465
plugins/search/creodias_s3.py                   25       1  96.00%   51
plugins/search/csw.py                          105      81  22.86%   98-99, 103-104, 112-160, 166-179, 187-219, 237-278
plugins/search/data_request_search.py          202      69  65.84%   189-192, 208, 219, 223-224, 235, 240, 245, 252, 265-268, 322-323, 327, 337-343, 348, 374-377, 385-396, 413, 415, 422-425, 427-428, 446-450, 483, 493, 504, 517, 523-538, 543
plugins/search/qssearch.py                     699      95  86.41%   450, 508, 522, 526-532, 560-564, 677-689, 734-737, 808-809, 857, 876, 883, 895, 952, 973, 976-977, 986-987, 996-997, 1006-1007, 1034, 1118-1123, 1127-1136, 1170, 1192, 1265-1266, 1272, 1362, 1459-1463, 1523, 1526, 1532-1533, 1554, 1582-1594, 1601, 1633-1635, 1645-1651, 1681, 1704, 1709-1710, 1725, 1741, 1813, 1825, 1832, 1843, 1866-1871, 1880, 1888-1898, 1920
plugins/search/stac_list_assets.py              25      10  60.00%   44-51, 75-85
plugins/search/static_stac_search.py            79      13  83.54%   100-126, 170, 183
rest/__init__.py                                 4       2  50.00%   21-22
rest/cache.py                                   33      22  33.33%   35-37, 44-70
rest/config.py                                  25       1  96.00%   35
rest/constants.py                                6       0  100.00%
rest/core.py                                   260     153  41.15%   157, 159, 161, 164-165, 179-189, 198-199, 205, 208, 249-303, 316-352, 383-420, 435-451, 467-476, 483-522, 539, 581-666, 705, 712-765
rest/errors.py                                  69      49  28.99%   62, 67-106, 111-114, 121-124, 132-162, 170-175, 190-196
rest/server.py                                 192     192  0.00%    18-592
rest/stac.py                                   319      68  78.68%   243, 309, 331, 383-386, 413-440, 471-473, 496, 528-529, 611-651, 673-689, 716, 781-785, 792, 846-847, 853, 908, 946, 979, 998-1000
rest/types/__init__.py                           0       0  100.00%
rest/types/collections_search.py                13      13  0.00%    18-44
rest/types/eodag_search.py                     180      20  88.89%   225-229, 262-264, 282, 285, 291, 295, 353, 371-386
rest/types/queryables.py                        64      20  68.75%   54-55, 60-63, 68-79, 105-110, 119-120, 174
rest/types/stac_search.py                      125      11  91.20%   126-128, 174, 189-191, 199, 203, 251, 254
rest/utils/__init__.py                          94      30  68.09%   72-78, 98, 101-102, 121-123, 136, 143, 168-176, 183-204
rest/utils/cql_evaluate.py                      48       5  89.58%   69, 76, 90, 97, 105
rest/utils/rfc3339.py                           23       5  78.26%   43-44, 48, 60, 62
types/__init__.py                              148      42  71.62%   67, 71, 80-84, 95-107, 135-137, 144-149, 204, 239, 249-265, 270, 272, 294, 299, 307, 317
types/bbox.py                                   43      19  55.81%   46-61, 72-74, 85-87, 99-101, 113-115, 123
types/download_args.py                           9       0  100.00%
types/queryables.py                            110       0  100.00%
types/search_args.py                            70      18  74.29%   60-64, 71-88, 103
types/whoosh.py                                 81      16  80.25%   129-132, 136-143, 155-161, 174-176
utils/__init__.py                              539      40  92.58%   81, 198-199, 208-235, 238, 253, 333-337, 410-414, 433-435, 449, 541, 556, 596-597, 626, 1001-1004, 1012-1013, 1051-1052, 1099-1100, 1240
utils/exceptions.py                             46       0  100.00%
utils/import_system.py                          28      19  32.14%   64-78, 89-99
utils/logging.py                                28       1  96.43%   41
utils/notebook.py                               44      23  47.73%   25-29, 36-41, 58-62, 72-78, 83-87
utils/repr.py                                   38       8  78.95%   51, 53, 57, 98, 122-129
utils/requests.py                               55      29  47.27%   51-52, 64, 85-96, 107-124, 128
utils/rest.py                                   36       1  97.22%   55
utils/s3.py                                     69       3  95.65%   162-163, 226
utils/stac_reader.py                           113      44  61.06%   63-85, 95-97, 101, 138, 154-159, 206-216, 226-256
TOTAL                                        10359    2034  80.36%
```

</details>


### Diff against develop

```
Filename                Stmts    Miss  Cover
--------------------  -------  ------  -------
utils/stac_reader.py       +2      -1  +1.60%
TOTAL                      +2      -1  +0.01%
```


Results for commit: 4097915af6313ac8d4e11d119ca81b1db414f67c


_Minimum allowed coverage is `70%`_


:recycle: This comment has been updated with latest results


<!-- Sticky Pull Request Commentartifacts/unit-test-results-python3.13-windows-latest/coverage.xml -->

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index a899b438..14a86092 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -3,6 +3,36 @@ Release history
 ===============
 
 
+v3.4.0 (2025-04-30)
+===================
+
+Bug Fixes
+---------
+
+* **plugins**: Stac providers datetime queryables handling (`#1625`_, `9417fd9`_)
+
+* **providers**: cop_ewds metadata mapping (`#1629`_, `30b5554`_)
+
+Features
+--------
+
+* **plugins**: Add queryables to cop_marine (`#1638`_, `bcc793e`_)
+
+Refactoring
+-----------
+
+* **core**: Use importlib.metadata instead of the deprecated pkg_resources (`#1631`_, `3675690`_, thanks `@avalentino <https://github.com/avalentino>`_)
+
+.. _#1625: https://github.com/CS-SI/eodag/pull/1625
+.. _#1629: https://github.com/CS-SI/eodag/pull/1629
+.. _#1631: https://github.com/CS-SI/eodag/pull/1631
+.. _#1638: https://github.com/CS-SI/eodag/pull/1638
+.. _30b5554: https://github.com/CS-SI/eodag/commit/30b5554d96c58a0aca53849bd38db80902823bdf
+.. _3675690: https://github.com/CS-SI/eodag/commit/3675690e04813de6b9402f0028277c091d0e51b0
+.. _9417fd9: https://github.com/CS-SI/eodag/commit/9417fd90049ccfb8ee30f6eef7e497da2c1bea60
+.. _bcc793e: https://github.com/CS-SI/eodag/commit/bcc793e83ae6c7fec3e282046e4516510e9015fb
+
+
 v3.3.2 (2025-04-24)
 ===================
 
diff --git a/README.rst b/README.rst
index 020bb524..49143639 100644
--- a/README.rst
+++ b/README.rst
@@ -187,7 +187,7 @@ An eodag instance can be exposed through a STAC compliant REST api from the comm
 
 .. code-block:: bash
 
-    docker run -p 5000:5000 --rm csspace/eodag-server:3.3.2
+    docker run -p 5000:5000 --rm csspace/eodag-server:3.4.0
 
 You can also browse over your STAC API server using `STAC Browser <https://github.com/radiantearth/stac-browser>`_.
 Simply run:
diff --git a/charts/eodag-server/Chart.yaml b/charts/eodag-server/Chart.yaml
index 65c71b31..18e5e0a1 100644
--- a/charts/eodag-server/Chart.yaml
+++ b/charts/eodag-server/Chart.yaml
@@ -1,5 +1,5 @@
 apiVersion: v2
-appVersion: 3.3.2
+appVersion: 3.4.0
 dependencies:
 - name: common
   repository:  oci://registry-1.docker.io/bitnamicharts
@@ -15,4 +15,4 @@ name: eodag-server
 sources:
 - https://github.com/CS-SI/eodag
 type: application
-version: 3.3.2
+version: 3.4.0
diff --git a/docs/conf.py b/docs/conf.py
index 69e1a396..adc7255d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -61,7 +61,7 @@ nbsphinx_prolog = """
    You can run this notebook in a live session with |Binder|.
 
 .. |Binder| image:: https://static.mybinder.org/badge.svg
-   :target: https://mybinder.org/v2/gh/CS-SI/eodag/master?urlpath=lab/tree/docs/api_user_guide/{{ docname }}
+   :target: https://mybinder.org/v2/gh/CS-SI/eodag/master?urlpath=lab/tree/docs/{{ docname }}
 """
 
 # sphinx-copybutton configurations
diff --git a/docs/notebooks/tutos/tuto_stac_client.ipynb b/docs/notebooks/tutos/tuto_stac_client.ipynb
index d9d91f13..0213947e 100644
--- a/docs/notebooks/tutos/tuto_stac_client.ipynb
+++ b/docs/notebooks/tutos/tuto_stac_client.ipynb
@@ -22981,7 +22981,7 @@
    "id": "seasonal-internet",
    "metadata": {},
    "source": [
-    "EODAG can search for items over a STAC static catalog using [StaticStacSearch](https://eodag.readthedocs.io/en/latest/plugins_reference/generated/eodag.plugins.search.static_stac_search.StaticStacSearch.html) plugin. Path to the catalog must be set as `search.api_endpoint` and the download plugin can also be set ([HTTPDownload](https://eodag.readthedocs.io/en/latest/plugins_reference/generated/eodag.plugins.download.http.HTTPDownload.html) by default) depending on the provider.\n",
+    "EODAG can search for items over a STAC static catalog using [StaticStacSearch](https://eodag.readthedocs.io/en/latest/plugins_reference/generated/eodag.plugins.search.static_stac_search.StaticStacSearch.html) plugin. Path to the catalog (or item) must be set as `search.api_endpoint` and the download plugin can also be set ([HTTPDownload](https://eodag.readthedocs.io/en/latest/plugins_reference/generated/eodag.plugins.download.http.HTTPDownload.html) by default) depending on the provider.\n",
     "\n",
     "Here is an example with a catalog from https://stacindex.org/catalogs/spot-orthoimages-canada-2005, which will use \n",
     " `HTTPDownload` as download plugin, without credentials as no authentication is needed for download.\n",
diff --git a/docs/stac_rest.rst b/docs/stac_rest.rst
index e8204dea..13314b41 100644
--- a/docs/stac_rest.rst
+++ b/docs/stac_rest.rst
@@ -115,7 +115,7 @@ available on `https://hub.docker.com/r/csspace/eodag-server <https://hub.docker.
 
 .. code-block:: bash
 
-    $ docker run -p 5000:5000 --rm csspace/eodag-server:3.3.2
+    $ docker run -p 5000:5000 --rm csspace/eodag-server:3.4.0
 
 Example
 -------
diff --git a/eodag/plugins/manager.py b/eodag/plugins/manager.py
index 550ddcee..e26ad450 100644
--- a/eodag/plugins/manager.py
+++ b/eodag/plugins/manager.py
@@ -20,10 +20,9 @@ from __future__ import annotations
 import logging
 import re
 from operator import attrgetter
-from pathlib import Path
 from typing import TYPE_CHECKING, Any, Iterator, Optional, Union, cast
 
-import pkg_resources
+import importlib_metadata
 
 from eodag.config import (
     AUTH_TOPIC_KEYS,
@@ -91,12 +90,12 @@ class PluginManager:
             # have it discovered as long as they declare an entry point of the type
             # 'eodag.plugins.search' for example in its setup script. See the setup
             # script of eodag for an example of how to do this.
-            for entry_point in pkg_resources.iter_entry_points(
-                "eodag.plugins.{}".format(topic)
+            for entry_point in importlib_metadata.entry_points(
+                group="eodag.plugins.{}".format(topic)
             ):
                 try:
                     entry_point.load()
-                except pkg_resources.DistributionNotFound:
+                except ModuleNotFoundError:
                     logger.debug(
                         "%s plugin skipped, eodag[%s] or eodag[all] needed",
                         entry_point.name,
@@ -112,18 +111,12 @@ class PluginManager:
                         "Check that the plugin module (%s) is importable",
                         entry_point.module_name,
                     )
-                if (
-                    entry_point.dist
-                    and entry_point.dist.key != "eodag"
-                    and entry_point.dist.location is not None
-                ):
+                if entry_point.dist and entry_point.dist.name != "eodag":
                     # use plugin providers if any
+                    name = entry_point.dist.name
+                    dist = entry_point.dist
                     plugin_providers_config_path = [
-                        str(x)
-                        for x in Path(
-                            entry_point.dist.location,
-                            pkg_resources.to_filename(entry_point.dist.key),
-                        ).rglob("providers.yml")
+                        str(x) for x in dist.locate_file(name).rglob("providers.yml")
                     ]
                     if plugin_providers_config_path:
                         plugin_providers_config = load_config(
diff --git a/eodag/plugins/search/build_search_result.py b/eodag/plugins/search/build_search_result.py
index ec25e2d1..51c66632 100644
--- a/eodag/plugins/search/build_search_result.py
+++ b/eodag/plugins/search/build_search_result.py
@@ -652,6 +652,9 @@ class ECMWFSearch(PostJsonSearch):
                             "missionEndDate", today().isoformat()
                         )
                     )
+                else:
+                    keywords[START] = start
+                    keywords[END] = end
 
     def _get_product_type_queryables(
         self, product_type: Optional[str], alias: Optional[str], filters: dict[str, Any]
diff --git a/eodag/plugins/search/qssearch.py b/eodag/plugins/search/qssearch.py
index 27da6d6d..ddff6d0f 100644
--- a/eodag/plugins/search/qssearch.py
+++ b/eodag/plugins/search/qssearch.py
@@ -70,6 +70,7 @@ from eodag.api.search_result import RawSearchResult
 from eodag.plugins.search import PreparedSearch
 from eodag.plugins.search.base import Search
 from eodag.types import json_field_definition_to_python, model_fields_to_annotated
+from eodag.types.queryables import Queryables
 from eodag.types.search_args import SortByList
 from eodag.utils import (
     DEFAULT_SEARCH_TIMEOUT,
@@ -80,6 +81,7 @@ from eodag.utils import (
     REQ_RETRY_TOTAL,
     USER_AGENT,
     _deprecated,
+    copy_deepcopy,
     deepcopy,
     dict_items_recursive_apply,
     format_dict_items,
@@ -1896,30 +1898,47 @@ class StacSearch(PostJsonSearch):
                     "No queryable found for %s on %s", product_type, self.provider
                 )
                 return None
-
             # convert json results to pydantic model fields
             field_definitions: dict[str, Any] = dict()
+            STAC_TO_EODAG_QUERYABLES = {
+                "start_datetime": "start",
+                "end_datetime": "end",
+                "datetime": None,
+                "bbox": "geom",
+            }
             for json_param, json_mtd in json_queryables.items():
-                param = (
+                param = STAC_TO_EODAG_QUERYABLES.get(
+                    json_param,
                     get_queryable_from_provider(
                         json_param, self.get_metadata_mapping(product_type)
                     )
-                    or json_param
+                    or json_param,
                 )
+                if param is None:
+                    continue
 
-                default = kwargs.get(param, None)
+                default = kwargs.get(param, json_mtd.get("default"))
                 annotated_def = json_field_definition_to_python(
                     json_mtd, default_value=default
                 )
                 field_definitions[param] = get_args(annotated_def)
 
             python_queryables = create_model("m", **field_definitions).model_fields
-            # replace geometry by geom
             geom_queryable = python_queryables.pop("geometry", None)
             if geom_queryable:
                 python_queryables["geom"] = geom_queryable
 
-        return model_fields_to_annotated(python_queryables)
+            queryables_dict = model_fields_to_annotated(python_queryables)
+
+            # append "datetime" as "start" & "end" if needed
+            if "datetime" in json_queryables:
+                eodag_queryables = copy_deepcopy(
+                    model_fields_to_annotated(Queryables.model_fields)
+                )
+                queryables_dict.setdefault("start", eodag_queryables["start"])
+                queryables_dict.setdefault("end", eodag_queryables["end"])
+
+            return queryables_dict
 
 
 class PostJsonSearchWithStacQueryables(StacSearch, PostJsonSearch):
diff --git a/eodag/plugins/search/static_stac_search.py b/eodag/plugins/search/static_stac_search.py
index da52303a..94769847 100644
--- a/eodag/plugins/search/static_stac_search.py
+++ b/eodag/plugins/search/static_stac_search.py
@@ -46,7 +46,7 @@ logger = logging.getLogger("eodag.search.static_stac_search")
 class StaticStacSearch(StacSearch):
     """Static STAC Catalog search plugin
 
-    This plugin first loads all STAC items found in the catalog, and converts them to
+    This plugin first loads all STAC items found in the catalog (or item), and converts them to
     EOProducts using :class:`~eodag.plugins.search.qssearch.StacSearch`.
     Then it uses crunchers to only keep products matching query parameters.
 
@@ -56,7 +56,7 @@ class StaticStacSearch(StacSearch):
     :param provider: provider name
     :param config: Search plugin configuration:
 
-        * :attr:`~eodag.config.PluginConfig.api_endpoint` (``str``) (**mandatory**): path to the catalog;
+        * :attr:`~eodag.config.PluginConfig.api_endpoint` (``str``) (**mandatory**): path to the catalog or item;
           in contrast to the api_endpoint for other plugin types this can be a url or local system path.
         * :attr:`~eodag.config.PluginConfig.max_connections` (``int``): Maximum number of concurrent
           connections for HTTP requests; default: ``100``
diff --git a/eodag/resources/providers.yml b/eodag/resources/providers.yml
index aa20d92a..bfd62ed9 100644
--- a/eodag/resources/providers.yml
+++ b/eodag/resources/providers.yml
@@ -6056,6 +6056,7 @@
       constraints_url: https://ewds.climate.copernicus.eu/api/catalogue/v1/collections/{dataset}/constraints.json
       form_url: https://ewds.climate.copernicus.eu/api/catalogue/v1/collections/{dataset}/form.json
     metadata_mapping:
+      productType: $.productType
       startTimeFromAscendingNode: '{$.startTimeFromAscendingNode#to_iso_utc_datetime}'
       completionTimeFromAscendingNode:
         - date={startTimeFromAscendingNode#to_iso_date}/{completionTimeFromAscendingNode#to_iso_date}
@@ -6190,3 +6191,5 @@
       release_version: 5
       metadata_mapping:
         <<: *day_month_year
+    GENERIC_PRODUCT_TYPE:
+      dataset: '{productType}'
diff --git a/eodag/utils/stac_reader.py b/eodag/utils/stac_reader.py
index 43d41b95..1db5eaf3 100644
--- a/eodag/utils/stac_reader.py
+++ b/eodag/utils/stac_reader.py
@@ -122,12 +122,13 @@ def fetch_stac_items(
     # URI opener used by PySTAC internally, instantiated here
     # to retrieve the timeout.
     _text_opener = _TextOpener(timeout, ssl_verify)
-    pystac.StacIO.read_text = _text_opener  # type: ignore[assignment]
+    stac_io = pystac.StacIO.default()
+    stac_io.read_text = _text_opener  # type: ignore[assignment]
 
-    stac_obj = pystac.read_file(stac_path)
+    stac_obj = pystac.read_file(stac_path, stac_io=stac_io)
     # Single STAC item
     if isinstance(stac_obj, pystac.Item):
-        return [stac_obj.to_dict()]
+        return [stac_obj.to_dict(transform_hrefs=False)]
     # STAC catalog
     elif isinstance(stac_obj, pystac.Catalog):
         return _fetch_stac_items_from_catalog(
@@ -152,7 +153,9 @@ def _fetch_stac_items_from_catalog(
     if extensions:
         extensions = extensions if isinstance(extensions, list) else [extensions]
         if "single-file-stac" in extensions:
-            items = [feature for feature in cat.to_dict()["features"]]
+            items = [
+                feature for feature in cat.to_dict(transform_hrefs=False)["features"]
+            ]
             return items
 
     # Making the links absolutes allow for both relative and absolute links to be handled.
@@ -201,9 +204,10 @@ def fetch_stac_collections(
 
     # URI opener used by PySTAC internally, instantiated here to retrieve the timeout.
     _text_opener = _TextOpener(timeout, ssl_verify)
-    pystac.StacIO.read_text = _text_opener  # type: ignore[assignment]
+    stac_io = pystac.StacIO.default()
+    stac_io.read_text = _text_opener  # type: ignore[assignment]
 
-    stac_obj = pystac.read_file(stac_path)
+    stac_obj = pystac.read_file(stac_path, stac_io=stac_io)
     if isinstance(stac_obj, pystac.Catalog):
         return _fetch_stac_collections_from_catalog(
             stac_obj, collection, max_connections, _text_opener
diff --git a/pyproject.toml b/pyproject.toml
index 025a5dd3..2bacb32e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -3,7 +3,7 @@ requires = ["setuptools>=45", "setuptools_scm[toml]>=6.2"]
 build-backend = "setuptools.build_meta"
 
 [tool.setuptools_scm]
-fallback_version = "3.3.3.dev0"
+fallback_version = "3.4.1.dev0"
 
 [tool.coverage.report]
 exclude_lines = [
diff --git a/setup.cfg b/setup.cfg
index 4e185f41..d8ae3391 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -43,6 +43,7 @@ install_requires =
     botocore
     click
     geojson
+    importlib_metadata >= 5.0
     jsonpath-ng
     lxml
     orjson
@@ -55,7 +56,6 @@ install_requires =
     python-dateutil
     PyYAML
     requests
-    setuptools
     shapely >= 2.0.6
     stream-zip
     tqdm


Test patch:
diff --git a/tests/integration/test_config_ext_plugin.py b/tests/integration/test_config_ext_plugin.py
index 375aa38a..70999d2d 100644
--- a/tests/integration/test_config_ext_plugin.py
+++ b/tests/integration/test_config_ext_plugin.py
@@ -15,11 +15,14 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
+import importlib
 import os
+import shutil
+import sys
 import unittest
 from tempfile import TemporaryDirectory
 
-import pkg_resources
+from pytest import MonkeyPatch
 
 from tests import TEST_RESOURCES_PATH
 from tests.context import EODataAccessGateway
@@ -29,6 +32,12 @@ from tests.utils import mock
 class TestExternalPluginConfig(unittest.TestCase):
     def setUp(self):
         super(TestExternalPluginConfig, self).setUp()
+
+        # clean sys path
+        self._saved_sys_path = sys.path[:]
+        sys.path[:] = [p for p in sys.path if "fake_ext_plugin" not in p]
+        importlib.invalidate_caches()
+
         # Mock home and eodag conf directory to tmp dir
         self.tmp_home_dir = TemporaryDirectory()
         self.expanduser_mock = mock.patch(
@@ -43,38 +52,31 @@ class TestExternalPluginConfig(unittest.TestCase):
 
         self.dag.providers_config.pop("fakeplugin_provider", None)
 
-        # remove entry point manually, see https://github.com/pypa/setuptools/issues/1759
-        distpath = pkg_resources.working_set.by_key.pop("eodag-fakeplugin").location
-        pkg_resources.working_set.entry_keys.pop(distpath)
-        pkg_resources.working_set.entries.remove(distpath)
-
         # stop Mock and remove tmp config dir
         self.expanduser_mock.stop()
         self.tmp_home_dir.cleanup()
+        # reset sys.path
+        sys.path[:] = self._saved_sys_path
+        importlib.invalidate_caches()
 
     def test_update_providers_from_ext_plugin(self):
         """Load fake external plugin and check if it updates providers config"""
 
         default_providers_count = len(self.dag.providers_config)
 
-        fakeplugin_location = os.path.join(TEST_RESOURCES_PATH, "fake_ext_plugin")
-
-        # Create the fake entry point definition
-        ep = pkg_resources.EntryPoint.parse(
-            "FakePluginAPI = eodag_fakeplugin:FakePluginAPI"
+        src = os.path.join(TEST_RESOURCES_PATH, "fake_ext_plugin")
+        fakeplugin_location = os.path.join(self.tmp_home_dir.name, "fake_ext_plugin")
+        shutil.copytree(src, fakeplugin_location)
+        shutil.move(
+            os.path.join(fakeplugin_location, "eodag_fakeplugin.egg-info_"),
+            os.path.join(fakeplugin_location, "eodag_fakeplugin.egg-info"),
         )
-        # Create a fake distribution to insert into the global working_set
-        d = pkg_resources.Distribution(
-            location=fakeplugin_location,
-            project_name="eodag_fakeplugin",
-            version="0.1",
-        )
-        ep.dist = d
-        # Add the mapping to the fake EntryPoint
-        d._ep_map = {"eodag.plugins.api": {"FakePluginAPI": ep}}
-        # Add the fake distribution to the global working_set
-        pkg_resources.working_set.add(d, fakeplugin_location)
 
-        # New EODataAccessGateway instance, check if new conf has been loaded
-        self.dag = EODataAccessGateway()
-        self.assertEqual(len(self.dag.providers_config), default_providers_count + 1)
+        with MonkeyPatch.context() as monkeypatch:
+            monkeypatch.syspath_prepend(fakeplugin_location)
+
+            # New EODataAccessGateway instance, check if new conf has been loaded
+            self.dag = EODataAccessGateway()
+            self.assertEqual(
+                len(self.dag.providers_config), default_providers_count + 1
+            )
diff --git a/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/PKG-INFO b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/PKG-INFO
new file mode 100644
index 00000000..f2576563
--- /dev/null
+++ b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/PKG-INFO
@@ -0,0 +1,3 @@
+Metadata-Version: 2.4
+Name: eodag_fakeplugin
+Version: 1.0
diff --git a/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/SOURCES.txt b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/SOURCES.txt
new file mode 100644
index 00000000..97267b8e
--- /dev/null
+++ b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/SOURCES.txt
@@ -0,0 +1,7 @@
+pyproject.toml
+eodag_fakeplugin/__init__.py
+eodag_fakeplugin.egg-info/PKG-INFO
+eodag_fakeplugin.egg-info/SOURCES.txt
+eodag_fakeplugin.egg-info/dependency_links.txt
+eodag_fakeplugin.egg-info/entry_points.txt
+eodag_fakeplugin.egg-info/top_level.txt
diff --git a/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/dependency_links.txt b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/dependency_links.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/entry_points.txt b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/entry_points.txt
new file mode 100644
index 00000000..e9758c2a
--- /dev/null
+++ b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/entry_points.txt
@@ -0,0 +1,2 @@
+[eodag.plugins.api]
+FakePluginAPI = eodag_fakeplugin:FakePluginAPI
diff --git a/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/top_level.txt b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/top_level.txt
new file mode 100644
index 00000000..70c07cc7
--- /dev/null
+++ b/tests/resources/fake_ext_plugin/eodag_fakeplugin.egg-info_/top_level.txt
@@ -0,0 +1,1 @@
+eodag_fakeplugin
diff --git a/tests/resources/fake_ext_plugin/pyproject.toml b/tests/resources/fake_ext_plugin/pyproject.toml
new file mode 100644
index 00000000..87e12f3b
--- /dev/null
+++ b/tests/resources/fake_ext_plugin/pyproject.toml
@@ -0,0 +1,16 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "eodag_fakeplugin"
+version = "1.0"
+
+[project.entry-points."eodag.plugins.api"]
+FakePluginAPI = "eodag_fakeplugin:FakePluginAPI"
+
+[tool.setuptools]
+packages = ["eodag_fakeplugin"]
+
+[tool.setuptools.package-data]
+eodag_fakeplugin = ["providers.yml"]
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 78e6988e..902cc0bd 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -92,7 +92,8 @@ class TestEodagCli(unittest.TestCase):
         """Calling eodag without arguments should print help message"""
         result = self.runner.invoke(eodag)
         self.assertIn("Usage: eodag [OPTIONS] COMMAND [ARGS]...", result.output)
-        self.assertEqual(result.exit_code, 0)
+        # Exit status 2 with no_args_is_help starting click >= 8.2.0
+        self.assertIn(result.exit_code, (0, 2))
 
     def test_eodag_with_only_verbose_opt(self):
         """Calling eodag only with -v option should print error message"""
diff --git a/tests/units/test_core.py b/tests/units/test_core.py
index 51915520..e7cc777b 100644
--- a/tests/units/test_core.py
+++ b/tests/units/test_core.py
@@ -31,7 +31,6 @@ from tempfile import TemporaryDirectory
 
 import yaml
 from lxml import html
-from pkg_resources import DistributionNotFound
 from pydantic import ValidationError
 from shapely import wkt
 from shapely.geometry import LineString, MultiPolygon, Polygon
@@ -1253,18 +1252,18 @@ class TestCore(TestCoreBase):
             os.environ.pop("EODAG__PEPS__SEARCH__NEED_AUTH", None)
             os.environ.pop("EODAG__PEPS__AUTH__CREDENTIALS__USERNAME", None)
 
-    @mock.patch("eodag.plugins.manager.pkg_resources.iter_entry_points", autospec=True)
+    @mock.patch("eodag.plugins.manager.importlib_metadata.entry_points", autospec=True)
     def test_prune_providers_list_skipped_plugin(self, mock_iter_ep):
         """Providers needing skipped plugin must be pruned on init"""
         empty_conf_file = str(
             res_files("eodag") / "resources" / "user_conf_template.yml"
         )
 
-        def skip_qssearch(topic):
+        def skip_qssearch(group):
             ep = mock.MagicMock()
-            if topic == "eodag.plugins.search":
+            if group == "eodag.plugins.search":
                 ep.name = "QueryStringSearch"
-                ep.load = mock.MagicMock(side_effect=DistributionNotFound())
+                ep.load = mock.MagicMock(side_effect=ModuleNotFoundError())
             return [ep]
 
         mock_iter_ep.side_effect = skip_qssearch
diff --git a/tests/units/test_search_plugins.py b/tests/units/test_search_plugins.py
index e5ea662b..7c31381e 100644
--- a/tests/units/test_search_plugins.py
+++ b/tests/units/test_search_plugins.py
@@ -2357,6 +2357,42 @@ class TestSearchPluginECMWFSearch(unittest.TestCase):
             "1985-10-26T00:00:00.000Z",
         )
 
+    def test_plugins_search_ecmwfsearch_with_year_month_day_filter(self):
+        """ECMWFSearch.query must use have datetime in response if year, month, day used in filters"""
+
+        results, _ = self.search_plugin.query(
+            prep=PreparedSearch(),
+            productType="ERA5_SL",
+            **{
+                "ecmwf:year": "2020",
+                "ecmwf:month": ["02"],
+                "ecmwf:day": ["20", "21"],
+                "ecmwf:time": ["01:00"],
+            },
+        )
+        eoproduct = results[0]
+
+        self.assertEqual(
+            eoproduct.properties["startTimeFromAscendingNode"],
+            "2020-02-20T01:00:00.000Z",
+        )
+        self.assertEqual(
+            eoproduct.properties["completionTimeFromAscendingNode"],
+            "2020-02-21T01:00:00.000Z",
+        )
+        self.assertEqual(
+            eoproduct.properties["ecmwf:year"],
+            "2020",
+        )
+        self.assertEqual(
+            eoproduct.properties["ecmwf:month"],
+            ["02"],
+        )
+        self.assertEqual(
+            eoproduct.properties["ecmwf:day"],
+            ["20", "21"],
+        )
+
     def test_plugins_search_ecmwfsearch_without_producttype(self):
         """
         ECMWFSearch.query must build a EOProduct from input parameters without product type.