Skip to content
This repository was archived by the owner on Apr 2, 2025. It is now read-only.

Commit 3d997ea

Browse files
author
Phil Varner
committed
Merge branch 'main' into jak/model-idea
2 parents 0046fae + 466a582 commit 3d997ea

17 files changed

+94
-38
lines changed

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
**PR Checklist:**
1111

12-
- [ ] I have added my changes to the [CHANGELOG](https://github.com/Element84/filmdrop-ui/blob/main/CHANGELOG.md) **or** a CHANGELOG entry is not required.
12+
- [ ] I have added my changes to the CHANGELOG **or** a CHANGELOG entry is not required.

CHANGELOG.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [0.3.0] - 2024-12-6
9+
10+
### Added
11+
12+
none
13+
14+
### Changed
15+
16+
- OrderStatusCode and ProviderRole are now StrEnum instead of (str, Enum)
17+
- All types using `Result[A, Exception]` have been replace with the equivalent type `ResultE[A]`
18+
19+
### Deprecated
20+
21+
none
22+
23+
### Removed
24+
25+
none
26+
27+
### Fixed
28+
29+
none
30+
31+
### Security
32+
33+
none
34+
835
## [v0.2.0] - 2024-11-23
936

1037
### Added
@@ -19,6 +46,8 @@ none
1946
- Order field `id` must be a string, instead of previously allowing int. This is because while an
2047
order ID may an integral numeric value, it is not a "number" in the sense that math will be performed
2148
order ID values, so string represents this better.
49+
- Order and OrderCollection extend _GeoJsonBase instead of Feature and FeatureCollection, to allow for tighter
50+
constraints on fields
2251

2352
### Deprecated
2453

@@ -50,6 +79,7 @@ Initial release
5079
- Add links `opportunities` and `create-order` to Product
5180
- Add link `create-order` to OpportunityCollection
5281

53-
[unreleased]: https://github.com/stapi-spec/stapi-fastapi/compare/v0.2.0...main
82+
[unreleased]: https://github.com/stapi-spec/stapi-fastapi/compare/v0.3.0...main
83+
[v0.3.0]: https://github.com/stapi-spec/stapi-fastapi/tree/v0.3.0
5484
[v0.2.0]: https://github.com/stapi-spec/stapi-fastapi/tree/v0.2.0
5585
[v0.1.0]: https://github.com/stapi-spec/stapi-fastapi/tree/v0.1.0

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# STAPI FastAPI - Sensor Tasking API with FastAPI
22

3-
WARNING: The whole [STAPI spec] is very much work in progress, so things are
3+
WARNING: The whole [STAPI spec] is very much a work in progress, so things are
44
guaranteed to be not correct.
55

66
## Usage

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ show_missing = true
5959
skip_empty = true
6060
sort = "Cover"
6161
omit = [
62-
"**/*_test.py",
63-
"**/conftest.py",
62+
"tests/**/*.py",
6463
"stapi_fastapi/__dev__.py",
6564
]
6665

src/stapi_fastapi/backends/product_backend.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from typing import Protocol
44

55
from fastapi import Request
6-
from returns.result import Result
6+
from returns.result import ResultE
77

88
from stapi_fastapi.models.opportunity import Opportunity, OpportunityRequest
99
from stapi_fastapi.models.order import Order, OrderRequest
@@ -16,7 +16,7 @@ async def search_opportunities(
1616
product_router: ProductRouter,
1717
search: OpportunityRequest,
1818
request: Request,
19-
) -> Result[list[Opportunity], Exception]:
19+
) -> ResultE[list[Opportunity]]:
2020
"""
2121
Search for ordering opportunities for the given search parameters.
2222
@@ -29,7 +29,7 @@ async def create_order(
2929
product_router: ProductRouter,
3030
search: OrderRequest,
3131
request: Request,
32-
) -> Result[Order, Exception]:
32+
) -> ResultE[Order]:
3333
"""
3434
Create a new order.
3535

src/stapi_fastapi/backends/root_backend.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,19 @@
22

33
from fastapi import Request
44
from returns.maybe import Maybe
5-
from returns.result import Result
5+
from returns.result import ResultE
66

77
from stapi_fastapi.models.order import Order, OrderCollection
88

99

1010
class RootBackend(Protocol): # pragma: nocover
11-
async def get_orders(self, request: Request) -> Result[OrderCollection, Exception]:
11+
async def get_orders(self, request: Request) -> ResultE[OrderCollection]:
1212
"""
1313
Return a list of existing orders.
1414
"""
1515
...
1616

