|
3 | 3 | from contextvars import ContextVar |
4 | 4 | from dataclasses import asdict, is_dataclass |
5 | 5 | from os import getenv |
6 | | -from typing import Any, Callable, cast |
| 6 | +from typing import Any, cast |
7 | 7 |
|
8 | 8 | from bson import ObjectId |
9 | 9 |
|
|
15 | 15 | AccessLevel, |
16 | 16 | Anchor, |
17 | 17 | AnchorState, |
18 | | - Architype, |
| 18 | + BaseArchitype, |
19 | 19 | NodeAnchor, |
20 | 20 | Permission, |
21 | 21 | Root, |
|
25 | 25 |
|
26 | 26 | SHOW_ENDPOINT_RETURNS = getenv("SHOW_ENDPOINT_RETURNS") == "true" |
27 | 27 | JASECI_CONTEXT = ContextVar["JaseciContext | None"]("JaseciContext") |
28 | | -SUPER_ROOT = ObjectId("000000000000000000000000") |
29 | | -PUBLIC_ROOT = ObjectId("000000000000000000000001") |
30 | 28 |
|
| 29 | +SUPER_ROOT_ID = ObjectId("000000000000000000000000") |
| 30 | +PUBLIC_ROOT_ID = ObjectId("000000000000000000000001") |
| 31 | +SUPER_ROOT = NodeAnchor.ref(f"n::{SUPER_ROOT_ID}") |
| 32 | +PUBLIC_ROOT = NodeAnchor.ref(f"n::{PUBLIC_ROOT_ID}") |
31 | 33 |
|
32 | | -class JaseciContext: |
| 34 | + |
| 35 | +class JaseciContext(ExecutionContext): |
33 | 36 | """Execution Context.""" |
34 | 37 |
|
35 | | - base: ExecutionContext |
36 | | - request: Request |
37 | | - datasource: MongoDB |
| 38 | + mem: MongoDB |
38 | 39 | reports: list |
39 | | - super_root: NodeAnchor |
| 40 | + system_root: NodeAnchor |
40 | 41 | root: NodeAnchor |
41 | | - entry: NodeAnchor |
42 | | - |
43 | | - def generate_super_root(self) -> NodeAnchor: |
44 | | - """Generate default super root.""" |
45 | | - super_root = NodeAnchor( |
46 | | - id=SUPER_ROOT, state=AnchorState(current_access_level=AccessLevel.WRITE) |
47 | | - ) |
48 | | - architype = super_root.architype = object.__new__(Root) |
49 | | - architype.__jac__ = super_root |
50 | | - self.datasource.set(super_root) |
51 | | - return super_root |
52 | | - |
53 | | - def generate_public_root(self) -> NodeAnchor: |
54 | | - """Generate default super root.""" |
55 | | - public_root = NodeAnchor( |
56 | | - id=PUBLIC_ROOT, |
57 | | - access=Permission(all=AccessLevel.WRITE), |
58 | | - state=AnchorState(current_access_level=AccessLevel.WRITE), |
59 | | - ) |
60 | | - architype = public_root.architype = object.__new__(Root) |
61 | | - architype.__jac__ = public_root |
62 | | - self.datasource.set(public_root) |
63 | | - return public_root |
64 | | - |
65 | | - async def load( |
66 | | - self, |
67 | | - anchor: NodeAnchor | None, |
68 | | - default: NodeAnchor | Callable[[], NodeAnchor], |
69 | | - ) -> NodeAnchor: |
70 | | - """Load initial anchors.""" |
71 | | - if anchor: |
72 | | - if not anchor.state.connected: |
73 | | - if _anchor := await self.datasource.find_one(NodeAnchor, anchor): |
74 | | - _anchor.state.current_access_level = AccessLevel.WRITE |
75 | | - return _anchor |
76 | | - else: |
77 | | - self.datasource.set(anchor) |
78 | | - return anchor |
79 | | - |
80 | | - return default() if callable(default) else default |
81 | | - |
82 | | - async def validate_access(self) -> bool: |
| 42 | + entry_node: NodeAnchor |
| 43 | + base: ExecutionContext |
| 44 | + request: Request |
| 45 | + |
| 46 | + def validate_access(self) -> bool: |
83 | 47 | """Validate access.""" |
84 | | - return await self.root.has_read_access(self.entry) |
| 48 | + return self.root.has_read_access(self.entry_node) |
85 | 49 |
|
86 | | - async def close(self) -> None: |
| 50 | + def close(self) -> None: |
87 | 51 | """Clean up context.""" |
88 | | - await self.datasource.close() |
| 52 | + self.mem.close() |
89 | 53 |
|
90 | 54 | @staticmethod |
91 | | - async def create( |
92 | | - request: Request, entry: NodeAnchor | None = None |
93 | | - ) -> "JaseciContext": |
| 55 | + def create(request: Request, entry: NodeAnchor | None = None) -> "JaseciContext": # type: ignore[override] |
94 | 56 | """Create JacContext.""" |
95 | 57 | ctx = JaseciContext() |
96 | 58 | ctx.base = ExecutionContext.get() |
97 | 59 | ctx.request = request |
98 | | - ctx.datasource = MongoDB() |
| 60 | + ctx.mem = MongoDB() |
99 | 61 | ctx.reports = [] |
100 | | - ctx.super_root = await ctx.load( |
101 | | - NodeAnchor(id=SUPER_ROOT), ctx.generate_super_root |
102 | | - ) |
103 | | - ctx.root = await ctx.load( |
104 | | - getattr(request, "_root", None) or NodeAnchor(id=PUBLIC_ROOT), |
105 | | - ctx.generate_public_root, |
106 | | - ) |
107 | | - ctx.entry = await ctx.load(entry, ctx.root) |
| 62 | + |
| 63 | + if not isinstance(system_root := ctx.mem.find_by_id(SUPER_ROOT), NodeAnchor): |
| 64 | + system_root = NodeAnchor( |
| 65 | + architype=object.__new__(Root), |
| 66 | + id=SUPER_ROOT_ID, |
| 67 | + access=Permission(), |
| 68 | + state=AnchorState(), |
| 69 | + persistent=True, |
| 70 | + edges=[], |
| 71 | + ) |
| 72 | + system_root.architype.__jac__ = system_root |
| 73 | + ctx.mem.set(system_root.id, system_root) |
| 74 | + |
| 75 | + ctx.system_root = system_root |
| 76 | + |
| 77 | + if _root := getattr(request, "_root", None): |
| 78 | + ctx.root = _root |
| 79 | + else: |
| 80 | + if not isinstance( |
| 81 | + public_root := ctx.mem.find_by_id(PUBLIC_ROOT), NodeAnchor |
| 82 | + ): |
| 83 | + public_root = NodeAnchor( |
| 84 | + architype=object.__new__(Root), |
| 85 | + id=PUBLIC_ROOT_ID, |
| 86 | + access=Permission(all=AccessLevel.WRITE), |
| 87 | + state=AnchorState(), |
| 88 | + persistent=True, |
| 89 | + edges=[], |
| 90 | + ) |
| 91 | + public_root.architype.__jac__ = public_root |
| 92 | + ctx.mem.set(public_root.id, public_root) |
| 93 | + |
| 94 | + ctx.root = public_root |
| 95 | + |
| 96 | + if entry: |
| 97 | + if not isinstance(entry_node := ctx.mem.find_by_id(entry), NodeAnchor): |
| 98 | + raise ValueError(f"Invalid anchor id {entry.ref_id} !") |
| 99 | + ctx.entry_node = entry_node |
| 100 | + else: |
| 101 | + ctx.entry_node = ctx.root |
108 | 102 |
|
109 | 103 | if _ctx := JASECI_CONTEXT.get(None): |
110 | | - await _ctx.close() |
| 104 | + _ctx.close() |
111 | 105 | JASECI_CONTEXT.set(ctx) |
112 | 106 |
|
113 | 107 | return ctx |
114 | 108 |
|
115 | 109 | @staticmethod |
116 | 110 | def get() -> "JaseciContext": |
117 | | - """Get current ExecutionContext.""" |
118 | | - if not isinstance(ctx := JASECI_CONTEXT.get(None), JaseciContext): |
119 | | - raise Exception("JaseciContext is not yet available!") |
120 | | - return ctx |
| 111 | + """Get current JaseciContext.""" |
| 112 | + if ctx := JASECI_CONTEXT.get(None): |
| 113 | + return ctx |
| 114 | + raise Exception("JaseciContext is not yet available!") |
121 | 115 |
|
122 | 116 | @staticmethod |
123 | | - def get_datasource() -> MongoDB: |
124 | | - """Get current datasource.""" |
125 | | - return JaseciContext.get().datasource |
| 117 | + def get_root() -> Root: # type: ignore[override] |
| 118 | + """Get current root.""" |
| 119 | + return cast(Root, JaseciContext.get().root.architype) |
126 | 120 |
|
127 | 121 | def response(self, returns: list[Any], status: int = 200) -> dict[str, Any]: |
128 | 122 | """Return serialized version of reports.""" |
@@ -154,7 +148,7 @@ def clean_response( |
154 | 148 | self.clean_response(key, dval, val) |
155 | 149 | case Anchor(): |
156 | 150 | cast(dict, obj)[key] = val.report() |
157 | | - case Architype(): |
| 151 | + case BaseArchitype(): |
158 | 152 | cast(dict, obj)[key] = val.__jac__.report() |
159 | 153 | case val if is_dataclass(val) and not isinstance(val, type): |
160 | 154 | cast(dict, obj)[key] = asdict(val) |
|
0 commit comments