aiohttp-try

This commit is contained in:
Untone 2023-11-29 10:23:41 +03:00
parent 36ab83d02f
commit 63eb952655
9 changed files with 77 additions and 68 deletions

View File

@ -1,11 +1,13 @@
[0.2.16] [0.2.16]
- resolvers: collab inviting logics - resolvers: collab inviting logics
- orm: invite entity
- schema: Reaction.range -> Reaction.quote
- resolvers: queries and mutations revision and renaming - resolvers: queries and mutations revision and renaming
- resolvers: delete_topic(slug) implemented - resolvers: delete_topic(slug) implemented
- resolvers: added get_shout_followers - resolvers: added get_shout_followers
- resolvers: load_shouts_by filters implemented - resolvers: load_shouts_by filters implemented
- orm: invite entity
- schema: Reaction.range -> Reaction.quote
- filters: time_ago -> after
- httpx -> aiohttp
[0.2.15] [0.2.15]
- schema: Shout.created_by removed - schema: Shout.created_by removed

View File

@ -7,6 +7,7 @@ from sentry_sdk.integrations.ariadne import AriadneIntegration
from sentry_sdk.integrations.redis import RedisIntegration from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.integrations.starlette import StarletteIntegration from sentry_sdk.integrations.starlette import StarletteIntegration
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
from starlette.applications import Starlette from starlette.applications import Starlette
from starlette.endpoints import HTTPEndpoint, Request from starlette.endpoints import HTTPEndpoint, Request
from starlette.responses import JSONResponse from starlette.responses import JSONResponse
@ -37,7 +38,13 @@ async def start_up():
sentry_sdk.init( sentry_sdk.init(
SENTRY_DSN, SENTRY_DSN,
enable_tracing=True, enable_tracing=True,
integrations=[StarletteIntegration(), AriadneIntegration(), SqlalchemyIntegration(), RedisIntegration()], integrations=[
StarletteIntegration(),
AriadneIntegration(),
SqlalchemyIntegration(),
RedisIntegration(),
AioHttpIntegration(),
],
) )
except Exception as e: except Exception as e:

View File

@ -9,14 +9,14 @@ readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.12" python = "^3.12"
SQLAlchemy = "^2.0.22" SQLAlchemy = "^2.0.22"
httpx = "^0.25.0"
psycopg2-binary = "^2.9.9" psycopg2-binary = "^2.9.9"
redis = {extras = ["hiredis"], version = "^5.0.1"} redis = {extras = ["hiredis"], version = "^5.0.1"}
uvicorn = "^0.24" uvicorn = "^0.24"
sentry-sdk = "^1.32.0" sentry-sdk = "^1.32.0"
gql = {git = "https://github.com/graphql-python/gql.git", rev = "master"} starlette = "^0.32.0.post1"
starlette = {git = "https://github.com/encode/starlette.git", rev = "master"} gql = "^3.4.1"
ariadne = {git = "https://github.com/tonyrewin/ariadne.git", rev = "master"} ariadne = "^0.21"
aiohttp = "^3.9.1"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
setuptools = "^69.0.2" setuptools = "^69.0.2"

View File

@ -321,7 +321,7 @@ async def load_reactions_by(_, info, by, limit=50, offset=0):
:topic - to filter by topic :topic - to filter by topic
:search - to search by reactions' body :search - to search by reactions' body
:comment - true if body.length > 0 :comment - true if body.length > 0
:time_ago - amount of time ago :after - amount of time ago
:sort - a fieldname to sort desc by default :sort - a fieldname to sort desc by default
} }
:param limit: int amount of shouts :param limit: int amount of shouts
@ -353,8 +353,8 @@ async def load_reactions_by(_, info, by, limit=50, offset=0):
if len(by.get("search", "")) > 2: if len(by.get("search", "")) > 2:
q = q.filter(Reaction.body.ilike(f'%{by["body"]}%')) q = q.filter(Reaction.body.ilike(f'%{by["body"]}%'))
if by.get("time_ago"): if by.get("after"):
after = int(time.time()) - int(by.get("time_ago", 0)) after = int(time.time()) - int(by.get("after", 0))
q = q.filter(Reaction.created_at > after) q = q.filter(Reaction.created_at > after)
order_way = asc if by.get("sort", "").startswith("-") else desc order_way = asc if by.get("sort", "").startswith("-") else desc

View File

