Skip to content

Commit

Permalink
Marketing auth (#14)
Browse files Browse the repository at this point in the history
 Удалил дэш
 Сделал возможность писать любой мусор
 Добавил мэппинг на пользователей Твой ФФ
  • Loading branch information
Dyakov Roman authored Mar 16, 2023
1 parent bc1491f commit a0f177b
Show file tree
Hide file tree
Showing 22 changed files with 237 additions and 192 deletions.
19 changes: 19 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
run:
source ./venv/bin/activate && uvicorn --reload --log-config logging_dev.conf marketing_api.routes.base:app

configure: venv
source ./venv/bin/activate && pip install -r requirements.dev.txt -r requirements.txt

venv:
python3.11 -m venv venv

format:
source ./venv/bin/activate && autoflake -r --in-place --remove-all-unused-imports ./marketing_api
source ./venv/bin/activate && isort ./marketing_api
source ./venv/bin/activate && black ./marketing_api

db:
docker run -d -p 5432:5432 -e POSTGRES_HOST_AUTH_METHOD=trust --name db-marketing-backend postgres:15

migrate:
source ./venv/bin/activate && alembic upgrade head
21 changes: 21 additions & 0 deletions logging_dev.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[loggers]
keys=root

[handlers]
keys=all

[formatters]
keys=main

[logger_root]
level=DEBUG
handlers=all

[handler_all]
class=StreamHandler
formatter=main
level=DEBUG
args=(sys.stdout,)

[formatter_main]
format=%(asctime)s %(levelname)-8s %(name)-15s %(message)s
3 changes: 2 additions & 1 deletion marketing_api/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from marketing_api.routes.base import app
import uvicorn

from marketing_api.routes.base import app


if __name__ == '__main__':
uvicorn.run(app)
Empty file.
27 changes: 0 additions & 27 deletions marketing_api/dashboard/base.py

This file was deleted.

3 changes: 0 additions & 3 deletions marketing_api/methods/__init__.py

This file was deleted.

91 changes: 0 additions & 91 deletions marketing_api/methods/dash_db.py

This file was deleted.

1 change: 1 addition & 0 deletions marketing_api/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .db import ActionsInfo


__all__ = ["ActionsInfo"]
3 changes: 2 additions & 1 deletion marketing_api/models/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import re
from sqlalchemy.ext.declarative import as_declarative, declared_attr

from sqlalchemy.orm import as_declarative, declared_attr


@as_declarative()
Expand Down
56 changes: 37 additions & 19 deletions marketing_api/models/db.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import enum
from datetime import datetime
import sqlalchemy.orm

import sqlalchemy as sa
from sqlalchemy.orm import relationship
from sqlalchemy import Column

from .base import Base
import enum


class Actions(str, enum.Enum):
Expand All @@ -12,26 +15,41 @@ class Actions(str, enum.Enum):
INSTALLED: str = "installed"


class ActionsInfo(Base):
"""Actions from user"""

id = Column(sqlalchemy.Integer, primary_key=True)
user_id = Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("user.id"))
action = Column(sqlalchemy.String, nullable=False)
path_from = Column(sqlalchemy.String, nullable=False)
path_to = Column(sqlalchemy.String, nullable=True)
additional_data = Column(sqlalchemy.String, nullable=True)
create_ts = Column(sqlalchemy.DateTime, nullable=False, default=datetime.utcnow)
class User(Base):
id = Column(sa.Integer, primary_key=True)
union_number = Column(sa.String, nullable=True)
auth_user_id = Column(sa.Integer, nullable=True)
modify_ts = Column(sa.DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
create_ts = Column(sa.DateTime, nullable=False, default=datetime.utcnow)

actions = relationship(
"ActionsInfo",
primaryjoin="foreign(ActionsInfo.user_id)==User.id",
uselist=True,
back_populates="user",
)

def __repr__(self):
return f"ActionInfo(user_id: {self.user_id}, action: {self.action}"
return f"User(id={self.id}, union_number={self.union_number}, auth_user_id={self.auth_user_id})"


class User(Base):
id = Column(sqlalchemy.Integer, primary_key=True)
union_number = Column(sqlalchemy.String, nullable=True)
modify_ts = Column(sqlalchemy.DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
create_ts = Column(sqlalchemy.DateTime, nullable=False, default=datetime.utcnow)
class ActionsInfo(Base):
"""Actions from user"""

id = Column(sa.Integer, primary_key=True)
user_id = Column(sa.Integer, nullable=False)
action = Column(sa.String, nullable=False)
path_from = Column(sa.String, nullable=True)
path_to = Column(sa.String, nullable=True)
additional_data = Column(sa.String, nullable=True)
create_ts = Column(sa.DateTime, nullable=False, default=datetime.utcnow)

user = relationship(
User,
primaryjoin="foreign(ActionsInfo.user_id)==User.id",
uselist=False,
back_populates="actions"
)

def __repr__(self):
return f"User(id: {self.id}, union_number: {self.union_number}"
return f"ActionInfo(user_id={self.user_id}, action={self.action})"
1 change: 1 addition & 0 deletions marketing_api/routes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .base import app


__all__ = ["app"]
77 changes: 48 additions & 29 deletions marketing_api/routes/base.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
from pydantic import ValidationError
from fastapi import FastAPI
from fastapi import Path
from fastapi.middleware.cors import CORSMiddleware
from fastapi_sqlalchemy import DBSessionMiddleware
import logging

from auth_lib.fastapi import UnionAuth
from fastapi import FastAPI, Depends
from fastapi.exceptions import HTTPException
from fastapi_sqlalchemy import db
from starlette.responses import PlainTextResponse, RedirectResponse
from starlette.middleware.wsgi import WSGIMiddleware
from sqlalchemy.exc import IntegrityError
import starlette
from fastapi.middleware.cors import CORSMiddleware
from fastapi_sqlalchemy import DBSessionMiddleware, db
from pydantic import ValidationError
from starlette.responses import PlainTextResponse

from marketing_api import __version__
from marketing_api.settings import get_settings
from marketing_api.models import ActionsInfo
from marketing_api.dashboard.base import dash_app
from marketing_api.models.db import User as DbUser
from marketing_api.settings import get_settings

from .models import ActionInfo, User, UserPatch


settings = get_settings()
logger = logging.getLogger(__name__)
app = FastAPI(
title='Сервис мониторинга активности пользователей',
description='API для проведения маркетинговых исследований',
version=__version__,

# Настраиваем интернет документацию
root_path=settings.ROOT_PATH if __version__ != 'dev' else '/',
docs_url=None if __version__ != 'dev' else '/docs',
Expand All @@ -33,35 +30,60 @@


@app.post('/v1/action')
async def write_action(user_action_info: ActionInfo):
db.session.add(ActionsInfo(**user_action_info.dict()))
async def write_action(
user_action_info: ActionInfo,
user=Depends(UnionAuth(auto_error=False, allow_none=True)),
):
"""Создать действие"""
user_id = user.get("id") if user else None
logger.debug(f"write_action by {user_id=}")
ai = ActionsInfo(**user_action_info.dict())
db.session.add(ai)
db.session.flush()
if ai.user:
logger.debug(ai.user)
ai.user.auth_user_id = user_id
db.session.flush()
else:
logger.warning(f"write_action with user {user_action_info.user_id} not exists!")
return PlainTextResponse(status_code=200)


@app.post('/v1/user', response_model=User)
async def create_user():
user = DbUser()
db.session.add(user)
async def create_user(user=Depends(UnionAuth(auto_error=False, allow_none=True))):
"""Создать уникальный идентификатор установки"""
user_id = user.get("id") if user else None
logger.debug(f"create_user by {user_id=}")
dbuser = DbUser()
dbuser.auth_user_id = user.get("id") if user else None
db.session.add(dbuser)
db.session.flush()
return user
return dbuser


@app.patch('/v1/user/{id}', response_model=User)
async def patch_user(id: int, patched_user: UserPatch):
async def patch_user(
id: int,
patched_user: UserPatch,
user=Depends(UnionAuth(["marketing.user.patch"]))
):
"""Изменить пользователя в маркетинге
Необходимые scopes: `marketing.user.patch`
"""
user_id = user.get("id") if user else None
logger.debug(f"patch_user by {user_id=}")
result: DbUser = db.session.query(DbUser).filter(DbUser.id == id).one_or_none()
if not result:
raise HTTPException(404, "No user found")
result.union_number = patched_user.union_number
if patched_user.union_number:
result.union_number = patched_user.union_number
if patched_user.auth_user_id:
result.union_number = patched_user.auth_user_id
db.session.flush()
return result


@app.get('/')
async def to_dashboard():
return RedirectResponse("/dashboard")


@app.exception_handler(ValidationError)
async def http_validation_error_handler(req, exc):
return PlainTextResponse("Invalid data", status_code=422)
Expand All @@ -85,6 +107,3 @@ async def http_error_handler(req, exc):
allow_methods=settings.CORS_ALLOW_METHODS,
allow_headers=settings.CORS_ALLOW_HEADERS,
)

app.mount("/dashboard", WSGIMiddleware(dash_app.server))

Loading

0 comments on commit a0f177b

Please sign in to comment.