Exemple #1
0
 def commit(self) -> None:
     """Commit both reader and writer sessions to keep them in sync, rolling back on psycopg2 errors"""
     try:
         self.writer_session.commit()
         self.reader_session.commit()
     except sa.exc.IntegrityError as e:
         self.writer_session.rollback()
         self.reader_session.rollback()
         logger.error(e.orig.pgerror, exc_info=True)
         # Explicitly catch foreign key errors to be reraised by the API as validation errors
         if isinstance(e.orig, psycopg2.errors.ForeignKeyViolation):
             raise errors.ForeignKeyError(e.orig.pgerror)
         raise errors.DatabaseError(e.orig.pgerror) from e
     except Exception as e:
         logger.error(e, exc_info=True)
         raise errors.DatabaseError(
             "Unhandled database exception during commit")
Exemple #2
0
 def context_session(self) -> Iterator[SqlSession]:
     """override base method to include exception handling"""
     try:
         yield from self.get_db()
     except sa.exc.StatementError as e:
         if isinstance(e.orig, psycopg2.errors.UniqueViolation):
             raise errors.ConflictError("resource already exists") from e
         elif isinstance(e.orig, psycopg2.errors.ForeignKeyViolation):
             raise errors.ForeignKeyError("collection does not exist") from e
         logger.error(e, exc_info=True)
         raise errors.DatabaseError("unhandled database error")
Exemple #3
0
    def all_collections(self, **kwargs) -> List[schemas.Collection]:
        """Read all collections from the database"""
        try:
            collections = self.reader_session.query(self.collection_table).all()
        except Exception as e:
            logger.error(e, exc_info=True)
            raise errors.DatabaseError(
                "Unhandled database error when getting item collection"
            )

        response = []
        for collection in collections:
            collection.base_url = str(kwargs["request"].base_url)
            response.append(schemas.Collection.from_orm(collection))
        return response
Exemple #4
0
 def lookup_id(self,
               item_id: str,
               table: Optional[Type[database.BaseModel]] = None) -> Query:
     """Create a query to access a single record from the table"""
     table = table or self.table
     try:
         query = self.reader_session.query(table).filter(
             table.id == item_id)
     except Exception as e:
         logger.error(e, exc_info=True)
         raise errors.DatabaseError("Unhandled database during ID lookup")
     if not self.row_exists(query):
         error_message = f"Row {item_id} does not exist"
         logger.warning(error_message)
         raise errors.NotFoundError(error_message)
     return query
Exemple #5
0
    def item_collection(
        self, id: str, limit: int = 10, token: str = None, **kwargs
    ) -> ItemCollection:
        """Read an item collection from the database"""
        try:
            collection_children = (
                self.reader_session.query(self.table)
                .join(self.collection_table)
                .filter(self.collection_table.id == id)
                .order_by(self.table.datetime.desc(), self.table.id)
            )
            count = None
            if config.settings.api_extension_is_enabled(config.ApiExtensions.context):
                count_query = collection_children.statement.with_only_columns(
                    [func.count()]
                ).order_by(None)
                count = collection_children.session.execute(count_query).scalar()
            token = self.pagination_client.get(token) if token else token
            page = get_page(collection_children, per_page=limit, page=(token or False))
            # Create dynamic attributes for each page
            page.next = (
                self.pagination_client.insert(keyset=page.paging.bookmark_next)
                if page.paging.has_next
                else None
            )
            page.previous = (
                self.pagination_client.insert(keyset=page.paging.bookmark_previous)
                if page.paging.has_previous
                else None
            )
        except errors.NotFoundError:
            raise
        except Exception as e:
            logger.error(e, exc_info=True)
            raise errors.DatabaseError(
                "Unhandled database error when getting collection children"
            )

        links = []
        if page.next:
            links.append(
                PaginationLink(
                    rel=Relations.next,
                    type="application/geo+json",
                    href=f"{kwargs['request'].base_url}collections/{id}/items?token={page.next}&limit={limit}",
                    method="GET",
                )
            )
        if page.previous:
            links.append(
                PaginationLink(
                    rel=Relations.previous,
                    type="application/geo+json",
                    href=f"{kwargs['request'].base_url}collections/{id}/items?token={page.previous}&limit={limit}",
                    method="GET",
                )
            )

        response_features = []
        for item in page:
            item.base_url = str(kwargs["request"].base_url)
            response_features.append(schemas.Item.from_orm(item))

        context_obj = None
        if config.settings.api_extension_is_enabled(ApiExtensions.context):
            context_obj = {"returned": len(page), "limit": limit, "matched": count}

        return ItemCollection(
            type="FeatureCollection",
            context=context_obj,
            features=response_features,
            links=links,
        )