Skip to content

Commit 6f5a8ff

Browse files
authored
Merge pull request #1 from Jahn16/feature/url-model
feature/url model
2 parents 1f3caff + a93d566 commit 6f5a8ff

27 files changed

+398
-32
lines changed

Dockerfile

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,17 @@ RUN pip install -r requirements.txt
99

1010
FROM python:3.10-slim@sha256:134b8763131e8a6800c1393108ec60efc3ed6d793243787bddd1b972ed10b722
1111

12-
RUN groupadd -g 999 user && useradd -r -u 999 -g user user
12+
RUN groupadd -g 999 fastmock && useradd -r -u 999 -g fastmock fastmock
1313

14-
RUN mkdir /usr/app && chown user:user /usr/app
14+
RUN mkdir /usr/app && chown fastmock:fastmock /usr/app
1515
WORKDIR /usr/app
1616

17-
COPY --chown=user:user --from=build /usr/app/venv ./venv
18-
COPY --chown=user:user . .
17+
COPY --chown=fastmock:fastmock --from=build /usr/app/venv ./venv
18+
COPY --chown=fastmock:fastmock . .
1919
USER 999
2020

2121
ENV PATH="/usr/app/venv/bin:$PATH"
22-
ENV FLASK_APP="app/app.py"
22+
EXPOSE 8000
2323

2424

