From ec80c1a96c487b8fbf0fa753e88f9440a999e79c Mon Sep 17 00:00:00 2001 From: Santiago Lo Coco Date: Tue, 29 Nov 2022 11:14:08 -0300 Subject: [PATCH] Update API, refactor and fix bugs Co-authored-by: Ezequiel Bellver Co-authored-by: Juan Barmasch --- bsition/api/api.py | 27 ++++--- bsition/api/components/document.py | 52 ------------ bsition/api/components/table.py | 79 ------------------- bsition/api/components/user.py | 37 --------- .../api/{components => endpoints}/__init__.py | 0 bsition/api/endpoints/documents.py | 39 +++++++++ bsition/api/endpoints/login.py | 24 ++++++ bsition/api/endpoints/tables.py | 60 ++++++++++++++ bsition/api/endpoints/users.py | 39 +++++++++ bsition/api/main.py | 8 ++ bsition/api/middleware/verify_token.py | 2 +- bsition/api/{routes => models}/__init__.py | 0 bsition/api/models/document.py | 15 ++++ bsition/api/models/table.py | 24 ++++++ bsition/api/models/user.py | 7 ++ bsition/api/routes/auth.py | 20 ----- bsition/api/utils/__init__.py | 0 bsition/api/{ => utils}/jwt.py | 2 +- bsition/backend/postgres.py | 12 ++- docker-compose.yml | 2 +- run.sh | 2 +- 21 files changed, 246 insertions(+), 205 deletions(-) delete mode 100644 bsition/api/components/document.py delete mode 100644 bsition/api/components/table.py delete mode 100644 bsition/api/components/user.py rename bsition/api/{components => endpoints}/__init__.py (100%) create mode 100644 bsition/api/endpoints/documents.py create mode 100644 bsition/api/endpoints/login.py create mode 100644 bsition/api/endpoints/tables.py create mode 100644 bsition/api/endpoints/users.py create mode 100644 bsition/api/main.py rename bsition/api/{routes => models}/__init__.py (100%) create mode 100644 bsition/api/models/document.py create mode 100644 bsition/api/models/table.py create mode 100644 bsition/api/models/user.py delete mode 100644 bsition/api/routes/auth.py create mode 100644 bsition/api/utils/__init__.py rename bsition/api/{ => utils}/jwt.py (92%) diff --git a/bsition/api/api.py b/bsition/api/api.py index 2534cc6..5493568 100644 --- a/bsition/api/api.py +++ b/bsition/api/api.py @@ -1,14 +1,17 @@ -from dotenv import load_dotenv -from fastapi import FastAPI +from fastapi import APIRouter +from fastapi.responses import JSONResponse -from bsition.api.components.document import documents_routes -from bsition.api.components.table import tables_routes -from bsition.api.components.user import users_routes -from bsition.api.routes.auth import auth_routes +from bsition.api.endpoints import documents, tables, users, login -app = FastAPI() -app.include_router(auth_routes, prefix="/api") -app.include_router(documents_routes, prefix="/api") -app.include_router(users_routes, prefix="/api") -app.include_router(tables_routes, prefix="/api") -load_dotenv() +router = APIRouter() + + +@router.get("") +def info(): + return JSONResponse(content={"message": "BSition API v1"}, status_code=200) + + +router.include_router(login.router) +router.include_router(documents.router, prefix="/documents") +router.include_router(users.router, prefix="/users") +router.include_router(tables.router, prefix="/tables") diff --git a/bsition/api/components/document.py b/bsition/api/components/document.py deleted file mode 100644 index 51f604c..0000000 --- a/bsition/api/components/document.py +++ /dev/null @@ -1,52 +0,0 @@ -import json - -from fastapi import APIRouter, Response, status -from pydantic import BaseModel -from pydantic.types import Optional - -from bsition.api.middleware.verify_token import VerifyTokenRoute -from bsition.backend import elastic, mongo - -documents_routes = APIRouter(route_class=VerifyTokenRoute) - - -class Document(BaseModel): - name: str - access: list - data: str - owner: str - - -class DocumentUpdate(BaseModel): - name: Optional[str] - access: Optional[list] - data: Optional[str] - - -@documents_routes.post("/documents") -def create(aux: Document, response: Response): - mongo.create_document(json.loads(json.dumps(aux.__dict__))) - response.status_code = status.HTTP_201_CREATED - - -@documents_routes.get("/documents") -def get_documents(query: str = None): - if query is not None: - return elastic.search("test-index", query) - return mongo.get_documents() - - -@documents_routes.get("/documents/{id}") -def get_by_id(id: str): - return mongo.get_document_by_id(id) - - -@documents_routes.put("/documents/{id}") -def edit_data(aux: DocumentUpdate, id: str, response: Response): - if aux.data is not None: - mongo.edit_data(id, aux.data) - if aux.access is not None: - mongo.edit_access(id, aux.access) - if aux.name is not None: - mongo.edit_name(id, aux.name) - response.status_code = status.HTTP_202_ACCEPTED diff --git a/bsition/api/components/table.py b/bsition/api/components/table.py deleted file mode 100644 index 9c63e92..0000000 --- a/bsition/api/components/table.py +++ /dev/null @@ -1,79 +0,0 @@ -from fastapi import APIRouter, Response, status -from pydantic import BaseModel -from pydantic.utils import Optional - -from bsition.api.middleware.verify_token import VerifyTokenRoute -from bsition.backend import postgres - -tables_routes = APIRouter(route_class=VerifyTokenRoute) - - -class Table(BaseModel): - name: Optional[str] - column: Optional[str] - type: Optional[str] - column_data: Optional[list] - row_number: Optional[str] - columns: Optional[list] - columns_data: Optional[list] - - -class Sort(BaseModel): - property: str - order: str - priority: str - - -class Filter(BaseModel): - property: str - value: str - function: str - - -@tables_routes.post("/tables") -def create_table(aux: Table, response: Response): - postgres.create_table(aux.name) - response.status_code = status.HTTP_201_CREATED - - -@tables_routes.put("/tables/{name}") -def edit_table(aux: Table, name: str): - if aux.column is not None and aux.type is not None: - postgres.add_column(name, aux.column, aux.type) - if aux.column_data is not None: - postgres.insert_columns(name, aux.column_data) - if aux.row_number is not None: - postgres.edit_columns(name, aux.columns, aux.columns_data, aux.row_number) - - -@tables_routes.post("/tables/{name}/sort") -def create_sort(name: str, response: Response): - postgres.create_sort(name) - response.status_code = status.HTTP_201_CREATED - - -@tables_routes.put("/tables/{name}/sort") -def add_sort(aux: Sort, name: str): - postgres.add_sort(name, aux.property, aux.order, aux.priority) - - -@tables_routes.get("/tables/{name}/sort") -def sort(name: str): - return postgres.sort(name) - - -@tables_routes.post("/tables/{name}/filter") -def create_filter(name: str, response: Response): - postgres.create_filter(name) - postgres.add_filter_trigger(name) - response.status_code = status.HTTP_201_CREATED - - -@tables_routes.put("/tables/{name}/filter") -def add_filter(aux: Filter, name: str): - postgres.add_filter(name, aux.property, aux.value, aux.function) - - -@tables_routes.get("/tables/{name}/filter") -def filter(name: str): - return postgres.filter(name) diff --git a/bsition/api/components/user.py b/bsition/api/components/user.py deleted file mode 100644 index 667fd66..0000000 --- a/bsition/api/components/user.py +++ /dev/null @@ -1,37 +0,0 @@ -from fastapi import APIRouter, Response, status -from pydantic import BaseModel - -from bsition.api.middleware.verify_token import VerifyTokenRoute -from bsition.backend import postgres - -users_routes = APIRouter() - - -class User(BaseModel): - username: str - password: str - - -@users_routes.post("/users") -def create_user(aux: User, response: Response): - postgres.add_user(aux.username, aux.password) - response.status_code = status.HTTP_201_CREATED - - -users_routes.route_class = VerifyTokenRoute - - -@users_routes.get("/users") -def get_users(): - return postgres.get_users() - - -@users_routes.get("/users/{id}") -def get_by_id(id: str): - return postgres.get_user_by_id(id) - - -@users_routes.put("/users/{id}") -def edit_user(aux: User, id: str, response: Response): - postgres.edit_user(id, aux.username, aux.password) - response.status_code = status.HTTP_202_ACCEPTED diff --git a/bsition/api/components/__init__.py b/bsition/api/endpoints/__init__.py similarity index 100% rename from bsition/api/components/__init__.py rename to bsition/api/endpoints/__init__.py diff --git a/bsition/api/endpoints/documents.py b/bsition/api/endpoints/documents.py new file mode 100644 index 0000000..0ca1ee9 --- /dev/null +++ b/bsition/api/endpoints/documents.py @@ -0,0 +1,39 @@ +import json + +from fastapi import APIRouter +from fastapi.responses import JSONResponse + +from bsition.api.middleware.verify_token import VerifyTokenRoute +from bsition.api.models.document import Document, DocumentUpdate +from bsition.backend import elastic, mongo + +router = APIRouter(route_class=VerifyTokenRoute) + + +@router.post("") +def create(aux: Document): + mongo.create_document(json.loads(json.dumps(aux.__dict__))) + return JSONResponse(content={"detail": "Document created"}, status_code=201) + + +@router.get("") +def get_documents(query: str = None): + if query is not None: + return elastic.search("test-index", query) + return mongo.get_documents() + + +@router.get("/{id}") +def get_by_id(id: str): + return mongo.get_document_by_id(id) + + +@router.put("/{id}") +def edit_data(aux: DocumentUpdate, id: str): + if aux.data is not None: + mongo.edit_data(id, aux.data) + if aux.access is not None: + mongo.edit_access(id, aux.access) + if aux.name is not None: + mongo.edit_name(id, aux.name) + return JSONResponse(content={"detail": "Document updated."}, status_code=202) diff --git a/bsition/api/endpoints/login.py b/bsition/api/endpoints/login.py new file mode 100644 index 0000000..0f47c44 --- /dev/null +++ b/bsition/api/endpoints/login.py @@ -0,0 +1,24 @@ +from fastapi import APIRouter, HTTPException +from fastapi.responses import JSONResponse + +from bsition.api.utils.jwt import write_token +from bsition.api.models.user import User +from bsition.backend.postgres import get_user_by_username_and_password + +router = APIRouter() + + +@router.post("/login") +def login(user: User): + if get_user_by_username_and_password(user.username, user.password) is not None: + return JSONResponse( + content={ + "access_token": write_token(user.dict()), + "token_type": "bearer" + }, + status_code=202) + else: + raise HTTPException( + status_code=400, + detail="User not found.", + ) diff --git a/bsition/api/endpoints/tables.py b/bsition/api/endpoints/tables.py new file mode 100644 index 0000000..a21ca95 --- /dev/null +++ b/bsition/api/endpoints/tables.py @@ -0,0 +1,60 @@ +from fastapi import APIRouter +from fastapi.responses import JSONResponse + +from bsition.api.middleware.verify_token import VerifyTokenRoute +from bsition.api.models.table import Table, Sort, Filter +from bsition.backend import postgres + +router = APIRouter(route_class=VerifyTokenRoute) + + +@router.post("") +def create_table(aux: Table): + postgres.create_table(aux.name) + return JSONResponse(content={"detail": "Table created."}, status_code=201) + + +@router.put("/{name}") +def edit_table(aux: Table, name: str): + if aux.column is not None and aux.type is not None: + postgres.add_column(name, aux.column, aux.type) + if aux.column_data is not None: + postgres.insert_columns(name, aux.column_data) + if aux.row_number is not None: + postgres.edit_columns(name, aux.columns, aux.columns_data, aux.row_number) + return JSONResponse(content={"detail": "Table updated."}, status_code=202) + + +@router.post("/{name}/sort") +def create_sort(name: str): + postgres.create_sort(name) + return JSONResponse(content={"detail": "Sort created."}, status_code=201) + + +@router.put("/{name}/sort") +def add_sort(aux: Sort, name: str): + postgres.add_sort(name, aux.property, aux.order, aux.priority) + return JSONResponse(content={"detail": "Sort updated."}, status_code=202) + + +@router.get("/{name}/sort") +def sort(name: str): + return postgres.sort(name) + + +@router.post("/{name}/filter") +def create_filter(name: str): + postgres.create_filter(name) + postgres.add_filter_trigger(name) + return JSONResponse(content={"detail": "Filter created."}, status_code=201) + + +@router.put("/{name}/filter") +def add_filter(aux: Filter, name: str): + postgres.add_filter(name, aux.property, aux.value, aux.function) + return JSONResponse(content={"detail": "Filter updated."}, status_code=202) + + +@router.get("/{name}/filter") +def filter(name: str): + return postgres.filter(name) diff --git a/bsition/api/endpoints/users.py b/bsition/api/endpoints/users.py new file mode 100644 index 0000000..7623c7e --- /dev/null +++ b/bsition/api/endpoints/users.py @@ -0,0 +1,39 @@ +from fastapi import APIRouter, HTTPException +from fastapi.responses import JSONResponse + +from bsition.api.middleware.verify_token import VerifyTokenRoute +from bsition.api.models.user import User +from bsition.backend import postgres + +router = APIRouter() + + +@router.post("") +def create_user(user: User): + if postgres.get_user_by_username_and_password(user.username, user.password) is not None: + raise HTTPException( + status_code=400, + detail="User already exists.", + ) + + postgres.add_user(user.username, user.password) + return JSONResponse(content={"detail": "User created."}, status_code=201) + + +router.route_class = VerifyTokenRoute + + +@router.get("") +def get_users(): + return postgres.get_users() + + +@router.get("/{id}") +def get_by_id(id: str): + return postgres.get_user_by_id(id) + + +@router.put("/{id}") +def edit_user(aux: User, id: str): + postgres.edit_user(id, aux.username, aux.password) + return JSONResponse(content={"detail": "User updated."}, status_code=202) diff --git a/bsition/api/main.py b/bsition/api/main.py new file mode 100644 index 0000000..29c46de --- /dev/null +++ b/bsition/api/main.py @@ -0,0 +1,8 @@ +from fastapi import FastAPI +from dotenv import load_dotenv +from bsition.api.api import router + +app = FastAPI() +app.include_router(router, prefix="/api") + +load_dotenv() diff --git a/bsition/api/middleware/verify_token.py b/bsition/api/middleware/verify_token.py index 5428bed..a85ef98 100644 --- a/bsition/api/middleware/verify_token.py +++ b/bsition/api/middleware/verify_token.py @@ -1,7 +1,7 @@ from fastapi import Request from fastapi.routing import APIRoute -from bsition.api.jwt import validate_token +from bsition.api.utils.jwt import validate_token class VerifyTokenRoute(APIRoute): diff --git a/bsition/api/routes/__init__.py b/bsition/api/models/__init__.py similarity index 100% rename from bsition/api/routes/__init__.py rename to bsition/api/models/__init__.py diff --git a/bsition/api/models/document.py b/bsition/api/models/document.py new file mode 100644 index 0000000..bbffa74 --- /dev/null +++ b/bsition/api/models/document.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel +from pydantic.types import Optional + + +class Document(BaseModel): + name: str + access: list + data: str + owner: str + + +class DocumentUpdate(BaseModel): + name: Optional[str] + access: Optional[list] + data: Optional[str] diff --git a/bsition/api/models/table.py b/bsition/api/models/table.py new file mode 100644 index 0000000..9fe6be3 --- /dev/null +++ b/bsition/api/models/table.py @@ -0,0 +1,24 @@ +from pydantic import BaseModel +from pydantic.utils import Optional + + +class Table(BaseModel): + name: Optional[str] + column: Optional[str] + type: Optional[str] + column_data: Optional[list] + row_number: Optional[str] + columns: Optional[list] + columns_data: Optional[list] + + +class Sort(BaseModel): + property: str + order: str + priority: str + + +class Filter(BaseModel): + property: str + value: str + function: str diff --git a/bsition/api/models/user.py b/bsition/api/models/user.py new file mode 100644 index 0000000..26353ea --- /dev/null +++ b/bsition/api/models/user.py @@ -0,0 +1,7 @@ +from pydantic import BaseModel +from pydantic.types import Optional + + +class User(BaseModel): + username: str + password: Optional[str] diff --git a/bsition/api/routes/auth.py b/bsition/api/routes/auth.py deleted file mode 100644 index 8cc8022..0000000 --- a/bsition/api/routes/auth.py +++ /dev/null @@ -1,20 +0,0 @@ -from fastapi import APIRouter -from fastapi.responses import JSONResponse -from pydantic import BaseModel - -from bsition.api.jwt import write_token -from bsition.backend.postgres import get_user_by_username - -auth_routes = APIRouter() - - -class User(BaseModel): - username: str - - -@auth_routes.post("/login") -def login(user: User): - if get_user_by_username(user.username) is not None: - return write_token(user.dict()) - else: - return JSONResponse(content={"message": "User not found"}, status_code=404) diff --git a/bsition/api/utils/__init__.py b/bsition/api/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bsition/api/jwt.py b/bsition/api/utils/jwt.py similarity index 92% rename from bsition/api/jwt.py rename to bsition/api/utils/jwt.py index 1b576ae..1e05f62 100644 --- a/bsition/api/jwt.py +++ b/bsition/api/utils/jwt.py @@ -11,7 +11,7 @@ def expire_date(days: int): def write_token(data: dict): return encode( - payload={**data, "exp": expire_date(2)}, key=getenv("SECRET"), algorithm="HS256" + payload={**data, "exp": expire_date(1)}, key=getenv("SECRET"), algorithm="HS256" ) diff --git a/bsition/backend/postgres.py b/bsition/backend/postgres.py index 56140d1..9608a1f 100644 --- a/bsition/backend/postgres.py +++ b/bsition/backend/postgres.py @@ -282,6 +282,17 @@ def get_user_by_username(username): return cur.fetchone() +def get_user_by_username_and_password(username, password): + conn = get_connection() + cur = conn.cursor() + cur.execute( + sql.SQL("SELECT * FROM users WHERE username = {username} AND password = {password}").format( + username=sql.Literal(username), password=sql.Literal(password) + ) + ) + return cur.fetchone() + + def edit_user(id, username, password): conn = get_connection() cur = conn.cursor() @@ -293,7 +304,6 @@ def edit_user(id, username, password): if data[i] is None: continue - print(id) cur.execute( sql.SQL("UPDATE users SET {col} = {value} WHERE id = {id}").format( col=sql.Identifier(column), diff --git a/docker-compose.yml b/docker-compose.yml index 849c249..a918ad1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,7 @@ services: environment: - xpack.security.enabled=false - discovery.type=single-node - - "ES_JAVA_OPTS=-Xms1G -Xmx1G" + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" volumes: - ./data/elasticsearch:/usr/share/elasticsearch/data ports: diff --git a/run.sh b/run.sh index 305c56f..3bde578 100644 --- a/run.sh +++ b/run.sh @@ -26,7 +26,7 @@ done if [ "$RUN" = 'install' ]; then poetry install elif [ "$RUN" = 'api' ]; then - poetry run uvicorn bsition.api.api:app --host 0.0.0.0 + poetry run uvicorn bsition.api.main:app --host 0.0.0.0 elif [ "$RUN" = 'configure' ]; then poetry run configure else