17-
async def get_order(
18-
self, order_id: str, request: Request
19-
) -> Result[Maybe[Order], Exception]:
17+
async def get_order(self, order_id: str, request: Request) -> ResultE[Maybe[Order]]:
2018
"""
2119
Get details for order with `order_id`.
2220

src/stapi_fastapi/models/conformance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from pydantic import BaseModel, Field
22

3-
CORE = "https://stapi.example.com/v0.2.0/core"
4-
OPPORTUNITIES = "https://stapi.example.com/v0.2.0/opportunities"
3+
CORE = "https://stapi.example.com/v0.1.0/core"
4+
OPPORTUNITIES = "https://stapi.example.com/v0.1.0/opportunities"
55

66

77
class Conformance(BaseModel):

src/stapi_fastapi/models/order.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
1-
from enum import Enum
2-
from typing import Any, Generic, Literal, Optional, TypeVar
1+
from enum import StrEnum
2+
from typing import Any, Dict, Generic, Iterator, Literal, Optional, TypeVar, Union
33

4-
from geojson_pydantic import Feature, FeatureCollection
4+
from geojson_pydantic.base import _GeoJsonBase
55
from geojson_pydantic.geometries import Geometry
66
from pydantic import (
77
AwareDatetime,
88
BaseModel,
99
ConfigDict,
1010
Field,
1111
StrictStr,
12+
field_validator,
1213
)
1314

1415
from stapi_fastapi.models.opportunity import OpportunityProperties
1516
from stapi_fastapi.models.shared import Link
1617
from stapi_fastapi.types.datetime_interval import DatetimeInterval
1718
from stapi_fastapi.types.filter import CQL2Filter
1819

20+
Props = TypeVar("Props", bound=Union[Dict[str, Any], BaseModel])
21+
Geom = TypeVar("Geom", bound=Geometry)
22+
1923

2024
class OrderParameters(BaseModel):
2125
model_config = ConfigDict(extra="forbid")
@@ -36,7 +40,7 @@ class OrderRequest(BaseModel, Generic[ORP]):
3640
model_config = ConfigDict(strict=True)
3741

3842

39-
class OrderStatusCode(str, Enum):
43+
class OrderStatusCode(StrEnum):
4044
received = "received"
4145
accepted = "accepted"
4246
rejected = "rejected"
@@ -71,14 +75,43 @@ class OrderProperties(BaseModel):
7175
model_config = ConfigDict(extra="allow")
7276

7377

74-
class Order(Feature[Geometry, OrderProperties]):
78+
# derived from geojson_pydantic.Feature
79+
class Order(_GeoJsonBase):
7580
# We need to enforce that orders have an id defined, as that is required to
7681
# retrieve them via the API
7782
id: StrictStr
7883
type: Literal["Feature"] = "Feature"
84+
85+
geometry: Geometry = Field(...)
86+
properties: OrderProperties = Field(...)
87+
7988
links: list[Link] = Field(default_factory=list)
8089

90+
__geojson_exclude_if_none__ = {"bbox", "id"}
91+
92+
@field_validator("geometry", mode="before")
93+
def set_geometry(cls, geometry: Any) -> Any:
94+
"""set geometry from geo interface or input"""
95+
if hasattr(geometry, "__geo_interface__"):
96+
return geometry.__geo_interface__
97+
98+
return geometry
8199

82-
class OrderCollection(FeatureCollection[Order]):
100+
101+
# derived from geojson_pydantic.FeatureCollection
102+
class OrderCollection(_GeoJsonBase):
83103
type: Literal["FeatureCollection"] = "FeatureCollection"
104+
features: list[Order]
84105
links: list[Link] = Field(default_factory=list)
106+
107+
def __iter__(self) -> Iterator[Order]: # type: ignore [override]
108+
"""iterate over features"""
109+
return iter(self.features)
110+
111+
def __len__(self) -> int:
112+
"""return features length"""
113+
return len(self.features)
114+
115+
def __getitem__(self, index: int) -> Order:
116+
"""get feature at a given index"""
117+
return self.features[index]

src/stapi_fastapi/models/product.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from enum import Enum
3+
from enum import StrEnum
44
from typing import TYPE_CHECKING, Literal, Optional, Self
55

66
from pydantic import AnyHttpUrl, BaseModel, Field
@@ -16,7 +16,7 @@
1616
type Constraints = BaseModel
1717

1818

19-
class ProviderRole(str, Enum):
19+
class ProviderRole(StrEnum):
2020
licensor = "licensor"
2121
producer = "producer"
2222
processor = "processor"

tests/application.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from fastapi import FastAPI, Request
66
from pydantic import BaseModel, Field, model_validator
77
from returns.maybe import Maybe
8-
from returns.result import Failure, Result, Success
8+
from returns.result import Failure, ResultE, Success
99

1010
from stapi_fastapi.backends.product_backend import ProductBackend
1111
from stapi_fastapi.backends.root_backend import RootBackend
@@ -40,15 +40,13 @@ class MockRootBackend(RootBackend):
4040
def __init__(self, orders: MockOrderDB) -> None:
4141
self._orders: MockOrderDB = orders
4242

43-
async def get_orders(self, request: Request) -> Result[OrderCollection, Exception]:
43+
async def get_orders(self, request: Request) -> ResultE[OrderCollection]:
4444
"""
4545
Show all orders.
4646
"""
4747
return Success(OrderCollection(features=list(self._orders.values())))
4848

49-
async def get_order(
50-
self, order_id: str, request: Request
51-
) -> Result[Maybe[Order], Exception]:
49+
async def get_order(self, order_id: str, request: Request) -> ResultE[Maybe[Order]]:
5250
"""
5351
Show details for order with `order_id`.
5452
"""
@@ -67,7 +65,7 @@ async def search_opportunities(
6765
product_router: ProductRouter,
6866
search: OpportunityRequest,
6967
request: Request,
70-
) -> Result[list[Opportunity], Exception]:
68+
) -> ResultE[list[Opportunity]]:
7169
try:
7270
return Success(
7371
[o.model_copy(update=search.model_dump()) for o in self._opportunities]
@@ -77,7 +75,7 @@ async def search_opportunities(
7775

7876
async def create_order(
7977
self, product_router: ProductRouter, payload: OrderRequest, request: Request
80-
) -> Result[Order, Exception]:
78+
) -> ResultE[Order]:
8179
"""
8280
Create a new order.
8381
"""

0 commit comments

Comments
 (0)