Skip to content

Commit

Permalink
add method to fetch emojis
Browse files Browse the repository at this point in the history
  • Loading branch information
kheina committed Dec 28, 2024
1 parent 43a3af0 commit da219ba
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 37 deletions.
2 changes: 2 additions & 0 deletions db/09/00-add-updated-col-to-emojis.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
alter table public.emojis
add column if not exists updated timestamp with time zone default now() not null;
11 changes: 9 additions & 2 deletions emojis/emoji.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from typing import Self
from datetime import datetime as pydatetime

from psycopg.errors import UniqueViolation

from shared.datetime import datetime
from shared.auth import KhUser
from shared.exceptions.http_error import BadRequest, Conflict, Forbidden, HttpErrorHandler, NotFound
from shared.models.auth import Scope
from shared.exceptions.http_error import BadRequest, Conflict, HttpErrorHandler, NotFound

from .models import AliasRequest, CreateRequest, Emoji, InternalEmoji, UpdateRequest
from .repository import EmojiRepository, users
Expand Down Expand Up @@ -33,6 +34,7 @@ async def create(self: Self, user: KhUser, req: CreateRequest) -> Emoji :
post_id = req.post_id.int() if req.post_id else None,
alt = req.alt,
filename = req.filename,
updated = datetime.now(),
)
await super().create(iemoji)
return await self.emoji(user, iemoji)
Expand Down Expand Up @@ -86,3 +88,8 @@ async def update(self: Self, user: KhUser, emoji: str, req: UpdateRequest) -> Em

await super().update(emoji, iemoji)
return await self.emoji(user, iemoji)


async def list_(self: Self, user: KhUser, latest: pydatetime) -> list[Emoji] :
iemoji = await super().list_(latest)
return await self.emojis(user, iemoji)
5 changes: 5 additions & 0 deletions emojis/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from typing import Literal, Optional

from pydantic import BaseModel, Field, validator
Expand All @@ -16,6 +17,7 @@ class InternalEmoji(BaseModel) :
owner: Optional[int] = None
post_id: Optional[int] = None
filename: str
updated: datetime


class Emoji(BaseModel) :
Expand All @@ -25,16 +27,19 @@ class Config:
validate_assignment = True

emoji: str
alias: Optional[str] = None
alt: Optional[str] = None
owner: Optional[UserPortable] = None
post_id: Optional[PostId] = None
filename: str
url: str = ''
updated: datetime

@validator('url', pre=True, always=True)
def validate_url(cls, v, values) :
if values['post_id'] :
return values['post_id'] + '/emoji/' + values['filename']

return 'emoji/' + values['filename']


Expand Down
88 changes: 77 additions & 11 deletions emojis/repository.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from typing import Optional, Self
from asyncio import Task, ensure_future
from collections import defaultdict
from datetime import datetime
from typing import Mapping, Optional, Self

from shared.auth import KhUser
from shared.caching import AerospikeCache
from shared.caching.key_value_store import KeyValueStore
from shared.exceptions.http_error import BadRequest, NotFound
from shared.models import PostId
from shared.models._shared import InternalUser, UserPortable
from shared.sql import SqlInterface
from users.repository import Users
from shared.maps import privacy_map

from .models import Emoji, InternalEmoji

Expand All @@ -28,14 +33,15 @@ async def create(self: Self, emoji: InternalEmoji) -> None :

@AerospikeCache('kheina', 'emojis', '{emoji}')
async def _read(self: Self, emoji: str) -> Optional[InternalEmoji] :
data: Optional[tuple[str, Optional[str], Optional[str], Optional[int], Optional[int], str]] = await self.query_async("""
data: Optional[tuple[str, Optional[str], Optional[str], Optional[int], Optional[int], str, datetime]] = await self.query_async("""
select
emojis.emoji,
emojis.alt,
emojis.alias,
emojis.owner,
emojis.post_id,
emojis.filename
emojis.filename,
emojis.updated
from kheina.public.emojis
where emojis.emoji = %s
limit 1;
Expand All @@ -55,19 +61,60 @@ async def _read(self: Self, emoji: str) -> Optional[InternalEmoji] :
owner = data[3],
post_id = data[4],
filename = data[5],
updated = data[6],
)