@ -63,9 +63,9 @@ def apply_filters(q, filters, author_id=None):
q = q.filter(Shout.title.ilike(f'%{filters.get("title")}%')) q = q.filter(Shout.title.ilike(f'%{filters.get("title")}%'))
if filters.get("body"): if filters.get("body"):
q = q.filter(Shout.body.ilike(f'%{filters.get("body")}%s')) q = q.filter(Shout.body.ilike(f'%{filters.get("body")}%s'))
if filters.get("time_ago"): if filters.get("after"):
before = int(time.time()) - int(filters.get("time_ago")) ts = int(filters.get("after"))
q = q.filter(Shout.created_at > before) q = q.filter(Shout.created_at > ts)
return q return q
@ -126,7 +126,7 @@ async def load_shouts_by(_, info, options):
visibility: "public", visibility: "public",
author: 'discours', author: 'discours',
topic: 'culture', topic: 'culture',
time_ago: 1234567 // unixtime after: 1234567 // unixtime
} }
offset: 0 offset: 0
limit: 50 limit: 50
@ -166,9 +166,9 @@ async def load_shouts_by(_, info, options):
}[filters.get("visibility")] }[filters.get("visibility")]
if by_visibility: if by_visibility:
q = q.filter(Shout.visibility > by_visibility) q = q.filter(Shout.visibility > by_visibility)
by_time_ago = filters.get("time_ago") after = filters.get("after")
if by_time_ago: if after:
q = q.filter(Shout.created_at < by_time_ago) q = q.filter(Shout.created_at > after)
order_by = options.get("order_by", Shout.published_at) order_by = options.get("order_by", Shout.published_at)
query_order_by = desc(order_by) if options.get("order_by_desc", True) else asc(order_by) query_order_by = desc(order_by) if options.get("order_by_desc", True) else asc(order_by)

View File

@ -238,7 +238,7 @@ input AuthorsBy {
name: String name: String
topic: String topic: String
order: String order: String
time_ago: Int after: Int
stat: String stat: String
} }
@ -252,7 +252,7 @@ input ShoutsFilterBy {
authors: [String] authors: [String]
layouts: [String] layouts: [String]
visibility: String visibility: String
time_ago: Int after: Int
stat: String stat: String
} }
@ -261,7 +261,7 @@ input LoadShoutsFilters {
author: String author: String
layouts: [String] layouts: [String]
visibility: String visibility: String
time_ago: Int after: Int
} }
input LoadShoutsOptions { input LoadShoutsOptions {
@ -280,7 +280,7 @@ input ReactionBy {
comment: Boolean comment: Boolean
topic: String topic: String
created_by: Int created_by: Int
time_ago: Int after: Int
sort: String sort: String
} }

View File

@ -1,5 +1,6 @@
from functools import wraps from functools import wraps
from httpx import AsyncClient, HTTPError import aiohttp
from aiohttp.web import HTTPUnauthorized
from settings import AUTH_URL from settings import AUTH_URL
@ -19,19 +20,19 @@ async def check_auth(req):
"variables": None, "variables": None,
} }
async with AsyncClient(timeout=30.0) as client: async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=30.0)) as session:
response = await client.post(AUTH_URL, headers=headers, json=gql) async with session.post(AUTH_URL, headers=headers, json=gql) as response:
print(f"[services.auth] response: {response.status_code} {response.text}") print(f"[services.auth] response: {response.status} {await response.text()}")
if response.status_code != 200: if response.status != 200:
return False, None return False, None
r = response.json() r = await response.json()
try: try:
user_id = r.get("data", {}).get(query_name, {}).get("user", {}).get("id", None) user_id = r.get("data", {}).get(query_name, {}).get("user", {}).get("id", None)
is_authenticated = user_id is not None is_authenticated = user_id is not None
return is_authenticated, user_id return is_authenticated, user_id
except Exception as e: except Exception as e:
print(f"{e}: {r}") print(f"{e}: {r}")
return False, None return False, None
def login_required(f): def login_required(f):
@ -44,10 +45,10 @@ def login_required(f):
if not is_authenticated: if not is_authenticated:
raise Exception("You are not logged in") raise Exception("You are not logged in")
else: else:
# Добавляем author_id в контекст # Add user_id to the context
context["user_id"] = user_id context["user_id"] = user_id
# Если пользователь аутентифицирован, выполняем резолвер # If the user is authenticated, execute the resolver
return await f(*args, **kwargs) return await f(*args, **kwargs)
return decorated_function return decorated_function
@ -59,7 +60,7 @@ def auth_request(f):
req = args[0] req = args[0]
is_authenticated, user_id = await check_auth(req) is_authenticated, user_id = await check_auth(req)
if not is_authenticated: if not is_authenticated:
raise HTTPError("please, login first") raise HTTPUnauthorized(text="Please, login first")
else: else:
req["user_id"] = user_id req["user_id"] = user_id
return await f(*args, **kwargs) return await f(*args, **kwargs)

