Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch endpoints #744

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0cddff7
Adding patch endpoints.
rhysrevans3 Aug 20, 2024
14d18f9
Merge branch 'main' of github.com:stac-utils/stac-fastapi into patch_…
rhysrevans3 Aug 20, 2024
0c5de64
Adding annotated from main.
rhysrevans3 Aug 20, 2024
bf2ddbb
Fixing and adding tests.
rhysrevans3 Aug 21, 2024
632d5a5
Updating changelog.
rhysrevans3 Aug 21, 2024
5f2b4fa
Fixing ruff errors.
rhysrevans3 Aug 21, 2024
010e2cb
Ruff format.
rhysrevans3 Aug 21, 2024
0ccded0
Switching to List for python 3.8.
rhysrevans3 Aug 21, 2024
1b46754
Updating docs make file.
rhysrevans3 Aug 21, 2024
79c769c
Switching from Item/Collection to Dict to allow partial updates.
rhysrevans3 Aug 23, 2024
68a65a0
Ruff format fix.
rhysrevans3 Aug 23, 2024
88b40d4
Fixing broken tests.
rhysrevans3 Aug 23, 2024
b7bcbd5
Adding missing asyncs for patchs.
rhysrevans3 Aug 23, 2024
cfc31c6
Moving request to kwargs for patch item and collection.
rhysrevans3 Aug 23, 2024
81dbcad
Switching to TypedDict.
rhysrevans3 Aug 28, 2024
f053f07
Merge branch 'main' of github.com:stac-utils/stac-fastapi into patch_…
rhysrevans3 Aug 28, 2024
bce099c
Adding hearder parameter to the input models.
rhysrevans3 Aug 28, 2024
336df70
Removing print statement.
rhysrevans3 Aug 28, 2024
36b7167
Removing basemodel from patch types.
rhysrevans3 Aug 28, 2024
0ecf3e5
Fixing imports.
rhysrevans3 Aug 28, 2024
47a0b48
Moving models to correct locations.
rhysrevans3 Aug 28, 2024
13a2377
Switching from attrs to basemodel for patch operations.
rhysrevans3 Aug 28, 2024
7e59d13
Switching to stac.PartialItem etc.
rhysrevans3 Aug 28, 2024
9d011eb
Updating PatchMoveCopy model.
rhysrevans3 Sep 3, 2024
ae6bb94
Merge branch 'main' of github.com:stac-utils/stac-fastapi into patch_…
rhysrevans3 Sep 4, 2024
e325cb2
Updating type for 3.8.
rhysrevans3 Sep 4, 2024
fefd493
Switching to StacBaseModels for patch operations.
rhysrevans3 Sep 18, 2024
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
Prev Previous commit
Next Next commit
Moving models to correct locations.
  • Loading branch information
rhysrevans3 committed Aug 28, 2024
commit 47a0b48dc8f48ca1a3992a0e7ca8b533789fbc64
Original file line number Diff line number Diff line change
@@ -1,52 +1,20 @@
"""Transaction extension."""

from typing import Any, Dict, List, Literal, Optional, Type, Union
from typing import List, Literal, Optional, Type, Union

import attr
from fastapi import APIRouter, Body, FastAPI, Header
from stac_pydantic import Collection, Item, ItemCollection
from stac_pydantic.shared import BBox, MimeTypes
from stac_pydantic.shared import MimeTypes
from starlette.responses import JSONResponse, Response
from typing_extensions import Annotated, TypedDict
from typing_extensions import Annotated

from stac_fastapi.api.models import CollectionUri, ItemUri
from stac_fastapi.api.routes import create_async_endpoint
from stac_fastapi.types.config import ApiSettings
from stac_fastapi.types.core import AsyncBaseTransactionsClient, BaseTransactionsClient
from stac_fastapi.types.extension import ApiExtension


class PartialCollection(TypedDict, total=False):
"""Partial STAC Collection."""

