octostar-python-client 0.1.759__py3-none-any.whl

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.
Files changed (257) hide show
  1. octostar/__init__.py +9 -0
  2. octostar/api/__init__.py +1 -0
  3. octostar/api/apps/__init__.py +0 -0
  4. octostar/api/apps/deploy_app.py +210 -0
  5. octostar/api/apps/execute_app_job.py +188 -0
  6. octostar/api/apps/get_app_logs.py +210 -0
  7. octostar/api/apps/get_apps_url.py +188 -0
  8. octostar/api/apps/get_job_logs.py +210 -0
  9. octostar/api/apps/get_job_progress.py +162 -0
  10. octostar/api/apps/kill_job.py +160 -0
  11. octostar/api/apps/list_app_jobs.py +276 -0
  12. octostar/api/apps/list_apps.py +251 -0
  13. octostar/api/apps/set_job_progress.py +216 -0
  14. octostar/api/apps/undeploy_app.py +160 -0
  15. octostar/api/metadata/__init__.py +0 -0
  16. octostar/api/metadata/get_version.py +232 -0
  17. octostar/api/metadata/get_whoami.py +232 -0
  18. octostar/api/notifications/__init__.py +0 -0
  19. octostar/api/notifications/delete_stream.py +222 -0
  20. octostar/api/notifications/get_subscriptions.py +240 -0
  21. octostar/api/notifications/publish_notification.py +275 -0
  22. octostar/api/notifications/pull_events_from_stream.py +282 -0
  23. octostar/api/notifications/push_event_to_stream.py +265 -0
  24. octostar/api/notifications/toast.py +264 -0
  25. octostar/api/ontology/__init__.py +0 -0
  26. octostar/api/ontology/fetch_ontology_data.py +275 -0
  27. octostar/api/ontology/get_ontologies.py +237 -0
  28. octostar/api/ontology/multi_query.py +297 -0
  29. octostar/api/ontology/query.py +276 -0
  30. octostar/api/pipeline/__init__.py +1 -0
  31. octostar/api/pipeline/get_processing_status.py +185 -0
  32. octostar/api/pipeline/update_processing_status.py +164 -0
  33. octostar/api/search/__init__.py +0 -0
  34. octostar/api/search/get_annotations.py +153 -0
  35. octostar/api/workspace_data/__init__.py +0 -0
  36. octostar/api/workspace_data/delete_blob.py +212 -0
  37. octostar/api/workspace_data/delete_entities.py +326 -0
  38. octostar/api/workspace_data/download_blob.py +235 -0
  39. octostar/api/workspace_data/get_attachment.py +336 -0
  40. octostar/api/workspace_data/get_files_tree.py +397 -0
  41. octostar/api/workspace_data/upload_blob.py +235 -0
  42. octostar/api/workspace_data/upsert_entities.py +284 -0
  43. octostar/api/workspace_permissions/__init__.py +0 -0
  44. octostar/api/workspace_permissions/get_permissions.py +325 -0
  45. octostar/api/workspace_tags/__init__.py +0 -0
  46. octostar/api/workspace_tags/delete_tag_from_entities.py +141 -0
  47. octostar/api/workspace_tags/tag_entities.py +180 -0
  48. octostar/client.py +492 -0
  49. octostar/errors.py +50 -0
  50. octostar/models/__init__.py +249 -0
  51. octostar/models/acknowledgement.py +74 -0
  52. octostar/models/acknowledgement_with_data.py +82 -0
  53. octostar/models/app_status.py +239 -0
  54. octostar/models/app_status_annotations.py +66 -0
  55. octostar/models/app_status_labels.py +69 -0
  56. octostar/models/app_with_url.py +82 -0
  57. octostar/models/child_processing_status.py +118 -0
  58. octostar/models/delete_entities_response_401.py +74 -0
  59. octostar/models/delete_entities_response_409.py +82 -0
  60. octostar/models/delete_entities_response_500.py +82 -0
  61. octostar/models/delete_stream_response_401.py +74 -0
  62. octostar/models/delete_tag_from_entities_response_401.py +74 -0
  63. octostar/models/deploy_app_json_body.py +90 -0
  64. octostar/models/deploy_app_json_body_secrets.py +65 -0
  65. octostar/models/deploy_app_response_200.py +98 -0
  66. octostar/models/deploy_app_response_200_data.py +60 -0
  67. octostar/models/deploy_app_response_400.py +82 -0
  68. octostar/models/deploy_app_response_403.py +82 -0
  69. octostar/models/deploy_app_response_404.py +82 -0
  70. octostar/models/deploy_app_response_409.py +82 -0
  71. octostar/models/deploy_app_response_500.py +82 -0
  72. octostar/models/entity.py +80 -0
  73. octostar/models/entity_response.py +99 -0
  74. octostar/models/entity_response_s3_urls.py +93 -0
  75. octostar/models/entity_response_s3_urls_additional_property.py +105 -0
  76. octostar/models/entity_response_s3_urls_additional_property_fields.py +114 -0
  77. octostar/models/execute_app_job_json_body.py +151 -0
  78. octostar/models/execute_app_job_json_body_annotation.py +65 -0
  79. octostar/models/execute_app_job_response_401.py +74 -0
  80. octostar/models/fetch_ontology_data_response_200.py +60 -0
  81. octostar/models/fetch_ontology_data_response_401.py +74 -0
  82. octostar/models/fetch_ontology_data_response_500.py +82 -0
  83. octostar/models/get_app_logs_response_401.py +74 -0
  84. octostar/models/get_app_logs_response_404.py +74 -0
  85. octostar/models/get_app_logs_response_500.py +82 -0
  86. octostar/models/get_apps_url_json_body.py +76 -0
  87. octostar/models/get_apps_url_response_401.py +74 -0
  88. octostar/models/get_apps_url_response_500.py +82 -0
  89. octostar/models/get_attachment_response_200.py +74 -0
  90. octostar/models/get_attachment_response_401.py +74 -0
  91. octostar/models/get_files_tree_response_200.py +106 -0
  92. octostar/models/get_files_tree_response_200_status.py +8 -0
  93. octostar/models/get_files_tree_response_400.py +111 -0
  94. octostar/models/get_files_tree_response_400_data.py +60 -0
  95. octostar/models/get_files_tree_response_400_status.py +8 -0
  96. octostar/models/get_files_tree_response_401.py +74 -0
  97. octostar/models/get_files_tree_response_500.py +111 -0
  98. octostar/models/get_files_tree_response_500_data.py +60 -0
  99. octostar/models/get_files_tree_response_500_status.py +8 -0
  100. octostar/models/get_job_logs_response_401.py +74 -0
  101. octostar/models/get_job_logs_response_404.py +74 -0
  102. octostar/models/get_job_logs_response_500.py +82 -0
  103. octostar/models/get_job_progress_response_401.py +74 -0
  104. octostar/models/get_object_response_401.py +74 -0
  105. octostar/models/get_ontologies_response_401.py +74 -0
  106. octostar/models/get_ontologies_response_500.py +81 -0
  107. octostar/models/get_permissions_response_200.py +98 -0
  108. octostar/models/get_permissions_response_400.py +82 -0
  109. octostar/models/get_permissions_response_401.py +74 -0
  110. octostar/models/get_permissions_response_500.py +82 -0
  111. octostar/models/get_processing_status_response_200.py +104 -0
  112. octostar/models/get_processing_status_response_200_data.py +87 -0
  113. octostar/models/get_processing_status_response_400.py +82 -0
  114. octostar/models/get_processing_status_response_500.py +82 -0
  115. octostar/models/get_subscriptions_response_200_item.py +74 -0
  116. octostar/models/get_version_response_200.py +74 -0
  117. octostar/models/get_version_response_404.py +74 -0
  118. octostar/models/get_whoami_response_200.py +129 -0
  119. octostar/models/get_whoami_response_401.py +74 -0
  120. octostar/models/insert_entity.py +114 -0
  121. octostar/models/insert_entity_base.py +266 -0
  122. octostar/models/insert_entity_relationships_item.py +107 -0
  123. octostar/models/insert_entity_request.py +94 -0
  124. octostar/models/internal_server_error.py +82 -0
  125. octostar/models/job_execution_result.py +146 -0
  126. octostar/models/job_status.py +196 -0
  127. octostar/models/job_status_labels.py +60 -0
  128. octostar/models/job_with_url.py +82 -0
  129. octostar/models/kill_job_response_401.py +74 -0
  130. octostar/models/list_app_jobs_response_401.py +74 -0
  131. octostar/models/list_app_jobs_response_500.py +82 -0
  132. octostar/models/list_apps_response_401.py +74 -0
  133. octostar/models/list_apps_response_500.py +82 -0
  134. octostar/models/multi_query_json_body.py +100 -0
  135. octostar/models/multi_query_json_body_queries_item.py +80 -0
  136. octostar/models/multi_query_response_400.py +82 -0
  137. octostar/models/multi_query_response_401.py +74 -0
  138. octostar/models/not_found_error.py +74 -0
  139. octostar/models/octostar_event.py +96 -0
  140. octostar/models/octostar_event_octostar_payload.py +100 -0
  141. octostar/models/octostar_event_octostar_payload_level.py +11 -0
  142. octostar/models/os_notification.py +122 -0
  143. octostar/models/processing_status.py +262 -0
  144. octostar/models/processing_status_code.py +14 -0
  145. octostar/models/progress_request.py +73 -0
  146. octostar/models/publish_notification_response_401.py +74 -0
  147. octostar/models/pull_events_from_stream_response_401.py +74 -0
  148. octostar/models/push_event_to_stream_response_401.py +74 -0
  149. octostar/models/query_json_body.py +101 -0
  150. octostar/models/query_json_body_params.py +60 -0
  151. octostar/models/query_response_400.py +82 -0
  152. octostar/models/query_response_401.py +74 -0
  153. octostar/models/set_job_progress_response_401.py +74 -0
  154. octostar/models/string_to_value_label_map.py +99 -0
  155. octostar/models/string_to_value_label_map_data.py +89 -0
  156. octostar/models/string_to_value_label_map_data_additional_property.py +80 -0
  157. octostar/models/successful_get_tags.py +103 -0
  158. octostar/models/successful_insertion.py +98 -0
  159. octostar/models/tag_entities_response_401.py +74 -0
  160. octostar/models/toast_level.py +11 -0
  161. octostar/models/toast_response_401.py +74 -0
  162. octostar/models/undeploy_app_response_401.py +74 -0
  163. octostar/models/update_processing_status_response_200.py +82 -0
  164. octostar/models/update_processing_status_response_400.py +82 -0
  165. octostar/models/update_processing_status_response_500.py +82 -0
  166. octostar/models/upsert_entities_response_401.py +74 -0
  167. octostar/models/upsert_entity.py +114 -0
  168. octostar/models/upsert_entity_base.py +266 -0
  169. octostar/models/upsert_entity_relationships_item.py +107 -0
  170. octostar/py.typed +1 -0
  171. octostar/types.py +54 -0
  172. octostar/utils/__init__.py +15 -0
  173. octostar/utils/chat/__init__.py +0 -0
  174. octostar/utils/chat/chat.py +513 -0
  175. octostar/utils/chat/detokenize.py +105 -0
  176. octostar/utils/chat/get_default_model.py +50 -0
  177. octostar/utils/chat/list_models.py +91 -0
  178. octostar/utils/chat/tokenize.py +105 -0
  179. octostar/utils/commons.py +226 -0
  180. octostar/utils/exceptions.py +134 -0
  181. octostar/utils/jobs/__init__.py +0 -0
  182. octostar/utils/jobs/apps/__init__.py +0 -0
  183. octostar/utils/jobs/apps/deploy_app.py +81 -0
  184. octostar/utils/jobs/apps/execute_app_job.py +114 -0
  185. octostar/utils/jobs/apps/get_app_logs.py +113 -0
  186. octostar/utils/jobs/apps/get_app_secret.py +102 -0
  187. octostar/utils/jobs/apps/get_apps_url.py +73 -0
  188. octostar/utils/jobs/apps/list_app_jobs.py +62 -0
  189. octostar/utils/jobs/apps/list_apps.py +126 -0
  190. octostar/utils/jobs/apps/undeploy_app.py +48 -0
  191. octostar/utils/jobs/get_job_logs.py +113 -0
  192. octostar/utils/jobs/get_job_progress.py +76 -0
  193. octostar/utils/jobs/kill_job.py +47 -0
  194. octostar/utils/jobs/set_job_progress.py +67 -0
  195. octostar/utils/meta/__init__.py +0 -0
  196. octostar/utils/meta/get_version.py +30 -0
  197. octostar/utils/meta/get_whoami.py +30 -0
  198. octostar/utils/notifications/__init__.py +0 -0
  199. octostar/utils/notifications/delete_stream.py +58 -0
  200. octostar/utils/notifications/get_my_subscriptions.py +49 -0
  201. octostar/utils/notifications/publish_notification.py +73 -0
  202. octostar/utils/notifications/pull_event_from_stream.py +63 -0
  203. octostar/utils/notifications/pull_events_from_stream.py +64 -0
  204. octostar/utils/notifications/push_event_to_stream.py +109 -0
  205. octostar/utils/notifications/push_events_to_stream.py +137 -0
  206. octostar/utils/notifications/toast.py +92 -0
  207. octostar/utils/ontology/__init__.py +10 -0
  208. octostar/utils/ontology/fetch_ontology_data.py +141 -0
  209. octostar/utils/ontology/get_ontologies.py +55 -0
  210. octostar/utils/ontology/multiquery_ontology.py +287 -0
  211. octostar/utils/ontology/query_ontology.py +186 -0
  212. octostar/utils/pipeline/__init__.py +1 -0
  213. octostar/utils/pipeline/get_processing_status.py +230 -0
  214. octostar/utils/pipeline/update_processing_status.py +286 -0
  215. octostar/utils/search/__init__.py +11 -0
  216. octostar/utils/search/bulk_update.py +138 -0
  217. octostar/utils/search/count.py +117 -0
  218. octostar/utils/search/get_entity_annotations.py +304 -0
  219. octostar/utils/search/get_index_definition.py +111 -0
  220. octostar/utils/search/multi_search.py +129 -0
  221. octostar/utils/workspace/__init__.py +0 -0
  222. octostar/utils/workspace/delete_entities.py +247 -0
  223. octostar/utils/workspace/delete_entity.py +81 -0
  224. octostar/utils/workspace/delete_relationship.py +78 -0
  225. octostar/utils/workspace/delete_relationships.py +85 -0
  226. octostar/utils/workspace/delete_temporary_blob.py +85 -0
  227. octostar/utils/workspace/extract_entities.py +140 -0
  228. octostar/utils/workspace/get_filepath_from_item.py +85 -0
  229. octostar/utils/workspace/get_filepaths_from_items.py +100 -0
  230. octostar/utils/workspace/get_files_tree.py +102 -0
  231. octostar/utils/workspace/get_item_from_filepath.py +102 -0
  232. octostar/utils/workspace/get_items_from_filepaths.py +108 -0
  233. octostar/utils/workspace/linkcharts/__init__.py +0 -0
  234. octostar/utils/workspace/linkcharts/create_linkchart.py +241 -0
  235. octostar/utils/workspace/permissions/PermissionLevel.py +8 -0
  236. octostar/utils/workspace/permissions/__init__.py +1 -0
  237. octostar/utils/workspace/permissions/get_permissions.py +81 -0
  238. octostar/utils/workspace/read_attachment.py +284 -0
  239. octostar/utils/workspace/read_file.py +113 -0
  240. octostar/utils/workspace/read_temporary_blob.py +428 -0
  241. octostar/utils/workspace/saved_searches/__init__.py +0 -0
  242. octostar/utils/workspace/saved_searches/create_saved_search.py +183 -0
  243. octostar/utils/workspace/tags/__init__.py +0 -0
  244. octostar/utils/workspace/tags/delete_tag_from_entities.py +96 -0
  245. octostar/utils/workspace/tags/tag_entities.py +175 -0
  246. octostar/utils/workspace/upsert_entities.py +268 -0
  247. octostar/utils/workspace/upsert_entity.py +110 -0
  248. octostar/utils/workspace/upsert_relationship.py +128 -0
  249. octostar/utils/workspace/upsert_relationships.py +194 -0
  250. octostar/utils/workspace/write_attachment.py +263 -0
  251. octostar/utils/workspace/write_file.py +335 -0
  252. octostar/utils/workspace/write_temporary_blob.py +218 -0
  253. octostar_python_client-0.1.759.dist-info/METADATA +159 -0
  254. octostar_python_client-0.1.759.dist-info/RECORD +257 -0
  255. octostar_python_client-0.1.759.dist-info/WHEEL +5 -0
  256. octostar_python_client-0.1.759.dist-info/licenses/LICENSE +21 -0
  257. octostar_python_client-0.1.759.dist-info/top_level.txt +1 -0