View File

@ -1,6 +1,6 @@
import asyncio import asyncio
import json import json
import httpx import aiohttp
from services.rediscache import redis from services.rediscache import redis
from orm.shout import Shout from orm.shout import Shout
@ -20,13 +20,13 @@ class SearchService:
cached = await redis.execute("GET", text) cached = await redis.execute("GET", text)
if not cached: if not cached:
async with SearchService.lock: async with SearchService.lock:
# Use httpx to send a request to ElasticSearch # Use aiohttp to send a request to ElasticSearch
async with httpx.AsyncClient() as client: async with aiohttp.ClientSession() as session:
search_url = f"https://search.discours.io/search?q={text}" search_url = f"https://search.discours.io/search?q={text}"
response = await client.get(search_url) async with session.get(search_url) as response:
if response.status_code == 200: if response.status == 200:
payload = response.json() payload = await response.json()
await redis.execute("SET", text, payload) await redis.execute("SET", text, json.dumps(payload))
return json.loads(payload) return payload
else: else:
return json.loads(cached) return json.loads(cached)

View File

@ -4,7 +4,7 @@ from datetime import timedelta, timezone, datetime
from os import environ from os import environ
from gql import Client, gql from gql import Client, gql
from gql.transport.httpx import HTTPXAsyncTransport from gql.transport.aiohttp import AIOHTTPTransport
from services.db import local_session from services.db import local_session
from orm.topic import Topic from orm.topic import Topic
@ -44,13 +44,11 @@ token = environ.get("ACKEE_TOKEN", "")
def create_client(headers=None, schema=None): def create_client(headers=None, schema=None):
return Client( transport = AIOHTTPTransport(
schema=schema, url="https://ackee.discours.io/api",
transport=HTTPXAsyncTransport( headers=headers,
url="https://ackee.discours.io/api",
headers=headers,
),
) )
return Client(schema=schema, transport=transport)
class ViewedStorage: class ViewedStorage:
@ -73,7 +71,7 @@ class ViewedStorage:
async with self.lock: async with self.lock:
if token: if token:
self.client = create_client({"Authorization": "Bearer %s" % str(token)}, schema=schema_str) self.client = create_client({"Authorization": "Bearer %s" % str(token)}, schema=schema_str)
print("[services.viewed] * authorized permanentely by ackee.discours.io: %s" % token) print("[services.viewed] * authorized permanently by ackee.discours.io: %s" % token)
else: else:
print("[services.viewed] * please set ACKEE_TOKEN") print("[services.viewed] * please set ACKEE_TOKEN")
self.disabled = True self.disabled = True
@ -85,19 +83,20 @@ class ViewedStorage:
start = time.time() start = time.time()
self = ViewedStorage self = ViewedStorage
try: try:
self.pages = await self.client.execute_async(load_pages) async with self.client as session:
self.pages = self.pages["domains"][0]["statistics"]["pages"] self.pages = await session.execute(load_pages)
shouts = {} self.pages = self.pages["domains"][0]["statistics"]["pages"]
try: shouts = {}
for page in self.pages: try:
p = page["value"].split("?")[0] for page in self.pages:
slug = p.split("discours.io/")[-1] p = page["value"].split("?")[0]
shouts[slug] = page["count"] slug = p.split("discours.io/")[-1]
for slug in shouts.keys(): shouts[slug] = page["count"]
await ViewedStorage.increment(slug, shouts[slug]) for slug in shouts.keys():
except Exception: await ViewedStorage.increment(slug, shouts[slug])
pass except Exception:
print("[services.viewed] ⎪ %d pages collected " % len(shouts.keys())) pass
print("[services.viewed] ⎪ %d pages collected " % len(shouts.keys()))
except Exception as e: except Exception as e:
raise e raise e
@ -108,7 +107,7 @@ class ViewedStorage:
async def get_facts(): async def get_facts():
self = ViewedStorage self = ViewedStorage
async with self.lock: async with self.lock:
return self.client.execute_async(load_facts) return await self.client.execute(load_facts)
@staticmethod @staticmethod
async def get_shout(shout_slug): async def get_shout(shout_slug):