Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/gfwapiclient/resources/bulk_downloads/file/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Global Fishing Watch (GFW) API Python Client - Download bulk Report (URL File).

This module provides the endpoint and associated functionalities for retrieving signed
URL to download file(s) (i.e., `"DATA"`, `"README"`, or `"GEOM"`) of the previously
created bulk report. It defines the `BulkReportFileEndPoint` class, which handles the
construction and execution of API requests, and the parsing of API responses for
Download bulk Report (URL File) API endpoint.

For detailed information about the Download bulk Report (URL File) API endpoint,
please refer to the official Global Fishing Watch API documentation:

See: https://globalfishingwatch.org/our-apis/documentation#download-bulk-report-url-file

For more details on the Download bulk Report (URL File) data caveats, please refer to
the official Global Fishing Watch API documentation:

See: https://globalfishingwatch.org/our-apis/documentation#sar-fixed-infrastructure-data-caveats
"""
64 changes: 64 additions & 0 deletions src/gfwapiclient/resources/bulk_downloads/file/endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""Global Fishing Watch (GFW) API Python Client - Download bulk Report (URL File) API endpoint."""

from gfwapiclient.http.client import HTTPClient
from gfwapiclient.http.endpoints import GetEndPoint
from gfwapiclient.http.models import RequestBody
from gfwapiclient.resources.bulk_downloads.file.models.request import (
BulkReportFileParams,
)
from gfwapiclient.resources.bulk_downloads.file.models.response import (
BulkReportFileItem,
BulkReportFileResult,
)


__all__ = ["BulkReportFileEndPoint"]


class BulkReportFileEndPoint(
GetEndPoint[
BulkReportFileParams, RequestBody, BulkReportFileItem, BulkReportFileResult
],
):
"""Download bulk Report (URL File) API endpoint.

This endpoint is used to retrieve signed URL to download file(s) (i.e., `"DATA"`,
`"README"`, or `"GEOM"`) of the previously created bulk report.

For more details on the Download bulk Report (URL File) API endpoint, please refer
to the official Global Fishing Watch API documentation:

See: https://globalfishingwatch.org/our-apis/documentation#download-bulk-report-url-file

For more details on the Download bulk Report (URL File) data caveats, please refer
to the official Global Fishing Watch API documentation:

See: https://globalfishingwatch.org/our-apis/documentation#sar-fixed-infrastructure-data-caveats
"""

def __init__(
self,
*,
bulk_report_id: str,
request_params: BulkReportFileParams,
http_client: HTTPClient,
) -> None:
"""Initializes a new `BulkReportFileEndPoint`.

Args:
bulk_report_id (str):
Unique identifier (ID) of the bulk report.

request_params (BulkReportFileParams):
The request query parameters.

http_client (HTTPClient):
The HTTP client used to make the API call.
"""
super().__init__(
path=f"bulk-reports/{bulk_report_id}/download-file-url",
request_params=request_params,
result_item_class=BulkReportFileItem,
result_class=BulkReportFileResult,
http_client=http_client,
)
17 changes: 17 additions & 0 deletions src/gfwapiclient/resources/bulk_downloads/file/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"""Global Fishing Watch (GFW) API Python Client - Download bulk Report (URL File) Models.

This module defines Pydantic data models used for interacting with the Download bulk
Report (URL File) API endpoint. These models are used to represent request parameters
and response data when obtaining signed URL to download file(s) (i.e., `"DATA"`,
`"README"`, or `"GEOM"`) of the previously created bulk report.

For detailed information about the Download bulk Report (URL File) API endpoint,
please refer to the official Global Fishing Watch API documentation:

See: https://globalfishingwatch.org/our-apis/documentation#download-bulk-report-url-file

For more details on the Download bulk Report (URL File) data caveats, please refer to
the official Global Fishing Watch API documentation:

See: https://globalfishingwatch.org/our-apis/documentation#sar-fixed-infrastructure-data-caveats
"""
36 changes: 36 additions & 0 deletions src/gfwapiclient/resources/bulk_downloads/file/models/request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Global Fishing Watch (GFW) API Python Client - Download bulk Report (URL File) Request Models."""

from typing import Final, Optional

from pydantic import Field

from gfwapiclient.http.models import RequestParams
from gfwapiclient.resources.bulk_downloads.base.models.request import BulkReportFileType


__all__ = ["BulkReportFileParams"]


BULK_REPORT_FILE_PARAMS_VALIDATION_ERROR_MESSAGE: Final[str] = (
"Get bulk report file download URL request parameters validation failed."
)


class BulkReportFileParams(RequestParams):
"""Request query parameters for Download bulk Report (URL File) API endpoint.