25-
EXPOSE 5000
2625
ENTRYPOINT ["./boot.sh"]
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""fixed typo in relationship in url
2+
3+
Revision ID: 0255b9c8dc8c
4+
Revises: 040d471bdf71
5+
Create Date: 2023-11-20 17:45:11.099114
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '0255b9c8dc8c'
14+
down_revision = '040d471bdf71'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
pass
22+
# ### end Alembic commands ###
23+
24+
25+
def downgrade() -> None:
26+
# ### commands auto generated by Alembic - please adjust! ###
27+
pass
28+
# ### end Alembic commands ###
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""added url and user relationship
2+
3+
Revision ID: 040d471bdf71
4+
Revises: cb19dfca774e
5+
Create Date: 2023-11-20 17:41:56.456822
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '040d471bdf71'
14+
down_revision = 'cb19dfca774e'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column('urls', sa.Column('owner_id', sa.Integer(), nullable=True))
22+
op.create_foreign_key(None, 'urls', 'users', ['owner_id'], ['id'])
23+
# ### end Alembic commands ###
24+
25+
26+
def downgrade() -> None:
27+
# ### commands auto generated by Alembic - please adjust! ###
28+
op.drop_constraint(None, 'urls', type_='foreignkey')
29+
op.drop_column('urls', 'owner_id')
30+
# ### end Alembic commands ###
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"""renamed url netloc to hostname
2+
3+
Revision ID: 388d699c0bd4
4+
Revises: 968a097da660
5+
Create Date: 2023-11-18 15:25:59.199354
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '388d699c0bd4'
14+
down_revision = '968a097da660'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.add_column('urls', sa.Column('hostname', sa.String(), nullable=True))
22+
op.drop_index('ix_urls_netloc', table_name='urls')
23+
op.create_index(op.f('ix_urls_hostname'), 'urls', ['hostname'], unique=False)
24+
op.drop_column('urls', 'netloc')
25+
# ### end Alembic commands ###
26+
27+
28+
def downgrade() -> None:
29+
# ### commands auto generated by Alembic - please adjust! ###
30+
op.add_column('urls', sa.Column('netloc', sa.VARCHAR(), autoincrement=False, nullable=True))
31+
op.drop_index(op.f('ix_urls_hostname'), table_name='urls')
32+
op.create_index('ix_urls_netloc', 'urls', ['netloc'], unique=False)
33+
op.drop_column('urls', 'hostname')
34+
# ### end Alembic commands ###
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""create urls table
2+
3+
Revision ID: 968a097da660
4+
Revises: 2259f20dedb2
5+
Create Date: 2023-11-18 15:17:37.496932
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = '968a097da660'
14+
down_revision = '2259f20dedb2'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table('urls',
22+
sa.Column('id', sa.UUID(), nullable=False),
23+
sa.Column('netloc', sa.String(), nullable=True),
24+
sa.PrimaryKeyConstraint('id')
25+
)
26+
op.create_index(op.f('ix_urls_netloc'), 'urls', ['netloc'], unique=False)
27+
op.add_column('requests', sa.Column('url_id', sa.UUID(), nullable=True))
28+
op.create_foreign_key(None, 'requests', 'urls', ['url_id'], ['id'])
29+
# ### end Alembic commands ###
30+
31+
32+
def downgrade() -> None:
33+
# ### commands auto generated by Alembic - please adjust! ###
34+
op.drop_constraint(None, 'requests', type_='foreignkey')
35+
op.drop_column('requests', 'url_id')
36+
op.drop_index(op.f('ix_urls_netloc'), table_name='urls')
37+
op.drop_table('urls')
38+
# ### end Alembic commands ###
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""added missing url field to request
2+
3+
Revision ID: cb19dfca774e
4+
Revises: 388d699c0bd4
5+
Create Date: 2023-11-18 16:20:31.306135
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
11+
12+
# revision identifiers, used by Alembic.
13+
revision = 'cb19dfca774e'
14+
down_revision = '388d699c0bd4'
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
pass
22+
# ### end Alembic commands ###
23+
24+
25+
def downgrade() -> None:
26+
# ### commands auto generated by Alembic - please adjust! ###
27+
pass
28+
# ### end Alembic commands ###

app/api/api_v1/endpoints/login.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import structlog
12
from fastapi import APIRouter, Depends, HTTPException
23
from fastapi.security import OAuth2PasswordRequestForm
34
from sqlalchemy.orm import Session
@@ -8,21 +9,25 @@
89
from app.security import create_access_token
910

1011
router = APIRouter()
12+
logger = structlog.get_logger()
1113

1214

1315
@router.post("", response_model=Token)
1416
async def login_for_access_token(
1517
db: Session = Depends(get_db),
1618
form_data: OAuth2PasswordRequestForm = Depends(),
1719
):
18-
user = authenticate_user(
19-
db, form_data.username, form_data.password
20-
)
20+
user = authenticate_user(db, form_data.username, form_data.password)
2121
if not user:
22+
logger.warning(
23+
"Authentication error for user",
24+
user_email=form_data.username,
25+
)
2226
raise HTTPException(
2327
status_code=401,
2428
detail="Incorrect username or password",
2529
headers={"WWW-Authenticate": "Bearer"},
2630
)
2731
access_token = create_access_token(subject=str(user.id))
32+
logger.info("User logged in successfully", user_id=user.id)
2833
return {"access_token": access_token, "token_type": "bearer"}
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
from typing import Any
22

3+
import structlog
34
from fastapi import APIRouter, Depends, HTTPException
5+
from fastapi import Request as StarletteRequest
46
from sqlalchemy.orm import Session
57

6-
from app.api.deps import get_db, get_current_user
7-
from app.schemas.user import User
8+
from app.api.deps import get_db
89
from app.crud.request import get_request
910

1011
router = APIRouter()
12+
logger = structlog.get_logger()
1113

1214

1315
@router.get("/{mock_endpoint:path}")
1416
@router.post("/{mock_endpoint:path}")
1517
def read_request_response(
1618
mock_endpoint: str,
17-
user: User = Depends(get_current_user),
19+
starlette_request: StarletteRequest,
1820
db: Session = Depends(get_db),
1921
) -> dict | Any:
20-
request = get_request(db, user.id, endpoint=mock_endpoint)
22+
hostname = starlette_request.url.hostname or ""
23+
url_id = hostname.split(".")[0]
24+
request = get_request(db, url_id=url_id, endpoint=f"/{mock_endpoint}")
2125
if not request:
26+
logger.info("Request not found", url_id=url_id)
2227
raise HTTPException(status_code=404, detail="Request not found")
28+
logger.info(
29+
"Sending mocked response for request",
30+
request_id=request.id,
31+
url_id=url_id,
32+
)
2333
return request.response

app/api/api_v1/endpoints/requests.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1+
import structlog
12
from fastapi import APIRouter, Depends, HTTPException
23
from sqlalchemy.orm import Session
34

45
from app.api.deps import get_db, get_current_user
56
from app.schemas.request import Request, RequestCreate, RequestUpdate
67
from app.schemas.user import User
78
from app.crud import request as crud_request
9+
from app.crud import url as crud_url
810

911
router = APIRouter()
12+
logger = structlog.get_logger()
1013

1114

1215
@router.get("/read", response_model=Request)
@@ -17,7 +20,9 @@ def read_request(
1720
):
1821
request = crud_request.get_request_by_id(db, request_id, user.id)
1922
if not request:
23+
logger.info("Request not found", request_id=request_id)
2024
raise HTTPException(status_code=404, detail="Request Not Found")
25+
logger.info("Retrieved request", request_id=request_id, user_id=user.id)
2126
return request
2227

2328

@@ -27,14 +32,20 @@ def create_request(
2732
user: User = Depends(get_current_user),
2833
db: Session = Depends(get_db),
2934
):
30-
db_request = crud_request.get_request(
31-
db=db, user_id=user.id, endpoint=request.endpoint
35+
db_url = crud_url.get_url(db, request.url, user.id)
36+
if not db_url:
37+
db_url = crud_url.create_url(db, request.url, user.id)
38+
logger.info(f"Created url for {request.url}", url_id=db_url.id)
39+
request = crud_request.create_request(
40+
db=db, request=request, user_id=user.id, url_id=str(db_url.id)
3241
)
33-
if db_request:
34-
raise HTTPException(
35-
status_code=400, detail="Endpoint already registered"
36-
)
37-
return crud_request.create_request(db=db, request=request, user_id=user.id)
42+
logger.info(
43+
"Created request",
44+
request_id=request.id,
45+
url_id=db_url.id,
46+
user_id=user.id,
47+
)
48+
return request
3849

3950

4051
@router.put("/update", response_model=Request)
@@ -47,7 +58,13 @@ def update_request(
4758
crud_request.update_request(db, request_in, request_id, user.id)
4859
updated_request = crud_request.get_request_by_id(db, request_id, user.id)
4960
if not updated_request:
61+
logger.warning(
62+
"Request not found for updating",
63+
request_id=request_id,
64+
user_id=user.id,
65+
)
5066
raise HTTPException(status_code=404, detail="Request Not Found")
67+
logger.info("Updated request", request_id=request_id, user_id=user.id)
5168
return updated_request
5269

5370

@@ -59,6 +76,12 @@ def delete_request(
5976
):
6077
request = crud_request.get_request_by_id(db, request_id, user.id)
6178
if not request:
79+
logger.warning(
80+
"Request not found for deleting",
81+
request_id=request_id,
82+
user_id=user.id,
83+
)
6284
raise HTTPException(status_code=404, detail="Request Not Found")
6385
crud_request.delete_request(db, request)
86+
logger.info("Deleted request", request_id=request_id, user_id=user.id)
6487
return request

app/api/api_v1/endpoints/user.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from fastapi import APIRouter, HTTPException, Depends
22
from sqlalchemy.orm import Session
3+
import structlog
34

45
from app.api.deps import get_db, get_current_user
56
from app.schemas.user import User, UserCreate, UserUpdate
67
from app.crud import user as crud_user
78

89

910
router = APIRouter()
11+
logger = structlog.get_logger()
1012

1113

1214
@router.get("/read", response_model=User)
@@ -15,16 +17,24 @@ def read_user(
1517
):
1618
db_user = crud_user.get_user(db, user_id=user.id)
1719
if not db_user:
20+
logger.warning("User not found", user_id=user.id)
1821
raise HTTPException(status_code=404, detail="User not found")
22+
logger.info("Retrieved user", user_id=db_user.id)
1923
return db_user
2024

2125

2226
@router.post("/create", response_model=User)
2327
def create_user(user: UserCreate, db: Session = Depends(get_db)):
2428
db_user = crud_user.get_user_by_email(db, email=user.email)
2529
if db_user:
30+
logger.warning(
31+
"Email already registered by another user",
32+
user_email=user.email,
33+
)
2634
raise HTTPException(status_code=400, detail="Email already registered")
27-
return crud_user.create_user(db=db, user=user)
35+
db_user = crud_user.create_user(db=db, user=user)
36+
logger.info("Created user", user_id=db_user.id)
37+
return db_user
2838

2939

3040
@router.put("/update", response_model=User)
@@ -36,7 +46,9 @@ def update_user(
3646
crud_user.update_user(db, user_in, user.id)
3747
db_user = crud_user.get_user(db, user.id)
3848
if not db_user:
49+
logger.warning("User not found while updating", user_id=user.id)
3950
raise HTTPException(status_code=404, detail="User not found")
51+
logger.info("Updated user", user_id=user.id)
4052
return db_user
4153

4254

@@ -45,4 +57,5 @@ def delete_user(
4557
user: User = Depends(get_current_user), db: Session = Depends(get_db)
4658
):
4759
crud_user.delete_user(db, user)
60+
logger.info("Deleted user", user_id=user.id)
4861
return user

0 commit comments

Comments
 (0)