Skip to content

Commit

Permalink
Merge branch 'main' into 1409-a-suggestion-for-cleaner-jac-imports
Browse files Browse the repository at this point in the history
  • Loading branch information
AshishMahendra committed Jan 9, 2025
2 parents 158e892 + 860ca6c commit 27d01b7
Show file tree
Hide file tree
Showing 52 changed files with 2,238 additions and 374 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/release-jac-splice-orc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Release jac-splice-orc to PyPI

on: workflow_dispatch

jobs:
release-splice-orc:
name: Release
runs-on: ubuntu-latest
defaults:
run:
working-directory: jac-splice-orc

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: "3.11"

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build package
run: python setup.py sdist bdist_wheel

- name: Publish package to PyPI
env:
TWINE_USERNAME: '__token__'
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: twine upload dist/*
49 changes: 26 additions & 23 deletions jac-cloud/jac_cloud/core/architype.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,33 +234,33 @@ def has_operations(self) -> bool:
def commit(session: ClientSession) -> None:
"""Commit current session."""
commit_retry = 0
commit_max_retry = BulkWrite.SESSION_MAX_COMMIT_RETRY
while commit_retry <= commit_max_retry:
while True:
try:
session.commit_transaction()
break
except (ConnectionFailure, OperationFailure) as ex:
if ex.has_error_label("UnknownTransactionCommitResult"):
if (
ex.has_error_label("UnknownTransactionCommitResult")
and commit_retry <= BulkWrite.SESSION_MAX_COMMIT_RETRY
):
commit_retry += 1
logger.error(
"Error commiting bulk write! "
f"Retrying [{commit_retry}/{commit_max_retry}] ..."
logger.exception(
"Error commiting session! "
f"Retrying [{commit_retry}/{BulkWrite.SESSION_MAX_COMMIT_RETRY}] ..."
)
continue
logger.error(
f"Error commiting bulk write after max retry [{commit_max_retry}] !"
logger.exception(
f"Error commiting session after max retry [{BulkWrite.SESSION_MAX_COMMIT_RETRY}] !"
)
raise
except Exception:
session.abort_transaction()
logger.error("Error commiting bulk write!")
logger.exception("Error commiting session!")
raise

def execute(self, session: ClientSession) -> None:
"""Execute all operations."""
transaction_retry = 0
transaction_max_retry = self.SESSION_MAX_TRANSACTION_RETRY
while transaction_retry <= transaction_max_retry:
while True:
try:
if node_operation := self.operations[NodeAnchor]:
NodeAnchor.Collection.bulk_write(node_operation, False, session)
Expand All @@ -273,19 +273,22 @@ def execute(self, session: ClientSession) -> None:
self.commit(session)
break
except (ConnectionFailure, OperationFailure) as ex:
if ex.has_error_label("TransientTransactionError"):
if (
ex.has_error_label("TransientTransactionError")
and transaction_retry <= self.SESSION_MAX_TRANSACTION_RETRY
):
transaction_retry += 1
logger.error(
logger.exception(
"Error executing bulk write! "
f"Retrying [{transaction_retry}/{transaction_max_retry}] ..."
f"Retrying [{transaction_retry}/{self.SESSION_MAX_TRANSACTION_RETRY}] ..."
)
continue
logger.error(
f"Error executing bulk write after max retry [{transaction_max_retry}] !"
logger.exception(
f"Error executing bulk write after max retry [{self.SESSION_MAX_TRANSACTION_RETRY}] !"
)
raise
except Exception:
logger.error("Error executing bulk write!")
logger.exception("Error executing bulk write!")
raise


Expand Down Expand Up @@ -557,7 +560,7 @@ def update(self, bulk_write: BulkWrite, propagate: bool = False) -> None:
############################################################
# POPULATE ADDED EDGES #
############################################################
added_edges: set[BaseAnchor | Anchor] = (
added_edges: set[BaseAnchor] = (
changes.get("$addToSet", {}).get("edges", {}).get("$each", [])
)
if added_edges:
Expand All @@ -575,7 +578,7 @@ def update(self, bulk_write: BulkWrite, propagate: bool = False) -> None:
############################################################
# POPULATE REMOVED EDGES #
############################################################
pulled_edges: set[BaseAnchor | Anchor] = (
pulled_edges: set[BaseAnchor] = (
changes.get("$pull", {}).get("edges", {}).get("$in", [])
)
if pulled_edges:
Expand Down Expand Up @@ -828,10 +831,10 @@ class WalkerAnchor(BaseAnchor, _WalkerAnchor): # type: ignore[misc]
"""Walker Anchor."""

architype: "WalkerArchitype"
path: list[Anchor] = field(default_factory=list)
next: list[Anchor] = field(default_factory=list)
path: list[NodeAnchor] = field(default_factory=list) # type: ignore[assignment]
next: list[NodeAnchor] = field(default_factory=list) # type: ignore[assignment]
returns: list[Any] = field(default_factory=list)
ignores: list[Anchor] = field(default_factory=list)
ignores: list[NodeAnchor] = field(default_factory=list) # type: ignore[assignment]
disengaged: bool = False

class Collection(BaseCollection["WalkerAnchor"]):
Expand Down
15 changes: 11 additions & 4 deletions jac-cloud/jac_cloud/jaseci/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,17 @@ async def lifespan(app: _FaststAPI) -> AsyncGenerator[None, _FaststAPI]:

populate_yaml_specs(cls.__app__)

from .routers import healthz_router, sso_router, user_router
from ..plugin.jaseci import walker_router

for router in [healthz_router, sso_router, user_router, walker_router]:
from .routers import healthz_router, sso_router, user_router, webhook_router
from ..plugin.jaseci import walker_router, webhook_walker_router

for router in [
healthz_router,
sso_router,
user_router,
webhook_router,
walker_router,
webhook_walker_router,
]:
cls.__app__.include_router(router)

@cls.__app__.exception_handler(Exception)
Expand Down
18 changes: 14 additions & 4 deletions jac-cloud/jac_cloud/jaseci/datasources/redis.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def keys(cls) -> list[bytes]:
return []

@classmethod
def set(cls, key: str, data: dict | bool) -> bool:
def set(cls, key: str, data: dict | bool | float) -> bool:
"""Push key value pair."""
try:
redis = cls.get_rd()
Expand Down Expand Up @@ -115,7 +115,7 @@ def hkeys(cls) -> list[str]:
return []

@classmethod
def hset(cls, key: str, data: dict | bool) -> bool:
def hset(cls, key: str, data: dict | bool | float) -> bool:
"""Push key value pair to group."""
try:
redis = cls.get_rd()
Expand Down Expand Up @@ -169,6 +169,16 @@ class TokenRedis(Redis):
__table__ = "token"


class WebhookRedis(Redis):
"""Token Memory Interface.
This interface is for Token Management.
You may override this if you wish to implement different structure
"""

__table__ = "webhook"


class AsyncRedis:
"""
Base Memory interface.
Expand Down Expand Up @@ -225,7 +235,7 @@ async def keys(cls) -> list[bytes]:
return []

@classmethod
async def set(cls, key: str, data: dict | bool) -> bool:
async def set(cls, key: str, data: dict | bool | float) -> bool:
"""Push key value pair."""
try:
redis = cls.get_rd()
Expand Down Expand Up @@ -266,7 +276,7 @@ async def hkeys(cls) -> list[str]:
return []

@classmethod
async def hset(cls, key: str, data: dict | bool) -> bool:
async def hset(cls, key: str, data: dict | bool | float) -> bool:
"""Push key value pair to group."""
try:
redis = cls.get_rd()
Expand Down
4 changes: 4 additions & 0 deletions jac-cloud/jac_cloud/jaseci/dtos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
UserResetPassword,
UserVerification,
)
from .webhook import Expiration, GenerateKey, KeyIDs


__all__ = [
Expand All @@ -18,4 +19,7 @@
"UserRequest",
"UserResetPassword",
"UserVerification",
"Expiration",
"GenerateKey",
"KeyIDs",
]
31 changes: 31 additions & 0 deletions jac-cloud/jac_cloud/jaseci/dtos/webhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Jaseci User DTOs."""

from typing import Literal

from annotated_types import Len

from pydantic import BaseModel, Field, StringConstraints

from typing_extensions import Annotated


class Expiration(BaseModel):
"""Key Expiration."""

count: Annotated[int, Field(strict=True, gt=0, default=60)] = 60
interval: Literal["seconds", "minutes", "hours", "days"] = "days"


class GenerateKey(BaseModel):
"""User Creation Request Model."""

name: Annotated[str, StringConstraints(min_length=1)]
walkers: list[str] = Field(default_factory=list)
nodes: list[str] = Field(default_factory=list)
expiration: Expiration = Field(default_factory=Expiration)


class KeyIDs(BaseModel):
"""User Creation Request Model."""

ids: Annotated[list[str], Len(min_length=1)]
3 changes: 2 additions & 1 deletion jac-cloud/jac_cloud/jaseci/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Jaseci Models."""

from .user import NO_PASSWORD, User
from .webhook import Webhook

__all__ = ["NO_PASSWORD", "User"]
__all__ = ["NO_PASSWORD", "User", "Webhook"]
63 changes: 63 additions & 0 deletions jac-cloud/jac_cloud/jaseci/models/webhook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Jaseci Models."""

from dataclasses import asdict, dataclass, field
from datetime import datetime
from typing import Any, Generator, Mapping, cast

from bson import ObjectId

from ..datasources.collection import Collection as BaseCollection


@dataclass(kw_only=True)
class Webhook:
"""Webhook Base Model."""

id: ObjectId = field(default_factory=ObjectId)
name: str
root_id: ObjectId
walkers: list[str]
nodes: list[str]
expiration: datetime
key: str

class Collection(BaseCollection["Webhook"]):
"""
Webhook collection interface.
This interface is for Webhook Credentials Management.
You may override this if you wish to implement different structure
"""

__collection__ = "webhook"
__indexes__ = [
{"keys": ["name"], "unique": True},
{"keys": ["key"], "unique": True},
]

@classmethod
def __document__(cls, doc: Mapping[str, Any]) -> "Webhook":
"""
Return parsed Webhook from document.
This the default Webhook parser after getting a single document.
You may override this to specify how/which class it will be casted/based.
"""
doc = cast(dict, doc)
return Webhook(id=doc.pop("_id"), **doc)

@classmethod
def find_by_root_id(cls, root_id: ObjectId) -> Generator["Webhook", None, None]:
"""Retrieve webhook via root_id."""
return cls.find({"root_id": root_id})

@classmethod
def find_by_key(cls, key: str) -> "Webhook | None":
"""Retrieve webhook via root_id."""
return cls.find_one({"key": key})

def __serialize__(self) -> dict:
"""Return serializable."""
data = asdict(self)
data["_id"] = data.pop("id")
return data
3 changes: 2 additions & 1 deletion jac-cloud/jac_cloud/jaseci/routers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
from .healthz import router as healthz_router
from .sso import router as sso_router
from .user import router as user_router
from .webhook import router as webhook_router

__all__ = ["healthz_router", "sso_router", "user_router"]
__all__ = ["healthz_router", "sso_router", "user_router", "webhook_router"]
Loading

0 comments on commit 27d01b7

Please sign in to comment.