Repository: CS-SI/eodag
Pull request #1429 (2024-12-06 08:43:50)

fix(server): load processing level as string in external stac collection
eodag expects a string value for processingLevel

github-actions[bot]: ## Test Results
    4 files  ±0      4 suites  ±0   6m 5s ⏱️ -1s
  582 tests ±0    579 ✅ ±0   3 💤 ±0  0 ❌ ±0 
2 328 runs  ±0  2 230 ✅ ±0  98 💤 ±0  0 ❌ ±0 

Results for commit 19ca8aa5. ± Comparison against base commit d709297e.

[test-results]:data:application/gzip;base64,H4sIAE66UmcC/02Myw7CIBBFf6Vh7YIOYgd/xvBMiG0xPFaN/y5oi12ec2fORpyfbSL34XoZSCo+dzAlyuzDWpHdeBV1ym3kCAc9UtG6qUn81dO/2k8XTvq5CtqFjTHE3cSytiYwwJ2OJgCjXf2aot+cml8+J3VYFp8rkFFoiVJypqhRMBmQQA2OyqKlgIJyjehAafL+ALr6GgwIAQAA

github-actions[bot]: ![badge](https://raw.githubusercontent.com/CS-SI/eodag/_xml_coverage_reports/data/server-processing-level/./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-735, 837-888, 892
config.py                                      428      27  93.69%   83-85, 94, 102, 106-108, 179, 190, 690-692, 805-808, 851-852, 861-862, 967, 1030-1035, 1037
crunch.py                                        5       5  0.00%    20-24
api/__init__.py                                  0       0  100.00%
api/core.py                                    779      72  90.76%   363, 653, 697-700, 738, 782, 816, 861-866, 892, 983, 1051, 1189, 1274-1286, 1322, 1324, 1352, 1356-1367, 1380-1386, 1469-1472, 1505-1525, 1577, 1594-1598, 1610-1613, 1949, 1973-1979, 2230, 2234-2238, 2247-2249, 2293-2294, 2323-2324
api/search_result.py                            58       4  93.10%   92, 101, 108, 122
api/product/__init__.py                          6       0  100.00%
api/product/_assets.py                          48       5  89.58%   75, 147, 155, 158-162
api/product/_product.py                        187      20  89.30%   70-72, 237-238, 313, 342, 399, 413-416, 429, 453-456, 499-505
api/product/metadata_mapping.py                700      76  89.14%   131-133, 230, 262-263, 309-310, 320-332, 334, 345, 351-363, 410-411, 448, 469-472, 495, 503-504, 590-591, 615-616, 622-625, 640-641, 790, 836, 989, 998-1002, 1019-1024, 1151, 1165-1185, 1205, 1210, 1339, 1353, 1424, 1476, 1499-1500, 1516-1520, 1536, 1544, 1563
api/product/drivers/__init__.py                  6       0  100.00%
api/product/drivers/base.py                      6       1  83.33%   38
plugins/__init__.py                              0       0  100.00%
plugins/base.py                                 21       2  90.48%   48, 55
plugins/manager.py                             172      15  91.28%   116-121, 171, 209, 231, 235, 259, 281, 399-402, 414-415
plugins/apis/__init__.py                         0       0  100.00%
plugins/apis/base.py                             4       0  100.00%
plugins/apis/ecmwf.py                           97      10  89.69%   170-172, 219-220, 246-248, 301-302
plugins/apis/usgs.py                           180      31  82.78%   155, 257, 291, 326-328, 333, 359-360, 365, 395-402, 413-418, 440-446, 448-454, 477
plugins/authentication/__init__.py               6       1  83.33%   31
plugins/authentication/aws_auth.py              19       0  100.00%
plugins/authentication/base.py                  17       2  88.24%   43, 56
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%   42-44, 48-51
plugins/authentication/openid_connect.py       206      27  86.89%   80-81, 93-111, 149, 155-183, 191, 323-326, 352
plugins/authentication/qsauth.py                34       1  97.06%   91
plugins/authentication/sas_auth.py              47       1  97.87%   76
plugins/authentication/token.py                 92      16  82.61%   114, 143, 145, 176-189, 245-249
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       7  76.67%   55-60, 63-64, 80-84
plugins/download/__init__.py                     0       0  100.00%
plugins/download/aws.py                        491     163  66.80%   282, 295, 362-365, 379-383, 425-427, 431, 463-464, 470-474, 503, 535, 539, 546, 576-584, 588, 620-628, 639-641, 672-746, 764-824, 835-840, 852-865, 890, 905-907, 910, 920-928, 936-949, 959-990, 997-1009, 1047, 1073, 1118-1120, 1340
plugins/download/base.py                       258      49  81.01%   136, 164, 300-301, 355-359, 365-366, 408, 411-425, 437, 441, 505-509, 539-540, 548-565, 572-580, 582-586, 629, 651, 673, 681
plugins/download/creodias_s3.py                 17       9  47.06%   53-67
plugins/download/http.py                       541     105  80.59%   237, 333-336, 339, 346-351, 369-384, 401, 413, 461, 468-474, 492, 506, 520, 528-530, 546-551, 562, 580, 623-626, 648, 688, 733, 747-753, 782-846, 864, 894-903, 925-926, 955-960, 966, 969, 986, 1003-1004, 1034-1035, 1042, 1104, 1119, 1178-1179, 1185, 1195, 1231, 1267, 1287, 1322-1324
plugins/download/s3rest.py                     116      24  79.31%   118, 154, 161, 196, 223-230, 233-235, 239, 250-256, 264-265, 268-272, 295, 316-319
plugins/search/__init__.py                      22       0  100.00%
plugins/search/base.py                         146      11  92.47%   100, 104, 128-134, 274, 294, 433
plugins/search/build_search_result.py          341      49  85.63%   249, 277-278, 314, 317, 386-389, 478-495, 523, 558, 560, 585-592, 622-624, 628, 650, 685, 736, 765-766, 781-796, 839, 864, 867, 870, 878, 997-998, 1004, 1015, 1073, 1124
plugins/search/cop_marine.py                   240      47  80.42%   56, 64-66, 76-77, 82, 87-88, 104, 106, 109, 175-176, 218, 224, 228, 232, 245, 256-257, 265, 293, 297, 312, 316, 320, 324, 328-332, 338-341, 344-358, 375, 424-428, 433, 445
plugins/search/creodias_s3.py                   55       3  94.55%   59, 77, 111
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%   188-191, 207, 218, 222-223, 234, 239, 244, 251, 264-267, 321-322, 326, 336-342, 347, 373-376, 384-395, 412, 414, 421-424, 426-427, 445-449, 482, 492, 503, 516, 522-537, 542
plugins/search/qssearch.py                     732      89  87.84%   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, 1105-1110, 1114-1123, 1157, 1179, 1256, 1346, 1431-1432, 1442, 1519-1523, 1585, 1588, 1594-1595, 1616, 1644-1656, 1663, 1695-1697, 1707-1713, 1743, 1766, 1771-1772, 1787, 1803, 1888-1891, 1896-1899, 1908, 1938-1942, 1948
plugins/search/static_stac_search.py            75      13  82.67%   98-124, 140, 153
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      66  74.62%   258, 266, 284-301, 316-354, 448, 485-524, 707, 714-767
rest/errors.py                                  69       5  92.75%   106, 116, 127, 143-144
rest/server.py                                 192      24  87.50%   94, 117-119, 293-298, 326, 522-524, 541-546, 575, 577, 581-582, 586-587
rest/stac.py                                   319      63  80.25%   306, 328, 380-383, 410-437, 468-470, 493, 525-526, 608-648, 670-686, 778-782, 789, 843-844, 905, 995-997
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       6  96.67%   225-229, 282, 285, 353, 385
rest/types/queryables.py                        57       5  91.23%   93-98, 162
rest/types/stac_search.py                      125       7  94.40%   138, 184, 199-201, 209, 213
rest/utils/__init__.py                          94      12  87.23%   111-112, 131-133, 185, 195-209
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                              128      42  67.19%   66, 79-83, 94-106, 134-136, 143-148, 189, 208, 230, 240-256, 261, 263, 285, 290, 298, 308
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                            108       1  99.07%   63
types/search_args.py                            70      18  74.29%   60-64, 71-88, 103
types/whoosh.py                                 15       0  100.00%
utils/__init__.py                              520      47  90.96%   85, 194-195, 204-231, 234, 249, 329-333, 406-410, 429-431, 444-458, 537, 552, 592-593, 957-960, 968-969, 1007-1008, 1055-1056, 1190
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/stac_reader.py                           111      45  59.46%   56-57, 63-85, 95-97, 101, 137, 153-156, 203-212, 222-252
TOTAL                                         9888    1642  83.39%
```

</details>


### Diff against develop

```
Filename        Stmts    Miss  Cover
------------  -------  ------  -------
rest/core.py       +3      +3  -0.87%
TOTAL              +3      +3  -0.03%
```


Results for commit: 19ca8aa53b0db27d2a20d81be8e028905c88f2bc


_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 -->
github-actions[bot]: ![badge](https://raw.githubusercontent.com/CS-SI/eodag/_xml_coverage_reports_win/data/server-processing-level/./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-735, 837-888, 892
config.py                                      428      28  93.46%   83-85, 94, 102, 106-108, 179, 190, 690-692, 805-808, 851-852, 861-862, 967, 998, 1030-1035, 1037
crunch.py                                        5       5  0.00%    20-24
api/__init__.py                                  0       0  100.00%
api/core.py                                    779      72  90.76%   363, 653, 697-700, 738, 782, 816, 861-866, 892, 983, 1051, 1189, 1274-1286, 1322, 1324, 1352, 1356-1367, 1380-1386, 1469-1472, 1505-1525, 1577, 1594-1598, 1610-1613, 1949, 1973-1979, 2230, 2234-2238, 2247-2249, 2293-2294, 2323-2324
api/search_result.py                            58       4  93.10%   92, 101, 108, 122
api/product/__init__.py                          6       0  100.00%
api/product/_assets.py                          48       5  89.58%   75, 147, 155, 158-162
api/product/_product.py                        187      20  89.30%   70-72, 237-238, 313, 342, 399, 413-416, 429, 453-456, 499-505
api/product/metadata_mapping.py                700      77  89.00%   131-133, 230, 262-263, 309-310, 320-332, 334, 345, 351-363, 410-411, 448, 469-472, 495, 503-504, 590-591, 615-616, 622-625, 640-641, 790, 836, 989, 998-1002, 1019-1024, 1151, 1165-1185, 1205, 1210, 1339, 1353, 1424, 1476, 1499-1500, 1503, 1516-1520, 1536, 1544, 1563
api/product/drivers/__init__.py                  6       0  100.00%
api/product/drivers/base.py                      6       1  83.33%   38
plugins/__init__.py                              0       0  100.00%
plugins/base.py                                 21       3  85.71%   48, 55, 68
plugins/manager.py                             172      15  91.28%   116-121, 171, 209, 231, 235, 259, 281, 399-402, 414-415
plugins/apis/__init__.py                         0       0  100.00%
plugins/apis/base.py                             4       0  100.00%
plugins/apis/ecmwf.py                           97      10  89.69%   170-172, 219-220, 246-248, 301-302
plugins/apis/usgs.py                           180      31  82.78%   155, 257, 291, 326-328, 333, 359-360, 365, 395-402, 413-418, 440-446, 448-454, 477
plugins/authentication/__init__.py               6       1  83.33%   31
plugins/authentication/aws_auth.py              19       0  100.00%
plugins/authentication/base.py                  17       2  88.24%   43, 56
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%   42-44, 48-51
plugins/authentication/openid_connect.py       206      27  86.89%   80-81, 93-111, 149, 155-183, 191, 323-326, 352
plugins/authentication/qsauth.py                34       1  97.06%   91
plugins/authentication/sas_auth.py              47       1  97.87%   76
plugins/authentication/token.py                 92      16  82.61%   114, 143, 145, 176-189, 245-249
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       7  76.67%   55-60, 63-64, 80-84
plugins/download/__init__.py                     0       0  100.00%
plugins/download/aws.py                        491     163  66.80%   282, 295, 362-365, 379-383, 425-427, 431, 463-464, 470-474, 503, 535, 539, 546, 576-584, 588, 620-628, 639-641, 672-746, 764-824, 835-840, 852-865, 890, 905-907, 910, 920-928, 936-949, 959-990, 997-1009, 1047, 1073, 1118-1120, 1340
plugins/download/base.py                       258      51  80.23%   136, 164, 231-233, 300-301, 355-359, 365-366, 408, 411-425, 437, 441, 505-509, 539-540, 548-565, 572-580, 582-586, 629, 651, 673, 681
plugins/download/creodias_s3.py                 17       9  47.06%   53-67
plugins/download/http.py                       541     106  80.41%   237, 333-336, 339, 346-351, 369-384, 401, 413, 461, 468-474, 492, 506, 520, 528-530, 546-551, 562, 580, 623-626, 648, 688, 733, 747-753, 782-846, 864, 894-903, 925-926, 955-960, 966, 969, 986, 1003-1004, 1017, 1034-1035, 1042, 1104, 1119, 1178-1179, 1185, 1195, 1231, 1267, 1287, 1322-1324
plugins/download/s3rest.py                     116      24  79.31%   118, 154, 161, 196, 223-230, 233-235, 239, 250-256, 264-265, 268-272, 295, 316-319
plugins/search/__init__.py                      22       0  100.00%
plugins/search/base.py                         146      11  92.47%   100, 104, 128-134, 274, 294, 433
plugins/search/build_search_result.py          341      50  85.34%   249, 277-278, 314, 317, 386-389, 478-495, 523, 558, 560, 585-592, 622-624, 628, 650, 685, 736, 750, 765-766, 781-796, 839, 864, 867, 870, 878, 997-998, 1004, 1015, 1073, 1124
plugins/search/cop_marine.py                   240      47  80.42%   56, 64-66, 76-77, 82, 87-88, 104, 106, 109, 175-176, 218, 224, 228, 232, 245, 256-257, 265, 293, 297, 312, 316, 320, 324, 328-332, 338-341, 344-358, 375, 424-428, 433, 445
plugins/search/creodias_s3.py                   55       3  94.55%   59, 77, 111
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%   188-191, 207, 218, 222-223, 234, 239, 244, 251, 264-267, 321-322, 326, 336-342, 347, 373-376, 384-395, 412, 414, 421-424, 426-427, 445-449, 482, 492, 503, 516, 522-537, 542
plugins/search/qssearch.py                     732     125  82.92%   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, 1105-1110, 1114-1123, 1157, 1179, 1256, 1346, 1431-1432, 1442, 1519-1523, 1585, 1588, 1594-1595, 1616, 1644-1656, 1663, 1695-1697, 1707-1713, 1743, 1766, 1771-1772, 1787, 1803, 1871-1981
plugins/search/static_stac_search.py            75      13  82.67%   98-124, 140, 153
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-354, 385-422, 437-453, 469-478, 485-524, 541, 583-668, 707, 714-767
rest/errors.py                                  69      49  28.99%   60, 65-100, 105-108, 115-118, 126-147, 155-160, 175-181
rest/server.py                                 192     192  0.00%    18-600
rest/stac.py                                   319      68  78.68%   240, 306, 328, 380-383, 410-437, 468-470, 493, 525-526, 608-648, 670-686, 713, 778-782, 789, 843-844, 850, 905, 943, 976, 995-997
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                        57      13  77.19%   50-51, 58-59, 66-67, 93-98, 107-108, 162
rest/types/stac_search.py                      125      11  91.20%   136-138, 184, 199-201, 209, 213, 261, 264
rest/utils/__init__.py                          94      30  68.09%   82-88, 108, 111-112, 131-133, 146, 153, 178-186, 193-214
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                              128      44  65.62%   66, 70, 79-83, 94-106, 134-136, 143-148, 189, 203, 208, 230, 240-256, 261, 263, 285, 290, 298, 308
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                            108       1  99.07%   63
types/search_args.py                            70      18  74.29%   60-64, 71-88, 103
types/whoosh.py                                 15       0  100.00%
utils/__init__.py                              520      47  90.96%   85, 194-195, 204-231, 234, 249, 329-333, 406-410, 429-431, 444-458, 537, 552, 592-593, 957-960, 968-969, 1007-1008, 1055-1056, 1190
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/stac_reader.py                           111      45  59.46%   56-57, 63-85, 95-97, 101, 137, 153-156, 203-212, 222-252
TOTAL                                         9888    2078  78.98%
```

</details>


### Diff against develop

```
Filename        Stmts    Miss  Cover
------------  -------  ------  -------
rest/core.py       +3      +3  -0.48%
TOTAL              +3      +3  -0.03%
```


Results for commit: 19ca8aa53b0db27d2a20d81be8e028905c88f2bc


_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/eodag/api/core.py b/eodag/api/core.py
index 21ce80ff..81817294 100644
--- a/eodag/api/core.py
+++ b/eodag/api/core.py
@@ -32,7 +32,7 @@ import yaml.parser
 from pkg_resources import resource_filename
 from whoosh import analysis, fields
 from whoosh.fields import Schema
-from whoosh.index import create_in, exists_in, open_dir
+from whoosh.index import exists_in, open_dir
 from whoosh.qparser import QueryParser
 
 from eodag.api.product.metadata_mapping import (
@@ -61,7 +61,7 @@ from eodag.plugins.search.build_search_result import MeteoblueSearch
 from eodag.plugins.search.qssearch import PostJsonSearch
 from eodag.types import model_fields_to_annotated
 from eodag.types.queryables import CommonQueryables, QueryablesDict
-from eodag.types.whoosh import EODAGQueryParser
+from eodag.types.whoosh import EODAGQueryParser, create_in
 from eodag.utils import (
     DEFAULT_DOWNLOAD_TIMEOUT,
     DEFAULT_DOWNLOAD_WAIT,
@@ -306,13 +306,18 @@ class EODataAccessGateway:
                     product_type, **{"md5": self.product_types_config_md5}
                 )
                 # add to index
-                ix_writer.add_document(
-                    **{
-                        k: v
-                        for k, v in versioned_product_type.items()
-                        if k in product_types_schema.names()
-                    }
-                )
+                try:
+                    ix_writer.add_document(
+                        **{
+                            k: v
+                            for k, v in versioned_product_type.items()
+                            if k in product_types_schema.names()
+                        }
+                    )
+                except TypeError as e:
+                    logger.error(
+                        f"Cannot write product type {product_type['ID']} into index. e={e} product_type={product_type}"
+                    )
             ix_writer.commit()
 
     def set_preferred_provider(self, provider: str) -> None:
@@ -2007,8 +2012,8 @@ class EODataAccessGateway:
         search_result: SearchResult,
         downloaded_callback: Optional[DownloadedCallback] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> List[str]:
         """Download all products resulting from a search.
@@ -2170,8 +2175,8 @@ class EODataAccessGateway:
         self,
         product: EOProduct,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> str:
         """Download a single product.
diff --git a/eodag/api/product/_product.py b/eodag/api/product/_product.py
index 23c55060..8d1d6fdc 100644
--- a/eodag/api/product/_product.py
+++ b/eodag/api/product/_product.py
@@ -281,8 +281,8 @@ class EOProduct:
     def download(
         self,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> str:
         """Download the EO product using the provided download plugin and the
diff --git a/eodag/plugins/apis/ecmwf.py b/eodag/plugins/apis/ecmwf.py
index a4d5c5fe..c299d723 100644
--- a/eodag/plugins/apis/ecmwf.py
+++ b/eodag/plugins/apis/ecmwf.py
@@ -178,8 +178,8 @@ class EcmwfApi(Api, ECMWFSearch):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> Optional[str]:
         """Download data from ECMWF MARS"""
@@ -268,8 +268,8 @@ class EcmwfApi(Api, ECMWFSearch):
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         downloaded_callback: Optional[DownloadedCallback] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> List[str]:
         """
diff --git a/eodag/plugins/apis/usgs.py b/eodag/plugins/apis/usgs.py
index 79bf49bd..87604575 100644
--- a/eodag/plugins/apis/usgs.py
+++ b/eodag/plugins/apis/usgs.py
@@ -297,8 +297,8 @@ class UsgsApi(Api):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> Optional[str]:
         """Download data from USGS catalogues"""
@@ -375,7 +375,7 @@ class UsgsApi(Api):
         logger.debug(f"Downloading {req_url}")
         ssl_verify = getattr(self.config, "ssl_verify", True)
 
-        @self._download_retry(product, wait, timeout)
+        @self._order_download_retry(product, wait, timeout)
         def download_request(
             product: EOProduct,
             fs_path: str,
@@ -467,8 +467,8 @@ class UsgsApi(Api):
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         downloaded_callback: Optional[DownloadedCallback] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> List[str]:
         """
diff --git a/eodag/plugins/download/aws.py b/eodag/plugins/download/aws.py
index f6924124..b7dcc2f4 100644
--- a/eodag/plugins/download/aws.py
+++ b/eodag/plugins/download/aws.py
@@ -251,8 +251,8 @@ class AwsDownload(Download):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> Optional[str]:
         """Download method for AWS S3 API.
@@ -649,8 +649,8 @@ class AwsDownload(Download):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> StreamResponse:
         r"""
@@ -1330,8 +1330,8 @@ class AwsDownload(Download):
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         downloaded_callback: Optional[DownloadedCallback] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> List[str]:
         """
diff --git a/eodag/plugins/download/base.py b/eodag/plugins/download/base.py
index b52e0d7a..f1f48eb1 100644
--- a/eodag/plugins/download/base.py
+++ b/eodag/plugins/download/base.py
@@ -112,8 +112,8 @@ class Download(PluginTopic):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> Optional[str]:
         r"""
@@ -142,8 +142,8 @@ class Download(PluginTopic):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> StreamResponse:
         r"""
@@ -448,8 +448,8 @@ class Download(PluginTopic):
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         downloaded_callback: Optional[DownloadedCallback] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> List[str]:
         """
@@ -589,14 +589,14 @@ class Download(PluginTopic):
 
         return paths
 
-    def _download_retry(
-        self, product: EOProduct, wait: int, timeout: int
+    def _order_download_retry(
+        self, product: EOProduct, wait: float, timeout: float
     ) -> Callable[[Callable[..., T]], Callable[..., T]]:
         """
-        Download retry decorator.
+        Order download retry decorator.
 
-        Retries the wrapped  download method after `wait` minutes if a NotAvailableError
-        exception is thrown until `timeout` minutes.
+        Retries the wrapped order_download method after ``wait`` minutes if a
+        ``NotAvailableError`` exception is thrown until ``timeout`` minutes.
 
         :param product: The EO product to download
         :param wait: If download fails, wait time in minutes between two download tries
@@ -605,7 +605,7 @@ class Download(PluginTopic):
         :returns: decorator
         """
 
-        def decorator(download: Callable[..., T]) -> Callable[..., T]:
+        def decorator(order_download: Callable[..., T]) -> Callable[..., T]:
             def download_and_retry(*args: Any, **kwargs: Unpack[DownloadConf]) -> T:
                 # initiate retry loop
                 start_time = datetime.now()
@@ -622,7 +622,7 @@ class Download(PluginTopic):
                     if datetime_now >= product.next_try:
                         product.next_try += timedelta(minutes=wait)
                         try:
-                            return download(*args, **kwargs)
+                            return order_download(*args, **kwargs)
 
                         except NotAvailableError as e:
                             if not getattr(self.config, "order_enabled", False):
@@ -638,7 +638,7 @@ class Download(PluginTopic):
                         ).seconds
                         retry_count += 1
                         retry_info = (
-                            f"[Retry #{retry_count}] Waited {wait_seconds}s, trying again to download ordered product"
+                            f"[Retry #{retry_count}] Waited {wait_seconds}s, checking order status again"
                             f" (retry every {wait}' for {timeout}')"
                         )
                         logger.info(not_available_info)
@@ -660,8 +660,8 @@ class Download(PluginTopic):
                         ).microseconds / 1e6
                         retry_count += 1
                         retry_info = (
-                            f"[Retry #{retry_count}] Waiting {wait_seconds}s until next download try"
-                            f" for ordered product (retry every {wait}' for {timeout}')"
+                            f"[Retry #{retry_count}] Waiting {wait_seconds}s until next order status check"
+                            f" (retry every {wait}' for {timeout}')"
                         )
                         logger.info(not_available_info)
                         # Retry-After info from Response header
@@ -682,12 +682,12 @@ class Download(PluginTopic):
                         logger.info(not_available_info)
                         raise NotAvailableError(
                             f"{product.properties['title']} is not available ({product.properties['storageStatus']})"
-                            f" and could not be downloaded, timeout reached"
+                            f" and order was not successfull, timeout reached"
                         )
                     elif datetime_now >= stop_time:
                         raise NotAvailableError(not_available_info)
 
-                return download(*args, **kwargs)
+                return order_download(*args, **kwargs)
 
             return download_and_retry
 
diff --git a/eodag/plugins/download/http.py b/eodag/plugins/download/http.py
index 88427cc2..0f7e48e9 100644
--- a/eodag/plugins/download/http.py
+++ b/eodag/plugins/download/http.py
@@ -149,7 +149,7 @@ class HTTPDownload(Download):
     def __init__(self, provider: str, config: PluginConfig) -> None:
         super(HTTPDownload, self).__init__(provider, config)
 
-    def order_download(
+    def _order(
         self,
         product: EOProduct,
         auth: Optional[AuthBase] = None,
@@ -273,7 +273,7 @@ class HTTPDownload(Download):
 
         return json_response
 
-    def order_download_status(
+    def _order_status(
         self,
         product: EOProduct,
         auth: Optional[AuthBase] = None,
@@ -566,8 +566,8 @@ class HTTPDownload(Download):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> Optional[str]:
         """Download a product using HTTP protocol.
@@ -627,13 +627,13 @@ class HTTPDownload(Download):
 
         url = product.remote_location
 
-        @self._download_retry(product, wait, timeout)
+        @self._order_download_retry(product, wait, timeout)
         def download_request(
             product: EOProduct,
             auth: AuthBase,
             progress_callback: ProgressCallback,
-            wait: int,
-            timeout: int,
+            wait: float,
+            timeout: float,
             **kwargs: Unpack[DownloadConf],
         ) -> None:
             chunks = self._stream_download(product, auth, progress_callback, **kwargs)
@@ -759,8 +759,8 @@ class HTTPDownload(Download):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> StreamResponse:
         r"""
@@ -902,6 +902,44 @@ class HTTPDownload(Download):
             else:
                 logger.error("Error while getting resource :\n%s", tb.format_exc())
 
+    def _order_request(
+        self,
+        product: EOProduct,
+        auth: Optional[AuthBase],
+    ) -> None:
+        if (
+            "orderLink" in product.properties
+            and product.properties.get("storageStatus") == OFFLINE_STATUS
+            and not product.properties.get("orderStatus")
+        ):
+            self._order(product=product, auth=auth)
+
+        if (
+            product.properties.get("orderStatusLink", None)
+            and product.properties.get("storageStatus") != ONLINE_STATUS
+        ):
+            self._order_status(product=product, auth=auth)
+
+    def order(
+        self,
+        product: EOProduct,
+        auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
+    ) -> None:
+        """
+        Order product and poll to check its status
+
+        :param product: The EO product to download
+        :param auth: (optional) authenticated object
+        :param wait: (optional) Wait time in minutes between two order status check
+        :param timeout: (optional) Maximum time in minutes before stop checking
+                        order status
+        """
+        self._order_download_retry(product, wait, timeout)(self._order_request)(
+            product, auth
+        )
+
     def _stream_download(
         self,
         product: EOProduct,
@@ -910,8 +948,9 @@ class HTTPDownload(Download):
         **kwargs: Unpack[DownloadConf],
     ) -> Iterator[Any]:
         """
-        fetches a zip file containing the assets of a given product as a stream
+        Fetches a zip file containing the assets of a given product as a stream
         and returns a generator yielding the chunks of the file
+
         :param product: product for which the assets should be downloaded
         :param auth: The configuration of a plugin of type Authentication
         :param progress_callback: A method or a callable object
@@ -928,18 +967,8 @@ class HTTPDownload(Download):
         ssl_verify = getattr(self.config, "ssl_verify", True)
 
         ordered_message = ""
-        if (
-            "orderLink" in product.properties
-            and product.properties.get("storageStatus") == OFFLINE_STATUS
-            and not product.properties.get("orderStatus")
-        ):
-            self.order_download(product=product, auth=auth)
-
-        if (
-            product.properties.get("orderStatusLink", None)
-            and product.properties.get("storageStatus") != ONLINE_STATUS
-        ):
-            self.order_download_status(product=product, auth=auth)
+        # retry handled at download level
+        self._order_request(product, auth)
 
         params = kwargs.pop("dl_url_params", None) or getattr(
             self.config, "dl_url_params", {}
@@ -1375,8 +1404,8 @@ class HTTPDownload(Download):
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         downloaded_callback: Optional[DownloadedCallback] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ):
         """
diff --git a/eodag/plugins/download/s3rest.py b/eodag/plugins/download/s3rest.py
index 1158a70e..4be91d87 100644
--- a/eodag/plugins/download/s3rest.py
+++ b/eodag/plugins/download/s3rest.py
@@ -95,8 +95,8 @@ class S3RestDownload(Download):
         product: EOProduct,
         auth: Optional[Union[AuthBase, Dict[str, str]]] = None,
         progress_callback: Optional[ProgressCallback] = None,
-        wait: int = DEFAULT_DOWNLOAD_WAIT,
-        timeout: int = DEFAULT_DOWNLOAD_TIMEOUT,
+        wait: float = DEFAULT_DOWNLOAD_WAIT,
+        timeout: float = DEFAULT_DOWNLOAD_TIMEOUT,
         **kwargs: Unpack[DownloadConf],
     ) -> Optional[str]:
         """Download method for S3 REST API.
@@ -130,9 +130,9 @@ class S3RestDownload(Download):
             and "storageStatus" in product.properties
             and product.properties["storageStatus"] != ONLINE_STATUS
         ):
-            self.http_download_plugin.order_download(product=product, auth=auth)
+            self.http_download_plugin._order(product=product, auth=auth)
 
-        @self._download_retry(product, wait, timeout)
+        @self._order_download_retry(product, wait, timeout)
         def download_request(
             product: EOProduct,
             auth: AuthBase,
@@ -142,9 +142,7 @@ class S3RestDownload(Download):
         ):
             # check order status
             if product.properties.get("orderStatusLink", None):
-                self.http_download_plugin.order_download_status(
-                    product=product, auth=auth
-                )
+                self.http_download_plugin._order_status(product=product, auth=auth)
 
             # get bucket urls
             bucket_name, prefix = get_bucket_name_and_prefix(
diff --git a/eodag/resources/providers.yml b/eodag/resources/providers.yml
index f2beb088..bac60465 100644
--- a/eodag/resources/providers.yml
+++ b/eodag/resources/providers.yml
@@ -6525,8 +6525,8 @@
         - '$.properties."spaceborne:polarization"'
       # Custom parameters (not defined in the base document referenced above)
       id:
-        - '{{"query":{{"accessService:endpointURL":{{"contains":"{id}"}}}}}}'
-        - '{$.properties."accessService:endpointURL"#replace_str(r"^.*\/(\w+)\.zip$",r"\1")}'
+        - '{{"query":{{"identifier":{{"eq":"{id}"}}}}}}'
+        - '$.properties.identifier'
       geometry:
         - '{{"intersects":{geometry#to_geojson}}}'
         - '($.geometry.`str()`.`sub(/^None$/, POLYGON((180 -90, 180 90, -180 90, -180 -90, 180 -90)))`)|($.geometry[*])'
diff --git a/eodag/rest/core.py b/eodag/rest/core.py
index 82b38181..ece8a0d3 100644
--- a/eodag/rest/core.py
+++ b/eodag/rest/core.py
@@ -327,13 +327,11 @@ def _order_and_update(
     if (
         product.properties.get("storageStatus") != ONLINE_STATUS
         and NOT_AVAILABLE in product.properties.get("orderStatusLink", "")
-        and hasattr(product.downloader, "order_download")
+        and hasattr(product.downloader, "_order")
     ):
         # first order
         logger.debug("Order product")
-        order_status_dict = product.downloader.order_download(
-            product=product, auth=auth
-        )
+        order_status_dict = product.downloader._order(product=product, auth=auth)
         query_args.update(order_status_dict or {})
 
     if (
@@ -344,11 +342,11 @@ def _order_and_update(
         product.properties["storageStatus"] = STAGING_STATUS
 
     if product.properties.get("storageStatus") == STAGING_STATUS and hasattr(
-        product.downloader, "order_download_status"
+        product.downloader, "_order_status"
     ):
         # check order status if needed
         logger.debug("Checking product order status")
-        product.downloader.order_download_status(product=product, auth=auth)
+        product.downloader._order_status(product=product, auth=auth)
 
     if product.properties.get("storageStatus") != ONLINE_STATUS:
         raise NotAvailableError("Product is not available yet")
@@ -728,11 +726,16 @@ def eodag_api_init() -> None:
             constellation: Union[str, List[str]] = ext_col.get("summaries", {}).get(
                 "constellation"
             )
+            processing_level: Union[str, List[str]] = ext_col.get("summaries", {}).get(
+                "processing:level"
+            )
             # Check if platform or constellation are lists and join them into a string if they are
             if isinstance(platform, list):
                 platform = ",".join(platform)
             if isinstance(constellation, list):
                 constellation = ",".join(constellation)
+            if isinstance(processing_level, list):
+                processing_level = ",".join(processing_level)
 
             update_fields = {
                 "title": ext_col.get("title"),
@@ -743,7 +746,7 @@ def eodag_api_init() -> None:
                 ),
                 "platform": constellation,
                 "platformSerialIdentifier": platform,
-                "processingLevel": ext_col.get("summaries", {}).get("processing:level"),
+                "processingLevel": processing_level,
                 "license": ext_col["license"],
                 "missionStartDate": ext_col["extent"]["temporal"]["interval"][0][0],
                 "missionEndDate": ext_col["extent"]["temporal"]["interval"][-1][1],
diff --git a/eodag/types/whoosh.py b/eodag/types/whoosh.py
index 04320134..2276695d 100644
--- a/eodag/types/whoosh.py
+++ b/eodag/types/whoosh.py
@@ -18,10 +18,13 @@
 from typing import List
 
 from whoosh.fields import Schema
+from whoosh.index import _DEF_INDEX_NAME, FileIndex
 from whoosh.matching import NullMatcher
 from whoosh.qparser import OrGroup, QueryParser, plugins
 from whoosh.query.positional import Phrase
 from whoosh.query.qcore import QueryError
+from whoosh.util.text import utf8encode
+from whoosh.writing import SegmentWriter
 
 
 class RobustPhrase(Phrase):
@@ -77,3 +80,126 @@ class EODAGQueryParser(QueryParser):
             phraseclass=RobustPhrase,
             group=OrGroup,
         )
+
+
+class CleanSegmentWriter(SegmentWriter):
+    """Override to clean up writer for failed document add when exceptions were absorbed
+    cf: https://github.com/whoosh-community/whoosh/pull/543
+    """
+
+    def add_document(self, **fields):
+        """Add document"""
+        self._check_state()
+        perdocwriter = self.perdocwriter
+        schema = self.schema
+        docnum = self.docnum
+        add_post = self.pool.add
+
+        docboost = self._doc_boost(fields)
+        fieldnames = sorted(
+            [name for name in fields.keys() if not name.startswith("_")]
+        )
+        self._check_fields(schema, fieldnames)
+
+        perdocwriter.start_doc(docnum)
+
+        try:
+            for fieldname in fieldnames:
+                value = fields.get(fieldname)
+                if value is None:
+                    continue
+                field = schema[fieldname]
+
+                length = 0
+                if field.indexed:
+                    # TODO: Method for adding progressive field values, ie
+                    # setting start_pos/start_char?
+                    fieldboost = self._field_boost(fields, fieldname, docboost)
+                    # Ask the field to return a list of (text, weight, vbytes)
+                    # tuples
+                    items = field.index(value)
+                    # Only store the length if the field is marked scorable
+                    scorable = field.scorable
+                    # Add the terms to the pool
+                    for tbytes, freq, weight, vbytes in items:
+                        weight *= fieldboost
+                        if scorable:
+                            length += freq
+                        add_post((fieldname, tbytes, docnum, weight, vbytes))
+
+                if field.separate_spelling():
+                    spellfield = field.spelling_fieldname(fieldname)
+                    for word in field.spellable_words(value):
+                        word = utf8encode(word)[0]
+                        add_post((spellfield, word, 0, 1, vbytes))
+
+                vformat = field.vector
+                if vformat:
+                    analyzer = field.analyzer
+                    # Call the format's word_values method to get posting values
+                    vitems = vformat.word_values(value, analyzer, mode="index")
+                    # Remove unused frequency field from the tuple
+                    vitems = sorted(
+                        (text, weight, vbytes) for text, _, weight, vbytes in vitems
+                    )
+                    perdocwriter.add_vector_items(fieldname, field, vitems)
+
+                # Allow a custom value for stored field/column
+                customval = fields.get("_stored_%s" % fieldname, value)
+
+                # Add the stored value and length for this field to the per-
+                # document writer
+                sv = customval if field.stored else None
+                perdocwriter.add_field(fieldname, field, sv, length)
+
+                column = field.column_type
+                if column and customval is not None:
+                    cv = field.to_column_value(customval)
+                    perdocwriter.add_column_value(fieldname, column, cv)
+        except Exception as ex:
+            # cancel doc
+            perdocwriter._doccount -= 1
+            perdocwriter._indoc = False
+            raise ex
+
+        perdocwriter.finish_doc()
+        self._added = True
+        self.docnum += 1
+
+
+class CleanFileIndex(FileIndex):
+    """Override to call CleanSegmentWriter"""
+
+    def writer(self, procs=1, **kwargs):
+        """file index writer"""
+        if procs > 1:
+            from whoosh.multiproc import MpWriter
+
+            return MpWriter(self, procs=procs, **kwargs)
+        else:
+            return CleanSegmentWriter(self, **kwargs)
+
+
+def create_in(dirname, schema, indexname=None):
+    """
+    Override to call the CleanFileIndex.
+
+    Convenience function to create an index in a directory. Takes care of
+    creating a FileStorage object for you.
+
+    :param dirname: the path string of the directory in which to create the
+        index.
+    :param schema: a :class:`whoosh.fields.Schema` object describing the
+        index's fields.
+    :param indexname: the name of the index to create; you only need to specify
+        this if you are creating multiple indexes within the same storage
+        object.
+    :returns: :class:`Index`
+    """
+
+    from whoosh.filedb.filestore import FileStorage
+
+    if not indexname:
+        indexname = _DEF_INDEX_NAME
+    storage = FileStorage(dirname)
+    return CleanFileIndex.create(storage, schema, indexname)
diff --git a/eodag/utils/__init__.py b/eodag/utils/__init__.py
index 2ee0c532..23f215aa 100644
--- a/eodag/utils/__init__.py
+++ b/eodag/utils/__init__.py
@@ -126,8 +126,8 @@ REQ_RETRY_BACKOFF_FACTOR = 2
 REQ_RETRY_STATUS_FORCELIST = [401, 429, 500, 502, 503, 504]
 
 # default wait times in minutes
-DEFAULT_DOWNLOAD_WAIT = 2  # in minutes
-DEFAULT_DOWNLOAD_TIMEOUT = 20  # in minutes
+DEFAULT_DOWNLOAD_WAIT = 0.2  # in minutes
+DEFAULT_DOWNLOAD_TIMEOUT = 10  # in minutes
 
 JSONPATH_MATCH = re.compile(r"^[\{\(]*\$(\..*)*$")
 WORKABLE_JSONPATH_MATCH = re.compile(r"^\$(\.[a-zA-Z0-9-_:\.\[\]\"\(\)=\?\*]+)*$")
@@ -143,6 +143,13 @@ DEFAULT_MAX_ITEMS_PER_PAGE = 50
 # default product-types start date
 DEFAULT_MISSION_START_DATE = "2015-01-01T00:00:00Z"
 
+# update missing mimetypes
+mimetypes.add_type("text/xml", ".xsd")
+mimetypes.add_type("application/x-grib", ".grib")
+mimetypes.add_type("application/x-grib2", ".grib2")
+# jp2 is missing on windows
+mimetypes.add_type("image/jp2", ".jp2")
+
 
 def _deprecated(reason: str = "", version: Optional[str] = None) -> Callable[..., Any]:
     """Simple decorator to mark functions/methods/classes as deprecated.
@@ -1412,17 +1419,32 @@ class StreamResponse:
 
 
 def guess_file_type(file: str) -> Optional[str]:
-    """guess the mime type of a file or URL based on its extension"""
-    mimetypes.add_type("text/xml", ".xsd")
-    mimetypes.add_type("application/x-grib", ".grib")
+    """Guess the mime type of a file or URL based on its extension,
+    using eodag extended mimetypes definition
+
+    >>> guess_file_type('foo.tiff')
+    'image/tiff'
+    >>> guess_file_type('foo.grib')
+    'application/x-grib'
+
+    :param file: file url or path
+    :returns: guessed mime type
+    """
     mime_type, _ = mimetypes.guess_type(file, False)
     return mime_type
 
 
 def guess_extension(type: str) -> Optional[str]:
-    """guess extension from mime type"""
-    mimetypes.add_type("text/xml", ".xsd")
-    mimetypes.add_type("application/x-grib", ".grib")
+    """Guess extension from mime type, using eodag extended mimetypes definition
+
+    >>> guess_extension('image/tiff')
+    '.tiff'
+    >>> guess_extension('application/x-grib')
+    '.grib'
+
+    :param type: mime type
+    :returns: guessed file extension
+    """
     return mimetypes.guess_extension(type, strict=False)
 
 


Test patch:
diff --git a/tests/units/test_download_plugins.py b/tests/units/test_download_plugins.py
index 603b5e42..4bf19d25 100644
--- a/tests/units/test_download_plugins.py
+++ b/tests/units/test_download_plugins.py
@@ -1188,7 +1188,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
     @mock.patch("eodag.plugins.download.http.requests.request", autospec=True)
     def test_plugins_download_http_order_get(self, mock_request):
-        """HTTPDownload.order_download() must request using orderLink and GET protocol"""
+        """HTTPDownload._order() must request using orderLink and GET protocol"""
         plugin = self.get_download_plugin(self.product)
         self.product.properties["downloadLink"] = "https://peps.cnes.fr/dummy"
         self.product.properties["orderLink"] = "http://somewhere/order"
@@ -1202,7 +1202,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
             auth_plugin.config.credentials = {"username": "foo", "password": "bar"}
             auth = auth_plugin.authenticate()
 
-            plugin.order_download(self.product, auth=auth)
+            plugin._order(self.product, auth=auth)
 
             mock_request.assert_called_once_with(
                 method="GET",
@@ -1220,7 +1220,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
     @mock.patch("eodag.plugins.download.http.requests.request", autospec=True)
     def test_plugins_download_http_order_get_raises_if_request_500(self, mock_request):
-        """HTTPDownload.order_download() must raise an error if request to backend
+        """HTTPDownload._order() must raise an error if request to backend
         provider failed"""
 
         # Configure mock to raise an error
@@ -1236,7 +1236,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
         # Verify that a DownloadError is raised
         with self.assertRaises(DownloadError) as context:
-            plugin.order_download(self.product, auth=auth)
+            plugin._order(self.product, auth=auth)
         self.assertIn("could not be ordered", str(context.exception))
 
         mock_request.assert_called_once_with(
@@ -1267,7 +1267,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
         # Test the function, expecting ValidationError to be raised
         with self.assertRaises(ValidationError) as context:
-            plugin.order_download(self.product, auth=auth)
+            plugin._order(self.product, auth=auth)
         self.assertIn("could not be ordered", str(context.exception))
 
         mock_request.assert_called_once_with(
@@ -1281,7 +1281,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
     @mock.patch("eodag.plugins.download.http.requests.request", autospec=True)
     def test_plugins_download_http_order_post(self, mock_request):
-        """HTTPDownload.order_download() must request using orderLink and POST protocol"""
+        """HTTPDownload._order() must request using orderLink and POST protocol"""
         plugin = self.get_download_plugin(self.product)
         self.product.properties["downloadLink"] = "https://peps.cnes.fr/dummy"
         self.product.properties["storageStatus"] = OFFLINE_STATUS
@@ -1293,7 +1293,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
         # orderLink without query query args
         self.product.properties["orderLink"] = "http://somewhere/order"
-        plugin.order_download(self.product, auth=auth)
+        plugin._order(self.product, auth=auth)
         mock_request.assert_called_once_with(
             method="POST",
             url=self.product.properties["orderLink"],
@@ -1305,7 +1305,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
         # orderLink with query query args
         mock_request.reset_mock()
         self.product.properties["orderLink"] = "http://somewhere/order?foo=bar"
-        plugin.order_download(self.product, auth=auth)
+        plugin._order(self.product, auth=auth)
         mock_request.assert_called_once_with(
             method="POST",
             url="http://somewhere/order",
@@ -1321,7 +1321,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
         self.product.properties[
             "orderLink"
         ] = 'http://somewhere/order?{"location": "dataset_id=lorem&data_version=202211", "cacheable": "true"}'
-        plugin.order_download(self.product, auth=auth)
+        plugin._order(self.product, auth=auth)
         mock_request.assert_called_once_with(
             method="POST",
             url="http://somewhere/order",
@@ -1336,7 +1336,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
         )
 
     def test_plugins_download_http_order_status(self):
-        """HTTPDownload.order_download_status() must request status using orderStatusLink"""
+        """HTTPDownload._order_status() must request status using orderStatusLink"""
         plugin = self.get_download_plugin(self.product)
         plugin.config.order_status = {
             "metadata_mapping": {
@@ -1363,7 +1363,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
             )
 
             with self.assertRaises(DownloadError):
-                plugin.order_download_status(self.product, auth=auth)
+                plugin._order_status(self.product, auth=auth)
 
             self.assertIn(
                 list(USER_AGENT.items())[0], responses.calls[0].request.headers.items()
@@ -1376,7 +1376,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
     def test_plugins_download_http_order_status_get_raises_if_request_500(
         self, mock_request
     ):
-        """HTTPDownload.order_download() must raise an error if request to backend
+        """HTTPDownload._order() must raise an error if request to backend
         provider failed"""
 
         # Configure mock to raise an error
@@ -1393,7 +1393,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
         # Verify that a DownloadError is raised
         with self.assertRaises(DownloadError) as context:
-            plugin.order_download_status(self.product, auth=auth)
+            plugin._order_status(self.product, auth=auth)
         self.assertIn("order status could not be checked", str(context.exception))
 
         mock_request.assert_called_once_with(
@@ -1428,7 +1428,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
 
         # Test the function, expecting ValidationError to be raised
         with self.assertRaises(ValidationError) as context:
-            plugin.order_download_status(self.product, auth=auth)
+            plugin._order_status(self.product, auth=auth)
         self.assertIn("order status could not be checked", str(context.exception))
 
         mock_request.assert_called_once_with(
@@ -1442,7 +1442,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
         )
 
     def test_plugins_download_http_order_status_search_again(self):
-        """HTTPDownload.order_download_status() must search again after success if needed"""
+        """HTTPDownload._order_status() must search again after success if needed"""
         plugin = self.get_download_plugin(self.product)
         plugin.config.order_status = {
             "metadata_mapping": {"status": "$.json.status"},
@@ -1484,7 +1484,7 @@ class TestDownloadPluginHttp(BaseDownloadPluginTest):
                 ),
             )
 
-            plugin.order_download_status(self.product, auth=auth)
+            plugin._order_status(self.product, auth=auth)
 
             self.assertEqual(
                 self.product.properties["downloadLink"], "http://new-download-link"
@@ -2020,12 +2020,8 @@ class TestDownloadPluginS3Rest(BaseDownloadPluginTest):
             productType="S2_MSI_L1C",
         )
 
-    @mock.patch(
-        "eodag.plugins.download.http.HTTPDownload.order_download_status", autospec=True
-    )
-    @mock.patch(
-        "eodag.plugins.download.http.HTTPDownload.order_download", autospec=True
-    )
+    @mock.patch("eodag.plugins.download.http.HTTPDownload._order_status", autospec=True)
+    @mock.patch("eodag.plugins.download.http.HTTPDownload._order", autospec=True)
     def test_plugins_download_s3rest_online(self, mock_order, mock_order_status):
         """S3RestDownload.download() must create outputfiles"""
 
@@ -2092,12 +2088,8 @@ class TestDownloadPluginS3Rest(BaseDownloadPluginTest):
         mock_order.assert_not_called()
         mock_order_status.assert_not_called()
 
-    @mock.patch(
-        "eodag.plugins.download.http.HTTPDownload.order_download_status", autospec=True
-    )
-    @mock.patch(
-        "eodag.plugins.download.http.HTTPDownload.order_download", autospec=True
-    )
+    @mock.patch("eodag.plugins.download.http.HTTPDownload._order_status", autospec=True)
+    @mock.patch("eodag.plugins.download.http.HTTPDownload._order", autospec=True)
     def test_plugins_download_s3rest_offline(self, mock_order, mock_order_status):
         """S3RestDownload.download() must order offline products"""