async def emoji(self: Self, user: KhUser, iemoji: InternalEmoji) -> Emoji :
return Emoji(
emoji = iemoji.alias or iemoji.emoji,
emoji = iemoji.emoji,
alias = iemoji.alias,
alt = iemoji.alt,
owner = await users.portable(user, await users._get_user(iemoji.owner)) if iemoji.owner else None,
post_id = PostId(iemoji.post_id) if iemoji.post_id else None,
filename = iemoji.filename,
updated = iemoji.updated,
)


async def emojis(self: Self, user: KhUser, iemojis: list[InternalEmoji]) -> list[Emoji] :
owners: list[int] = list(set(filter(None, map(lambda x : x.owner, iemojis))))
users_task: Task[dict[int, InternalUser]] = ensure_future(users._get_users(owners))
following: Mapping[int, Optional[bool]]

if await user.authenticated(False) :
following = await users.following_many(user.user_id, owners)

else :
following = defaultdict(lambda : None)

iusers: dict[int, InternalUser] = await users_task
emojis: list[Emoji] = []

for iemoji in iemojis :
iuser: Optional[InternalUser] = iusers.get(iemoji.owner) if iemoji.owner else None
emojis.append(
Emoji(
emoji = iemoji.emoji,
alias = iemoji.alias,
alt = iemoji.alt,
owner = UserPortable(
name = iuser.name,
handle = iuser.handle,
privacy = users._validate_privacy(await privacy_map.get(iuser.privacy)),
icon = iuser.icon,
verified = iuser.verified,
following = following[iuser.user_id],
) if iuser else None,
post_id = PostId(iemoji.post_id) if iemoji.post_id else None,
filename = iemoji.filename,
updated = iemoji.updated,
)
)

return emojis


@AerospikeCache('kheina', 'emoji_alias', '{emoji}', _kvs=aliaskvs)
async def aliases(self: Self, emoji: str) -> list[str] :
data: list[tuple[str]] = await self.query_async("""
Expand Down Expand Up @@ -146,16 +193,35 @@ async def delete(self: Self, emoji: str) -> None :
raise NotImplementedError("doesn't exist yet")


@AerospikeCache('kheina', 'emoji_search', '{emoji_substring}', TTL_hours=1)
async def list(self: Self, emoji_substring: str) -> list[str] :
data: list[tuple[str]] = await self.query_async("""
select emojis.emoji
# @AerospikeCache('kheina', 'emoji_search', '{emoji_substring}', TTL_hours=1)
async def list_(self: Self, latest: datetime) -> list[InternalEmoji] :
data: list[tuple[str, Optional[str], Optional[int], Optional[int], Optional[str], str, datetime]] = await self.query_async("""
select
emojis.emoji,
emojis.alias,
emojis.owner,
emojis.post_id,
emojis.alt,
emojis.filename,
emojis.updated
from kheina.public.emojis
where emojis.emoji like '%%' || %s || '%%';
where emojis.updated > %s
limit 10000;
""", (
emoji_substring,
latest,
),
fetch_all = True,
)

return list(map(lambda x : x[0], data))
return [
InternalEmoji(
emoji = row[0],
alias = row[1],
owner = row[2],
post_id = row[3],
alt = row[4],
filename = row[5],
updated = row[6],
)
for row in data
]
7 changes: 4 additions & 3 deletions emojis/router.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from fastapi import APIRouter

from shared.models.auth import Scope
Expand Down Expand Up @@ -45,10 +46,10 @@ async def v1GetEmoji(req: Request, emoji: str) -> Emoji :
return await emojis.read(req.user, emoji)


@emojisRouter.get('/{emoji}')
@emojisRouter.get('/{latest}')
@timed.root
async def v1ListEmojis(emoji: str) -> list[str] :
return await emojis.list(emoji)
async def v1ListEmojis(req: Request, latest: datetime) -> list[Emoji] :
return await emojis.list_(req.user, latest)


app = APIRouter(
Expand Down
2 changes: 2 additions & 0 deletions init.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from shared.base64 import b64decode, b64encode
from shared.caching.key_value_store import KeyValueStore
from shared.config.credentials import decryptCredentialFile, fetch
from .shared.datetime import datetime
from shared.logging import TerminalAgent
from shared.sql import SqlInterface

Expand Down Expand Up @@ -254,6 +255,7 @@ async def uploadEmojis() -> None :
emoji = f'{text}{suffix}',
alt = alt,
filename = filename,
updated = datetime.now(),
))
uploaded += 1
glyphs.discard(key)
Expand Down
2 changes: 1 addition & 1 deletion k8s.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ spec:
spec:
containers:
- name: fuzzly-backend
image: us-central1-docker.pkg.dev/kheinacom/fuzzly-repo/fuzzly-backend@sha256:cf3dd846353a1535cef05de4f8b1dec7694ef14f1bf51528fb2aa5099c4fc216
image: us-central1-docker.pkg.dev/kheinacom/fuzzly-repo/fuzzly-backend@sha256:98ee41408b83da92dab442c6c699084c838c881242226d520e34434961e0c843
env:
- name: pod_ip
valueFrom:
Expand Down
7 changes: 5 additions & 2 deletions posts/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -895,13 +895,16 @@ async def deletePost(self: Self, user: KhUser, post_id: PostId) -> None :
raise NotFound(f'no data was found for the provided post id: {post_id}.')

