Skip to content

Api

Main module with dekube-geolake API endpoints defined

download_request_result(request, request_id) async

Download result of the request

Source code in api/app/main.py
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
@app.get("/download/{request_id}", tags=[tags.REQUEST])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /download/{request_id}"},
)
# @requires([scopes.AUTHENTICATED]) # TODO: mange download auth in the web component
async def download_request_result(
    request: Request,
    request_id: int,
):
    """Download result of the request"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /download/{request_id}"}
    )
    try:
        return file_handler.download_request_result(request_id=request_id)
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err
    except FileNotFoundError as err:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="File was not found!"
        ) from err

estimate(request, dataset_id, product_id, query, unit=None) async

Estimate the resulting size of the query

Source code in api/app/main.py
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
@app.post("/datasets/{dataset_id}/{product_id}/estimate", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "POST /datasets/{dataset_id}/{product_id}/estimate"},
)
async def estimate(
    request: Request,
    dataset_id: str,
    product_id: str,
    query: GeoQuery,
    unit: str = None,
):
    """Estimate the resulting size of the query"""
    app.state.api_http_requests_total.inc(
        {"route": "POST /datasets/{dataset_id}/{product_id}/estimate"}
    )
    try:
        return dataset_handler.estimate(
            dataset_id=dataset_id,
            product_id=product_id,
            query=query,
            unit=unit,
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

geolake_info() async

Return current version of the GeoLake API

Source code in api/app/main.py
109
110
111
112
@app.get("/", tags=[tags.BASIC])
async def geolake_info():
    """Return current version of the GeoLake API"""
    return f"GeoLake API {__version__}"

get_datasets(request) async

List all products eligible for a user defined by user_token

Source code in api/app/main.py
115
116
117
118
119
120
121
122
123
124
125
126
127
@app.get("/datasets", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds, labels={"route": "GET /datasets"}
)
async def get_datasets(request: Request):
    """List all products eligible for a user defined by user_token"""
    app.state.api_http_requests_total.inc({"route": "GET /datasets"})
    try:
        return dataset_handler.get_datasets(
            user_roles_names=request.auth.scopes
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_first_product_details(request, dataset_id) async

Get details for the 1st product of the dataset

Source code in api/app/main.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@app.get("/datasets/{dataset_id}", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /datasets/{dataset_id}"},
)
async def get_first_product_details(
    request: Request,
    dataset_id: str,
):
    """Get details for the 1st product of the dataset"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /datasets/{dataset_id}"}
    )
    try:
        return dataset_handler.get_product_details(
            user_roles_names=request.auth.scopes,
            dataset_id=dataset_id,
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_metadata(request, dataset_id, product_id) async

Get metadata of the given product

Source code in api/app/main.py
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
@app.get("/datasets/{dataset_id}/{product_id}/metadata", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /datasets/{dataset_id}/{product_id}/metadata"},
)
async def get_metadata(
    request: Request,
    dataset_id: str,
    product_id: str,
):
    """Get metadata of the given product"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /datasets/{dataset_id}/{product_id}/metadata"}
    )
    try:
        return dataset_handler.get_metadata(
            dataset_id=dataset_id, product_id=product_id
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_product_details(request, dataset_id, product_id) async

Get details for the requested product if user is authorized

Source code in api/app/main.py
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
@app.get("/datasets/{dataset_id}/{product_id}", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /datasets/{dataset_id}/{product_id}"},
)
async def get_product_details(
    request: Request,
    dataset_id: str,
    product_id: str,
):
    """Get details for the requested product if user is authorized"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /datasets/{dataset_id}/{product_id}"}
    )
    try:
        return dataset_handler.get_product_details(
            user_roles_names=request.auth.scopes,
            dataset_id=dataset_id,
            product_id=product_id,
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_request_resulting_size(request, request_id) async

Get size of the file being the result of the request

Source code in api/app/main.py
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
@app.get("/requests/{request_id}/size", tags=[tags.REQUEST])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /requests/{request_id}/size"},
)
@requires([scopes.AUTHENTICATED])
async def get_request_resulting_size(
    request: Request,
    request_id: int,
):
    """Get size of the file being the result of the request"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /requests/{request_id}/size"}
    )
    try:
        return request_handler.get_request_resulting_size(
            request_id=request_id
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_request_status(request, request_id) async

Get status of the request without authentication

Source code in api/app/main.py
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
@app.get("/requests/{request_id}/status", tags=[tags.REQUEST])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /requests/{request_id}/status"},
)
@requires([scopes.AUTHENTICATED])
async def get_request_status(
    request: Request,
    request_id: int,
):
    """Get status of the request without authentication"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /requests/{request_id}/status"}
    )
    try:
        return request_handler.get_request_status(
            user_id=request.user.id, request_id=request_id
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_request_uri(request, request_id) async

Get download URI for the request

Source code in api/app/main.py
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
@app.get("/requests/{request_id}/uri", tags=[tags.REQUEST])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "GET /requests/{request_id}/uri"},
)
@requires([scopes.AUTHENTICATED])
async def get_request_uri(
    request: Request,
    request_id: int,
):
    """Get download URI for the request"""
    app.state.api_http_requests_total.inc(
        {"route": "GET /requests/{request_id}/uri"}
    )
    try:
        return request_handler.get_request_uri(request_id=request_id)
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