Represents request query parameters to obtain signed URL to download the file
(i.e., `"DATA"`, `"README"`, or `"GEOM"`) of the previously created bulk report.

For more details on the Download bulk Report (URL File) API endpoint supported
request parameters, please refer to the official Global Fishing Watch API
documentation:

See: https://globalfishingwatch.org/our-apis/documentation#download-bulk-report-url-parameters-for-get-requests

Attributes:
file (Optional[BulkReportFileType]):
Type of bulk report file (i.e., `"DATA"`, `"README"`, or `"GEOM"`).
"""

file: Optional[BulkReportFileType] = Field(BulkReportFileType.DATA, alias="file")
60 changes: 60 additions & 0 deletions src/gfwapiclient/resources/bulk_downloads/file/models/response.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Global Fishing Watch (GFW) API Python Client - Download bulk Report (URL File) Response Models."""

from typing import Optional, Type

from pydantic import Field

from gfwapiclient.http.models import Result, ResultItem


__all__ = ["BulkReportFileItem", "BulkReportFileResult"]


class BulkReportFileItem(ResultItem):
"""Result item for the Download bulk Report (URL File) API endpoint.

Represents signed URL to download the file (i.e., `"DATA"`, `"README"`,
or `"GEOM"`) of the previously created bulk report.

For more details on the Download bulk Report (URL File) API endpoint supported
response bodies, please refer to the official Global Fishing Watch API
documentation:

See: https://globalfishingwatch.org/our-apis/documentation#download-bulk-report-http-response

Attributes:
url (Optional[str]):
Signed URL to download the file.
"""

url: Optional[str] = Field(None, alias="url")


class BulkReportFileResult(Result[BulkReportFileItem]):
"""Result for the Download bulk Report (URL File) API endpoint.

For more details on the Download bulk Report (URL File) API endpoint supported
response bodies, please refer to the official Global Fishing Watch API
documentation:

See: https://globalfishingwatch.org/our-apis/documentation#download-bulk-report-http-response

Attributes:
_result_item_class (Type[BulkReportFileItem]):
The model used for individual result items.

_data (BulkReportFileItem):
The bulk report file item returned in the response.
"""

_result_item_class: Type[BulkReportFileItem]
_data: BulkReportFileItem

def __init__(self, data: BulkReportFileItem) -> None:
"""Initializes a new `BulkReportFileResult`.

Args:
data (BulkReportFileItem):
The bulk report file download details.
"""
super().__init__(data=data)
3 changes: 3 additions & 0 deletions tests/fixtures/bulk_downloads/bulk_report_file_item.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"url": "https://storage.googleapis.com/api-bulk-release-us-central1/adbb9b62-5c08-4142-82e0-b2b575f3e058/data.gz"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"file": "DATA"
}
35 changes: 35 additions & 0 deletions tests/resources/bulk_downloads/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,38 @@ def mock_raw_bulk_report_list_request_params(
"bulk_downloads/bulk_report_list_request_params.json"
)
return raw_bulk_report_list_request_params


@pytest.fixture
def mock_raw_bulk_report_file_request_params(
load_json_fixture: Callable[[str], Dict[str, Any]],
) -> Dict[str, Any]:
"""Fixture for mock raw bulk report file request parameters.

Returns:
Dict[str, Any]:
Raw `BulkReportFileParams` sample data as dictionary.
"""
raw_bulk_report_file_request_params: Dict[str, Any] = load_json_fixture(
"bulk_downloads/bulk_report_file_request_params.json"
)
return raw_bulk_report_file_request_params


@pytest.fixture
def mock_raw_bulk_report_file_item(
load_json_fixture: Callable[[str], Dict[str, Any]],
) -> Dict[str, Any]:
"""Fixture for a mock raw bulk report file item.

This fixture loads sample JSON data representing a single
`BulkReportFileItem` from a fixture file.

Returns:
Dict[str, Any]:
Raw `BulkReportFileItem` sample data as a dictionary.
"""
raw_bulk_report_file_item: Dict[str, Any] = load_json_fixture(
"bulk_downloads/bulk_report_file_item.json"
)
return raw_bulk_report_file_item
1 change: 1 addition & 0 deletions tests/resources/bulk_downloads/file/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for `gfwapiclient.resources.bulk_downloads.file`."""
1 change: 1 addition & 0 deletions tests/resources/bulk_downloads/file/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for `gfwapiclient.resources.bulk_downloads.file.models`."""
18 changes: 18 additions & 0 deletions tests/resources/bulk_downloads/file/models/test_request_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""Tests for `gfwapiclient.resources.bulk_downloads.file.models.request`."""

from typing import Any, Dict

from gfwapiclient.resources.bulk_downloads.file.models.request import (
BulkReportFileParams,
)


def test_bulk_report_file_request_params_serializes_all_fields(
mock_raw_bulk_report_file_request_params: Dict[str, Any],
) -> None:
"""Test that `BulkReportFileParams` serializes all fields correctly."""
bulk_report_file_request_params: BulkReportFileParams = BulkReportFileParams(
**mock_raw_bulk_report_file_request_params
)
assert bulk_report_file_request_params.file is not None
assert bulk_report_file_request_params.to_query_params() is not None
27 changes: 27 additions & 0 deletions tests/resources/bulk_downloads/file/models/test_response_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Tests for `gfwapiclient.resources.bulk_downloads.file.models.response`."""

