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

switch to attrs #73

Merged
merged 2 commits into from
Jan 21, 2021
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
717 changes: 393 additions & 324 deletions Pipfile.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


install_requires = [
"attrs",
"uvicorn[standard]",
"gunicorn",
"fastapi>=0.60.0",
Expand Down
24 changes: 11 additions & 13 deletions stac_api/api/app.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""fastapi app creation"""
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Type

import attr
from fastapi import APIRouter, FastAPI
from fastapi.openapi.utils import get_openapi
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from stac_pydantic import ItemCollection
from stac_pydantic.api import ConformanceClasses, LandingPage
from starlette.requests import Request

from stac_api.api.extensions import FieldsExtension
Expand All @@ -24,23 +26,19 @@
from stac_api.errors import DEFAULT_STATUS_CODES, add_exception_handlers
from stac_api.models import schemas
from stac_api.utils.dependencies import READER, WRITER
from stac_pydantic import ItemCollection
from stac_pydantic.api import ConformanceClasses, LandingPage


@dataclass
@attr.s
class StacApi:
"""StacApi"""

settings: ApiSettings
client: BaseCoreClient
extensions: Optional[List[ApiExtension]] = field( # type:ignore
default_factory=list
)
exceptions: Dict[Type[Exception], int] = field(
default_factory=lambda: DEFAULT_STATUS_CODES
settings: ApiSettings = attr.ib()
client: BaseCoreClient = attr.ib()
extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))
exceptions: Dict[Type[Exception], int] = attr.ib(
default=attr.Factory(lambda: DEFAULT_STATUS_CODES)
)
app: FastAPI = FastAPI()
app: FastAPI = attr.ib(default=attr.Factory(FastAPI))

def get_extension(self, extension: Type[ApiExtension]) -> Optional[ApiExtension]:
"""check if an api extension is enabled"""
Expand Down Expand Up @@ -205,7 +203,7 @@ async def create_db_connection(request: Request, call_next):
writer.close()
return resp

def __post_init__(self):
def __attrs_post_init__(self):
"""post-init hook"""
# inject settings
self.app.debug = self.settings.debug
Expand Down
5 changes: 2 additions & 3 deletions stac_api/api/extensions/context.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""context extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class ContextExtension(ApiExtension):
"""
stac-api context extension
Expand Down
4 changes: 2 additions & 2 deletions stac_api/api/extensions/extension.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""base api extension"""
import abc
from dataclasses import dataclass

import attr
from fastapi import FastAPI


@dataclass # type:ignore
@attr.s
class ApiExtension(abc.ABC):
"""orchestration for API extensions"""

Expand Down
26 changes: 14 additions & 12 deletions stac_api/api/extensions/fields.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
"""fields extension"""
from dataclasses import dataclass, field
from typing import Set

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class FieldsExtension(ApiExtension):
"""
stac-api fields extension
(https://github.com/radiantearth/stac-api-spec/tree/master/extensions/fields)
"""

default_includes: Set[str] = field(
default_factory=lambda: {
"id",
"type",
"geometry",
"bbox",
"links",
"assets",
"properties.datetime",
}
default_includes: Set[str] = attr.ib(
default=attr.Factory(
lambda: {
"id",
"type",
"geometry",
"bbox",
"links",
"assets",
"properties.datetime",
Copy link
Member

Choose a reason for hiding this comment

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

❤️

}
)
)

def register(self, app: FastAPI) -> None:
Expand Down
5 changes: 2 additions & 3 deletions stac_api/api/extensions/query.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""query extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class QueryExtension(ApiExtension):
"""
stac-api query extension
Expand Down
5 changes: 2 additions & 3 deletions stac_api/api/extensions/sort.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""sort extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI

from stac_api.api.extensions.extension import ApiExtension


@dataclass
@attr.s
class SortExtension(ApiExtension):
"""
stac-api query extension
Expand Down
12 changes: 6 additions & 6 deletions stac_api/api/extensions/tiles.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
"""tiles extension"""
from dataclasses import dataclass

import attr
from fastapi import FastAPI
from starlette.requests import Request
from starlette.responses import HTMLResponse

from stac_api.api.extensions.extension import ApiExtension
from stac_api.api.models import ItemUri
from stac_api.api.routes import create_endpoint_with_depends
from stac_api.clients.tiles.ogc import TilesClient
from stac_api.models.ogc import TileSetResource
from starlette.requests import Request
from starlette.responses import HTMLResponse


@dataclass
@attr.s
class TilesExtension(ApiExtension):
"""titiler extension"""

client: TilesClient = TilesClient()
client: TilesClient = attr.ib(default=attr.Factory(TilesClient))

def register(self, app: FastAPI) -> None:
"""register extension with the application"""
Expand Down
7 changes: 3 additions & 4 deletions stac_api/api/extensions/transaction.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""stac-api transaction extension"""
from dataclasses import dataclass

import attr
from fastapi import APIRouter, FastAPI

from stac_api.api.extensions.extension import ApiExtension
Expand All @@ -10,14 +9,14 @@
from stac_api.models import schemas


@dataclass
@attr.s
class TransactionExtension(ApiExtension):
"""
stac-api transaction extension
(https://github.com/radiantearth/stac-api-spec/blob/master/extensions/transaction/README.md)
"""

client: BaseTransactionsClient
client: BaseTransactionsClient = attr.ib()

def register(self, app: FastAPI) -> None:
"""register extension with the application"""
Expand Down
41 changes: 20 additions & 21 deletions stac_api/api/models.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"""api request/response models"""

import abc
from dataclasses import dataclass
from typing import Dict, Optional, Type, Union

import attr
from fastapi import Body, Path

from pydantic import BaseModel, create_model
from pydantic.fields import UndefinedType

Expand Down Expand Up @@ -43,7 +42,7 @@ def _create_request_model(model: Type[BaseModel]) -> Type[BaseModel]:
return create_model(model.__name__, **fields, __base__=model)


@dataclass # type:ignore
@attr.s # type:ignore
class APIRequest(abc.ABC):
"""Generic API Request base class"""

Expand All @@ -53,29 +52,29 @@ def kwargs(self) -> Dict:
...


@dataclass # type:ignore
@attr.s # type:ignore
class CollectionUri(APIRequest):
"""Delete collection"""

collectionId: str = Path(..., description="Collection ID")
collectionId: str = attr.ib(default=Path(..., description="Collection ID"))

def kwargs(self) -> Dict:
"""kwargs"""
return {"id": self.collectionId}


@dataclass
@attr.s
class ItemUri(CollectionUri):
"""Delete item"""

itemId: str = Path(..., description="Item ID")
itemId: str = attr.ib(default=Path(..., description="Item ID"))

def kwargs(self) -> Dict:
"""kwargs"""
return {"id": self.itemId}


@dataclass
@attr.s
class EmptyRequest(APIRequest):
"""Empty request"""

Expand All @@ -84,31 +83,31 @@ def kwargs(self) -> Dict:
return {}


@dataclass
@attr.s
class ItemCollectionUri(CollectionUri):
"""Get item collection"""

limit: int = 10
token: str = None
limit: int = attr.ib(default=10)
token: str = attr.ib(default=None)

def kwargs(self) -> Dict:
"""kwargs"""
return {"id": self.collectionId, "limit": self.limit, "token": self.token}


@dataclass
@attr.s
class SearchGetRequest(APIRequest):
"""GET search request"""

collections: Optional[str] = None
ids: Optional[str] = None
bbox: Optional[str] = None
datetime: Optional[Union[str]] = None
limit: Optional[int] = 10
query: Optional[str] = None
token: Optional[str] = None
fields: Optional[str] = None
sortby: Optional[str] = None
collections: Optional[str] = attr.ib(default=None)
ids: Optional[str] = attr.ib(default=None)
bbox: Optional[str] = attr.ib(default=None)
datetime: Optional[Union[str]] = attr.ib(default=None)
limit: Optional[int] = attr.ib(default=10)
query: Optional[str] = attr.ib(default=None)
token: Optional[str] = attr.ib(default=None)
fields: Optional[str] = attr.ib(default=None)
sortby: Optional[str] = attr.ib(default=None)

def kwargs(self) -> Dict:
"""kwargs"""
Expand Down
10 changes: 5 additions & 5 deletions stac_api/clients/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Base clients."""
import abc
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any, Dict, List, Optional, Type, Union

import attr
from stac_pydantic import ItemCollection
from stac_pydantic.api import ConformanceClasses, LandingPage

Expand All @@ -13,7 +13,7 @@
NumType = Union[float, int]


@dataclass # type:ignore
@attr.s # type:ignore
class BaseTransactionsClient(abc.ABC):
"""Base transactions client"""

Expand Down Expand Up @@ -52,7 +52,7 @@ def delete_collection(self, id: str, **kwargs) -> schemas.Collection:
...


@dataclass # type: ignore
@attr.s # type: ignore
class BulkTransactionsClient(abc.ABC):
"""bulk transactions client"""

Expand All @@ -68,11 +68,11 @@ def bulk_item_insert(self, items: List[Dict], chunks: Optional[int] = None) -> N
raise NotImplementedError


@dataclass # type:ignore
@attr.s # type:ignore
class BaseCoreClient(abc.ABC):
"""Base client for core endpoints defined by stac"""

extensions: List[ApiExtension] = field(default_factory=list)
extensions: List[ApiExtension] = attr.ib(default=attr.Factory(list))

def extension_is_enabled(self, extension: Type[ApiExtension]) -> bool:
"""check if an api extension is enabled"""
Expand Down
6 changes: 3 additions & 3 deletions stac_api/clients/postgres/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Postgresql base client"""
import abc
import logging
from dataclasses import dataclass
from typing import Optional, Type

import attr
import sqlalchemy as sa
from sqlalchemy.orm import Query

Expand All @@ -15,11 +15,11 @@
logger = logging.getLogger(__name__)


@dataclass
@attr.s
class PostgresClient(abc.ABC):
"""Database CRUD operations on the defined table"""

table: Optional[Type[database.BaseModel]] = None
table: Type[database.BaseModel] = attr.ib(default=database.BaseModel)

@property
def reader_session(self):
Expand Down
Loading