@@ -0,0 +1,268 @@
1
+ from types import SimpleNamespace
2
+ from typing import Dict, TypedDict, Union, List, Any
3
+ import uuid
4
+ import httpx
5
+ import logging
6
+
7
+ _logger = logging.getLogger(__name__)
8
+
9
+ from ..commons import network_retry_strategy
10
+ from ...client import Client, get_default_client
11
+ from ..exceptions import ApiConnectionError, ApiValidationError
12
+
13
+ MAX_ERROR_DETAILS = 10
14
+
15
+
16
+ class _EntityBaseRequired(TypedDict):
17
+ entity_type: str
18
+ fields: Dict[str, Any]
19
+
20
+
21
+ class EntityBase(_EntityBaseRequired, total=False):
22
+ os_entity_uid: str
23
+
24
+
25
+ def _raise_validation_error(
26
+ message,
27
+ client,
28
+ *,
29
+ entity_errors=None,
30
+ missing_entity_ids=None,
31
+ mismatched_keys=None,
32
+ ):
33
+ # status_code=200 mirrors the actual HTTP status — the server accepted
34
+ # the request and returned a structured per-entity breakdown; the
35
+ # failures were inside that response body, not at the transport layer.
36
+ raise ApiValidationError(
37
+ "upsert_entities",
38
+ SimpleNamespace(status_code=200, content=message),
39
+ client,
40
+ entity_errors=entity_errors,
41
+ missing_entity_ids=missing_entity_ids,
42
+ mismatched_keys=mismatched_keys,
43
+ )
44
+
45
+
46
+ def _validate_response(body, entities, data, response, client):
47
+ expected_ids = {e["os_entity_uid"] for e in entities}
48
+ returned_ids = {e["entity_id"] for e in data.get("entities", [])}
49
+ errors_list = data.get("errors", [])
50
+ error_ids = {e["entity_id"] for e in errors_list}
51
+ accounted_ids = returned_ids | error_ids
52
+
53
+ missing_ids = expected_ids - accounted_ids
54
+ if missing_ids:
55
+ missing_entities = [e for e in body if e["entity_id"] in missing_ids]
56
+ _raise_validation_error(
57
+ f"Entities missing from response (neither in entities nor errors): "
58
+ f"{missing_ids}, payload: {missing_entities}",
59
+ client,
60
+ missing_entity_ids=missing_ids,
61
+ )
62
+
63
+ expected_keys = {(e["entity_id"], e["os_workspace"]) for e in body}
64
+ returned_keys = {
65
+ (e["entity_id"], e["os_workspace"]) for e in data.get("entities", [])
66
+ }
67
+ mismatched = returned_keys - expected_keys
68
+ if mismatched:
69
+ _raise_validation_error(
70
+ f"Returned entities have unexpected (entity_id, os_workspace) keys "
71
+ f"(possibly stale records): {mismatched}",
72
+ client,
73
+ mismatched_keys=list(mismatched),
74
+ )
75
+
76
+ if errors_list:
77
+ lines = [
78
+ f" {e['entity_id']}: {e['error']}" for e in errors_list[:MAX_ERROR_DETAILS]
79
+ ]
80
+ if len(errors_list) > MAX_ERROR_DETAILS:
81
+ lines.append(f" ... and {len(errors_list) - MAX_ERROR_DETAILS} more")
82
+ _raise_validation_error(
83
+ f"{len(errors_list)} entities failed to upsert:\n" + "\n".join(lines),
84
+ client,
85
+ entity_errors=errors_list,
86
+ )
87
+
88
+ return list({e["entity_id"]: e for e in data.get("entities", [])}.values())
89
+
90
+
91
+ def sync(
92
+ os_workspace: Union[str, List[str]],
93
+ entities: List[EntityBase],
94
+ ontology_name: str = None,
95
+ client: Client = None,
96
+ ):
97
+ """
98
+ # Create or update a set of local entities in a workspace
99
+
100
+ ## Arguments
101
+ - `os_workspace`: The workspace ID, or a list of workspace IDs (one per entity)
102
+ - `entities`: The entities to update or create, as a list of dictionaries
103
+ Each dictionary must contain:
104
+ - `entity_type`: The concept name for the entity
105
+ - `fields`: A dictionary of fields for the entity, according to the concept definition in the ontology
106
+ Each dictionary can optionally contain:
107
+ - `os_entity_uid`: The ID for the entity. Only necessary in case of an update
108
+ - `ontology_name`: The name of the ontology
109
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used
110
+
111
+ ## Returns
112
+ The list of created/updated entity records
113
+
114
+ ## Raises
115
+ - `NotImplementedError`: If the operation is not supported for the ontology
116
+ - `ApiValidationError`: The server accepted the request but rejected
117
+ one or more entities. The per-entity breakdown is on the
118
+ exception as structured attributes (see the class docstring for
119
+ the shape of each):
120
+ - `entity_errors`: full per-entity error rows from the server
121
+ (``list[{"entity_id": str, "error": str}]``)
122
+ - `missing_entity_ids`: ``set[str]`` of IDs the server returned
123
+ no record for
124
+ - `mismatched_keys`: ``list[(entity_id, os_workspace)]`` of
125
+ entries returned under an unexpected workspace
126
+ - `invalid_fields`: ``dict[entity_id, list[field_name]]`` for
127
+ per-field rejections — drop these fields and re-upsert to
128
+ recover
129
+ - `reserved_keyword_entity_types`: ``set[str]`` of entity-type
130
+ tokens flagged as reserved keywords by the query parser
131
+ Subclass of `ApiConnectionError` for backward compatibility.
132
+ - `ApiConnectionError`: If the transport layer failed (network error,
133
+ non-2xx response from the upsert endpoint).
134
+ """
135
+ assert len(entities) > 0
136
+ for entity in entities:
137
+ entity["os_entity_uid"] = entity.get("os_entity_uid", None) or str(uuid.uuid4())
138
+ if "fields" not in entity:
139
+ entity["fields"] = dict()
140
+ if isinstance(os_workspace, str):
141
+ os_workspace = [os_workspace] * len(entities)
142
+ assert len(os_workspace) == len(entities)
143
+ if not ontology_name:
144
+ if not client:
145
+ client = get_default_client()
146
+ ontology_name = client.ontology
147
+ if ontology_name != client.ontology:
148
+ raise NotImplementedError(
149
+ "This operation is currently only supported on the current ontology!"
150
+ )
151
+ body = [
152
+ {
153
+ **entities[i]["fields"],
154
+ "entity_id": entities[i]["os_entity_uid"],
155
+ "entity_type": entities[i]["entity_type"],
156
+ "os_workspace": os_workspace[i],
157
+ }
158
+ for i in range(len(entities))
159
+ ]
160
+ endpoint_url = f"{client.get_base_url_v1()}/api/v1/entities/upsert"
161
+ headers = {
162
+ "Content-Type": "application/json",
163
+ "Authorization": f"Bearer {client.token}",
164
+ "x-ontology": client.ontology,
165
+ }
166
+ response = None
167
+ try:
168
+ for attempt in network_retry_strategy():
169
+ with attempt:
170
+ with httpx.Client() as httpx_client:
171
+ response = httpx_client.post(
172
+ endpoint_url, json=body, headers=headers, timeout=120
173
+ )
174
+ response.raise_for_status()
175
+ except Exception:
176
+ raise ApiConnectionError("upsert_entities", response, client)
177
+
178
+ return _validate_response(body, entities, response.json(), response, client)
179
+
180
+
181
+ async def asyncio(
182
+ os_workspace: Union[str, List[str]],
183
+ entities: List[EntityBase],
184
+ ontology_name: str = None,
185
+ client: Client = None,
186
+ ):
187
+ """
188
+ # Create or update asynchronously a set of local entities in a workspace
189
+
190
+ ## Arguments
191
+ - `os_workspace`: The workspace ID, or a list of workspace IDs (one per entity)
192
+ - `entities`: The entities to update or create, as a list of dictionaries
193
+ Each dictionary must contain:
194
+ - `entity_type`: The concept name for the entity
195
+ - `fields`: A dictionary of fields for the entity, according to the concept definition in the ontology
196
+ Each dictionary can optionally contain:
197
+ - `os_entity_uid`: The ID for the entity. Only necessary in case of an update
198
+ - `ontology_name`: The name of the ontology
199
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used
200
+
201
+ ## Returns
202
+ The list of created/updated entity records
203
+
204
+ ## Raises
205
+ - `NotImplementedError`: If the operation is not supported for the ontology
206
+ - `ApiValidationError`: The server accepted the request but rejected
207
+ one or more entities. The per-entity breakdown is on the
208
+ exception as structured attributes (see the class docstring for
209
+ the shape of each):
210
+ - `entity_errors`: full per-entity error rows from the server
211
+ (``list[{"entity_id": str, "error": str}]``)
212
+ - `missing_entity_ids`: ``set[str]`` of IDs the server returned
213
+ no record for
214
+ - `mismatched_keys`: ``list[(entity_id, os_workspace)]`` of
215
+ entries returned under an unexpected workspace
216
+ - `invalid_fields`: ``dict[entity_id, list[field_name]]`` for
217
+ per-field rejections — drop these fields and re-upsert to
218
+ recover
219
+ - `reserved_keyword_entity_types`: ``set[str]`` of entity-type
220
+ tokens flagged as reserved keywords by the query parser
221
+ Subclass of `ApiConnectionError` for backward compatibility.
222
+ - `ApiConnectionError`: If the transport layer failed (network error,
223
+ non-2xx response from the upsert endpoint).
224
+ """
225
+ assert len(entities) > 0
226
+ for entity in entities:
227
+ entity["os_entity_uid"] = entity.get("os_entity_uid", None) or str(uuid.uuid4())
228
+ if "fields" not in entity:
229
+ entity["fields"] = dict()
230
+ if isinstance(os_workspace, str):
231
+ os_workspace = [os_workspace] * len(entities)
232
+ assert len(os_workspace) == len(entities)
233
+ if not ontology_name:
234
+ if not client:
235
+ client = get_default_client()
236
+ ontology_name = client.ontology
237
+ if ontology_name != client.ontology:
238
+ raise NotImplementedError(
239
+ "This operation is currently only supported on the current ontology!"
240
+ )
241
+ body = [
242
+ {
243
+ **entities[i]["fields"],
244
+ "entity_id": entities[i]["os_entity_uid"],
245
+ "entity_type": entities[i]["entity_type"],
246
+ "os_workspace": os_workspace[i],
247
+ }
248
+ for i in range(len(entities))
249
+ ]
250
+ endpoint_url = f"{client.get_base_url_v1()}/api/v1/entities/upsert"
251
+ headers = {
252
+ "Content-Type": "application/json",
253
+ "Authorization": f"Bearer {client.token}",
254
+ "x-ontology": client.ontology,
255
+ }
256
+ response = None
257
+ try:
258
+ for attempt in network_retry_strategy():
259
+ with attempt:
260
+ async with httpx.AsyncClient() as httpx_client:
261
+ response = await httpx_client.post(
262
+ endpoint_url, json=body, headers=headers, timeout=120
263
+ )
264
+ response.raise_for_status()
265
+ except Exception:
266
+ raise ApiConnectionError("upsert_entities", response, client)
267
+
268
+ return _validate_response(body, entities, response.json(), response, client)
@@ -0,0 +1,110 @@
1
+ from typing import Dict, Union, Any
2
+ import uuid
3
+ import os
4
+
5
+ from ...client import Client
6
+ from . import upsert_entities
7
+
8
+
9
+ def sync(
10
+ os_workspace: str,
11
+ os_entity_type: str,
12
+ fields: Dict[str, Any],
13
+ os_entity_uid: Union[str, None] = None,
14
+ ontology_name: str = None,
15
+ client: Client = None,
16
+ ):
17
+ """
18
+ # Create or update a local entity in a workspace
19
+
20
+ ## Arguments
21
+ - `os_workspace`: The workspace ID
22
+ - `os_entity_type`: The concept name for the entity
23
+ - `fields`: A dictionary of fields for the entity, according to the concept definition in the ontology
24
+ - `os_entity_uid`: The ID for the entity. Only necessary in case of an update
25
+ - `ontology_name`: The name of the ontology
26
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used
27
+
28
+ ## Returns
29
+ The created/updated entity record
30
+
31
+ ## Raises
32
+ - `NotImplementedError`: If the operation is not supported for the ontology
33
+ - `ApiValidationError`: The server rejected the entity. The
34
+ breakdown is exposed as structured attributes on the exception
35
+ (see the class docstring for the shape of each):
36
+ - `entity_errors`: ``list[{"entity_id": str, "error": str}]``
37
+ - `invalid_fields`: ``dict[entity_id, list[field_name]]`` —
38
+ drop these fields and re-upsert to recover
39
+ - `reserved_keyword_entity_types`: ``set[str]`` of entity-type
40
+ tokens flagged as reserved keywords
41
+ Subclass of `ApiConnectionError` for backward compatibility.
42
+ - `ApiConnectionError`: If the transport layer failed (network error,
43
+ non-2xx response).
44
+ """
45
+ result = upsert_entities.sync(
46
+ os_workspace,
47
+ [
48
+ {
49
+ "os_workspace": os_workspace,
50
+ "entity_type": os_entity_type,
51
+ "os_entity_uid": os_entity_uid,
52
+ "fields": fields,
53
+ }
54
+ ],
55
+ ontology_name=ontology_name,
56
+ client=client,
57
+ )
58
+ return result[0]
59
+
60
+
61
+ async def asyncio(
62
+ os_workspace: str,
63
+ os_entity_type: str,
64
+ fields: Dict[str, Any],
65
+ os_entity_uid: Union[str, None] = None,
66
+ ontology_name: str = None,
67
+ client: Client = None,
68
+ ):
69
+ """
70
+ # Create or update asynchronously a local entity in a workspace
71
+
72
+ ## Arguments
73
+ - `os_workspace`: The workspace ID
74
+ - `os_entity_type`: The concept name for the entity
75
+ - `fields`: A dictionary of fields for the entity, according to the concept definition in the ontology
76
+ - `os_entity_uid`: The ID for the entity. Only necessary in case of an update
77
+ - `ontology_name`: The name of the ontology
78
+ - `client`: The Client with which to connect to Octostar. If None, the default one is used
79
+
80
+ ## Returns
81
+ The created/updated entity record
82
+
83
+ ## Raises
84
+ - `NotImplementedError`: If the operation is not supported for the ontology
85
+ - `ApiValidationError`: The server rejected the entity. The
86
+ breakdown is exposed as structured attributes on the exception
87
+ (see the class docstring for the shape of each):
88
+ - `entity_errors`: ``list[{"entity_id": str, "error": str}]``
89
+ - `invalid_fields`: ``dict[entity_id, list[field_name]]`` —
90
+ drop these fields and re-upsert to recover
91
+ - `reserved_keyword_entity_types`: ``set[str]`` of entity-type
92
+ tokens flagged as reserved keywords
93
+ Subclass of `ApiConnectionError` for backward compatibility.
94
+ - `ApiConnectionError`: If the transport layer failed (network error,
95
+ non-2xx response).
96
+ """
97
+ result = await upsert_entities.asyncio(
98
+ os_workspace,
99
+ [
100
+ {
101
+ "os_workspace": os_workspace,
102
+ "entity_type": os_entity_type,
103
+ "os_entity_uid": os_entity_uid,
104
+ "fields": fields,
105
+ }
106
+ ],
107
+ ontology_name=ontology_name,
108
+ client=client,
109
+ )
110
+ return result[0]
@@ -0,0 +1,128 @@
1
+ from typing import Dict, Union, Any, Optional
2
+ import uuid
3
+ import os
4
+ import logging
5
+
6
+ _logger = logging.getLogger(__name__)
7
+
8
+ from ..ontology import query_ontology
9
+ from . import upsert_entities
10
+ from ...types import Unset, UNSET
11
+ from ...client import Client, get_default_client
12
+
13
+
14
+ def sync(
15
+ os_entity_uid_from: str,
16
+ entity_type_from: str,
17
+ os_entity_uid_to: str,
18
+ entity_type_to: str,
19
+ os_relationship_name: str,
20
+ os_relationship_workspace: str,
21
+ relationship_fields: Dict[str, Any] = None,
22
+ os_relationship_uid: Optional[str] = None,
23
+ allow_multi_cardinality: bool = True,
24
+ ontology_name: str = None,
25
+ client: Client = None,
26
+ ):
27
+ """
28
+ # Create or update a local relationship between two entities, a source and a target
29
+
30
+ ## Arguments
31
+ - `os_entity_uid_from`: The source entity ID
32
+ - `entity_type_from`: The source entity concept name
33
+ - `os_entity_uid_to`: The target entity ID
34
+ - `entity_type_to`: The target entity concept name
35
+ - `os_relationship_name`: The name of the relationship, from source to target
36
+ - `os_relationship_workspace`: The workspace for the relationship
37
+ - `relationship_fields`: A dictionary of additional properties for the relationship
38
+ - `os_relationship_uid`: The ID for the relationship. Only necessary in case of an update
39
+ - `allow_multi_cardinality`: If True, allows multiple relationships from the same source, target, and relationship name. Otherwise, it updates them.
40
+ Note this uniqueness is only applied per-workspace
41
+ - `ontology_name`: The name of the ontology
42
+ - `client`: The Client with which to connect to Octostar
43
+
44
+ ## Returns
45
+ The created/updated relationship record
46
+
47
+ ## Raises
48
+ - `NotImplementedError`: If the operation is not supported for the ontology or for the given data
49
+ - `ApiValidationError`: The server rejected the relationship (the
50
+ underlying ``os_workspace_relationship`` record failed
51
+ validation, the endpoint entity_type is a reserved keyword,
52
+ etc.). The breakdown is exposed as structured attributes (see
53
+ the class docstring for the shape of each):
54
+ - `entity_errors`: ``list[{"entity_id": str, "error": str}]``
55
+ - `invalid_fields`: ``dict[entity_id, list[field_name]]`` —
56
+ drop these fields and re-upsert to recover
57
+ - `reserved_keyword_entity_types`: ``set[str]`` of entity-type
58
+ tokens flagged as reserved keywords
59
+ Subclass of `ApiConnectionError` for backward compatibility.
60
+ - `ApiConnectionError`: If the transport layer failed (network error,
61
+ non-2xx response).
62
+ - `ValueError`: If relationship uniqueness is enforced when the relationship is already not unique
63
+ """
64
+ if relationship_fields is None:
65
+ relationship_fields = {}
66
+ if not os_relationship_uid:
67
+ os_relationship_uid = str(uuid.uuid4())
68
+ if not client:
69
+ client = get_default_client()
70
+ if not ontology_name:
71
+ ontology_name = client.ontology
72
+ if ontology_name != client.ontology:
73
+ raise NotImplementedError(
74
+ "This operation is currently only supported on the current ontology!"
75
+ )
76
+ if not allow_multi_cardinality:
77
+ check_existence_query = f"""SELECT os_entity_uid FROM dtimbr.os_workspace_relationship
78
+ WHERE os_workspace='{os_relationship_workspace}' AND
79
+ os_relationship_name='{os_relationship_name}' AND
80
+ os_entity_uid_from='{os_entity_uid_from}' AND
81
+ os_entity_type_from='{entity_type_from}' AND
82
+ os_entity_uid_to='{os_entity_uid_to}' AND
83
+ os_entity_type_to='{entity_type_to}' """
84
+ result = query_ontology.sync(check_existence_query, client=client)
85
+ if result:
86
+ if len(result) > 1:
87
+ raise ValueError(
88
+ "Cannot enforce cardinality = 1 when cardinality is already greater than 1!"
89
+ )
90
+ os_relationship_uid = result[0]["os_entity_uid"]
91
+ result = upsert_entities.sync(
92
+ os_relationship_workspace,
93
+ [
94
+ {
95
+ "entity_type": "os_workspace_relationship",
96
+ "os_entity_uid": os_relationship_uid,
97
+ "fields": {
98
+ **relationship_fields,
99
+ "os_entity_uid_from": os_entity_uid_from,
100
+ "os_entity_type_from": entity_type_from,
101
+ "os_entity_uid_to": os_entity_uid_to,
102
+ "os_entity_type_to": entity_type_to,
103
+ "os_relationship_name": os_relationship_name,
104
+ },
105
+ }
106
+ ],
107
+ client=client,
108
+ )
109
+ return result[0]
110
+
111
+
112
+ async def asyncio(
113
+ os_workspace_from: str,
114
+ os_entity_uid_from: str,
115
+ entity_type_from: str,
116
+ os_workspace_to: Union[None, str],
117
+ os_entity_uid_to: str,
118
+ entity_type_to: str,
119
+ os_relationship_name: str,
120
+ relationship_fields: Dict[str, Any] = None,
121
+ os_relationship_uid: Union[str, Unset] = UNSET,
122
+ ontology_name: str = None,
123
+ client: Client = None,
124
+ ):
125
+ """
126
+ # NOT IMPLEMENTED
127
+ """
128
+ raise NotImplementedError()