Skip to content

Commit ecddbbc

Browse files
authored
Merge pull request #322 from ynput/enhancement/321-refactor-usage-of-attrib-to-allattrib
GraphQl: Use 'allAttrib' instead of 'attrib'
2 parents cabc36a + 5bc8618 commit ecddbbc

File tree

8 files changed

+228
-111
lines changed

8 files changed

+228
-111
lines changed

ayon_api/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@
148148
enroll_event_job,
149149
get_attributes_schema,
150150
reset_attributes_schema,
151+
reset_attributes_cache,
152+
set_attributes_cache_timeout,
151153
set_attribute_config,
152154
remove_attribute_config,
153155
get_attributes_for_type,
@@ -433,6 +435,8 @@
433435
"enroll_event_job",
434436
"get_attributes_schema",
435437
"reset_attributes_schema",
438+
"reset_attributes_cache",
439+
"set_attributes_cache_timeout",
436440
"set_attribute_config",
437441
"remove_attribute_config",
438442
"get_attributes_for_type",

ayon_api/_api.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
EnrollEventData,
5656
AttributeScope,
5757
AttributeSchemaDataDict,
58-
AttributeSchemaDict,
5958
AttributesSchemaDict,
6059
AddonsInfoDict,
6160
InstallersInfoDict,
@@ -3582,10 +3581,30 @@ def get_attributes_schema(
35823581

35833582

35843583
def reset_attributes_schema() -> None:
3584+
"""Reset attributes schema cache.
3585+
3586+
DEPRECATED:
3587+
Use 'reset_attributes_cache' instead.
3588+
3589+
"""
35853590
con = get_server_api_connection()
35863591
return con.reset_attributes_schema()
35873592

35883593

3594+
def reset_attributes_cache() -> None:
3595+
con = get_server_api_connection()
3596+
return con.reset_attributes_cache()
3597+
3598+
3599+
def set_attributes_cache_timeout(
3600+
timeout: int,
3601+
) -> None:
3602+
con = get_server_api_connection()
3603+
return con.set_attributes_cache_timeout(
3604+
timeout=timeout,
3605+
)
3606+
3607+
35893608
def set_attribute_config(
35903609
attribute_name: str,
35913610
data: AttributeSchemaDataDict,
@@ -3622,7 +3641,7 @@ def remove_attribute_config(
36223641

36233642
def get_attributes_for_type(
36243643
entity_type: AttributeScope,
3625-
) -> dict[str, AttributeSchemaDict]:
3644+
) -> dict[str, AttributeSchemaDataDict]:
36263645
"""Get attribute schemas available for an entity type.
36273646
36283647
Example::
@@ -3670,6 +3689,9 @@ def get_attributes_fields_for_type(
36703689
) -> set[str]:
36713690
"""Prepare attribute fields for entity type.
36723691
3692+
DEPRECATED: Field 'attrib' is marked as deprecated and should not be
3693+
used for GraphQL queries.
3694+
36733695
Returns:
36743696
set[str]: Attributes fields for entity type.
36753697

ayon_api/_api_helpers/attributes.py

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,107 @@
11
from __future__ import annotations
22

3+
import copy
4+
import time
35
import typing
46
from typing import Optional
5-
import copy
67

78
from .base import BaseServerAPI
89

910
if typing.TYPE_CHECKING:
1011
from ayon_api.typing import (
11-
AttributeSchemaDataDict,
1212
AttributeSchemaDict,
13+
AttributeSchemaDataDict,
1314
AttributesSchemaDict,
1415
AttributeScope,
1516
)
1617

18+
class _AttributesCache:
19+
_schema = None
20+
_last_fetch = 0
21+
_timeout = 60
22+
_attributes_by_type = {}
23+
24+
def reset_schema(self) -> None:
25+
self._schema = None
26+
self._last_fetch = 0
27+
self._attributes_by_type = {}
28+
29+
def set_timeout(self, timeout: int) -> None:
30+
self._timeout = timeout
31+
32+
def get_schema(self) -> AttributesSchemaDict:
33+
return copy.deepcopy(self._schema)
34+
35+
def set_schema(self, schema: AttributesSchemaDict) -> None:
36+
self._schema = schema
37+
self._last_fetch = time.time()
38+
39+
def is_valid(self) -> bool:
40+
if self._schema is None:
41+
return False
42+
return time.time() - self._last_fetch < self._timeout
43+
44+
def invalidate(self) -> None:
45+
if not self.is_valid():
46+
self.reset_schema()
47+
48+
def get_attributes_for_type(
49+
self, entity_type: AttributeScope
50+
) -> list[AttributeSchemaDict]:
51+
attributes = self._attributes_by_type.get(entity_type)
52+
if attributes is not None:
53+
return attributes
54+
55+
attributes_schema = self.get_schema()
56+
if attributes_schema is None:
57+
raise ValueError("Attributes schema is not cached.")
58+
59+
attributes = []
60+
for attr in attributes_schema["attributes"]:
61+
if entity_type not in attr["scope"]:
62+
continue
63+
attributes.append(attr)
64+
65+
self._attributes_by_type[entity_type] = attributes
66+
return attributes
67+
68+
1769

1870
class AttributesAPI(BaseServerAPI):
19-
_attributes_schema = None
20-
_entity_type_attributes_cache = {}
71+
_attributes_cache = _AttributesCache()
2172

2273
def get_attributes_schema(
2374
self, use_cache: bool = True
2475
) -> AttributesSchemaDict:
2576
if not use_cache:
26-
self.reset_attributes_schema()
77+
self._attributes_cache.reset_schema()
78+
else:
79+
self._attributes_cache.invalidate()
2780

28-
if self._attributes_schema is None:
81+
if not self._attributes_cache.is_valid():
2982
result = self.get("attributes")
3083
result.raise_for_status()
31-
self._attributes_schema = result.data
32-
return copy.deepcopy(self._attributes_schema)
84+
self._attributes_cache.set_schema(result.data)
85+
return self._attributes_cache.get_schema()
3386

3487
def reset_attributes_schema(self) -> None:
35-
self._attributes_schema = None
36-
self._entity_type_attributes_cache = {}
88+
"""Reset attributes schema cache.
89+
90+
DEPRECATED:
91+
Use 'reset_attributes_cache' instead.
92+
93+
"""
94+
self.log.warning(
95+
"Used deprecated function 'reset_attributes_schema'."
96+
" Please use 'reset_attributes_cache' instead."
97+
)
98+
self.reset_attributes_cache()
99+
100+
def reset_attributes_cache(self) -> None:
101+
self._attributes_cache.reset_schema()
102+
103+
def set_attributes_cache_timeout(self, timeout: int) -> None:
104+
self._attributes_cache.set_timeout(timeout)
37105

38106
def set_attribute_config(
39107
self,
@@ -64,12 +132,10 @@ def set_attribute_config(
64132
position=position,
65133
builtin=builtin
66134
)
67-
if response.status_code != 204:
68-
# TODO raise different exception
69-
raise ValueError(
70-
f"Attribute \"{attribute_name}\" was not created/updated."
71-
f" {response.detail}"
72-
)
135+
response.raise_for_status(
136+
f"Attribute \"{attribute_name}\" was not created/updated."
137+
f" {response.detail}"
138+
)
73139

74140
self.reset_attributes_schema()
75141

@@ -92,7 +158,7 @@ def remove_attribute_config(self, attribute_name: str) -> None:
92158

93159
def get_attributes_for_type(
94160
self, entity_type: AttributeScope
95-
) -> dict[str, AttributeSchemaDict]:
161+
) -> dict[str, AttributeSchemaDataDict]:
96162
"""Get attribute schemas available for an entity type.
97163
98164
Example::
@@ -129,29 +195,32 @@ def get_attributes_for_type(
129195
for entered entity type.
130196
131197
"""
132-
attributes = self._entity_type_attributes_cache.get(entity_type)
133-
if attributes is None:
134-
attributes_schema = self.get_attributes_schema()
135-
attributes = {}
136-
for attr in attributes_schema["attributes"]:
137-
if entity_type not in attr["scope"]:
138-
continue
139-
attr_name = attr["name"]
140-
attributes[attr_name] = attr["data"]
141-
142-
self._entity_type_attributes_cache[entity_type] = attributes
143-
144-
return copy.deepcopy(attributes)
198+
# Make sure attributes are cached
199+
self.get_attributes_schema()
200+
return {
201+
attr["name"]: attr["data"]
202+
for attr in self._attributes_cache.get_attributes_for_type(
203+
entity_type
204+
)
205+
}
145206

146207
def get_attributes_fields_for_type(
147208
self, entity_type: AttributeScope
148209
) -> set[str]:
149210
"""Prepare attribute fields for entity type.
150211
212+
DEPRECATED: Field 'attrib' is marked as deprecated and should not be
213+
used for GraphQL queries.
214+
151215
Returns:
152216
set[str]: Attributes fields for entity type.
153217
154218
"""
219+
self.log.warning(
220+
"Method 'get_attributes_fields_for_type' is deprecated and should"
221+
" not be used for GraphQL queries. Use 'allAttrib' field instead"
222+
" of 'attrib'."
223+
)
155224
attributes = self.get_attributes_for_type(entity_type)
156225
return {
157226
f"attrib.{attr}"

ayon_api/_api_helpers/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
ProjectDict,
1616
StreamType,
1717
AttributeScope,
18+
AttributeSchemaDataDict,
1819
)
1920

2021
_PLACEHOLDER = object()
@@ -134,7 +135,7 @@ def get_user(
134135

135136
def get_attributes_for_type(
136137
self, entity_type: AttributeScope
137-
) -> set[str]:
138+
) -> dict[str, AttributeSchemaDataDict]:
138139
raise NotImplementedError()
139140

140141
def get_attributes_fields_for_type(

ayon_api/_api_helpers/lists.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,17 @@ def get_entity_lists(
5151
if fields is None:
5252
fields = self.get_default_fields_for_type("entityList")
5353

54-
# List does not have 'attrib' field but has 'allAttrib' field
55-
# which is json string and contains only values that are set
5654
o_fields = tuple(fields)
5755
fields = set()
58-
requires_attrib = False
56+
add_all_attrib = False
5957
for field in o_fields:
6058
if field == "attrib" or field.startswith("attrib."):
61-
requires_attrib = True
62-
field = "allAttrib"
63-
fields.add(field)
59+
add_all_attrib = True
60+
else:
61+
fields.add(field)
62+
63+
if add_all_attrib:
64+
fields.add("allAttrib")
6465

6566
if "items" in fields:
6667
fields.discard("items")
@@ -71,8 +72,8 @@ def get_entity_lists(
7172
"items.position",
7273
}
7374

74-
available_attribs = []
75-
if requires_attrib:
75+
available_attribs = {}
76+
if "allAttrib" in fields:
7677
available_attribs = self.get_attributes_for_type("list")
7778

7879
if active is not None:
@@ -97,17 +98,13 @@ def get_entity_lists(
9798
if isinstance(attributes, str):
9899
entity_list["attributes"] = json.loads(attributes)
99100

100-
if requires_attrib:
101-
all_attrib = json.loads(
102-
entity_list.get("allAttrib") or "{}"
103-
)
104-
entity_list["attrib"] = {
105-
attrib_name: all_attrib.get(attrib_name)
106-
for attrib_name in available_attribs
107-
}
108-
109101
self._convert_entity_data(entity_list)
110102

103+
attrib = entity_list.get("attrib")
104+
if attrib is not None:
105+
for attrib_name, attrib_data in available_attribs.items():
106+
attrib.setdefault(attrib_name, attrib_data["default"])
107+
111108
yield entity_list
112109

113110
def get_entity_list_rest(

0 commit comments

Comments
 (0)