async with self.transaction() as t :
ensure_future(PostKVS.remove_async(post_id))
await t.query_async("""
delete from kheina.public.posts
where posts.post_id = %s;
""", (
post_id.int(),
),
)
ensure_future(PostKVS.remove_async(post_id))
await self.delete_files_async(f'{post_id}/')

if post.filename :
assert await self.delete_files_async(post_id), 'at least one file is expected to be deleted'

await t.commit()
10 changes: 5 additions & 5 deletions probe/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@


probes = APIRouter(
prefix='/health',
tags=['health'],
include_in_schema=False,
prefix = '/health',
tags = ['health'],
include_in_schema = False,
)
probe = Probe()


@probes.get('/liveness', status_code=204)
@probes.get('/liveness', status_code = 204)
@timed.root
async def healthz() -> None :
return


@probes.get('/readiness', status_code=204)
@probes.get('/readiness', status_code = 204)
@timed.root
async def readyz() -> None :
return await probe.readyz()
32 changes: 31 additions & 1 deletion server.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@

timed.logger = lambda n, x : print(json.dumps({ n: x.dict() }))

app = FastAPI()
app = FastAPI(
title = 'fuzz.ly',
# docs_url = None,
# redoc_url = None,
)
app.add_middleware(ExceptionMiddleware, handlers={ Exception: jsonErrorHandler }, debug=False)
app.add_exception_handler(BaseError, jsonErrorHandler)

Expand Down Expand Up @@ -92,6 +96,32 @@
environ.get('pod_host', 'localhost'),
])
app.add_middleware(KhAuthMiddleware, required=False)
# app.mount('/static', StaticFiles(directory = 'static'), name = 'static')


# @app.get('/docs', include_in_schema = False)
# async def custom_swagger_ui_html():
# return get_swagger_ui_html(
# openapi_url = app.openapi_url or '',
# title = app.title + ' - Swagger UI',
# oauth2_redirect_url = app.swagger_ui_oauth2_redirect_url,
# swagger_js_url = 'https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js',
# swagger_css_url = 'https://unpkg.com/swagger-ui-dist@5/swagger-ui.css',
# )


# @app.get(app.swagger_ui_oauth2_redirect_url or '', include_in_schema = False)
# async def swagger_ui_redirect():
# return get_swagger_ui_oauth2_redirect_html()


# @app.get('/redoc', include_in_schema = False)
# async def redoc_html():
# return get_redoc_html(
# openapi_url = app.openapi_url or '',
# title = app.title + ' - ReDoc',
# redoc_js_url = 'https://unpkg.com/redoc@next/bundles/redoc.standalone.js',
# )


@app.on_event('startup')
Expand Down
Loading

0 comments on commit da219ba

Please sign in to comment.