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

Enhance SQL Registry Error Messages #674

Merged
merged 4 commits into from
Sep 23, 2022
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
47 changes: 46 additions & 1 deletion registry/sql-registry/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import os
import traceback
from typing import Optional
from uuid import UUID
from fastapi import APIRouter, FastAPI, HTTPException
from fastapi.responses import JSONResponse
from starlette.middleware.cors import CORSMiddleware
from registry import *
from registry.db_registry import DbRegistry
from registry.db_registry import DbRegistry, ConflictError
from registry.models import AnchorDef, AnchorFeatureDef, DerivedFeatureDef, EntityType, ProjectDef, SourceDef, to_snake

rp = "/"
Expand All @@ -28,6 +30,49 @@
allow_headers=["*"],
)

def exc_to_content(e: Exception) -> dict:
content={"message": str(e)}
if os.environ.get("REGISTRY_DEBUGGING"):
content["traceback"] = "".join(traceback.TracebackException.from_exception(e).format())
return content

@app.exception_handler(ConflictError)
async def conflict_error_handler(_, exc: ConflictError):
return JSONResponse(
status_code=409,
content=exc_to_content(exc),
)


@app.exception_handler(ValueError)
async def value_error_handler(_, exc: ValueError):
return JSONResponse(
status_code=400,
content=exc_to_content(exc),
)

@app.exception_handler(TypeError)
async def type_error_handler(_, exc: ValueError):
return JSONResponse(
status_code=400,
content=exc_to_content(exc),
)


@app.exception_handler(KeyError)
async def key_error_handler(_, exc: KeyError):
return JSONResponse(
status_code=404,
content=exc_to_content(exc),
)

@app.exception_handler(IndexError)
async def index_error_handler(_, exc: IndexError):
return JSONResponse(
status_code=404,
content=exc_to_content(exc),
)


@router.get("/projects")
def get_projects() -> list[str]:
Expand Down
2 changes: 1 addition & 1 deletion registry/sql-registry/registry/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from registry.models import *
from registry.interface import Registry
from registry.database import DbConnection, connect
from registry.db_registry import DbRegistry
from registry.db_registry import DbRegistry, ConflictError
26 changes: 15 additions & 11 deletions registry/sql-registry/registry/db_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
from registry.models import AnchorAttributes, AnchorDef, AnchorFeatureAttributes, AnchorFeatureDef, DerivedFeatureAttributes, DerivedFeatureDef, Edge, EntitiesAndRelations, Entity, EntityRef, EntityType, ProjectAttributes, ProjectDef, RelationshipType, SourceAttributes, SourceDef, _to_type, _to_uuid
import json

class ConflictError(Exception):
pass


def quote(id):
if isinstance(id, str):
Expand All @@ -16,7 +19,6 @@ def quote(id):
else:
return ",".join([quote(i) for i in id])


class DbRegistry(Registry):
def __init__(self):
self.conn = connect()
Expand All @@ -41,6 +43,8 @@ def get_entity_id(self, id_or_name: Union[str, UUID]) -> UUID:
# It is a name
ret = self.conn.query(
f"select entity_id from entities where qualified_name=%s", str(id_or_name))
if len(ret) == 0:
raise KeyError(f"Entity {id_or_name} not found")
return ret[0]["entity_id"]

def get_neighbors(self, id_or_name: Union[str, UUID], relationship: RelationshipType) -> list[Edge]:
Expand Down Expand Up @@ -138,7 +142,7 @@ def create_project(self, definition: ProjectDef) -> UUID:
len(r), definition.qualified_name)
# The entity with same name already exists but with different type
if _to_type(r[0]["entity_type"], EntityType) != EntityType.Project:
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
# Just return the existing project id
return _to_uuid(r[0]["entity_id"])
Expand Down Expand Up @@ -166,7 +170,7 @@ def create_project_datasource(self, project_id: UUID, definition: SourceDef) ->
len(r), definition.qualified_name)
# The entity with same name already exists but with different type
if _to_type(r[0]["entity_type"], EntityType) != EntityType.Source:
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
attr: SourceAttributes = _to_type(
json.loads(r[0]["attributes"]), SourceAttributes)
Expand All @@ -179,7 +183,7 @@ def create_project_datasource(self, project_id: UUID, definition: SourceDef) ->
# Creating exactly same entity
# Just return the existing id
return _to_uuid(r[0]["entity_id"])
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
id = uuid4()
c.execute(f"insert into entities (entity_id, entity_type, qualified_name, attributes) values (%s, %s, %s, %s)",
Expand Down Expand Up @@ -207,15 +211,15 @@ def create_project_anchor(self, project_id: UUID, definition: AnchorDef) -> UUID
len(r), definition.qualified_name)
# The entity with same name already exists but with different type
if _to_type(r[0]["entity_type"], EntityType) != EntityType.Anchor:
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
attr: AnchorAttributes = _to_type(
json.loads(r[0]["attributes"]), AnchorAttributes)
if attr.name == definition.name:
# Creating exactly same entity
# Just return the existing id
return _to_uuid(r[0]["entity_id"])
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
c.execute("select entity_id, qualified_name from entities where entity_id = %s and entity_type = %s", (str(
definition.source_id), str(EntityType.Source)))
Expand Down Expand Up @@ -257,7 +261,7 @@ def create_project_anchor_feature(self, project_id: UUID, anchor_id: UUID, defin
len(r), definition.qualified_name)
# The entity with same name already exists but with different type
if _to_type(r[0]["entity_type"], EntityType) != EntityType.AnchorFeature:
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
attr: AnchorFeatureAttributes = _to_type(
json.loads(r[0]["attributes"]), AnchorFeatureAttributes)
Expand All @@ -269,7 +273,7 @@ def create_project_anchor_feature(self, project_id: UUID, anchor_id: UUID, defin
# Just return the existing id
return _to_uuid(r[0]["entity_id"])
# The existing entity has different definition, that's a conflict
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
source_id = anchor.attributes.source.id
id = uuid4()
Expand Down Expand Up @@ -305,7 +309,7 @@ def create_project_derived_feature(self, project_id: UUID, definition: DerivedFe
len(r), definition.qualified_name)
# The entity with same name already exists but with different type, that's conflict
if _to_type(r[0]["entity_type"], EntityType) != EntityType.DerivedFeature:
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
attr: DerivedFeatureAttributes = _to_type(
json.loads(r[0]["attributes"]), DerivedFeatureAttributes)
Expand All @@ -317,7 +321,7 @@ def create_project_derived_feature(self, project_id: UUID, definition: DerivedFe
# Just return the existing id
return _to_uuid(r[0]["entity_id"])
# The existing entity has different definition, that's a conflict
raise ValueError("Entity %s already exists" %
raise ConflictError("Entity %s already exists" %
definition.qualified_name)
r1 = []
# Fill `input_anchor_features`, from `definition` we have ids only, we still need qualified names
Expand Down Expand Up @@ -429,7 +433,7 @@ def _get_entity(self, id_or_name: Union[str, UUID]) -> Entity:
where entity_id = %s
''', self.get_entity_id(id_or_name))
if not row:
raise ValueError(f"Entity {id_or_name} not found")
raise KeyError(f"Entity {id_or_name} not found")
row=row[0]
row["attributes"] = json.loads(row["attributes"])
return _to_type(row, Entity)
Expand Down