contentgrid-application-client 0.2.4__tar.gz → 0.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {contentgrid_application_client-0.2.4/src/contentgrid_application_client.egg-info → contentgrid_application_client-0.2.6}/PKG-INFO +2 -2
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/src/contentgrid_application_client/application.py +87 -12
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6/src/contentgrid_application_client.egg-info}/PKG-INFO +2 -2
- contentgrid_application_client-0.2.6/src/contentgrid_application_client.egg-info/requires.txt +1 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/fixtures.py +2 -1
- contentgrid_application_client-0.2.6/tests/test_profile.py +285 -0
- contentgrid_application_client-0.2.6/tests/test_relations.py +315 -0
- contentgrid_application_client-0.2.4/src/contentgrid_application_client.egg-info/requires.txt +0 -1
- contentgrid_application_client-0.2.4/tests/test_profile.py +0 -102
- contentgrid_application_client-0.2.4/tests/test_relations.py +0 -222
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/LICENSE +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/README.md +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/pyproject.toml +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/pytest.ini +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/requirements.txt +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/setup.cfg +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/setup.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/src/contentgrid_application_client/__init__.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/src/contentgrid_application_client/py.typed +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/src/contentgrid_application_client.egg-info/SOURCES.txt +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/src/contentgrid_application_client.egg-info/dependency_links.txt +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/src/contentgrid_application_client.egg-info/top_level.txt +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/example-docs/document.jpg +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/example-docs/resume.pdf +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_collection.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_content.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_curies.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_entity_creation.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_hal_form_type_check.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_openapi.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_prompting.py +0 -0
- {contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_search.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentgrid-application-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Python Client for interacting with ContentGrid Applications
|
|
5
5
|
Author-email: Ranec Belpaire <ranec.belpaire@xenit.eu>
|
|
6
6
|
License: Copyright 2024 Xenit Solutions
|
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
22
|
Requires-Python: >=3.5
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: contentgrid-hal-client==0.2.
|
|
25
|
+
Requires-Dist: contentgrid-hal-client==0.2.6
|
|
26
26
|
Dynamic: license-file
|
|
27
27
|
Dynamic: requires-dist
|
|
28
28
|
|
|
@@ -5,6 +5,7 @@ import requests
|
|
|
5
5
|
import os
|
|
6
6
|
import logging
|
|
7
7
|
import mimetypes
|
|
8
|
+
import json
|
|
8
9
|
|
|
9
10
|
from contentgrid_hal_client.exceptions import BadRequest, IncorrectAttributeType, MissingRequiredAttribute, NotFound, MissingHALTemplate
|
|
10
11
|
from contentgrid_hal_client.hal import CurieRegistry, HALFormsClient, HALLink, HALResponse, InteractiveHALResponse
|
|
@@ -131,12 +132,12 @@ class EntityCollection(InteractiveApplicationResponse):
|
|
|
131
132
|
return cast(HALLink, self.get_link("profile"))
|
|
132
133
|
|
|
133
134
|
def get_entity_profile(self) -> "EntityProfile":
|
|
134
|
-
return self.client.
|
|
135
|
+
return self.client.load_or_fetch_entity_profile(self.get_entity_profile_link())
|
|
135
136
|
|
|
136
137
|
def create_entity(self, attributes: dict, attribute_validation=True) -> "EntityObject":
|
|
137
138
|
self.client._transform_hal_links_to_uris(attributes=attributes)
|
|
138
139
|
if attribute_validation:
|
|
139
|
-
entity_profile = self.client.
|
|
140
|
+
entity_profile = self.client.load_or_fetch_entity_profile(self.get_entity_profile_link())
|
|
140
141
|
self.client._validate_form_payload(template=entity_profile.get_template("create-form"), payload=attributes)
|
|
141
142
|
response = self.client.post(self.get_self_link().uri, json=attributes, headers={"Content-Type": "application/json"})
|
|
142
143
|
data = self.client._validate_json_response(response)
|
|
@@ -251,10 +252,17 @@ class EntityObject(InteractiveApplicationResponse):
|
|
|
251
252
|
return entity_profile
|
|
252
253
|
raise NotFound(f"No entity profile describes {self_link_uri}")
|
|
253
254
|
|
|
254
|
-
def get_relation_collection(self, relation_name: str, size: int = 20, params: dict = {}):
|
|
255
|
+
def get_relation_collection(self, relation_name: str, size: int = 20, params: dict = {}) -> EntityCollection:
|
|
255
256
|
params = self.client._add_page_and_size_to_params(size=size, params=params)
|
|
256
257
|
relation_link = self.get_relation_link(relation_name=relation_name)
|
|
257
258
|
return self.client.follow_link(relation_link, infer_type=EntityCollection, params=params)
|
|
259
|
+
|
|
260
|
+
def get_related_entity(self, relation_name: str) -> "EntityObject":
|
|
261
|
+
relation_link = self.get_relation_link(relation_name=relation_name)
|
|
262
|
+
return self.client.follow_link(relation_link, infer_type=EntityObject)
|
|
263
|
+
|
|
264
|
+
def set_related_entity(self, relation_name: str, related_entity : "EntityObject") -> None:
|
|
265
|
+
self.put_relation(relation_name=relation_name, related_entity_links=[related_entity], prefix_client_endpoint=True)
|
|
258
266
|
|
|
259
267
|
def put_relation(self, relation_name: str, related_entity_links: List[str | HALLink | Self], prefix_client_endpoint : bool = True) -> None:
|
|
260
268
|
set_template = self.get_template(f"set-{relation_name}")
|
|
@@ -376,8 +384,8 @@ class EntityObject(InteractiveApplicationResponse):
|
|
|
376
384
|
lines.append(f" - {content_link.name} (content attribute name): {content_link.uri}")
|
|
377
385
|
return "\n".join(lines)
|
|
378
386
|
|
|
379
|
-
class Profile(
|
|
380
|
-
def __init__(self, data: dict, client:
|
|
387
|
+
class Profile(InteractiveApplicationResponse):
|
|
388
|
+
def __init__(self, data: dict, client: "ContentGridApplicationClient", curie_registry: CurieRegistry = None) -> None:
|
|
381
389
|
super().__init__(data, client, curie_registry)
|
|
382
390
|
|
|
383
391
|
def get_entity_links(self) -> List[HALLink]:
|
|
@@ -385,12 +393,12 @@ class Profile(InteractiveHALResponse):
|
|
|
385
393
|
|
|
386
394
|
def get_entity_profiles_generator(self) -> Generator["EntityProfile", None, None]:
|
|
387
395
|
for entity_profile_link in self.get_entity_links():
|
|
388
|
-
yield self.client.
|
|
396
|
+
yield self.client.load_or_fetch_entity_profile(entity_profile_link)
|
|
389
397
|
|
|
390
398
|
def get_entity_profile(self, singular_entity_name: str) -> "EntityProfile": # type: ignore
|
|
391
399
|
for entity_profile_link in self.get_entity_links():
|
|
392
400
|
if entity_profile_link.name == singular_entity_name:
|
|
393
|
-
return self.client.
|
|
401
|
+
return self.client.load_or_fetch_entity_profile(entity_profile_link)
|
|
394
402
|
raise NotFound(f"Entity Profile {singular_entity_name} does not exist.")
|
|
395
403
|
|
|
396
404
|
def prompt(self) -> str:
|
|
@@ -423,7 +431,7 @@ class RelationProfile(InteractiveApplicationResponse):
|
|
|
423
431
|
def get_related_entity_profile(self) -> "EntityProfile":
|
|
424
432
|
related_profile_link = self.get_related_entity_profile_link()
|
|
425
433
|
if related_profile_link:
|
|
426
|
-
return self.client.
|
|
434
|
+
return self.client.load_or_fetch_entity_profile(related_profile_link)
|
|
427
435
|
else:
|
|
428
436
|
raise NotFound(f"Related entity profile link not found on relation profile {self.name}")
|
|
429
437
|
|
|
@@ -495,7 +503,32 @@ class AttributeProfile(InteractiveApplicationResponse):
|
|
|
495
503
|
|
|
496
504
|
def has_constrained_values(self) -> bool:
|
|
497
505
|
return self.get_constraint("allowed-values") is not None
|
|
498
|
-
|
|
506
|
+
|
|
507
|
+
def has_allowed_values_constraint(self) -> bool:
|
|
508
|
+
return self.has_constrained_values()
|
|
509
|
+
|
|
510
|
+
def has_unique_constraint(self) -> bool:
|
|
511
|
+
return self.get_constraint("unique") is not None
|
|
512
|
+
|
|
513
|
+
def has_created_by_constraint(self) -> bool:
|
|
514
|
+
return self.get_constraint("created-by") is not None
|
|
515
|
+
|
|
516
|
+
def has_created_date_constraint(self) -> bool:
|
|
517
|
+
return self.get_constraint("created-date") is not None
|
|
518
|
+
|
|
519
|
+
def has_modified_by_constraint(self) -> bool:
|
|
520
|
+
return self.get_constraint("modified-by") is not None
|
|
521
|
+
|
|
522
|
+
def has_modified_date_constraint(self) -> bool:
|
|
523
|
+
return self.get_constraint("modified-date") is not None
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def is_audit_attribute(self) -> bool:
|
|
527
|
+
return self.has_created_by_constraint() or \
|
|
528
|
+
self.has_created_date_constraint() or \
|
|
529
|
+
self.has_modified_by_constraint() or \
|
|
530
|
+
self.has_modified_date_constraint()
|
|
531
|
+
|
|
499
532
|
def get_allowed_values(self) -> Optional[List[str]]:
|
|
500
533
|
if self.has_constrained_values():
|
|
501
534
|
return self.get_constraint("allowed-values").values # type: ignore
|
|
@@ -821,9 +854,10 @@ class ContentGridApplicationClient(HALFormsClient):
|
|
|
821
854
|
client_id: str = None,
|
|
822
855
|
client_secret: str = None,
|
|
823
856
|
token: str = None,
|
|
824
|
-
attribute_validation: bool =
|
|
857
|
+
attribute_validation: bool = False,
|
|
825
858
|
session_cookie: str = None,
|
|
826
859
|
pool_maxsize: int = 10,
|
|
860
|
+
use_profile_cache: bool = True
|
|
827
861
|
) -> None:
|
|
828
862
|
logging.info("Initializing ContentGridApplicationClient...")
|
|
829
863
|
super().__init__(
|
|
@@ -837,8 +871,38 @@ class ContentGridApplicationClient(HALFormsClient):
|
|
|
837
871
|
pool_maxsize=pool_maxsize,
|
|
838
872
|
)
|
|
839
873
|
self.attribute_validation = attribute_validation
|
|
840
|
-
|
|
874
|
+
self.use_profile_cache = use_profile_cache
|
|
875
|
+
self.profile_cache: dict[str, dict[str, Any]] = {}
|
|
876
|
+
|
|
877
|
+
# PROFILE CACHING
|
|
878
|
+
def load_profile_cache(self, url: str, json_data: dict | str) -> None:
|
|
879
|
+
"""
|
|
880
|
+
Load JSON data into the profile cache.
|
|
881
|
+
|
|
882
|
+
Args:
|
|
883
|
+
url: The URL to cache the response for (relative to client_endpoint)
|
|
884
|
+
json_data: The JSON data to cache (as dict or JSON string)
|
|
885
|
+
"""
|
|
886
|
+
if isinstance(json_data, str):
|
|
887
|
+
json_data = json.loads(json_data)
|
|
888
|
+
|
|
889
|
+
self.profile_cache[url] = cast(dict, json_data)
|
|
890
|
+
logging.info(f"Loaded JSON data into profile cache for {url}")
|
|
891
|
+
|
|
892
|
+
def clear_profile_cache(self):
|
|
893
|
+
logging.info(f"Clearing profile cache... clearing {len(self.profile_cache.keys())} entries")
|
|
894
|
+
self.profile_cache = {}
|
|
895
|
+
|
|
841
896
|
def get_profile(self) -> Profile:
|
|
897
|
+
if self.use_profile_cache:
|
|
898
|
+
profile_cache_key = self.client_endpoint.click("/profile").normalize().to_text()
|
|
899
|
+
if profile_cache_key in self.profile_cache:
|
|
900
|
+
data = self.profile_cache[profile_cache_key]
|
|
901
|
+
return Profile(data, client=self)
|
|
902
|
+
response = self.get("/profile", headers={"Accept": "application/json"})
|
|
903
|
+
data = self._validate_json_response(response)
|
|
904
|
+
self.profile_cache[profile_cache_key] = data
|
|
905
|
+
return Profile(data, client=self)
|
|
842
906
|
response = self.get("/profile", headers={"Accept": "application/json"})
|
|
843
907
|
data = self._validate_json_response(response)
|
|
844
908
|
return Profile(data, client=self)
|
|
@@ -848,6 +912,17 @@ class ContentGridApplicationClient(HALFormsClient):
|
|
|
848
912
|
|
|
849
913
|
def get_entity_profiles(self) -> List[EntityProfile]:
|
|
850
914
|
return [entity_profile for entity_profile in self.get_profile().get_entity_profiles_generator()]
|
|
915
|
+
|
|
916
|
+
def load_or_fetch_entity_profile(self, profile_link : HALLink, params: dict={}) -> EntityProfile:
|
|
917
|
+
if self.use_profile_cache:
|
|
918
|
+
if profile_link.uri in self.profile_cache:
|
|
919
|
+
data = self.profile_cache[profile_link.uri]
|
|
920
|
+
return EntityProfile(data=data, client=self)
|
|
921
|
+
response = self.get(profile_link.uri, params=params)
|
|
922
|
+
data = self._validate_json_response(response)
|
|
923
|
+
self.profile_cache[profile_link.uri] = data
|
|
924
|
+
return EntityProfile(data=data, client=self)
|
|
925
|
+
return self.follow_link(profile_link, infer_type=EntityProfile, params=params)
|
|
851
926
|
|
|
852
927
|
def fetch_openapi_yaml(self) -> tuple[str, bytes]:
|
|
853
928
|
res = self.get("/openapi.yml")
|
|
@@ -1009,7 +1084,7 @@ class ContentGridApplicationClient(HALFormsClient):
|
|
|
1009
1084
|
for hal_property in template.properties:
|
|
1010
1085
|
if hal_property.required:
|
|
1011
1086
|
if hal_property.name not in payload.keys():
|
|
1012
|
-
raise MissingRequiredAttribute(f"Required attribute {
|
|
1087
|
+
raise MissingRequiredAttribute(f"Required attribute {hal_property.name} not present in payload.")
|
|
1013
1088
|
|
|
1014
1089
|
def _prefix_url_in_form_payload(self, template: HALFormsTemplate, payload: dict) -> None:
|
|
1015
1090
|
"""Prefix URL properties in the payload with the client_endpoint if needed.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentgrid-application-client
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.6
|
|
4
4
|
Summary: Python Client for interacting with ContentGrid Applications
|
|
5
5
|
Author-email: Ranec Belpaire <ranec.belpaire@xenit.eu>
|
|
6
6
|
License: Copyright 2024 Xenit Solutions
|
|
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
22
22
|
Requires-Python: >=3.5
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: contentgrid-hal-client==0.2.
|
|
25
|
+
Requires-Dist: contentgrid-hal-client==0.2.6
|
|
26
26
|
Dynamic: license-file
|
|
27
27
|
Dynamic: requires-dist
|
|
28
28
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
contentgrid-hal-client==0.2.6
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/fixtures.py
RENAMED
|
@@ -30,7 +30,8 @@ def auth_manager() -> ApplicationAuthenticationManager:
|
|
|
30
30
|
def cg_client(auth_manager: ApplicationAuthenticationManager) -> ContentGridApplicationClient:
|
|
31
31
|
return ContentGridApplicationClient(
|
|
32
32
|
client_endpoint=CONTENTGRID_CLIENT_ENDPOINT,
|
|
33
|
-
auth_manager=auth_manager
|
|
33
|
+
auth_manager=auth_manager,
|
|
34
|
+
attribute_validation=True
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
@fixture
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
from contentgrid_application_client import ContentGridApplicationClient, Profile, EntityProfile, AttributeProfile
|
|
2
|
+
import pytest
|
|
3
|
+
from contentgrid_hal_client.exceptions import NotFound
|
|
4
|
+
from fixtures import auth_manager, cg_client # noqa: F401
|
|
5
|
+
|
|
6
|
+
def test_get_profile_attributes(cg_client: ContentGridApplicationClient):
|
|
7
|
+
profile = cg_client.get_profile()
|
|
8
|
+
entity_links = profile.get_entity_links()
|
|
9
|
+
assert len(entity_links) > 0
|
|
10
|
+
assert all(link.name for link in entity_links)
|
|
11
|
+
assert all(link.uri for link in entity_links)
|
|
12
|
+
|
|
13
|
+
def test_entity_profile_details(cg_client: ContentGridApplicationClient):
|
|
14
|
+
profile = cg_client.get_profile()
|
|
15
|
+
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
16
|
+
|
|
17
|
+
# Test basic entity profile properties
|
|
18
|
+
assert isinstance(entity_profile, EntityProfile)
|
|
19
|
+
assert entity_profile.name is not None
|
|
20
|
+
assert entity_profile.title is not None
|
|
21
|
+
assert entity_profile.description is not None
|
|
22
|
+
|
|
23
|
+
def test_attribute_profiles(cg_client: ContentGridApplicationClient):
|
|
24
|
+
profile = cg_client.get_profile()
|
|
25
|
+
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
26
|
+
|
|
27
|
+
attribute_profiles = entity_profile.get_attribute_profiles()
|
|
28
|
+
if not attribute_profiles:
|
|
29
|
+
pytest.skip(f"Entity {entity_profile.name} has no attribute profiles to test.")
|
|
30
|
+
|
|
31
|
+
# Test first attribute profile properties
|
|
32
|
+
first_attribute = attribute_profiles[0]
|
|
33
|
+
assert isinstance(first_attribute, AttributeProfile)
|
|
34
|
+
assert first_attribute.name is not None
|
|
35
|
+
assert first_attribute.type is not None
|
|
36
|
+
assert isinstance(first_attribute.required, bool)
|
|
37
|
+
assert isinstance(first_attribute.read_only, bool)
|
|
38
|
+
|
|
39
|
+
def test_invalid_entity_profile(cg_client: ContentGridApplicationClient):
|
|
40
|
+
with pytest.raises(NotFound):
|
|
41
|
+
cg_client.get_entity_profile("non_existent_entity")
|
|
42
|
+
|
|
43
|
+
def test_nested_attributes(cg_client: ContentGridApplicationClient):
|
|
44
|
+
profile = cg_client.get_profile()
|
|
45
|
+
# Use a known entity type if available, otherwise the first one
|
|
46
|
+
entity_name = "anonymous-candidate" if "anonymous-candidate" in [link.name for link in profile.get_entity_links()] else profile.get_entity_links()[0].name
|
|
47
|
+
entity_profile = cg_client.get_entity_profile(entity_name)
|
|
48
|
+
|
|
49
|
+
found_nested = False
|
|
50
|
+
for attribute in entity_profile.get_attribute_profiles():
|
|
51
|
+
nested_attributes = attribute.get_nested_attributes()
|
|
52
|
+
if nested_attributes:
|
|
53
|
+
found_nested = True
|
|
54
|
+
assert all(isinstance(nested, AttributeProfile) for nested in nested_attributes)
|
|
55
|
+
assert all(hasattr(nested, 'name') for nested in nested_attributes)
|
|
56
|
+
if not found_nested:
|
|
57
|
+
print(f"INFO: No nested attributes found for entity {entity_name} to test.")
|
|
58
|
+
|
|
59
|
+
def test_content_attribute_detection(cg_client: ContentGridApplicationClient):
|
|
60
|
+
profile = cg_client.get_profile()
|
|
61
|
+
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
62
|
+
|
|
63
|
+
for attribute in entity_profile.get_attribute_profiles():
|
|
64
|
+
if attribute.is_content_attribute():
|
|
65
|
+
nested_attrs = attribute.get_nested_attributes()
|
|
66
|
+
assert any(attr.name in ["length", "mimetype", "filename"] for attr in nested_attrs)
|
|
67
|
+
|
|
68
|
+
def test_relation_profiles(cg_client: ContentGridApplicationClient):
|
|
69
|
+
profile = cg_client.get_profile()
|
|
70
|
+
entity_profile = cg_client.get_entity_profile("anonymous-candidate")
|
|
71
|
+
|
|
72
|
+
relation_profiles = entity_profile.get_relation_profiles()
|
|
73
|
+
assert len(relation_profiles) >= 0
|
|
74
|
+
|
|
75
|
+
if relation_profiles:
|
|
76
|
+
first_relation = relation_profiles[0]
|
|
77
|
+
assert first_relation.name is not None
|
|
78
|
+
assert first_relation.title is not None
|
|
79
|
+
assert isinstance(first_relation.required, bool)
|
|
80
|
+
assert isinstance(first_relation.many_source_per_target, bool)
|
|
81
|
+
assert isinstance(first_relation.many_target_per_source, bool)
|
|
82
|
+
|
|
83
|
+
def test_get_specific_relation_profile(cg_client: ContentGridApplicationClient):
|
|
84
|
+
profile = cg_client.get_profile()
|
|
85
|
+
entity_profile = cg_client.get_entity_profile("anonymous-candidate")
|
|
86
|
+
|
|
87
|
+
if entity_profile.get_relation_profiles():
|
|
88
|
+
relation_name = entity_profile.get_relation_profiles()[0].name
|
|
89
|
+
relation_profile = entity_profile.get_relation_profile(relation_name)
|
|
90
|
+
assert relation_profile.name == relation_name
|
|
91
|
+
|
|
92
|
+
# Test related entity profile retrieval
|
|
93
|
+
related_entity_profile = relation_profile.get_related_entity_profile()
|
|
94
|
+
assert isinstance(related_entity_profile, EntityProfile)
|
|
95
|
+
assert related_entity_profile.name is not None
|
|
96
|
+
|
|
97
|
+
def test_invalid_relation_profile(cg_client: ContentGridApplicationClient):
|
|
98
|
+
profile = cg_client.get_profile()
|
|
99
|
+
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
100
|
+
|
|
101
|
+
with pytest.raises(ValueError):
|
|
102
|
+
entity_profile.get_relation_profile("non_existent_relation")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_load_profile_cache_with_dict(cg_client: ContentGridApplicationClient):
|
|
106
|
+
"""Test loading profile cache with dict data"""
|
|
107
|
+
test_data = {
|
|
108
|
+
"_links": {
|
|
109
|
+
"self": {"href": "/profile/test-entity"}
|
|
110
|
+
},
|
|
111
|
+
"name": "test-entity",
|
|
112
|
+
"description": "Test entity"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
cg_client.load_profile_cache("/profile/test-entity", test_data)
|
|
116
|
+
|
|
117
|
+
# Verify it's in the cache
|
|
118
|
+
assert "/profile/test-entity" in cg_client.profile_cache
|
|
119
|
+
cached_data = cg_client.profile_cache["/profile/test-entity"]
|
|
120
|
+
assert cached_data == test_data
|
|
121
|
+
|
|
122
|
+
def test_load_profile_cache_with_json_string(cg_client: ContentGridApplicationClient):
|
|
123
|
+
"""Test loading profile cache with JSON string"""
|
|
124
|
+
import json
|
|
125
|
+
test_data = {
|
|
126
|
+
"_links": {
|
|
127
|
+
"self": {"href": "/profile/test-entity2"}
|
|
128
|
+
},
|
|
129
|
+
"name": "test-entity2"
|
|
130
|
+
}
|
|
131
|
+
json_string = json.dumps(test_data)
|
|
132
|
+
|
|
133
|
+
cg_client.load_profile_cache("/profile/test-entity2", json_string)
|
|
134
|
+
|
|
135
|
+
# Verify it's in the cache
|
|
136
|
+
assert "/profile/test-entity2" in cg_client.profile_cache
|
|
137
|
+
cached_data = cg_client.profile_cache["/profile/test-entity2"]
|
|
138
|
+
assert cached_data == test_data
|
|
139
|
+
|
|
140
|
+
def test_profile_cache_hit_returns_cached_response(cg_client: ContentGridApplicationClient):
|
|
141
|
+
"""Test that cached profile responses are returned on subsequent requests"""
|
|
142
|
+
# First request to populate cache
|
|
143
|
+
profile1 = cg_client.get_profile()
|
|
144
|
+
initial_cache_size = len(cg_client.profile_cache)
|
|
145
|
+
|
|
146
|
+
# Second request should use cache
|
|
147
|
+
profile2 = cg_client.get_profile()
|
|
148
|
+
|
|
149
|
+
# Cache size should not have increased
|
|
150
|
+
assert len(cg_client.profile_cache) == initial_cache_size
|
|
151
|
+
|
|
152
|
+
# Both profiles should have the same data
|
|
153
|
+
assert profile1.data == profile2.data
|
|
154
|
+
|
|
155
|
+
def test_profile_cache_only_affects_profile_endpoints(cg_client: ContentGridApplicationClient):
|
|
156
|
+
"""Test that cache only affects profile endpoints"""
|
|
157
|
+
initial_cache_size = len(cg_client.profile_cache)
|
|
158
|
+
|
|
159
|
+
# Get profile - should be cached
|
|
160
|
+
cg_client.get_profile()
|
|
161
|
+
|
|
162
|
+
# Cache should have grown
|
|
163
|
+
assert len(cg_client.profile_cache) > initial_cache_size
|
|
164
|
+
|
|
165
|
+
# Getting entity collection should not add to profile cache
|
|
166
|
+
profile = cg_client.get_profile()
|
|
167
|
+
if profile.get_entity_links():
|
|
168
|
+
entity_name = profile.get_entity_links()[0].name
|
|
169
|
+
entity_profile = cg_client.get_entity_profile(entity_name)
|
|
170
|
+
cache_size_after_entity_profile = len(cg_client.profile_cache)
|
|
171
|
+
|
|
172
|
+
# Getting the entity collection should not be cached
|
|
173
|
+
try:
|
|
174
|
+
cg_client.get_entity_collection(entity_name, size=1)
|
|
175
|
+
except Exception:
|
|
176
|
+
pass # We don't care if this fails, just testing cache behavior
|
|
177
|
+
|
|
178
|
+
# Profile cache size should not change from non-profile requests
|
|
179
|
+
assert len(cg_client.profile_cache) >= cache_size_after_entity_profile
|
|
180
|
+
|
|
181
|
+
def test_profile_cache_caches_entity_profiles(cg_client: ContentGridApplicationClient):
|
|
182
|
+
"""Test that entity profile requests are also cached"""
|
|
183
|
+
profile = cg_client.get_profile()
|
|
184
|
+
|
|
185
|
+
if not profile.get_entity_links():
|
|
186
|
+
pytest.skip("No entity profiles available to test caching")
|
|
187
|
+
|
|
188
|
+
entity_name = profile.get_entity_links()[0].name
|
|
189
|
+
initial_cache_size = len(cg_client.profile_cache)
|
|
190
|
+
|
|
191
|
+
# First request
|
|
192
|
+
entity_profile1 = cg_client.get_entity_profile(entity_name)
|
|
193
|
+
cache_size_after_first = len(cg_client.profile_cache)
|
|
194
|
+
|
|
195
|
+
# Cache should have grown (at least entity profile URL)
|
|
196
|
+
assert cache_size_after_first > initial_cache_size
|
|
197
|
+
|
|
198
|
+
# Second request - should hit cache
|
|
199
|
+
entity_profile2 = cg_client.get_entity_profile(entity_name)
|
|
200
|
+
|
|
201
|
+
# Cache size should not change
|
|
202
|
+
assert len(cg_client.profile_cache) == cache_size_after_first
|
|
203
|
+
|
|
204
|
+
# Data should be the same
|
|
205
|
+
assert entity_profile1.name == entity_profile2.name
|
|
206
|
+
|
|
207
|
+
def test_clear_empty_profile_cache(cg_client: ContentGridApplicationClient):
|
|
208
|
+
"""Test clearing an empty cache doesn't cause issues"""
|
|
209
|
+
# Clear empty cache
|
|
210
|
+
cg_client.clear_profile_cache()
|
|
211
|
+
|
|
212
|
+
# Verify cache is still empty
|
|
213
|
+
assert len(cg_client.profile_cache) == 0
|
|
214
|
+
assert cg_client.profile_cache == {}
|
|
215
|
+
|
|
216
|
+
def test_clear_profile_cache_removes_all_entries(cg_client: ContentGridApplicationClient):
|
|
217
|
+
"""Test that clearing cache removes all cached entries"""
|
|
218
|
+
# Populate cache with some data
|
|
219
|
+
test_data1 = {"_links": {"self": {"href": "/profile/entity1"}}, "name": "entity1"}
|
|
220
|
+
test_data2 = {"_links": {"self": {"href": "/profile/entity2"}}, "name": "entity2"}
|
|
221
|
+
|
|
222
|
+
cg_client.load_profile_cache("/profile/entity1", test_data1)
|
|
223
|
+
cg_client.load_profile_cache("/profile/entity2", test_data2)
|
|
224
|
+
|
|
225
|
+
# Verify entries are in cache
|
|
226
|
+
assert len(cg_client.profile_cache) >= 2
|
|
227
|
+
|
|
228
|
+
# Clear cache
|
|
229
|
+
cg_client.clear_profile_cache()
|
|
230
|
+
|
|
231
|
+
# Verify cache is empty
|
|
232
|
+
assert len(cg_client.profile_cache) == 0
|
|
233
|
+
assert "/profile/entity1" not in cg_client.profile_cache
|
|
234
|
+
assert "/profile/entity2" not in cg_client.profile_cache
|
|
235
|
+
|
|
236
|
+
def test_clear_profile_cache_allows_repopulation(cg_client: ContentGridApplicationClient):
|
|
237
|
+
"""Test that cache works normally after being cleared"""
|
|
238
|
+
# Populate cache
|
|
239
|
+
profile1 = cg_client.get_profile()
|
|
240
|
+
assert len(cg_client.profile_cache) > 0
|
|
241
|
+
|
|
242
|
+
# Clear cache
|
|
243
|
+
cg_client.clear_profile_cache()
|
|
244
|
+
assert len(cg_client.profile_cache) == 0
|
|
245
|
+
|
|
246
|
+
# Repopulate cache - should work normally
|
|
247
|
+
profile2 = cg_client.get_profile()
|
|
248
|
+
assert len(cg_client.profile_cache) > 0
|
|
249
|
+
|
|
250
|
+
# Data should be consistent
|
|
251
|
+
assert profile1.data == profile2.data
|
|
252
|
+
|
|
253
|
+
def test_clear_profile_cache_doesnt_affect_caching_flag(cg_client: ContentGridApplicationClient):
|
|
254
|
+
"""Test that clearing cache doesn't disable caching"""
|
|
255
|
+
# Check caching is enabled
|
|
256
|
+
assert cg_client.use_profile_cache is True
|
|
257
|
+
|
|
258
|
+
# Load and clear cache
|
|
259
|
+
cg_client.load_profile_cache("/profile/test", {"name": "test"})
|
|
260
|
+
cg_client.clear_profile_cache()
|
|
261
|
+
|
|
262
|
+
# Caching should still be enabled
|
|
263
|
+
assert cg_client.use_profile_cache is True
|
|
264
|
+
|
|
265
|
+
# Cache should still work
|
|
266
|
+
cg_client.get_profile()
|
|
267
|
+
assert len(cg_client.profile_cache) > 0
|
|
268
|
+
|
|
269
|
+
def test_clear_profile_cache_forces_fresh_request(cg_client: ContentGridApplicationClient):
|
|
270
|
+
"""Test that clearing cache forces a fresh request on next call"""
|
|
271
|
+
# First request - populates cache
|
|
272
|
+
cg_client.get_profile()
|
|
273
|
+
cache_size_after_first = len(cg_client.profile_cache)
|
|
274
|
+
assert cache_size_after_first > 0
|
|
275
|
+
|
|
276
|
+
# Clear cache
|
|
277
|
+
cg_client.clear_profile_cache()
|
|
278
|
+
assert len(cg_client.profile_cache) == 0
|
|
279
|
+
|
|
280
|
+
# Next request should repopulate cache (new request made)
|
|
281
|
+
cg_client.get_profile()
|
|
282
|
+
cache_size_after_clear = len(cg_client.profile_cache)
|
|
283
|
+
|
|
284
|
+
# Cache should be populated again
|
|
285
|
+
assert cache_size_after_clear > 0
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
from contentgrid_application_client import ContentGridApplicationClient, EntityCollection
|
|
2
|
+
from contentgrid_application_client.application import EntityObject
|
|
3
|
+
from contentgrid_hal_client.exceptions import Forbidden, MissingHALTemplate, NotFound
|
|
4
|
+
from contentgrid_hal_client.hal import HALLink
|
|
5
|
+
from contentgrid_hal_client.hal_forms import HALFormsTemplate, HALFormsProperty, HALFormsPropertyType, HALFormsMethod
|
|
6
|
+
from fixtures import auth_manager, cg_client # noqa: F401
|
|
7
|
+
from unittest.mock import MagicMock
|
|
8
|
+
import pytest
|
|
9
|
+
import hyperlink
|
|
10
|
+
import uuid
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TestCreateTextUriListPayload:
|
|
14
|
+
"""Unit tests for _create_text_uri_list_payload method"""
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def entity(self):
|
|
18
|
+
"""Create an EntityObject with a mock client for testing"""
|
|
19
|
+
mock_client = MagicMock()
|
|
20
|
+
mock_client.client_endpoint = hyperlink.parse("https://api.example.com/").normalize()
|
|
21
|
+
entity = EntityObject.__new__(EntityObject)
|
|
22
|
+
entity.client = mock_client
|
|
23
|
+
return entity
|
|
24
|
+
|
|
25
|
+
def test_uri_list_with_relative_paths(self, entity):
|
|
26
|
+
"""Test that relative paths are properly prefixed with client endpoint"""
|
|
27
|
+
result = entity._create_text_uri_list_payload(["/entities/123", "/entities/456"])
|
|
28
|
+
|
|
29
|
+
assert result == "https://api.example.com/entities/123\nhttps://api.example.com/entities/456"
|
|
30
|
+
|
|
31
|
+
def test_uri_list_with_full_urls(self, entity):
|
|
32
|
+
"""Test that full URLs matching endpoint are not modified"""
|
|
33
|
+
full_url = "https://api.example.com/entities/123"
|
|
34
|
+
result = entity._create_text_uri_list_payload([full_url])
|
|
35
|
+
|
|
36
|
+
assert result == full_url
|
|
37
|
+
|
|
38
|
+
def test_uri_list_with_hal_link(self, entity):
|
|
39
|
+
"""Test that HALLink objects are properly converted"""
|
|
40
|
+
hal_link = HALLink(uri="/entities/123")
|
|
41
|
+
result = entity._create_text_uri_list_payload([hal_link])
|
|
42
|
+
|
|
43
|
+
assert result == "https://api.example.com/entities/123"
|
|
44
|
+
|
|
45
|
+
def test_uri_list_with_hal_link_full_url(self, entity):
|
|
46
|
+
"""Test that HALLink with full URL is not double-prefixed"""
|
|
47
|
+
hal_link = HALLink(uri="https://api.example.com/entities/123")
|
|
48
|
+
result = entity._create_text_uri_list_payload([hal_link])
|
|
49
|
+
|
|
50
|
+
assert result == "https://api.example.com/entities/123"
|
|
51
|
+
|
|
52
|
+
def test_uri_list_without_prefix(self, entity):
|
|
53
|
+
"""Test that prefix_client_endpoint=False returns URIs unchanged"""
|
|
54
|
+
result = entity._create_text_uri_list_payload(["/entities/123"], prefix_client_endpoint=False)
|
|
55
|
+
|
|
56
|
+
assert result == "/entities/123"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class TestPrefixUrlInFormPayload:
|
|
60
|
+
"""Unit tests for _prefix_url_in_form_payload method"""
|
|
61
|
+
|
|
62
|
+
@pytest.fixture
|
|
63
|
+
def client(self):
|
|
64
|
+
"""Create a mock ContentGridApplicationClient for testing"""
|
|
65
|
+
mock_client = MagicMock(spec=ContentGridApplicationClient)
|
|
66
|
+
mock_client.client_endpoint = hyperlink.parse("https://api.example.com/").normalize()
|
|
67
|
+
mock_client._prefix_url_in_form_payload = ContentGridApplicationClient._prefix_url_in_form_payload.__get__(mock_client)
|
|
68
|
+
return mock_client
|
|
69
|
+
|
|
70
|
+
def test_prefix_url_property_with_relative_path(self, client):
|
|
71
|
+
"""Test that URL properties with relative paths are prefixed"""
|
|
72
|
+
template = HALFormsTemplate(
|
|
73
|
+
method=HALFormsMethod.POST,
|
|
74
|
+
properties=[HALFormsProperty(name="link", type=HALFormsPropertyType.url)]
|
|
75
|
+
)
|
|
76
|
+
payload = {"link": "/entities/123"}
|
|
77
|
+
|
|
78
|
+
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
79
|
+
|
|
80
|
+
assert payload["link"] == "https://api.example.com/entities/123"
|
|
81
|
+
|
|
82
|
+
def test_prefix_url_property_avoids_double_slashes(self, client):
|
|
83
|
+
"""Test that double slashes are avoided"""
|
|
84
|
+
template = HALFormsTemplate(
|
|
85
|
+
method=HALFormsMethod.POST,
|
|
86
|
+
properties=[HALFormsProperty(name="link", type=HALFormsPropertyType.url)]
|
|
87
|
+
)
|
|
88
|
+
payload = {"link": "/entities/123"}
|
|
89
|
+
|
|
90
|
+
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
91
|
+
|
|
92
|
+
assert "//" not in payload["link"].replace("https://", "")
|
|
93
|
+
|
|
94
|
+
def test_prefix_url_property_with_full_url(self, client):
|
|
95
|
+
"""Test that full URLs are not double-prefixed"""
|
|
96
|
+
template = HALFormsTemplate(
|
|
97
|
+
method=HALFormsMethod.POST,
|
|
98
|
+
properties=[HALFormsProperty(name="link", type=HALFormsPropertyType.url)]
|
|
99
|
+
)
|
|
100
|
+
payload = {"link": "https://api.example.com/entities/123"}
|
|
101
|
+
|
|
102
|
+
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
103
|
+
|
|
104
|
+
assert payload["link"] == "https://api.example.com/entities/123"
|
|
105
|
+
|
|
106
|
+
def test_prefix_url_property_list(self, client):
|
|
107
|
+
"""Test that URL properties in lists are prefixed"""
|
|
108
|
+
template = HALFormsTemplate(
|
|
109
|
+
method=HALFormsMethod.POST,
|
|
110
|
+
properties=[HALFormsProperty(name="links", type=HALFormsPropertyType.url)]
|
|
111
|
+
)
|
|
112
|
+
payload = {"links": ["/entities/123", "/entities/456"]}
|
|
113
|
+
|
|
114
|
+
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
115
|
+
|
|
116
|
+
assert payload["links"] == [
|
|
117
|
+
"https://api.example.com/entities/123",
|
|
118
|
+
"https://api.example.com/entities/456"
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
def test_non_url_properties_unchanged(self, client):
|
|
122
|
+
"""Test that non-URL properties are not modified"""
|
|
123
|
+
template = HALFormsTemplate(
|
|
124
|
+
method=HALFormsMethod.POST,
|
|
125
|
+
properties=[
|
|
126
|
+
HALFormsProperty(name="name", type=HALFormsPropertyType.text),
|
|
127
|
+
HALFormsProperty(name="link", type=HALFormsPropertyType.url)
|
|
128
|
+
]
|
|
129
|
+
)
|
|
130
|
+
payload = {"name": "/some/path", "link": "/entities/123"}
|
|
131
|
+
|
|
132
|
+
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
133
|
+
|
|
134
|
+
assert payload["name"] == "/some/path"
|
|
135
|
+
assert payload["link"] == "https://api.example.com/entities/123"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class TestToManyRelations:
|
|
139
|
+
entity_name = "candidate"
|
|
140
|
+
relation_name = "skills"
|
|
141
|
+
|
|
142
|
+
def test_relations(self, cg_client: ContentGridApplicationClient):
|
|
143
|
+
entity = cg_client.create_entity(self.entity_name, {"name": "test"})
|
|
144
|
+
try:
|
|
145
|
+
# fetch related skills
|
|
146
|
+
related_skills = cg_client.get_entity_relation_collection(
|
|
147
|
+
entity.get_self_link(), self.relation_name
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
assert isinstance(related_skills, EntityCollection)
|
|
151
|
+
|
|
152
|
+
# entity just instantiated so no skills
|
|
153
|
+
assert len(related_skills.get_entities()) == 0
|
|
154
|
+
|
|
155
|
+
skills = cg_client.get_entity_collection(singular_entity_name="skill")
|
|
156
|
+
|
|
157
|
+
amt_skills_to_sample = 4
|
|
158
|
+
skills_selection = skills.get_entities()[:amt_skills_to_sample]
|
|
159
|
+
assert len(skills_selection) == amt_skills_to_sample
|
|
160
|
+
|
|
161
|
+
skills_selection[1] = skills_selection[1].get_self_link().uri
|
|
162
|
+
skills_selection[2] = skills_selection[2].get_self_link()
|
|
163
|
+
|
|
164
|
+
cg_client.post_entity_relation(
|
|
165
|
+
entity.get_self_link(),
|
|
166
|
+
relation_name=self.relation_name,
|
|
167
|
+
related_entity_links=skills_selection[:-1],
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
related_skills.refetch()
|
|
171
|
+
|
|
172
|
+
assert len(related_skills.get_entities()) == amt_skills_to_sample - 1
|
|
173
|
+
|
|
174
|
+
# post last skill
|
|
175
|
+
cg_client.post_entity_relation(
|
|
176
|
+
entity_link=entity.get_self_link(),
|
|
177
|
+
relation_name=self.relation_name,
|
|
178
|
+
related_entity_links=[skills_selection[-1]],
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
related_skills = cg_client.get_entity_relation_collection(
|
|
182
|
+
entity_link=entity.get_self_link(), relation_name=self.relation_name
|
|
183
|
+
)
|
|
184
|
+
assert len(related_skills.get_entities()) == amt_skills_to_sample
|
|
185
|
+
finally:
|
|
186
|
+
entity.delete()
|
|
187
|
+
|
|
188
|
+
def test_put_on_to_many_not_supported(self, cg_client: ContentGridApplicationClient):
|
|
189
|
+
entity = cg_client.create_entity(self.entity_name, {"name": "test"})
|
|
190
|
+
try:
|
|
191
|
+
skills = cg_client.get_entity_collection(singular_entity_name="skill")
|
|
192
|
+
skills_selection = skills.get_entities()[:2]
|
|
193
|
+
|
|
194
|
+
# PUT on to-many relations should raise Forbidden
|
|
195
|
+
with pytest.raises(MissingHALTemplate):
|
|
196
|
+
cg_client.put_entity_relation(
|
|
197
|
+
entity.get_self_link(),
|
|
198
|
+
relation_name=self.relation_name,
|
|
199
|
+
related_entity_links=skills_selection,
|
|
200
|
+
)
|
|
201
|
+
finally:
|
|
202
|
+
entity.delete()
|
|
203
|
+
|
|
204
|
+
def test_clear_relations(self, cg_client: ContentGridApplicationClient):
|
|
205
|
+
entity = cg_client.create_entity(self.entity_name, {"name": "test"})
|
|
206
|
+
try:
|
|
207
|
+
# no related exist yet
|
|
208
|
+
entity.clear_relation(relation_name=self.relation_name)
|
|
209
|
+
|
|
210
|
+
skills = cg_client.get_entity_collection(singular_entity_name="skill")
|
|
211
|
+
skills_selection = skills.get_entities()[:1]
|
|
212
|
+
cg_client.post_entity_relation(
|
|
213
|
+
entity.get_self_link(),
|
|
214
|
+
relation_name=self.relation_name,
|
|
215
|
+
related_entity_links=skills_selection,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
assert len(entity.get_relation_collection(relation_name=self.relation_name).get_entities()) == 1
|
|
219
|
+
|
|
220
|
+
# clear relations again
|
|
221
|
+
entity.clear_relation(relation_name=self.relation_name)
|
|
222
|
+
finally:
|
|
223
|
+
entity.delete()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class TestToOneRelations:
|
|
228
|
+
candidate_entity_name = "candidate"
|
|
229
|
+
redacted_candidate_entity_name = "anonymous-candidate"
|
|
230
|
+
customer_company_entity_name = "customer-company"
|
|
231
|
+
relation_name = "redacted"
|
|
232
|
+
relation_name2 = "proposal"
|
|
233
|
+
|
|
234
|
+
@pytest.fixture
|
|
235
|
+
def entity(self):
|
|
236
|
+
"""Create an EntityObject with a mock client for testing"""
|
|
237
|
+
mock_client = MagicMock()
|
|
238
|
+
entity = EntityObject.__new__(EntityObject)
|
|
239
|
+
entity.client = mock_client
|
|
240
|
+
return entity
|
|
241
|
+
|
|
242
|
+
def test_get_related_entity_follows_relation_link(self, entity):
|
|
243
|
+
"""Test that get_related_entity follows the expected relation link"""
|
|
244
|
+
relation_link = HALLink(uri="/entities/456", name="redacted")
|
|
245
|
+
expected_related_entity = MagicMock(spec=EntityObject)
|
|
246
|
+
|
|
247
|
+
entity.get_relation_link = MagicMock(return_value=relation_link)
|
|
248
|
+
entity.client.follow_link.return_value = expected_related_entity
|
|
249
|
+
|
|
250
|
+
result = entity.get_related_entity("redacted")
|
|
251
|
+
|
|
252
|
+
assert result == expected_related_entity
|
|
253
|
+
entity.get_relation_link.assert_called_once_with(relation_name="redacted")
|
|
254
|
+
entity.client.follow_link.assert_called_once_with(relation_link, infer_type=EntityObject)
|
|
255
|
+
|
|
256
|
+
def test_get_related_entity_raises_when_relation_not_found(self, entity):
|
|
257
|
+
"""Test that relation lookup errors are propagated"""
|
|
258
|
+
entity.get_relation_link = MagicMock(side_effect=NotFound("missing relation"))
|
|
259
|
+
|
|
260
|
+
with pytest.raises(NotFound):
|
|
261
|
+
entity.get_related_entity("missing-relation")
|
|
262
|
+
|
|
263
|
+
entity.client.follow_link.assert_not_called()
|
|
264
|
+
|
|
265
|
+
def test_get_related_entity_raises_when_related_entity_not_found(self, entity):
|
|
266
|
+
"""Test that follow_link NotFound errors are propagated"""
|
|
267
|
+
relation_link = HALLink(uri="/entities/456", name="redacted")
|
|
268
|
+
entity.get_relation_link = MagicMock(return_value=relation_link)
|
|
269
|
+
entity.client.follow_link = MagicMock(side_effect=NotFound("related entity not found"))
|
|
270
|
+
|
|
271
|
+
with pytest.raises(NotFound, match="related entity not found"):
|
|
272
|
+
entity.get_related_entity("redacted")
|
|
273
|
+
|
|
274
|
+
entity.get_relation_link.assert_called_once_with(relation_name="redacted")
|
|
275
|
+
entity.client.follow_link.assert_called_once_with(relation_link, infer_type=EntityObject)
|
|
276
|
+
|
|
277
|
+
def test_get_related_entity_raises_when_access_is_forbidden(self, entity):
|
|
278
|
+
"""Test that follow_link Forbidden errors are propagated"""
|
|
279
|
+
relation_link = HALLink(uri="/entities/456", name="redacted")
|
|
280
|
+
entity.get_relation_link = MagicMock(return_value=relation_link)
|
|
281
|
+
entity.client.follow_link = MagicMock(side_effect=Forbidden("access denied"))
|
|
282
|
+
|
|
283
|
+
with pytest.raises(Forbidden, match="access denied"):
|
|
284
|
+
entity.get_related_entity("redacted")
|
|
285
|
+
|
|
286
|
+
entity.get_relation_link.assert_called_once_with(relation_name="redacted")
|
|
287
|
+
entity.client.follow_link.assert_called_once_with(relation_link, infer_type=EntityObject)
|
|
288
|
+
|
|
289
|
+
def test_get_related_entity(self, cg_client: ContentGridApplicationClient):
|
|
290
|
+
entity = cg_client.create_entity(self.candidate_entity_name, attributes={"name": "test"})
|
|
291
|
+
related_entity = None
|
|
292
|
+
new_company = None
|
|
293
|
+
try:
|
|
294
|
+
related_entity = cg_client.create_entity(self.redacted_candidate_entity_name, attributes={"original": entity.get_self_link().uri})
|
|
295
|
+
fetched_related_entity = entity.get_related_entity(self.relation_name)
|
|
296
|
+
|
|
297
|
+
assert isinstance(fetched_related_entity, EntityObject)
|
|
298
|
+
assert fetched_related_entity.get_self_link().uri == related_entity.get_self_link().uri
|
|
299
|
+
|
|
300
|
+
company_name = f"company-{uuid.uuid4().hex[:8]}"
|
|
301
|
+
new_company = cg_client.create_entity(self.customer_company_entity_name, attributes={"name": company_name})
|
|
302
|
+
|
|
303
|
+
related_entity.set_related_entity(relation_name=self.relation_name2, related_entity=new_company)
|
|
304
|
+
fetched_company = related_entity.get_related_entity(self.relation_name2)
|
|
305
|
+
|
|
306
|
+
assert isinstance(fetched_company, EntityObject)
|
|
307
|
+
assert fetched_company.get_self_link().uri == new_company.get_self_link().uri
|
|
308
|
+
finally:
|
|
309
|
+
if new_company is not None:
|
|
310
|
+
new_company.delete()
|
|
311
|
+
if related_entity is not None:
|
|
312
|
+
related_entity.delete()
|
|
313
|
+
entity.delete()
|
|
314
|
+
|
|
315
|
+
|
contentgrid_application_client-0.2.4/src/contentgrid_application_client.egg-info/requires.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
contentgrid-hal-client==0.2.4
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
from contentgrid_application_client import ContentGridApplicationClient, Profile, EntityProfile, AttributeProfile
|
|
2
|
-
import pytest
|
|
3
|
-
from contentgrid_hal_client.exceptions import NotFound
|
|
4
|
-
from fixtures import auth_manager, cg_client # noqa: F401
|
|
5
|
-
|
|
6
|
-
def test_get_profile_attributes(cg_client: ContentGridApplicationClient):
|
|
7
|
-
profile = cg_client.get_profile()
|
|
8
|
-
entity_links = profile.get_entity_links()
|
|
9
|
-
assert len(entity_links) > 0
|
|
10
|
-
assert all(link.name for link in entity_links)
|
|
11
|
-
assert all(link.uri for link in entity_links)
|
|
12
|
-
|
|
13
|
-
def test_entity_profile_details(cg_client: ContentGridApplicationClient):
|
|
14
|
-
profile = cg_client.get_profile()
|
|
15
|
-
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
16
|
-
|
|
17
|
-
# Test basic entity profile properties
|
|
18
|
-
assert isinstance(entity_profile, EntityProfile)
|
|
19
|
-
assert entity_profile.name is not None
|
|
20
|
-
assert entity_profile.title is not None
|
|
21
|
-
assert entity_profile.description is not None
|
|
22
|
-
|
|
23
|
-
def test_attribute_profiles(cg_client: ContentGridApplicationClient):
|
|
24
|
-
profile = cg_client.get_profile()
|
|
25
|
-
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
26
|
-
|
|
27
|
-
attribute_profiles = entity_profile.get_attribute_profiles()
|
|
28
|
-
if not attribute_profiles:
|
|
29
|
-
pytest.skip(f"Entity {entity_profile.name} has no attribute profiles to test.")
|
|
30
|
-
|
|
31
|
-
# Test first attribute profile properties
|
|
32
|
-
first_attribute = attribute_profiles[0]
|
|
33
|
-
assert isinstance(first_attribute, AttributeProfile)
|
|
34
|
-
assert first_attribute.name is not None
|
|
35
|
-
assert first_attribute.type is not None
|
|
36
|
-
assert isinstance(first_attribute.required, bool)
|
|
37
|
-
assert isinstance(first_attribute.read_only, bool)
|
|
38
|
-
|
|
39
|
-
def test_invalid_entity_profile(cg_client: ContentGridApplicationClient):
|
|
40
|
-
with pytest.raises(NotFound):
|
|
41
|
-
cg_client.get_entity_profile("non_existent_entity")
|
|
42
|
-
|
|
43
|
-
def test_nested_attributes(cg_client: ContentGridApplicationClient):
|
|
44
|
-
profile = cg_client.get_profile()
|
|
45
|
-
# Use a known entity type if available, otherwise the first one
|
|
46
|
-
entity_name = "anonymous-candidate" if "anonymous-candidate" in [link.name for link in profile.get_entity_links()] else profile.get_entity_links()[0].name
|
|
47
|
-
entity_profile = cg_client.get_entity_profile(entity_name)
|
|
48
|
-
|
|
49
|
-
found_nested = False
|
|
50
|
-
for attribute in entity_profile.get_attribute_profiles():
|
|
51
|
-
nested_attributes = attribute.get_nested_attributes()
|
|
52
|
-
if nested_attributes:
|
|
53
|
-
found_nested = True
|
|
54
|
-
assert all(isinstance(nested, AttributeProfile) for nested in nested_attributes)
|
|
55
|
-
assert all(hasattr(nested, 'name') for nested in nested_attributes)
|
|
56
|
-
if not found_nested:
|
|
57
|
-
print(f"INFO: No nested attributes found for entity {entity_name} to test.")
|
|
58
|
-
|
|
59
|
-
def test_content_attribute_detection(cg_client: ContentGridApplicationClient):
|
|
60
|
-
profile = cg_client.get_profile()
|
|
61
|
-
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
62
|
-
|
|
63
|
-
for attribute in entity_profile.get_attribute_profiles():
|
|
64
|
-
if attribute.is_content_attribute():
|
|
65
|
-
nested_attrs = attribute.get_nested_attributes()
|
|
66
|
-
assert any(attr.name in ["length", "mimetype", "filename"] for attr in nested_attrs)
|
|
67
|
-
|
|
68
|
-
def test_relation_profiles(cg_client: ContentGridApplicationClient):
|
|
69
|
-
profile = cg_client.get_profile()
|
|
70
|
-
entity_profile = cg_client.get_entity_profile("anonymous-candidate")
|
|
71
|
-
|
|
72
|
-
relation_profiles = entity_profile.get_relation_profiles()
|
|
73
|
-
assert len(relation_profiles) >= 0
|
|
74
|
-
|
|
75
|
-
if relation_profiles:
|
|
76
|
-
first_relation = relation_profiles[0]
|
|
77
|
-
assert first_relation.name is not None
|
|
78
|
-
assert first_relation.title is not None
|
|
79
|
-
assert isinstance(first_relation.required, bool)
|
|
80
|
-
assert isinstance(first_relation.many_source_per_target, bool)
|
|
81
|
-
assert isinstance(first_relation.many_target_per_source, bool)
|
|
82
|
-
|
|
83
|
-
def test_get_specific_relation_profile(cg_client: ContentGridApplicationClient):
|
|
84
|
-
profile = cg_client.get_profile()
|
|
85
|
-
entity_profile = cg_client.get_entity_profile("anonymous-candidate")
|
|
86
|
-
|
|
87
|
-
if entity_profile.get_relation_profiles():
|
|
88
|
-
relation_name = entity_profile.get_relation_profiles()[0].name
|
|
89
|
-
relation_profile = entity_profile.get_relation_profile(relation_name)
|
|
90
|
-
assert relation_profile.name == relation_name
|
|
91
|
-
|
|
92
|
-
# Test related entity profile retrieval
|
|
93
|
-
related_entity_profile = relation_profile.get_related_entity_profile()
|
|
94
|
-
assert isinstance(related_entity_profile, EntityProfile)
|
|
95
|
-
assert related_entity_profile.name is not None
|
|
96
|
-
|
|
97
|
-
def test_invalid_relation_profile(cg_client: ContentGridApplicationClient):
|
|
98
|
-
profile = cg_client.get_profile()
|
|
99
|
-
entity_profile = cg_client.get_entity_profile(profile.get_entity_links()[0].name)
|
|
100
|
-
|
|
101
|
-
with pytest.raises(ValueError):
|
|
102
|
-
entity_profile.get_relation_profile("non_existent_relation")
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
from contentgrid_application_client import ContentGridApplicationClient, EntityCollection
|
|
2
|
-
from contentgrid_application_client.application import EntityObject
|
|
3
|
-
from contentgrid_hal_client.exceptions import Forbidden, MissingHALTemplate
|
|
4
|
-
from contentgrid_hal_client.hal import HALLink
|
|
5
|
-
from contentgrid_hal_client.hal_forms import HALFormsTemplate, HALFormsProperty, HALFormsPropertyType, HALFormsMethod
|
|
6
|
-
from fixtures import auth_manager, cg_client # noqa: F401
|
|
7
|
-
from unittest.mock import MagicMock
|
|
8
|
-
import pytest
|
|
9
|
-
import hyperlink
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestCreateTextUriListPayload:
|
|
13
|
-
"""Unit tests for _create_text_uri_list_payload method"""
|
|
14
|
-
|
|
15
|
-
@pytest.fixture
|
|
16
|
-
def entity(self):
|
|
17
|
-
"""Create an EntityObject with a mock client for testing"""
|
|
18
|
-
mock_client = MagicMock()
|
|
19
|
-
mock_client.client_endpoint = hyperlink.parse("https://api.example.com/").normalize()
|
|
20
|
-
entity = EntityObject.__new__(EntityObject)
|
|
21
|
-
entity.client = mock_client
|
|
22
|
-
return entity
|
|
23
|
-
|
|
24
|
-
def test_uri_list_with_relative_paths(self, entity):
|
|
25
|
-
"""Test that relative paths are properly prefixed with client endpoint"""
|
|
26
|
-
result = entity._create_text_uri_list_payload(["/entities/123", "/entities/456"])
|
|
27
|
-
|
|
28
|
-
assert result == "https://api.example.com/entities/123\nhttps://api.example.com/entities/456"
|
|
29
|
-
|
|
30
|
-
def test_uri_list_with_full_urls(self, entity):
|
|
31
|
-
"""Test that full URLs matching endpoint are not modified"""
|
|
32
|
-
full_url = "https://api.example.com/entities/123"
|
|
33
|
-
result = entity._create_text_uri_list_payload([full_url])
|
|
34
|
-
|
|
35
|
-
assert result == full_url
|
|
36
|
-
|
|
37
|
-
def test_uri_list_with_hal_link(self, entity):
|
|
38
|
-
"""Test that HALLink objects are properly converted"""
|
|
39
|
-
hal_link = HALLink(uri="/entities/123")
|
|
40
|
-
result = entity._create_text_uri_list_payload([hal_link])
|
|
41
|
-
|
|
42
|
-
assert result == "https://api.example.com/entities/123"
|
|
43
|
-
|
|
44
|
-
def test_uri_list_with_hal_link_full_url(self, entity):
|
|
45
|
-
"""Test that HALLink with full URL is not double-prefixed"""
|
|
46
|
-
hal_link = HALLink(uri="https://api.example.com/entities/123")
|
|
47
|
-
result = entity._create_text_uri_list_payload([hal_link])
|
|
48
|
-
|
|
49
|
-
assert result == "https://api.example.com/entities/123"
|
|
50
|
-
|
|
51
|
-
def test_uri_list_without_prefix(self, entity):
|
|
52
|
-
"""Test that prefix_client_endpoint=False returns URIs unchanged"""
|
|
53
|
-
result = entity._create_text_uri_list_payload(["/entities/123"], prefix_client_endpoint=False)
|
|
54
|
-
|
|
55
|
-
assert result == "/entities/123"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class TestPrefixUrlInFormPayload:
|
|
59
|
-
"""Unit tests for _prefix_url_in_form_payload method"""
|
|
60
|
-
|
|
61
|
-
@pytest.fixture
|
|
62
|
-
def client(self):
|
|
63
|
-
"""Create a mock ContentGridApplicationClient for testing"""
|
|
64
|
-
mock_client = MagicMock(spec=ContentGridApplicationClient)
|
|
65
|
-
mock_client.client_endpoint = hyperlink.parse("https://api.example.com/").normalize()
|
|
66
|
-
mock_client._prefix_url_in_form_payload = ContentGridApplicationClient._prefix_url_in_form_payload.__get__(mock_client)
|
|
67
|
-
return mock_client
|
|
68
|
-
|
|
69
|
-
def test_prefix_url_property_with_relative_path(self, client):
|
|
70
|
-
"""Test that URL properties with relative paths are prefixed"""
|
|
71
|
-
template = HALFormsTemplate(
|
|
72
|
-
method=HALFormsMethod.POST,
|
|
73
|
-
properties=[HALFormsProperty(name="link", type=HALFormsPropertyType.url)]
|
|
74
|
-
)
|
|
75
|
-
payload = {"link": "/entities/123"}
|
|
76
|
-
|
|
77
|
-
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
78
|
-
|
|
79
|
-
assert payload["link"] == "https://api.example.com/entities/123"
|
|
80
|
-
|
|
81
|
-
def test_prefix_url_property_avoids_double_slashes(self, client):
|
|
82
|
-
"""Test that double slashes are avoided"""
|
|
83
|
-
template = HALFormsTemplate(
|
|
84
|
-
method=HALFormsMethod.POST,
|
|
85
|
-
properties=[HALFormsProperty(name="link", type=HALFormsPropertyType.url)]
|
|
86
|
-
)
|
|
87
|
-
payload = {"link": "/entities/123"}
|
|
88
|
-
|
|
89
|
-
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
90
|
-
|
|
91
|
-
assert "//" not in payload["link"].replace("https://", "")
|
|
92
|
-
|
|
93
|
-
def test_prefix_url_property_with_full_url(self, client):
|
|
94
|
-
"""Test that full URLs are not double-prefixed"""
|
|
95
|
-
template = HALFormsTemplate(
|
|
96
|
-
method=HALFormsMethod.POST,
|
|
97
|
-
properties=[HALFormsProperty(name="link", type=HALFormsPropertyType.url)]
|
|
98
|
-
)
|
|
99
|
-
payload = {"link": "https://api.example.com/entities/123"}
|
|
100
|
-
|
|
101
|
-
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
102
|
-
|
|
103
|
-
assert payload["link"] == "https://api.example.com/entities/123"
|
|
104
|
-
|
|
105
|
-
def test_prefix_url_property_list(self, client):
|
|
106
|
-
"""Test that URL properties in lists are prefixed"""
|
|
107
|
-
template = HALFormsTemplate(
|
|
108
|
-
method=HALFormsMethod.POST,
|
|
109
|
-
properties=[HALFormsProperty(name="links", type=HALFormsPropertyType.url)]
|
|
110
|
-
)
|
|
111
|
-
payload = {"links": ["/entities/123", "/entities/456"]}
|
|
112
|
-
|
|
113
|
-
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
114
|
-
|
|
115
|
-
assert payload["links"] == [
|
|
116
|
-
"https://api.example.com/entities/123",
|
|
117
|
-
"https://api.example.com/entities/456"
|
|
118
|
-
]
|
|
119
|
-
|
|
120
|
-
def test_non_url_properties_unchanged(self, client):
|
|
121
|
-
"""Test that non-URL properties are not modified"""
|
|
122
|
-
template = HALFormsTemplate(
|
|
123
|
-
method=HALFormsMethod.POST,
|
|
124
|
-
properties=[
|
|
125
|
-
HALFormsProperty(name="name", type=HALFormsPropertyType.text),
|
|
126
|
-
HALFormsProperty(name="link", type=HALFormsPropertyType.url)
|
|
127
|
-
]
|
|
128
|
-
)
|
|
129
|
-
payload = {"name": "/some/path", "link": "/entities/123"}
|
|
130
|
-
|
|
131
|
-
client._prefix_url_in_form_payload(template=template, payload=payload)
|
|
132
|
-
|
|
133
|
-
assert payload["name"] == "/some/path"
|
|
134
|
-
assert payload["link"] == "https://api.example.com/entities/123"
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
class TestRelations:
|
|
138
|
-
entity_name = "candidate"
|
|
139
|
-
relation_name = "skills"
|
|
140
|
-
|
|
141
|
-
def test_relations(self, cg_client: ContentGridApplicationClient):
|
|
142
|
-
entity = cg_client.create_entity(self.entity_name, {"name": "test"})
|
|
143
|
-
|
|
144
|
-
# fetch related skills
|
|
145
|
-
related_skills = cg_client.get_entity_relation_collection(
|
|
146
|
-
entity.get_self_link(), self.relation_name
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
assert isinstance(related_skills, EntityCollection)
|
|
150
|
-
|
|
151
|
-
# entity just instantiated so no skills
|
|
152
|
-
assert len(related_skills.get_entities()) == 0
|
|
153
|
-
|
|
154
|
-
skills = cg_client.get_entity_collection(singular_entity_name="skill")
|
|
155
|
-
|
|
156
|
-
amt_skills_to_sample = 4
|
|
157
|
-
skills_selection = skills.get_entities()[:amt_skills_to_sample]
|
|
158
|
-
assert len(skills_selection) == amt_skills_to_sample
|
|
159
|
-
|
|
160
|
-
skills_selection[1] = skills_selection[1].get_self_link().uri
|
|
161
|
-
skills_selection[2] = skills_selection[2].get_self_link()
|
|
162
|
-
|
|
163
|
-
cg_client.post_entity_relation(
|
|
164
|
-
entity.get_self_link(),
|
|
165
|
-
relation_name=self.relation_name,
|
|
166
|
-
related_entity_links=skills_selection[:-1],
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
related_skills.refetch()
|
|
170
|
-
|
|
171
|
-
assert len(related_skills.get_entities()) == amt_skills_to_sample - 1
|
|
172
|
-
|
|
173
|
-
# post last skill
|
|
174
|
-
cg_client.post_entity_relation(
|
|
175
|
-
entity_link=entity.get_self_link(),
|
|
176
|
-
relation_name=self.relation_name,
|
|
177
|
-
related_entity_links=[skills_selection[-1]],
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
related_skills = cg_client.get_entity_relation_collection(
|
|
181
|
-
entity_link=entity.get_self_link(), relation_name=self.relation_name
|
|
182
|
-
)
|
|
183
|
-
assert len(related_skills.get_entities()) == amt_skills_to_sample
|
|
184
|
-
|
|
185
|
-
entity.delete()
|
|
186
|
-
|
|
187
|
-
def test_put_on_to_many_not_supported(self, cg_client: ContentGridApplicationClient):
|
|
188
|
-
entity = cg_client.create_entity(self.entity_name, {"name": "test"})
|
|
189
|
-
|
|
190
|
-
skills = cg_client.get_entity_collection(singular_entity_name="skill")
|
|
191
|
-
skills_selection = skills.get_entities()[:2]
|
|
192
|
-
|
|
193
|
-
# PUT on to-many relations should raise Forbidden
|
|
194
|
-
with pytest.raises(MissingHALTemplate):
|
|
195
|
-
cg_client.put_entity_relation(
|
|
196
|
-
entity.get_self_link(),
|
|
197
|
-
relation_name=self.relation_name,
|
|
198
|
-
related_entity_links=skills_selection,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
entity.delete()
|
|
202
|
-
|
|
203
|
-
def test_clear_relations(self, cg_client: ContentGridApplicationClient):
|
|
204
|
-
entity = cg_client.create_entity(self.entity_name, {"name": "test"})
|
|
205
|
-
# no related exist yet
|
|
206
|
-
entity.clear_relation(relation_name=self.relation_name)
|
|
207
|
-
|
|
208
|
-
skills = cg_client.get_entity_collection(singular_entity_name="skill")
|
|
209
|
-
skills_selection = skills.get_entities()[:1]
|
|
210
|
-
cg_client.post_entity_relation(
|
|
211
|
-
entity.get_self_link(),
|
|
212
|
-
relation_name=self.relation_name,
|
|
213
|
-
related_entity_links=skills_selection,
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
assert len(entity.get_relation_collection(relation_name=self.relation_name).get_entities()) == 1
|
|
217
|
-
|
|
218
|
-
#clear relations again
|
|
219
|
-
entity.clear_relation(relation_name=self.relation_name)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
File without changes
|
|
File without changes
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/requirements.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_content.py
RENAMED
|
File without changes
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_curies.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_openapi.py
RENAMED
|
File without changes
|
|
File without changes
|
{contentgrid_application_client-0.2.4 → contentgrid_application_client-0.2.6}/tests/test_search.py
RENAMED
|
File without changes
|