type: Optional[str]
stac_version: Optional[str]
stac_extensions: Optional[List[str]]
id: Optional[str]
title: Optional[str]
description: Optional[str]
links: List[Dict[str, Any]]
keywords: Optional[List[str]]
license: Optional[str]
providers: Optional[List[Dict[str, Any]]]
extent: Optional[Dict[str, Any]]
summaries: Optional[Dict[str, Any]]
assets: Optional[Dict[str, Any]]


class PartialItem(TypedDict, total=False):
"""Partial STAC Item."""

type: Optional[Literal["Feature"]]
stac_version: Optional[str]
stac_extensions: Optional[List[str]]
id: Optional[str]
geometry: Optional[Dict[str, Any]]
bbox: Optional[BBox]
properties: Optional[Dict[str, Any]]
links: Optional[List[Dict[str, Any]]]
assets: Optional[Dict[str, Any]]
collection: Optional[str]
from stac_fastapi.types.stac import PartialCollection, PartialItem, PatchOperation


@attr.s
Expand All @@ -56,39 +24,6 @@ class PostItem(CollectionUri):
item: Annotated[Union[Item, ItemCollection], Body()] = attr.ib(default=None)


@attr.s
class PatchAddReplaceTest:
"""Add, Replace or Test Operation."""

path: str = attr.ib()
op: Literal["add", "replace", "test"] = attr.ib()
value: Any = attr.ib()


@attr.s
class PatchRemove:
"""Remove Operation."""

path: str = attr.ib()
op: Literal["remove"] = attr.ib()


@attr.s
class PatchMoveCopy:
"""Move or Copy Operation."""

path: str = attr.ib()
op: Literal["move", "copy"] = attr.ib()

def __attrs_init__(self, *args, **kwargs):
"""Init function to add 'from' field."""
super().__init__(*args, **kwargs)
self.__setattr__("from", kwargs["from"])


PatchOperation = Union[PatchAddReplaceTest, PatchMoveCopy, PatchRemove]


@attr.s
class PutItem(ItemUri):
"""Update Item."""
Expand Down
14 changes: 10 additions & 4 deletions stac_fastapi/extensions/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from stac_fastapi.extensions.core import TransactionExtension
from stac_fastapi.types.config import ApiSettings
from stac_fastapi.types.core import BaseCoreClient, BaseTransactionsClient
from stac_fastapi.types.transaction import PatchOperation
from stac_fastapi.types.stac import PatchOperation


class DummyCoreClient(BaseCoreClient):
Expand Down Expand Up @@ -145,7 +145,9 @@ def test_merge_patch_item(client: TestClient, item: Item) -> None:


