Skip to content

Commit b68fe9d

Browse files
update sim
1 parent d660a7f commit b68fe9d

File tree

18 files changed

+1066
-486
lines changed

18 files changed

+1066
-486
lines changed

src/quart_sqlalchemy/retry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ async def add_user_post(db, user_id, post_values):
9999
import sqlalchemy.exc
100100
import sqlalchemy.orm
101101
import tenacity
102+
from tenacity import RetryError
102103

103104

104105
sa = sqlalchemy

src/quart_sqlalchemy/sim/app.py

Lines changed: 55 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,88 @@
1-
import json
21
import logging
3-
import re
42
import typing as t
3+
from copy import deepcopy
4+
from functools import wraps
55

6-
import sqlalchemy as sa
7-
from pydantic import BaseModel
86
from quart import g
97
from quart import Quart
108
from quart import request
11-
from quart import Request
129
from quart import Response
10+
from quart.typing import ResponseReturnValue
11+
from quart_schema import APIKeySecurityScheme
12+
from quart_schema import HttpSecurityScheme
1313
from quart_schema import QuartSchema
14+
from werkzeug.utils import import_string
1415

15-
from quart_sqlalchemy import Base
16-
from quart_sqlalchemy import SQLAlchemyConfig
17-
from quart_sqlalchemy.framework import QuartSQLAlchemy
18-
from quart_sqlalchemy.sim.util import ObjectID
1916

20-
21-
AUTHORIZATION_PATTERN = re.compile(r"Bearer (?P<token>.+)")
2217
logging.basicConfig(level=logging.INFO)
2318
logger = logging.getLogger(__name__)
2419

2520

26-
class MyBase(Base):
27-
type_annotation_map = {ObjectID: sa.Integer}
28-
29-
30-
app = Quart(__name__)
31-
db = QuartSQLAlchemy(
32-
SQLAlchemyConfig.parse_obj(
33-
{
34-
"model_class": MyBase,
35-
"binds": {
36-
"default": {
37-
"engine": {"url": "sqlite:///file:mem.db?mode=memory&cache=shared&uri=true"},
38-
"session": {"expire_on_commit": False},
39-
},
40-
"read-replica": {
41-
"engine": {"url": "sqlite:///file:mem.db?mode=memory&cache=shared&uri=true"},
42-
"session": {"expire_on_commit": False},
43-
"read_only": True,
44-
},
45-
"async": {
46-
"engine": {
47-
"url": "sqlite+aiosqlite:///file:mem.db?mode=memory&cache=shared&uri=true"
48-
},
49-
"session": {"expire_on_commit": False},
50-
},
51-
},
52-
}
53-
)
21+
BLUEPRINTS = ("quart_sqlalchemy.sim.views.api",)
22+
EXTENSIONS = (
23+
"quart_sqlalchemy.sim.db.db",
24+
"quart_sqlalchemy.sim.app.schema",
25+
"quart_sqlalchemy.sim.auth.auth",
5426
)
55-
openapi = QuartSchema(app)
56-
57-
58-
class RequestAuth(BaseModel):
59-
client: t.Optional[t.Any] = None
60-
user: t.Optional[t.Any] = None
61-
62-
@property
63-
def has_client(self):
64-
return self.client is not None
65-
66-
@property
67-
def has_user(self):
68-
return self.user is not None
69-
70-
@property
71-
def is_anonymous(self):
72-
return all([self.has_client is False, self.has_user is False])
73-
7427

75-
def get_request_client(request: Request):
76-
api_key = request.headers.get("X-Public-API-Key")
77-
if not api_key:
78-
return
28+
DEFAULT_CONFIG = {
29+
"QUART_AUTH_SECURITY_SCHEMES": {
30+
"public-api-key": APIKeySecurityScheme(in_="header", name="X-Public-API-Key"),
31+
"session-token-bearer": HttpSecurityScheme(scheme="bearer", bearer_format="opaque"),
32+
},
33+
"REGISTER_BLUEPRINTS": ["quart_sqlalchemy.sim.views.api"],
34+
}
7935

80-
with g.bind.Session() as session:
81-
try:
82-
magic_client = g.h.MagicClient(session).get_by_public_api_key(api_key)
83-
except ValueError:
84-
return
85-
else:
86-
return magic_client
8736

37+
schema = QuartSchema(security_schemes=DEFAULT_CONFIG["QUART_AUTH_SECURITY_SCHEMES"])
8838

89-
def get_request_user(request: Request):
90-
auth_header = request.headers.get("Authorization")
9139

92-
if not auth_header:
93-
return
94-
m = AUTHORIZATION_PATTERN.match(auth_header)
95-
if m is None:
96-
raise RuntimeError("invalid authorization header")
40+
def wrap_response(func: t.Callable) -> t.Callable:
41+
@wraps(func)
42+
async def decorator(result: ResponseReturnValue) -> Response:
43+
# import pdb
9744

98-
auth_token = m.group("auth_token")
45+
# pdb.set_trace()
46+
return await func(result)
9947

100-
with g.bind.Session() as session:
101-
try:
102-
auth_user = g.h.AuthUser(session).get_by_session_token(auth_token)
103-
except ValueError:
104-
return
105-
else:
106-
return auth_user
48+
return decorator
10749

10850

109-
@app.before_request
110-
def set_ethereum_network():
111-
g.request_network = request.headers.get("X-Fortmatic-Network", "GOERLI").upper()
51+
def create_app(
52+
override_config: t.Optional[t.Dict[str, t.Any]] = None,
53+
extensions: t.Sequence[str] = EXTENSIONS,
54+
blueprints: t.Sequence[str] = BLUEPRINTS,
55+
):
56+
override_config = override_config or {}
11257

58+
config = deepcopy(DEFAULT_CONFIG)
59+
config.update(override_config)
11360

114-
@app.before_request
115-
def set_bind_handlers_for_request():
116-
from quart_sqlalchemy.sim.handle import Handlers
61+
app = Quart(__name__)
62+
app.config.from_mapping(config)
11763

118-
g.db = db
64+
for path in extensions:
65+
extension = import_string(path)
66+
extension.init_app(app)
11967

120-
method = request.method
121-
if method in ["GET", "OPTIONS", "TRACE", "HEAD"]:
122-
bind = "read-replica"
123-
else:
124-
bind = "default"
68+
for path in blueprints:
69+
bp = import_string(path)
70+
app.register_blueprint(bp)
12571

126-
g.bind = db.get_bind(bind)
127-
g.h = Handlers(g.bind)
72+
@app.before_request
73+
def set_ethereum_network():
74+
g.network = request.headers.get("X-Ethereum-Network", "GOERLI").upper()
12875

76+
# app.make_response = wrap_response(app.make_response)
12977

130-
@app.before_request
131-
def set_request_auth():
132-
g.auth = RequestAuth(
133-
client=get_request_client(request),
134-
user=get_request_user(request),
135-
)
78+
return app
13679

13780

138-
@app.after_request
139-
async def add_json_response_envelope(response: Response) -> Response:
140-
if response.mimetype != "application/json":
141-
return response
142-
data = await response.get_json()
143-
payload = dict(status="ok", message="", data=data)
144-
response.set_data(json.dumps(payload))
145-
return response
81+
# @app.after_request
82+
# async def add_json_response_envelope(response: Response) -> Response:
83+
# if response.mimetype != "application/json":
84+
# return response
85+
# data = await response.get_json()
86+
# payload = dict(status="ok", message="", data=data)
87+
# response.set_data(json.dumps(payload))
88+
# return response

0 commit comments

Comments
 (0)