from typing import Any, Dict, cast

from gfwapiclient.resources.bulk_downloads.file.models.response import (
BulkReportFileItem,
BulkReportFileResult,
)


def test_bulk_report_detail_item_deserializes_all_fields(
mock_raw_bulk_report_file_item: Dict[str, Any],
) -> None:
"""Test that `BulkReportFileItem` deserializes all fields correctly."""
bulk_report_file_item: BulkReportFileItem = BulkReportFileItem(
**mock_raw_bulk_report_file_item
)
assert bulk_report_file_item.url is not None


def test_bulk_report_detail_result_deserializes_all_fields(
mock_raw_bulk_report_file_item: Dict[str, Any],
) -> None:
"""Test that `BulkReportFileResult` deserializes all fields correctly."""
data: BulkReportFileItem = BulkReportFileItem(**mock_raw_bulk_report_file_item)
result = BulkReportFileResult(data=data)
assert cast(BulkReportFileItem, result.data()) == data
72 changes: 72 additions & 0 deletions tests/resources/bulk_downloads/file/test_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Tests for `gfwapiclient.resources.bulk_downloads.file.endpoints`."""

from typing import Any, Dict, Final, cast

import httpx
import pytest
import respx

from gfwapiclient.exceptions.base import GFWAPIClientError
from gfwapiclient.http.client import HTTPClient
from gfwapiclient.resources.bulk_downloads.file.endpoints import (
BulkReportFileEndPoint,
)
from gfwapiclient.resources.bulk_downloads.file.models.request import (
BulkReportFileParams,
)
from gfwapiclient.resources.bulk_downloads.file.models.response import (
BulkReportFileItem,
BulkReportFileResult,
)


bulk_report_id: Final[str] = "adbb9b62-5c08-4142-82e0-b2b575f3e058"


@pytest.mark.asyncio
@pytest.mark.respx
async def test_bulk_report_file_endpoint_request_success(
mock_http_client: HTTPClient,
mock_raw_bulk_report_file_request_params: Dict[str, Any],
mock_raw_bulk_report_file_item: Dict[str, Any],
mock_responsex: respx.MockRouter,
) -> None:
"""Test `BulkReportFileEndPoint` request succeeds with a valid response."""
mock_responsex.get(f"bulk-reports/{bulk_report_id}/download-file-url").respond(
200, json=mock_raw_bulk_report_file_item
)
request_params: BulkReportFileParams = BulkReportFileParams(
**mock_raw_bulk_report_file_request_params
)
endpoint: BulkReportFileEndPoint = BulkReportFileEndPoint(
bulk_report_id=bulk_report_id,
request_params=request_params,
http_client=mock_http_client,
)
result: BulkReportFileResult = await endpoint.request()
data = cast(BulkReportFileItem, result.data())
assert isinstance(result, BulkReportFileResult)
assert isinstance(data, BulkReportFileItem)


@pytest.mark.asyncio
@pytest.mark.respx
async def test_bulk_report_file_endpoint_request_failure(
mock_http_client: HTTPClient,
mock_raw_bulk_report_file_request_params: Dict[str, Any],
mock_responsex: respx.MockRouter,
) -> None:
"""Test `BulkReportFileEndPoint` request fails with an invalid response."""
mock_responsex.get(f"bulk-reports/{bulk_report_id}/download-file-url").mock(
return_value=httpx.Response(status_code=400, json={"error": "Bad Request"})
)
request_params: BulkReportFileParams = BulkReportFileParams(
**mock_raw_bulk_report_file_request_params
)
endpoint: BulkReportFileEndPoint = BulkReportFileEndPoint(
bulk_report_id=bulk_report_id,
request_params=request_params,
http_client=mock_http_client,
)
with pytest.raises(GFWAPIClientError):
await endpoint.request()