def test_json_patch_item(client: TestClient) -> None:
operations = [{"op": "add", "path": "properties.new_prop", "value": "new_prop_value"}]
operations = [
{"op": "add", "path": "properties.new_prop", "value": "new_prop_value"}
]
headers = {"Content-Type": "application/json-patch+json"}
response = client.patch(
"/collections/a-collection/items/an-item",
Expand Down Expand Up @@ -189,7 +191,9 @@ def test_merge_patch_collection(client: TestClient, collection: Collection) -> N


def test_json_patch_collection(client: TestClient) -> None:
operations = [{"op": "add", "path": "summaries.new_prop", "value": "new_prop_value"}]
operations = [
{"op": "add", "path": "summaries.new_prop", "value": "new_prop_value"}
]
headers = {"Content-Type": "application/json-patch+json"}
response = client.patch(
"/collections/a-collection/items/an-item",
Expand Down Expand Up @@ -269,7 +273,9 @@ def collection() -> Collection:
"description": "A test collection",
"extent": {
"spatial": {"bbox": [[-180, -90, 180, 90]]},
"temporal": {"interval": [["2000-01-01T00:00:00Z", "2024-01-01T00:00:00Z"]]},
"temporal": {
"interval": [["2000-01-01T00:00:00Z", "2024-01-01T00:00:00Z"]]
},
},
"links": [],
"assets": {},
Expand Down
9 changes: 3 additions & 6 deletions stac_fastapi/types/stac_fastapi/types/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@
from stac_fastapi.types import stac
from stac_fastapi.types.config import ApiSettings
from stac_fastapi.types.conformance import BASE_CONFORMANCE_CLASSES
from stac_fastapi.types.extension import (
ApiExtension,
PartialCollection,
PartialItem,
PatchOperation,
)
from stac_fastapi.types.extension import ApiExtension
from stac_fastapi.types.requests import get_base_url
from stac_fastapi.types.rfc3339 import DateTimeType
from stac_fastapi.types.search import BaseSearchPostRequest
from stac_fastapi.types.stac import (PartialCollection, PartialItem,
PatchOperation)

__all__ = [
"NumType",
Expand Down
67 changes: 67 additions & 0 deletions stac_fastapi/types/stac_fastapi/types/stac.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from typing import Any, Dict, List, Literal, Optional, Union

import attr
from stac_pydantic.shared import BBox
from typing_extensions import TypedDict

Expand Down Expand Up @@ -74,3 +75,69 @@ class Collections(TypedDict, total=False):

collections: List[Collection]
links: List[Dict[str, Any]]


class PartialCollection(TypedDict, total=False):
"""Partial STAC Collection."""

type: Optional[str]
stac_version: Optional[str]
stac_extensions: Optional[List[str]]
id: Optional[str]
title: Optional[str]
description: Optional[str]
links: List[Dict[str, Any]]
keywords: Optional[List[str]]
license: Optional[str]
providers: Optional[List[Dict[str, Any]]]
extent: Optional[Dict[str, Any]]
summaries: Optional[Dict[str, Any]]
assets: Optional[Dict[str, Any]]


class PartialItem(TypedDict, total=False):
"""Partial STAC Item."""

type: Optional[Literal["Feature"]]
stac_version: Optional[str]
stac_extensions: Optional[List[str]]
id: Optional[str]
geometry: Optional[Dict[str, Any]]
bbox: Optional[BBox]
properties: Optional[Dict[str, Any]]
links: Optional[List[Dict[str, Any]]]
assets: Optional[Dict[str, Any]]
collection: Optional[str]


@attr.s
class PatchAddReplaceTest:
"""Add, Replace or Test Operation."""

path: str = attr.ib()
op: Literal["add", "replace", "test"] = attr.ib()
value: Any = attr.ib()


@attr.s
class PatchRemove:
"""Remove Operation."""

path: str = attr.ib()
op: Literal["remove"] = attr.ib()


@attr.s
class PatchMoveCopy:
"""Move or Copy Operation."""

path: str = attr.ib()
op: Literal["move", "copy"] = attr.ib()

def __attrs_init__(self, *args, **kwargs):
"""Init function to add 'from' field."""
super().__init__(*args, **kwargs)
self.__setattr__("from", kwargs["from"])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because we're using pydantic model I think we can use Field

class PatchMoveCopy(BaseModel):
   """Move or Copy Operation."""

   path: str
   op: Literal["move", "copy"]
   fromurl: str = Field(alias="from")

but then the attribute is .fromurl not .from

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I hadn't seen alias before that looks neater.

I've also added:

model_config = ConfigDict(populate_by_name=True)

which means you can do this:

patch_operation = PatchMoveCopy(**{"path": "hello", "from":"world", "op": "move"})

And overrode the model_dump method so by_alias defaults to True to use the alias in the model dump

patch_operation.model_dump()
#> {'path': 'hello', 'op': 'move', 'from': 'world'}

Not sure if this is the right thing to do or if we should expect people to use:

patch_operation.model_dump(by_alias=True)



PatchOperation = Union[PatchAddReplaceTest, PatchMoveCopy, PatchRemove]
Loading