stac-fastapi.types 2.5.3__tar.gz → 2.5.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/PKG-INFO +1 -1
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/core.py +6 -10
- stac_fastapi_types-2.5.5/stac_fastapi/types/rfc3339.py +126 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/version.py +1 -1
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/PKG-INFO +1 -1
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/tests/test_rfc3339.py +5 -1
- stac_fastapi_types-2.5.3/stac_fastapi/types/rfc3339.py +0 -104
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/README.md +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/setup.cfg +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/setup.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/__init__.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/config.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/conformance.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/errors.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/extension.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/links.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/requests.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/search.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi/types/stac.py +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/SOURCES.txt +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/dependency_links.txt +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/not-zip-safe +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/requires.txt +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/top_level.txt +0 -0
- {stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/tests/test_limit.py +0 -0
|
@@ -109,10 +109,10 @@ class BaseTransactionsClient(abc.ABC):
|
|
|
109
109
|
) -> Optional[Union[stac_types.Collection, Response]]:
|
|
110
110
|
"""Perform a complete update on an existing collection.
|
|
111
111
|
|
|
112
|
-
Called with `PUT /collections/{collection_id}`. It is expected that this
|
|
113
|
-
already exists. The update should do a diff against the saved
|
|
114
|
-
perform any necessary updates. Partial updates are not
|
|
115
|
-
transactions extension.
|
|
112
|
+
Called with `PUT /collections/{collection_id}`. It is expected that this
|
|
113
|
+
collection already exists. The update should do a diff against the saved
|
|
114
|
+
collection and perform any necessary updates. Partial updates are not
|
|
115
|
+
supported by the transactions extension.
|
|
116
116
|
|
|
117
117
|
Args:
|
|
118
118
|
collection_id: id of the existing collection to be updated
|
|
@@ -404,9 +404,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
404
404
|
"rel": "service-desc",
|
|
405
405
|
"type": "application/vnd.oai.openapi+json;version=3.0",
|
|
406
406
|
"title": "OpenAPI service description",
|
|
407
|
-
"href":
|
|
408
|
-
str(request.base_url), request.app.openapi_url.lstrip("/")
|
|
409
|
-
),
|
|
407
|
+
"href": str(request.url_for("openapi")),
|
|
410
408
|
}
|
|
411
409
|
)
|
|
412
410
|
|
|
@@ -416,9 +414,7 @@ class BaseCoreClient(LandingPageMixin, abc.ABC):
|
|
|
416
414
|
"rel": "service-doc",
|
|
417
415
|
"type": "text/html",
|
|
418
416
|
"title": "OpenAPI service documentation",
|
|
419
|
-
"href":
|
|
420
|
-
str(request.base_url), request.app.docs_url.lstrip("/")
|
|
421
|
-
),
|
|
417
|
+
"href": str(request.url_for("swagger_ui_html")),
|
|
422
418
|
}
|
|
423
419
|
)
|
|
424
420
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""rfc3339."""
|
|
2
|
+
import re
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from typing import Optional, Tuple, Union
|
|
5
|
+
|
|
6
|
+
import iso8601
|
|
7
|
+
from fastapi import HTTPException
|
|
8
|
+
from pystac.utils import datetime_to_str
|
|
9
|
+
|
|
10
|
+
RFC33339_PATTERN = (
|
|
11
|
+
r"^(\d\d\d\d)\-(\d\d)\-(\d\d)(T|t)(\d\d):(\d\d):(\d\d)([.]\d+)?"
|
|
12
|
+
r"(Z|([-+])(\d\d):(\d\d))$"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
DateTimeType = Union[
|
|
16
|
+
datetime,
|
|
17
|
+
Tuple[datetime, datetime],
|
|
18
|
+
Tuple[datetime, None],
|
|
19
|
+
Tuple[None, datetime],
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def rfc3339_str_to_datetime(s: str) -> datetime:
|
|
24
|
+
"""Convert a string conforming to RFC 3339 to a :class:`datetime.datetime`.
|
|
25
|
+
|
|
26
|
+
Uses :meth:`iso8601.parse_date` under the hood.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
s (str) : The string to convert to :class:`datetime.datetime`.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
str: The datetime represented by the ISO8601 (RFC 3339) formatted string.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
ValueError: If the string is not a valid RFC 3339 string.
|
|
36
|
+
"""
|
|
37
|
+
# Uppercase the string
|
|
38
|
+
s = s.upper()
|
|
39
|
+
|
|
40
|
+
# Match against RFC3339 regex.
|
|
41
|
+
result = re.match(RFC33339_PATTERN, s)
|
|
42
|
+
if not result:
|
|
43
|
+
raise ValueError("Invalid RFC3339 datetime.")
|
|
44
|
+
|
|
45
|
+
# Parse with pyiso8601
|
|
46
|
+
return iso8601.parse_date(s)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def parse_single_date(date_str: str) -> datetime:
|
|
50
|
+
"""
|
|
51
|
+
Parse a single RFC3339 date string into a datetime object.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
date_str (str): A string representing the date in RFC3339 format.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
datetime: A datetime object parsed from the date_str.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
ValueError: If the date_str is empty or contains the placeholder '..'.
|
|
61
|
+
"""
|
|
62
|
+
if ".." in date_str or not date_str:
|
|
63
|
+
raise ValueError("Invalid date format.")
|
|
64
|
+
return rfc3339_str_to_datetime(date_str)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]:
|
|
68
|
+
"""
|
|
69
|
+
Extract a tuple of datetime objects from an interval string defined by the OGC API.
|
|
70
|
+
The interval can either be a single datetime or a range with start and end datetime.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
interval (Optional[str]): The interval string to convert to datetime objects,
|
|
74
|
+
or None if no datetime is specified.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Optional[DateTimeType]: A tuple of datetime.datetime objects or
|
|
78
|
+
None if input is None.
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
HTTPException: If the string is not valid for various reasons such as being empty,
|
|
82
|
+
having more than one slash, or if date formats are invalid.
|
|
83
|
+
"""
|
|
84
|
+
if interval is None:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
if not interval:
|
|
88
|
+
raise HTTPException(status_code=400, detail="Empty interval string is invalid.")
|
|
89
|
+
|
|
90
|
+
values = interval.split("/")
|
|
91
|
+
if len(values) > 2:
|
|
92
|
+
raise HTTPException(
|
|
93
|
+
status_code=400,
|
|
94
|
+
detail="Interval string contains more than one forward slash.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
start = parse_single_date(values[0]) if values[0] not in ["..", ""] else None
|
|
99
|
+
end = (
|
|
100
|
+
parse_single_date(values[1])
|
|
101
|
+
if len(values) > 1 and values[1] not in ["..", ""]
|
|
102
|
+
else None
|
|
103
|
+
)
|
|
104
|
+
except (ValueError, iso8601.ParseError) as e:
|
|
105
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
106
|
+
|
|
107
|
+
if start is None and end is None:
|
|
108
|
+
raise HTTPException(
|
|
109
|
+
status_code=400, detail="Double open-ended intervals are not allowed."
|
|
110
|
+
)
|
|
111
|
+
if start is not None and end is not None and start > end:
|
|
112
|
+
raise HTTPException(
|
|
113
|
+
status_code=400, detail="Start datetime cannot be before end datetime."
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return start, end
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def now_in_utc() -> datetime:
|
|
120
|
+
"""Return a datetime value of now with the UTC timezone applied."""
|
|
121
|
+
return datetime.now(timezone.utc)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def now_to_rfc3339_str() -> str:
|
|
125
|
+
"""Return an RFC 3339 string representing now."""
|
|
126
|
+
return datetime_to_str(now_in_utc())
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""Library version."""
|
|
2
|
-
__version__ = "2.5.
|
|
2
|
+
__version__ = "2.5.5"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datetime import timezone
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from fastapi import HTTPException
|
|
4
5
|
|
|
5
6
|
from stac_fastapi.types.rfc3339 import (
|
|
6
7
|
now_in_utc,
|
|
@@ -86,8 +87,11 @@ def test_parse_valid_str_to_datetime(test_input):
|
|
|
86
87
|
|
|
87
88
|
@pytest.mark.parametrize("test_input", invalid_intervals)
|
|
88
89
|
def test_parse_invalid_interval_to_datetime(test_input):
|
|
89
|
-
with pytest.raises(
|
|
90
|
+
with pytest.raises(HTTPException) as exc_info:
|
|
90
91
|
str_to_interval(test_input)
|
|
92
|
+
assert (
|
|
93
|
+
exc_info.value.status_code == 400
|
|
94
|
+
), "Should return a 400 status code for invalid intervals"
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
@pytest.mark.parametrize("test_input", valid_intervals)
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"""rfc3339."""
|
|
2
|
-
import re
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
from typing import Optional, Tuple, Union
|
|
5
|
-
|
|
6
|
-
import iso8601
|
|
7
|
-
from pystac.utils import datetime_to_str
|
|
8
|
-
|
|
9
|
-
RFC33339_PATTERN = (
|
|
10
|
-
r"^(\d\d\d\d)\-(\d\d)\-(\d\d)(T|t)(\d\d):(\d\d):(\d\d)([.]\d+)?"
|
|
11
|
-
r"(Z|([-+])(\d\d):(\d\d))$"
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
DateTimeType = Union[
|
|
15
|
-
datetime,
|
|
16
|
-
Tuple[datetime, datetime],
|
|
17
|
-
Tuple[datetime, None],
|
|
18
|
-
Tuple[None, datetime],
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def rfc3339_str_to_datetime(s: str) -> datetime:
|
|
23
|
-
"""Convert a string conforming to RFC 3339 to a :class:`datetime.datetime`.
|
|
24
|
-
|
|
25
|
-
Uses :meth:`iso8601.parse_date` under the hood.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
s (str) : The string to convert to :class:`datetime.datetime`.
|
|
29
|
-
|
|
30
|
-
Returns:
|
|
31
|
-
str: The datetime represented by the ISO8601 (RFC 3339) formatted string.
|
|
32
|
-
|
|
33
|
-
Raises:
|
|
34
|
-
ValueError: If the string is not a valid RFC 3339 string.
|
|
35
|
-
"""
|
|
36
|
-
# Uppercase the string
|
|
37
|
-
s = s.upper()
|
|
38
|
-
|
|
39
|
-
# Match against RFC3339 regex.
|
|
40
|
-
result = re.match(RFC33339_PATTERN, s)
|
|
41
|
-
if not result:
|
|
42
|
-
raise ValueError("Invalid RFC3339 datetime.")
|
|
43
|
-
|
|
44
|
-
# Parse with pyiso8601
|
|
45
|
-
return iso8601.parse_date(s)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def str_to_interval(interval: Optional[str]) -> Optional[DateTimeType]:
|
|
49
|
-
"""Extract a tuple of datetimes from an interval string.
|
|
50
|
-
|
|
51
|
-
Interval strings are defined by
|
|
52
|
-
OGC API - Features Part 1 for the datetime query parameter value. These follow the
|
|
53
|
-
form '1985-04-12T23:20:50.52Z/1986-04-12T23:20:50.52Z', and allow either the start
|
|
54
|
-
or end (but not both) to be open-ended with '..' or ''.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
interval (str or None): The interval string to convert to a tuple of
|
|
58
|
-
datetime.datetime objects, or None if no datetime is specified.
|
|
59
|
-
|
|
60
|
-
Returns:
|
|
61
|
-
Optional[DateTimeType]: A tuple of datetime.datetime objects or None if
|
|
62
|
-
input is None.
|
|
63
|
-
|
|
64
|
-
Raises:
|
|
65
|
-
ValueError: If the string is not a valid interval string and not None.
|
|
66
|
-
"""
|
|
67
|
-
if interval is None:
|
|
68
|
-
return None
|
|
69
|
-
|
|
70
|
-
if not interval:
|
|
71
|
-
raise ValueError("Empty interval string is invalid.")
|
|
72
|
-
|
|
73
|
-
values = interval.split("/")
|
|
74
|
-
if len(values) == 1:
|
|
75
|
-
# Single date for == date case
|
|
76
|
-
return rfc3339_str_to_datetime(values[0])
|
|
77
|
-
elif len(values) > 2:
|
|
78
|
-
raise ValueError(
|
|
79
|
-
f"Interval string '{interval}' contains more than one forward slash."
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
start = None
|
|
83
|
-
end = None
|
|
84
|
-
if values[0] not in ["..", ""]:
|
|
85
|
-
start = rfc3339_str_to_datetime(values[0])
|
|
86
|
-
if values[1] not in ["..", ""]:
|
|
87
|
-
end = rfc3339_str_to_datetime(values[1])
|
|
88
|
-
|
|
89
|
-
if start is None and end is None:
|
|
90
|
-
raise ValueError("Double open-ended intervals are not allowed.")
|
|
91
|
-
if start is not None and end is not None and start > end:
|
|
92
|
-
raise ValueError("Start datetime cannot be before end datetime.")
|
|
93
|
-
else:
|
|
94
|
-
return start, end
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def now_in_utc() -> datetime:
|
|
98
|
-
"""Return a datetime value of now with the UTC timezone applied."""
|
|
99
|
-
return datetime.now(timezone.utc)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def now_to_rfc3339_str() -> str:
|
|
103
|
-
"""Return an RFC 3339 string representing now."""
|
|
104
|
-
return datetime_to_str(now_in_utc())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/requires.txt
RENAMED
|
File without changes
|
{stac_fastapi_types-2.5.3 → stac_fastapi_types-2.5.5}/stac_fastapi.types.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|