get_requests(request) async

Get all requests for the user

Source code in api/app/main.py
367
368
369
370
371
372
373
374
375
376
377
378
379
380
@app.get("/requests", tags=[tags.REQUEST])
@timer(
    app.state.api_request_duration_seconds, labels={"route": "GET /requests"}
)
@requires([scopes.AUTHENTICATED])
async def get_requests(
    request: Request,
):
    """Get all requests for the user"""
    app.state.api_http_requests_total.inc({"route": "GET /requests"})
    try:
        return request_handler.get_requests(request.user.id)
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

query(request, dataset_id, product_id, query) async

Schedule the job of data retrieve

Source code in api/app/main.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
@app.post("/datasets/{dataset_id}/{product_id}/execute", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "POST /datasets/{dataset_id}/{product_id}/execute"},
)
@requires([scopes.AUTHENTICATED])
async def query(
    request: Request,
    dataset_id: str,
    product_id: str,
    query: GeoQuery,
):
    """Schedule the job of data retrieve"""
    app.state.api_http_requests_total.inc(
        {"route": "POST /datasets/{dataset_id}/{product_id}/execute"}
    )
    try:
        return dataset_handler.async_query(
            user_id=request.user.id,
            dataset_id=dataset_id,
            product_id=product_id,
            query=query,
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

workflow(request, tasks) async

Schedule the job of workflow processing

Source code in api/app/main.py
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
@app.post("/datasets/workflow", tags=[tags.DATASET])
@timer(
    app.state.api_request_duration_seconds,
    labels={"route": "POST /datasets/workflow"},
)
@requires([scopes.AUTHENTICATED])
async def workflow(
    request: Request,
    tasks: Workflow,
):
    """Schedule the job of workflow processing"""
    app.state.api_http_requests_total.inc({"route": "POST /datasets/workflow"})
    try:
        return dataset_handler.run_workflow(
            user_id=request.user.id,
            workflow=tasks,
        )
    except exc.BaseGeoLakeException as err:
        raise err.wrap_around_http_exception() from err

Modules realizing logic for dataset-related endpoints

async_query(user_id, dataset_id, product_id, query)

Realize the logic for the endpoint:

POST /datasets/{dataset_id}/{product_id}/execute

Query the data and return the ID of the request.

Parameters

user_id : str ID of the user executing the query dataset_id : str ID of the dataset product_id : str ID of the product query : GeoQuery Query to perform

Returns

request_id : int ID of the request

Raises

MaximumAllowedSizeExceededError if the allowed size is below the estimated one EmptyDatasetError if estimated size is zero

Source code in api/app/endpoint_handlers/dataset.py
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
@log_execution_time(log)
@assert_product_exists
def async_query(
    user_id: str,
    dataset_id: str,
    product_id: str,
    query: GeoQuery,
):
    """Realize the logic for the endpoint:

    `POST /datasets/{dataset_id}/{product_id}/execute`

    Query the data and return the ID of the request.

    Parameters
    ----------
    user_id : str
        ID of the user executing the query
    dataset_id : str
        ID of the dataset
    product_id : str
        ID of the product
    query : GeoQuery
        Query to perform

    Returns
    -------
    request_id : int
        ID of the request

    Raises
    -------
    MaximumAllowedSizeExceededError
        if the allowed size is below the estimated one
    EmptyDatasetError
        if estimated size is zero

    """
    log.debug("geoquery: %s", query)
    if _is_etimate_enabled(dataset_id, product_id):
        estimated_size = estimate(dataset_id, product_id, query, "GB").get("value")
        allowed_size = data_store.product_metadata(dataset_id, product_id).get(
            "maximum_query_size_gb", DEFAULT_MAX_REQUEST_SIZE_GB
        )
        if estimated_size > allowed_size:
            raise exc.MaximumAllowedSizeExceededError(
                dataset_id=dataset_id,
                product_id=product_id,
                estimated_size_gb=estimated_size,
                allowed_size_gb=allowed_size,
            )
        if estimated_size == 0.0:
            raise exc.EmptyDatasetError(
                dataset_id=dataset_id, product_id=product_id
            )
    broker_conn = pika.BlockingConnection(
        pika.ConnectionParameters(
            host=os.getenv("BROKER_SERVICE_HOST", "broker")
        )
    )
    broker_channel = broker_conn.channel()

    request_id = DBManager().create_request(
        user_id=user_id,
        dataset=dataset_id,
        product=product_id,
        query=json.dumps(query.model_dump_original()),
    )

    # TODO: find a separator; for the moment use "\"
    message = MESSAGE_SEPARATOR.join(
        [str(request_id), "query", dataset_id, product_id, query.json()]
    )

    broker_channel.basic_publish(
        exchange="",
        routing_key="query_queue",
        body=message,
        properties=pika.BasicProperties(
            delivery_mode=2,  # make message persistent
        ),
    )
    broker_conn.close()
    return request_id

estimate(dataset_id, product_id, query, unit=None)

Realize the logic for the nedpoint:

POST /datasets/{dataset_id}/{product_id}/estimate

Estimate the size of the resulting data. No authentication is needed for estimation query.

Parameters

dataset_id : str ID of the dataset product_id : str ID of the product query : GeoQuery Query to perform unit : str One of unit [bytes, kB, MB, GB] to present the result. If None, unit will be inferred.

Returns

size_details : dict Estimated size of the query in the form: python { "value": val, "units": units }

Source code in api/app/endpoint_handlers/dataset.py
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
@log_execution_time(log)
@assert_product_exists
def estimate(
    dataset_id: str,
    product_id: str,
    query: GeoQuery,
    unit: Optional[str] = None,
):
    """Realize the logic for the nedpoint:

    `POST /datasets/{dataset_id}/{product_id}/estimate`

    Estimate the size of the resulting data.
    No authentication is needed for estimation query.

    Parameters
    ----------
    dataset_id : str
        ID of the dataset
    product_id : str
        ID of the product
    query : GeoQuery
        Query to perform
    unit : str
        One of unit [bytes, kB, MB, GB] to present the result. If `None`,
        unit will be inferred.

    Returns
    -------
    size_details : dict
        Estimated size of  the query in the form:
        ```python
        {
            "value": val,
            "units": units
        }
        ```
    """
    query_bytes_estimation = data_store.estimate(dataset_id, product_id, query)
    return make_bytes_readable_dict(
        size_bytes=query_bytes_estimation, units=unit
    )

get_datasets(user_roles_names)

Realize the logic for the endpoint:

GET /datasets

Get datasets names, their metadata and products names (if eligible for a user). If no eligible products are found for a dataset, it is not included.

Parameters

user_roles_names : list of str List of user's roles

Returns

datasets : list of dict A list of dictionaries with datasets information (including metadata and eligible products lists)

Raises

MissingKeyInCatalogEntryError If the dataset catalog entry does not contain the required key

Source code in api/app/endpoint_handlers/dataset.py
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
@log_execution_time(log)
def get_datasets(user_roles_names: list[str]) -> list[dict]:
    """Realize the logic for the endpoint:

    `GET /datasets`

    Get datasets names, their metadata and products names (if eligible for a user).
    If no eligible products are found for a dataset, it is not included.

    Parameters
    ----------
    user_roles_names : list of str
        List of user's roles

    Returns
    -------
    datasets : list of dict
        A list of dictionaries with datasets information (including metadata and
        eligible products lists)

    Raises
    -------
    MissingKeyInCatalogEntryError
        If the dataset catalog entry does not contain the required key
    """
    log.debug(
        "getting all eligible products for datasets...",
    )
    datasets = []
    for dataset_id in data_store.dataset_list():
        log.debug(
            "getting info and eligible products for `%s`",
            dataset_id,
        )
        dataset_info = data_store.dataset_info(dataset_id=dataset_id)
        try:
            eligible_prods = {
                prod_name: prod_info
                for prod_name, prod_info in dataset_info["products"].items()
                if is_role_eligible_for_product(
                    product_role_name=prod_info.get("role"),
                    user_roles_names=user_roles_names,
                )
            }
        except KeyError as err:
            log.error(
                "dataset `%s` does not have products defined",
                dataset_id,
                exc_info=True,
            )
            raise exc.MissingKeyInCatalogEntryError(
                key="products", dataset=dataset_id
            ) from err
        else:
            if len(eligible_prods) == 0:
                log.debug(
                    "no eligible products for dataset `%s` for the role `%s`."
                    " dataset skipped",
                    dataset_id,
                    user_roles_names,
                )
            else:
                dataset_info["products"] = eligible_prods
                datasets.append(dataset_info)
    return datasets

get_metadata(dataset_id, product_id)

Realize the logic for the endpoint:

GET /datasets/{dataset_id}/{product_id}/metadata

Get metadata for the product.

Parameters

dataset_id : str ID of the dataset product_id : str ID of the product

Source code in api/app/endpoint_handlers/dataset.py
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
@log_execution_time(log)
@assert_product_exists
def get_metadata(dataset_id: str, product_id: str):
    """Realize the logic for the endpoint:

    `GET /datasets/{dataset_id}/{product_id}/metadata`

    Get metadata for the product.

    Parameters
    ----------
    dataset_id : str
        ID of the dataset
    product_id : str
        ID of the product
    """
    log.debug(
        "getting metadata for '{dataset_id}.{product_id}'",
    )
    return data_store.product_metadata(dataset_id, product_id)

get_product_details(user_roles_names, dataset_id, product_id=None)

Realize the logic for the endpoint:

GET /datasets/{dataset_id}/{product_id}

Get details for the given product indicated by dataset_id and product_id arguments.

Parameters

user_roles_names : list of str List of user's roles dataset_id : str ID of the dataset product_id : optional, str ID of the product. If None the 1st product will be considered

Returns

details : dict Details for the given product

Raises

AuthorizationFailed If user is not authorized for the resources

Source code in api/app/endpoint_handlers/dataset.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
@log_execution_time(log)
@assert_product_exists
def get_product_details(
    user_roles_names: list[str],
    dataset_id: str,
    product_id: Optional[str] = None,
) -> dict:
    """Realize the logic for the endpoint:

    `GET /datasets/{dataset_id}/{product_id}`

    Get details for the given product indicated by `dataset_id`
    and `product_id` arguments.

    Parameters
    ----------
    user_roles_names : list of str
        List of user's roles
    dataset_id : str
        ID of the dataset
    product_id : optional, str
        ID of the product. If `None` the 1st product will be considered

    Returns
    -------
    details : dict
        Details for the given product

    Raises
    -------
    AuthorizationFailed
        If user is not authorized for the resources
    """
    log.debug(
        "getting details for eligible products of `%s`",
        dataset_id,
    )
    try:
        if product_id:
            return data_store.product_details(
                dataset_id=dataset_id,
                product_id=product_id,
                role=user_roles_names,
                use_cache=True,
            )
        else:
            return data_store.first_eligible_product_details(
                dataset_id=dataset_id, role=user_roles_names, use_cache=True
            )
    except datastore_exception.UnauthorizedError as err:
        raise exc.AuthorizationFailed from err

run_workflow(user_id, workflow)

Realize the logic for the endpoint:

POST /datasets/workflow

Schedule the workflow and return the ID of the request.

Parameters

user_id : str ID of the user executing the query workflow : Workflow Workflow to perform

Returns

request_id : int ID of the request

Raises

MaximumAllowedSizeExceededError if the allowed size is below the estimated one EmptyDatasetError if estimated size is zero

Source code in api/app/endpoint_handlers/dataset.py
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
@log_execution_time(log)
def run_workflow(
    user_id: str,
    workflow: Workflow,
):
    """Realize the logic for the endpoint:

    `POST /datasets/workflow`

    Schedule the workflow and return the ID of the request.

    Parameters
    ----------
    user_id : str
        ID of the user executing the query
    workflow : Workflow
        Workflow to perform

    Returns
    -------
    request_id : int
        ID of the request

    Raises
    -------
    MaximumAllowedSizeExceededError
        if the allowed size is below the estimated one
    EmptyDatasetError
        if estimated size is zero

    """
    log.debug("geoquery: %s", workflow)
    broker_conn = pika.BlockingConnection(
        pika.ConnectionParameters(
            host=os.getenv("BROKER_SERVICE_HOST", "broker")
        )
    )
    broker_channel = broker_conn.channel()
    request_id = DBManager().create_request(
        user_id=user_id,
        dataset=workflow.dataset_id,
        product=workflow.product_id,
        query=workflow.json(),
    )

    # TODO: find a separator; for the moment use "\"
    message = MESSAGE_SEPARATOR.join(
        [str(request_id), "workflow", workflow.json()]
    )

    broker_channel.basic_publish(
        exchange="",
        routing_key="query_queue",
        body=message,
        properties=pika.BasicProperties(
            delivery_mode=2,  # make message persistent
        ),
    )
    broker_conn.close()
    return request_id

sync_query(user_id, dataset_id, product_id, query)

Realize the logic for the endpoint:

POST /datasets/{dataset_id}/{product_id}/execute

Query the data and return the result of the request.

Parameters

user_id : str ID of the user executing the query dataset_id : str ID of the dataset product_id : str ID of the product query : GeoQuery Query to perform

Returns

request_id : int ID of the request

Raises

MaximumAllowedSizeExceededError if the allowed size is below the estimated one EmptyDatasetError if estimated size is zero

Source code in api/app/endpoint_handlers/dataset.py
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
@log_execution_time(log)
@assert_product_exists
def sync_query(
    user_id: str,
    dataset_id: str,
    product_id: str,
    query: GeoQuery,
):
    """Realize the logic for the endpoint:

    `POST /datasets/{dataset_id}/{product_id}/execute`

    Query the data and return the result of the request.

    Parameters
    ----------
    user_id : str
        ID of the user executing the query
    dataset_id : str
        ID of the dataset
    product_id : str
        ID of the product
    query : GeoQuery
        Query to perform

    Returns
    -------
    request_id : int
        ID of the request

    Raises
    -------
    MaximumAllowedSizeExceededError
        if the allowed size is below the estimated one
    EmptyDatasetError
        if estimated size is zero

    """

    import time
    request_id = async_query(user_id, dataset_id, product_id, query)
    status, _ = DBManager().get_request_status_and_reason(request_id)
    log.debug("sync query: status: %s", status)
    while status in (RequestStatus.RUNNING, RequestStatus.QUEUED, 
                     RequestStatus.PENDING):
        time.sleep(1)
        status, _ = DBManager().get_request_status_and_reason(request_id)
        log.debug("sync query: status: %s", status)

    if status is RequestStatus.DONE:
        download_details = DBManager().get_download_details_for_request_id(
                request_id
        )
        return FileResponse(
            path=download_details.location_path,
            filename=download_details.location_path.split(os.sep)[-1],
        )
    raise exc.ProductRetrievingError(
        dataset_id=dataset_id, 
        product_id=product_id,
        status=status.name)

Module with functions to handle file related endpoints

download_request_result(request_id)

Realize the logic for the endpoint:

GET /download/{request_id}

Get location path of the file being the result of the request with request_id.

Parameters

request_id : int ID of the request

Returns

path : str The location of the resulting file

Raises

RequestNotYetAccomplished If geolake request was not yet finished FileNotFoundError If file was not found

Source code in api/app/endpoint_handlers/file.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@log_execution_time(log)
def download_request_result(request_id: int):
    """Realize the logic for the endpoint:

    `GET /download/{request_id}`

    Get location path of the file being the result of
    the request with `request_id`.

    Parameters
    ----------
    request_id : int
        ID of the request

    Returns
    -------
    path : str
        The location of the resulting file

    Raises
    -------
    RequestNotYetAccomplished
        If geolake request was not yet finished
    FileNotFoundError
        If file was not found
    """
    log.debug(
        "preparing downloads for request id: %s",
        request_id,
    )
    (
        request_status,
        _,
    ) = DBManager().get_request_status_and_reason(request_id=request_id)
    if request_status is not RequestStatus.DONE:
        log.debug(
            "request with id: '%s' does not exist or it is not finished yet!",
            request_id,
        )
        raise exc.RequestNotYetAccomplished(request_id=request_id)
    download_details = DBManager().get_download_details_for_request(
        request_id=request_id
    )
    if not os.path.exists(download_details.location_path):
        log.error(
            "file '%s' does not exists!",
            download_details.location_path,
        )
        raise FileNotFoundError
    return FileResponse(
        path=download_details.location_path,
        filename=download_details.location_path.split(os.sep)[-1],
    )

Modules with functions realizing logic for requests-related endpoints

get_request_resulting_size(request_id)

Realize the logic for the endpoint:

GET /requests/{request_id}/size

Get size of the file being the result of the request with request_id

Parameters

request_id : int ID of the request

Returns

size : int Size in bytes

Raises

RequestNotFound If the request was not found

Source code in api/app/endpoint_handlers/request.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@log_execution_time(log)
def get_request_resulting_size(request_id: int):
    """Realize the logic for the endpoint:

    `GET /requests/{request_id}/size`

    Get size of the file being the result of the request with `request_id`

    Parameters
    ----------
    request_id : int
        ID of the request

    Returns
    -------
    size : int
        Size in bytes

    Raises
    -------
    RequestNotFound
        If the request was not found
    """
    if request := DBManager().get_request_details(request_id):
        size = request.download.size_bytes
        if not size or size == 0:
            raise exc.EmptyDatasetError(dataset_id=request.dataset, 
                                        product_id=request.product)
        return size
    log.info(
        "request with id '%s' could not be found",
        request_id,
    )
    raise exc.RequestNotFound(request_id=request_id)

get_request_status(user_id, request_id)

Realize the logic for the endpoint:

GET /requests/{request_id}/status

Get request status and the reason of the eventual fail. The second item is None, it status is other than failed.

Parameters

user_id : str ID of the user whose request's status is about to be checed request_id : int ID of the request

Returns

status : tuple Tuple of status and fail reason.

Source code in api/app/endpoint_handlers/request.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
@log_execution_time(log)
def get_request_status(user_id: str, request_id: int):
    """Realize the logic for the endpoint:

    `GET /requests/{request_id}/status`

    Get request status and the reason of the eventual fail.
    The second item is `None`, it status is other than failed.

    Parameters
    ----------
    user_id : str
        ID of the user whose request's status is about to be checed
    request_id : int
        ID of the request

    Returns
    -------
    status : tuple
        Tuple of status and fail reason.
    """
    # NOTE: maybe verification should be added if user checks only him\her requests
    try:
        status, reason = DBManager().get_request_status_and_reason(request_id)
    except IndexError as err:
        log.error(
            "request with id: '%s' was not found!",
            request_id,
        )
        raise exc.RequestNotFound(request_id=request_id) from err
    return {"status": status.name, "fail_reason": reason}

get_request_uri(request_id)

Realize the logic for the endpoint:

GET /requests/{request_id}/uri

Get URI for the request.

Parameters

request_id : int ID of the request

Returns

uri : str URI for the download associated with the given request

Source code in api/app/endpoint_handlers/request.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
@log_execution_time(log)
def get_request_uri(request_id: int):
    """
    Realize the logic for the endpoint:

    `GET /requests/{request_id}/uri`

    Get URI for the request.

    Parameters
    ----------
    request_id : int
        ID of the request

    Returns
    -------
    uri : str
        URI for the download associated with the given request
    """
    try:
        download_details = DBManager().get_download_details_for_request_id(
            request_id
        )
    except IndexError as err:
        log.error(
            "request with id: '%s' was not found!",
            request_id,
        )
        raise exc.RequestNotFound(request_id=request_id) from err
    if download_details is None:
        (
            request_status,
            _,
        ) = DBManager().get_request_status_and_reason(request_id)
        log.info(
            "download URI not found for request id: '%s'."
            " Request status is '%s'",
            request_id,
            request_status,
        )
        raise exc.RequestStatusNotDone(
            request_id=request_id, request_status=request_status
        )
    return download_details.download_uri

get_requests(user_id)

Realize the logic for the endpoint:

GET /requests

Get details of all requests for the user.

Parameters

user_id : str ID of the user for whom requests are taken

Returns

requests : list List of all requests done by the user

Source code in api/app/endpoint_handlers/request.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@log_execution_time(log)
def get_requests(user_id: str):
    """Realize the logic for the endpoint:

    `GET /requests`

    Get details of all requests for the user.

    Parameters
    ----------
    user_id : str
        ID of the user for whom requests are taken

    Returns
    -------
    requests : list
        List of all requests done by the user
    """
    return DBManager().get_requests_for_user_id(user_id=user_id)