skyflow-flowvault-python 1.0.0__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 (202) hide show
  1. common/.coveragerc +4 -0
  2. common/__init__.py +3 -0
  3. common/client/__init__.py +0 -0
  4. common/client/base_skyflow.py +345 -0
  5. common/client/utils/__init__.py +1 -0
  6. common/client/utils/_utils.py +127 -0
  7. common/errors/__init__.py +1 -0
  8. common/errors/_skyflow_error.py +17 -0
  9. common/generated/__init__.py +0 -0
  10. common/generated/rest/__init__.py +24 -0
  11. common/generated/rest/authentication/__init__.py +4 -0
  12. common/generated/rest/authentication/client.py +181 -0
  13. common/generated/rest/authentication/raw_client.py +241 -0
  14. common/generated/rest/client.py +153 -0
  15. common/generated/rest/core/__init__.py +52 -0
  16. common/generated/rest/core/api_error.py +23 -0
  17. common/generated/rest/core/client_wrapper.py +86 -0
  18. common/generated/rest/core/datetime_utils.py +28 -0
  19. common/generated/rest/core/file.py +67 -0
  20. common/generated/rest/core/force_multipart.py +16 -0
  21. common/generated/rest/core/http_client.py +543 -0
  22. common/generated/rest/core/http_response.py +55 -0
  23. common/generated/rest/core/jsonable_encoder.py +100 -0
  24. common/generated/rest/core/pydantic_utilities.py +255 -0
  25. common/generated/rest/core/query_encoder.py +58 -0
  26. common/generated/rest/core/remove_none_from_dict.py +11 -0
  27. common/generated/rest/core/request_options.py +35 -0
  28. common/generated/rest/core/serialization.py +276 -0
  29. common/generated/rest/environment.py +8 -0
  30. common/generated/rest/errors/__init__.py +9 -0
  31. common/generated/rest/errors/bad_request_error.py +14 -0
  32. common/generated/rest/errors/not_found_error.py +14 -0
  33. common/generated/rest/errors/unauthorized_error.py +14 -0
  34. common/generated/rest/py.typed +0 -0
  35. common/generated/rest/types/__init__.py +9 -0
  36. common/generated/rest/types/googlerpc_status.py +22 -0
  37. common/generated/rest/types/protobuf_any.py +21 -0
  38. common/generated/rest/types/v_1_get_auth_token_response.py +33 -0
  39. common/generated/rest/version.py +6 -0
  40. common/service_account/__init__.py +1 -0
  41. common/service_account/_utils.py +248 -0
  42. common/service_account/client/__init__.py +0 -0
  43. common/service_account/client/auth_client.py +13 -0
  44. common/utils/__init__.py +4 -0
  45. common/utils/_helpers.py +18 -0
  46. common/utils/_skyflow_messages.py +445 -0
  47. common/utils/_utils.py +50 -0
  48. common/utils/constants.py +291 -0
  49. common/utils/enums/__init__.py +12 -0
  50. common/utils/enums/content_types.py +9 -0
  51. common/utils/enums/detect_entities.py +73 -0
  52. common/utils/enums/detect_output_transcriptions.py +8 -0
  53. common/utils/enums/env.py +13 -0
  54. common/utils/enums/log_level.py +8 -0
  55. common/utils/enums/masking_method.py +5 -0
  56. common/utils/enums/redaction_type.py +7 -0
  57. common/utils/enums/request_method.py +8 -0
  58. common/utils/enums/token_mode.py +6 -0
  59. common/utils/enums/token_type.py +6 -0
  60. common/utils/logger/__init__.py +2 -0
  61. common/utils/logger/_log_helpers.py +47 -0
  62. common/utils/logger/_logger.py +50 -0
  63. common/utils/validations/__init__.py +11 -0
  64. common/utils/validations/_validations.py +267 -0
  65. common/vault/base_vault_client.py +122 -0
  66. common/vault/base_vault_controller.py +58 -0
  67. common/vault/data/__init__.py +2 -0
  68. common/vault/data/_base_insert_request.py +7 -0
  69. common/vault/data/_base_insert_response.py +11 -0
  70. skyflow/__init__.py +2 -0
  71. skyflow/client/__init__.py +1 -0
  72. skyflow/client/_http_config_builder.py +45 -0
  73. skyflow/client/skyflow.py +15 -0
  74. skyflow/error/__init__.py +3 -0
  75. skyflow/generated/__init__.py +0 -0
  76. skyflow/generated/rest/__init__.py +164 -0
  77. skyflow/generated/rest/_default_clients.py +32 -0
  78. skyflow/generated/rest/client.py +385 -0
  79. skyflow/generated/rest/core/__init__.py +127 -0
  80. skyflow/generated/rest/core/api_error.py +23 -0
  81. skyflow/generated/rest/core/client_wrapper.py +148 -0
  82. skyflow/generated/rest/core/datetime_utils.py +70 -0
  83. skyflow/generated/rest/core/file.py +67 -0
  84. skyflow/generated/rest/core/force_multipart.py +18 -0
  85. skyflow/generated/rest/core/http_client.py +940 -0
  86. skyflow/generated/rest/core/http_response.py +63 -0
  87. skyflow/generated/rest/core/http_sse/__init__.py +42 -0
  88. skyflow/generated/rest/core/http_sse/_api.py +455 -0
  89. skyflow/generated/rest/core/http_sse/_decoders.py +74 -0
  90. skyflow/generated/rest/core/http_sse/_exceptions.py +7 -0
  91. skyflow/generated/rest/core/http_sse/_models.py +17 -0
  92. skyflow/generated/rest/core/jsonable_encoder.py +133 -0
  93. skyflow/generated/rest/core/logging.py +107 -0
  94. skyflow/generated/rest/core/parse_error.py +36 -0
  95. skyflow/generated/rest/core/pydantic_utilities.py +486 -0
  96. skyflow/generated/rest/core/query_encoder.py +58 -0
  97. skyflow/generated/rest/core/remove_none_from_dict.py +11 -0
  98. skyflow/generated/rest/core/request_options.py +40 -0
  99. skyflow/generated/rest/core/serialization.py +347 -0
  100. skyflow/generated/rest/environment.py +8 -0
  101. skyflow/generated/rest/errors/__init__.py +53 -0
  102. skyflow/generated/rest/errors/bad_request_error.py +11 -0
  103. skyflow/generated/rest/errors/forbidden_error.py +11 -0
  104. skyflow/generated/rest/errors/internal_server_error.py +11 -0
  105. skyflow/generated/rest/errors/not_found_error.py +11 -0
  106. skyflow/generated/rest/errors/too_many_requests_error.py +11 -0
  107. skyflow/generated/rest/errors/unauthorized_error.py +11 -0
  108. skyflow/generated/rest/py.typed +0 -0
  109. skyflow/generated/rest/query/__init__.py +4 -0
  110. skyflow/generated/rest/query/client.py +139 -0
  111. skyflow/generated/rest/query/raw_client.py +239 -0
  112. skyflow/generated/rest/raw_client.py +86 -0
  113. skyflow/generated/rest/records/__init__.py +34 -0
  114. skyflow/generated/rest/records/client.py +685 -0
  115. skyflow/generated/rest/records/raw_client.py +988 -0
  116. skyflow/generated/rest/records/types/__init__.py +34 -0
  117. skyflow/generated/rest/records/types/update_request_update_type.py +5 -0
  118. skyflow/generated/rest/tokens/__init__.py +4 -0
  119. skyflow/generated/rest/tokens/client.py +246 -0
  120. skyflow/generated/rest/tokens/raw_client.py +507 -0
  121. skyflow/generated/rest/types/__init__.py +116 -0
  122. skyflow/generated/rest/types/column_redactions.py +33 -0
  123. skyflow/generated/rest/types/delete_response.py +23 -0
  124. skyflow/generated/rest/types/delete_response_object.py +42 -0
  125. skyflow/generated/rest/types/detokenize_response.py +23 -0
  126. skyflow/generated/rest/types/detokenize_response_object.py +54 -0
  127. skyflow/generated/rest/types/error_response.py +20 -0
  128. skyflow/generated/rest/types/error_response_error.py +28 -0
  129. skyflow/generated/rest/types/execute_query_record_response.py +22 -0
  130. skyflow/generated/rest/types/execute_query_response.py +26 -0
  131. skyflow/generated/rest/types/execute_query_response_metadata.py +26 -0
  132. skyflow/generated/rest/types/get_request_data.py +60 -0
  133. skyflow/generated/rest/types/get_response.py +23 -0
  134. skyflow/generated/rest/types/get_tokens_from_values_request_object.py +30 -0
  135. skyflow/generated/rest/types/get_tokens_from_values_response.py +23 -0
  136. skyflow/generated/rest/types/google_protobuf_value.py +8 -0
  137. skyflow/generated/rest/types/http_code.py +6 -0
  138. skyflow/generated/rest/types/insert_record_data.py +36 -0
  139. skyflow/generated/rest/types/insert_response.py +23 -0
  140. skyflow/generated/rest/types/record_response_object.py +70 -0
  141. skyflow/generated/rest/types/token_group_redactions.py +33 -0
  142. skyflow/generated/rest/types/tokenize_response_object.py +49 -0
  143. skyflow/generated/rest/types/unique_value.py +22 -0
  144. skyflow/generated/rest/types/update_record_data.py +52 -0
  145. skyflow/generated/rest/types/update_record_data_update_type.py +5 -0
  146. skyflow/generated/rest/types/update_response.py +23 -0
  147. skyflow/generated/rest/types/upsert.py +45 -0
  148. skyflow/generated/rest/types/upsert_update_type.py +5 -0
  149. skyflow/generated/rest/version.py +6 -0
  150. skyflow/service_account/__init__.py +15 -0
  151. skyflow/utils/__init__.py +9 -0
  152. skyflow/utils/_http_config.py +36 -0
  153. skyflow/utils/_response_parsing.py +66 -0
  154. skyflow/utils/_retry.py +97 -0
  155. skyflow/utils/_skyflow_messages.py +114 -0
  156. skyflow/utils/_utils.py +54 -0
  157. skyflow/utils/_version.py +1 -0
  158. skyflow/utils/enums/__init__.py +3 -0
  159. skyflow/utils/enums/_custom_header_key.py +10 -0
  160. skyflow/utils/enums/_env_urls.py +9 -0
  161. skyflow/utils/enums/_upsert_type.py +7 -0
  162. skyflow/utils/validations/__init__.py +9 -0
  163. skyflow/utils/validations/_validations.py +195 -0
  164. skyflow/vault/__init__.py +0 -0
  165. skyflow/vault/client/__init__.py +0 -0
  166. skyflow/vault/client/client.py +102 -0
  167. skyflow/vault/controller/__init__.py +1 -0
  168. skyflow/vault/controller/_vault.py +475 -0
  169. skyflow/vault/data/__init__.py +30 -0
  170. skyflow/vault/data/_column_redactions.py +4 -0
  171. skyflow/vault/data/_delete_options.py +8 -0
  172. skyflow/vault/data/_delete_request.py +5 -0
  173. skyflow/vault/data/_delete_response.py +9 -0
  174. skyflow/vault/data/_delete_response_record.py +9 -0
  175. skyflow/vault/data/_detokenize_options.py +8 -0
  176. skyflow/vault/data/_detokenize_request.py +9 -0
  177. skyflow/vault/data/_detokenize_response.py +9 -0
  178. skyflow/vault/data/_detokenize_response_record.py +16 -0
  179. skyflow/vault/data/_detokenize_response_record_metadata.py +10 -0
  180. skyflow/vault/data/_get_options.py +8 -0
  181. skyflow/vault/data/_get_request.py +17 -0
  182. skyflow/vault/data/_get_request_record.py +13 -0
  183. skyflow/vault/data/_get_response.py +9 -0
  184. skyflow/vault/data/_get_response_record.py +17 -0
  185. skyflow/vault/data/_insert_options.py +8 -0
  186. skyflow/vault/data/_insert_request.py +11 -0
  187. skyflow/vault/data/_insert_request_record.py +9 -0
  188. skyflow/vault/data/_insert_response.py +9 -0
  189. skyflow/vault/data/_insert_response_record.py +17 -0
  190. skyflow/vault/data/_request_context.py +16 -0
  191. skyflow/vault/data/_token.py +8 -0
  192. skyflow/vault/data/_token_group_redactions.py +4 -0
  193. skyflow/vault/data/_update_options.py +8 -0
  194. skyflow/vault/data/_update_request.py +11 -0
  195. skyflow/vault/data/_update_request_record.py +6 -0
  196. skyflow/vault/data/_update_response.py +9 -0
  197. skyflow/vault/data/_update_response_record.py +17 -0
  198. skyflow/vault/data/_upsert_options.py +7 -0
  199. skyflow_flowvault_python-1.0.0.dist-info/METADATA +896 -0
  200. skyflow_flowvault_python-1.0.0.dist-info/RECORD +202 -0
  201. skyflow_flowvault_python-1.0.0.dist-info/WHEEL +5 -0
  202. skyflow_flowvault_python-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,475 @@
1
+ import json
2
+
3
+ from common.errors import SkyflowError
4
+ from common.utils import SkyflowMessages as CommonMessages
5
+ from common.utils.constants import SKY_META_DATA_HEADER
6
+ from common.utils.logger import log_info, log_error_log
7
+ from common.vault.base_vault_controller import BaseVaultController
8
+ from skyflow.generated.rest import (
9
+ ColumnRedactions,
10
+ GetRequestData,
11
+ InsertRecordData,
12
+ TokenGroupRedactions as WireTokenGroupRedactions,
13
+ UniqueValue,
14
+ UpdateRecordData,
15
+ Upsert,
16
+ )
17
+ from skyflow.generated.rest.core import ApiError, ParsingError
18
+ from skyflow.utils import SkyflowMessages, get_metrics
19
+ from skyflow.utils.enums import UpsertType
20
+ from skyflow.utils._response_parsing import parse_tokens, parse_hashed_data, parse_metadata
21
+ from skyflow.utils.validations import (
22
+ validate_insert_request,
23
+ validate_get_request,
24
+ validate_update_request,
25
+ validate_delete_request,
26
+ validate_detokenize_request,
27
+ )
28
+ from skyflow.vault.data import (
29
+ InsertRequest,
30
+ InsertResponse,
31
+ InsertResponseRecord,
32
+ GetRequest,
33
+ GetResponse,
34
+ GetResponseRecord,
35
+ UpdateRequest,
36
+ UpdateResponse,
37
+ UpdateResponseRecord,
38
+ DeleteRequest,
39
+ DeleteResponse,
40
+ DeleteResponseRecord,
41
+ DetokenizeRequest,
42
+ DetokenizeResponse,
43
+ DetokenizeResponseRecord,
44
+ InsertOptions,
45
+ GetOptions,
46
+ UpdateOptions,
47
+ DeleteOptions,
48
+ DetokenizeOptions,
49
+ RequestContext,
50
+ )
51
+
52
+ REQUEST_ID_HEADER = "x-request-id"
53
+ ADDITIONAL_HEADERS_KEY = "additional_headers"
54
+ UNKNOWN_ERROR_MESSAGE = "Unknown error"
55
+ OPERATION_INSERT = "INSERT"
56
+ OPERATION_GET = "GET"
57
+ OPERATION_UPDATE = "UPDATE"
58
+ OPERATION_DELETE = "DELETE"
59
+ OPERATION_DETOKENIZE = "DETOKENIZE"
60
+
61
+
62
+ class VaultController(BaseVaultController):
63
+ _skyflow_messages = SkyflowMessages
64
+
65
+ def __init__(self, vault_client):
66
+ super().__init__(vault_client)
67
+
68
+ def insert(self, request: InsertRequest, options: InsertOptions = None) -> InsertResponse:
69
+ log_info(SkyflowMessages.Info.VALIDATE_INSERT_REQUEST.value, self._vault_client.get_logger())
70
+ validate_insert_request(self._vault_client.get_logger(), request)
71
+ self._validate_table_name_if_present(request.table_name)
72
+ for record in request.records:
73
+ self._validate_table_name_if_present(record.table_name)
74
+ self._validate_field_values(record.data)
75
+ log_info(SkyflowMessages.Info.INSERT_REQUEST_RESOLVED.value, self._vault_client.get_logger())
76
+ self._vault_client.initialize_client_configuration()
77
+
78
+ records_api = self._vault_client.get_records_api()
79
+
80
+ needs_per_record_table = any(r.table_name is not None for r in request.records)
81
+ needs_per_record_upsert = any(r.upsert is not None for r in request.records)
82
+
83
+ wire_records = [
84
+ self.__build_wire_record(record, request, needs_per_record_table, needs_per_record_upsert)
85
+ for record in request.records
86
+ ]
87
+
88
+ try:
89
+ log_info(SkyflowMessages.Info.INSERT_TRIGGERED.value, self._vault_client.get_logger())
90
+ upsert_kwargs = self.__omit_none(
91
+ upsert=None if needs_per_record_upsert else self.__to_upsert(request.upsert),
92
+ )
93
+ raw_response = records_api.with_raw_response.insert_records(
94
+ vault_id=self._vault_client.get_vault_id(),
95
+ table_name=request.table_name,
96
+ records=wire_records,
97
+ request_options=self.__unary_request_options(OPERATION_INSERT, options),
98
+ **upsert_kwargs,
99
+ )
100
+ request_id = self.__extract_request_id(raw_response.headers)
101
+ records = [
102
+ InsertResponseRecord(**self.__record_kwargs(record, include_data=True, request_id=request_id))
103
+ for record in (raw_response.data.records or [])
104
+ ]
105
+ except Exception as e:
106
+ log_error_log(SkyflowMessages.ErrorLogs.INSERT_RECORDS_REJECTED.value, self._vault_client.get_logger())
107
+ error_records, request_id = self.__unary_error_records(e)
108
+ if error_records is None:
109
+ raise self.__to_skyflow_error(e)
110
+ records = [
111
+ InsertResponseRecord(**self.__record_kwargs(record, include_data=True, request_id=request_id))
112
+ for record in error_records
113
+ ]
114
+
115
+ log_info(SkyflowMessages.Info.INSERT_SUCCESS.value, self._vault_client.get_logger())
116
+ return InsertResponse(records=records)
117
+
118
+ def get(self, request: GetRequest, options: GetOptions = None) -> GetResponse:
119
+ log_info(SkyflowMessages.Info.VALIDATE_GET_REQUEST.value, self._vault_client.get_logger())
120
+ validate_get_request(self._vault_client.get_logger(), request)
121
+ self._validate_table_name_if_present(request.table_name)
122
+ log_info(SkyflowMessages.Info.GET_REQUEST_RESOLVED.value, self._vault_client.get_logger())
123
+ self._vault_client.initialize_client_configuration()
124
+
125
+ records_api = self._vault_client.get_records_api()
126
+
127
+ if request.records is not None:
128
+ call_kwargs = {'records': self.__to_get_request_data(request.records)}
129
+ error_count = len(request.records)
130
+ else:
131
+ call_kwargs = {
132
+ 'table_name': request.table_name,
133
+ 'skyflow_i_ds': request.skyflow_ids,
134
+ 'unique_values': self.__to_unique_values(request.unique_values),
135
+ 'columns': request.columns,
136
+ 'column_redactions': self.__to_column_redactions(request.column_redactions),
137
+ 'limit': request.limit,
138
+ 'offset': request.offset,
139
+ }
140
+ error_count = len(request.skyflow_ids or request.unique_values or [])
141
+
142
+ try:
143
+ log_info(SkyflowMessages.Info.GET_TRIGGERED.value, self._vault_client.get_logger())
144
+ raw_response = records_api.with_raw_response.get_records(
145
+ vault_id=self._vault_client.get_vault_id(),
146
+ request_options=self.__unary_request_options(OPERATION_GET, options),
147
+ **call_kwargs,
148
+ )
149
+ request_id = self.__extract_request_id(raw_response.headers)
150
+ records = [
151
+ GetResponseRecord(**self.__record_kwargs(record, include_data=True, request_id=request_id))
152
+ for record in (raw_response.data.records or [])
153
+ ]
154
+ except Exception as e:
155
+ log_error_log(SkyflowMessages.ErrorLogs.GET_RECORDS_REJECTED.value, self._vault_client.get_logger())
156
+ error_records, request_id = self.__unary_error_records(e)
157
+ if error_records is None:
158
+ raise self.__to_skyflow_error(e)
159
+ records = [
160
+ GetResponseRecord(**self.__record_kwargs(record, include_data=True, request_id=request_id))
161
+ for record in error_records
162
+ ]
163
+
164
+ log_info(SkyflowMessages.Info.GET_SUCCESS.value, self._vault_client.get_logger())
165
+ return GetResponse(records=records)
166
+
167
+ def update(self, request: UpdateRequest, options: UpdateOptions = None) -> UpdateResponse:
168
+ log_info(SkyflowMessages.Info.VALIDATE_UPDATE_REQUEST.value, self._vault_client.get_logger())
169
+ validate_update_request(self._vault_client.get_logger(), request)
170
+ self._validate_table_name_if_present(request.table_name)
171
+ for record in request.records:
172
+ self._validate_table_name_if_present(record.table_name)
173
+ if record.data is not None:
174
+ self._validate_field_values(record.data)
175
+ log_info(SkyflowMessages.Info.UPDATE_REQUEST_RESOLVED.value, self._vault_client.get_logger())
176
+ self._vault_client.initialize_client_configuration()
177
+
178
+ records_api = self._vault_client.get_records_api()
179
+
180
+ try:
181
+ needs_per_record_table = any(r.table_name is not None for r in request.records)
182
+
183
+ wire_records = [
184
+ self.__build_update_wire_record(record, request, needs_per_record_table)
185
+ for record in request.records
186
+ ]
187
+
188
+ log_info(SkyflowMessages.Info.UPDATE_TRIGGERED.value, self._vault_client.get_logger())
189
+ update_type_kwargs = self.__omit_none(update_type=self.__to_update_type(request.update_type))
190
+ raw_response = records_api.with_raw_response.update_records(
191
+ vault_id=self._vault_client.get_vault_id(),
192
+ table_name=request.table_name,
193
+ records=wire_records,
194
+ request_options=self.__unary_request_options(OPERATION_UPDATE, options),
195
+ **update_type_kwargs,
196
+ )
197
+ request_id = self.__extract_request_id(raw_response.headers)
198
+ records = [
199
+ UpdateResponseRecord(**self.__record_kwargs(record, include_data=True, request_id=request_id))
200
+ for record in (raw_response.data.records or [])
201
+ ]
202
+ except Exception as e:
203
+ log_error_log(SkyflowMessages.ErrorLogs.UPDATE_RECORDS_REJECTED.value, self._vault_client.get_logger())
204
+ error_records, request_id = self.__unary_error_records(e)
205
+ if error_records is None:
206
+ raise self.__to_skyflow_error(e)
207
+ records = [
208
+ UpdateResponseRecord(**self.__record_kwargs(record, include_data=True, request_id=request_id))
209
+ for record in error_records
210
+ ]
211
+
212
+ log_info(SkyflowMessages.Info.UPDATE_SUCCESS.value, self._vault_client.get_logger())
213
+ return UpdateResponse(records=records)
214
+
215
+ def delete(self, request: DeleteRequest, options: DeleteOptions = None) -> DeleteResponse:
216
+ log_info(SkyflowMessages.Info.VALIDATE_DELETE_REQUEST.value, self._vault_client.get_logger())
217
+ validate_delete_request(self._vault_client.get_logger(), request)
218
+ self._validate_table_name_if_present(request.table_name)
219
+ log_info(SkyflowMessages.Info.DELETE_REQUEST_RESOLVED.value, self._vault_client.get_logger())
220
+ self._vault_client.initialize_client_configuration()
221
+
222
+ records_api = self._vault_client.get_records_api()
223
+ items = request.ids or request.unique_values or []
224
+
225
+ try:
226
+ log_info(SkyflowMessages.Info.DELETE_TRIGGERED.value, self._vault_client.get_logger())
227
+ raw_response = records_api.with_raw_response.delete_records(
228
+ vault_id=self._vault_client.get_vault_id(),
229
+ table_name=request.table_name,
230
+ skyflow_i_ds=request.ids,
231
+ unique_values=self.__to_unique_values(request.unique_values),
232
+ request_options=self.__unary_request_options(OPERATION_DELETE, options),
233
+ )
234
+ request_id = self.__extract_request_id(raw_response.headers)
235
+ records = [self.__delete_row(record, request_id) for record in (raw_response.data.records or [])]
236
+ except Exception as e:
237
+ log_error_log(SkyflowMessages.ErrorLogs.DELETE_RECORDS_REJECTED.value, self._vault_client.get_logger())
238
+ error_records, request_id = self.__unary_error_records(e)
239
+ if error_records is None:
240
+ raise self.__to_skyflow_error(e)
241
+ records = [self.__delete_row(record, request_id) for record in error_records]
242
+
243
+ log_info(SkyflowMessages.Info.DELETE_SUCCESS.value, self._vault_client.get_logger())
244
+ return DeleteResponse(records=records)
245
+
246
+ def detokenize(self, request: DetokenizeRequest, options: DetokenizeOptions = None) -> DetokenizeResponse:
247
+ log_info(SkyflowMessages.Info.VALIDATE_DETOKENIZE_REQUEST.value, self._vault_client.get_logger())
248
+ validate_detokenize_request(self._vault_client.get_logger(), request)
249
+ log_info(SkyflowMessages.Info.DETOKENIZE_REQUEST_RESOLVED.value, self._vault_client.get_logger())
250
+ self._vault_client.initialize_client_configuration()
251
+
252
+ tokens_api = self._vault_client.get_tokens_api()
253
+
254
+ try:
255
+ log_info(SkyflowMessages.Info.DETOKENIZE_TRIGGERED.value, self._vault_client.get_logger())
256
+ raw_response = tokens_api.with_raw_response.detokenize(
257
+ vault_id=self._vault_client.get_vault_id(),
258
+ tokens=request.tokens,
259
+ token_group_redactions=self.__to_token_group_redactions(request.token_group_redactions),
260
+ request_options=self.__unary_request_options(OPERATION_DETOKENIZE, options),
261
+ )
262
+ request_id = self.__extract_request_id(raw_response.headers)
263
+ records = [self.__detokenize_row(resp, request_id) for resp in (raw_response.data.response or [])]
264
+ except Exception as e:
265
+ log_error_log(SkyflowMessages.ErrorLogs.DETOKENIZE_RECORDS_REJECTED.value, self._vault_client.get_logger())
266
+ error_records, request_id = self.__unary_error_records(e)
267
+ if error_records is None:
268
+ raise self.__to_skyflow_error(e)
269
+ records = [self.__detokenize_row(resp, request_id) for resp in error_records]
270
+
271
+ log_info(SkyflowMessages.Info.DETOKENIZE_SUCCESS.value, self._vault_client.get_logger())
272
+ return DetokenizeResponse(records=records)
273
+
274
+ def __error_body_records(self, e):
275
+ body = getattr(e, 'body', None)
276
+ if isinstance(body, dict):
277
+ records = body.get('records')
278
+ if records is None:
279
+ records = body.get('response')
280
+ else:
281
+ records = getattr(body, 'records', None) or getattr(body, 'response', None)
282
+ if records is None:
283
+ extra = getattr(body, '__pydantic_extra__', None)
284
+ if isinstance(extra, dict):
285
+ records = extra.get('records') or extra.get('response')
286
+ return records if isinstance(records, list) and records else None
287
+
288
+ def __unary_error_records(self, e):
289
+ records = self.__error_body_records(e)
290
+ if not records:
291
+ return None, None
292
+ return records, self.__extract_request_id(getattr(e, 'headers', None))
293
+
294
+ def __wire_record_value(self, record, wire_key, attr):
295
+ if isinstance(record, dict):
296
+ return record.get(wire_key)
297
+ return getattr(record, attr, None)
298
+
299
+ def __build_wire_record(self, record, request, needs_per_record_table, needs_per_record_upsert):
300
+ return InsertRecordData(data=record.data, **self.__omit_none(
301
+ tokens=record.tokens,
302
+ table_name=(record.table_name or request.table_name) if needs_per_record_table else None,
303
+ upsert=self.__to_upsert(record.upsert or request.upsert) if needs_per_record_upsert else None,
304
+ ))
305
+
306
+ def __build_update_wire_record(self, record, request, needs_per_record_table):
307
+ return UpdateRecordData(
308
+ skyflow_id=record.skyflow_id,
309
+ data=record.data,
310
+ **self.__omit_none(
311
+ tokens=record.tokens,
312
+ table_name=(record.table_name or request.table_name) if needs_per_record_table else None,
313
+ ),
314
+ )
315
+
316
+ def __omit_none(self, **kwargs):
317
+ return {k: v for k, v in kwargs.items() if v is not None}
318
+
319
+ def __to_update_type(self, update_type):
320
+ if update_type is None:
321
+ return None
322
+ return update_type.value if isinstance(update_type, UpsertType) else update_type
323
+
324
+ def __build_headers(self):
325
+ headers = {SKY_META_DATA_HEADER: json.dumps(get_metrics())}
326
+ token = self._vault_client.get_current_bearer_token()
327
+ if token:
328
+ headers['Authorization'] = f'Bearer {token}'
329
+ return headers
330
+
331
+ def __request_options(self, custom_headers=None):
332
+ headers = self.__build_headers()
333
+ if custom_headers:
334
+ headers.update(custom_headers)
335
+ return {ADDITIONAL_HEADERS_KEY: headers}
336
+
337
+ def __unary_request_options(self, operation, options):
338
+ interceptor = options.interceptor if options is not None else None
339
+ custom_headers = None
340
+ if interceptor is not None:
341
+ context = RequestContext(operation)
342
+ interceptor(context)
343
+ custom_headers = {str(key): value for key, value in context.headers.items()}
344
+ return self.__request_options(custom_headers)
345
+
346
+ def __to_upsert(self, upsert):
347
+ if upsert is None:
348
+ return None
349
+ update_type = upsert.update_type
350
+ return Upsert(
351
+ update_type=update_type.value if update_type else None,
352
+ unique_columns=upsert.unique_columns,
353
+ )
354
+
355
+ def __to_unique_values(self, unique_values):
356
+ if unique_values is None:
357
+ return None
358
+ return [UniqueValue(data=value) for value in unique_values]
359
+
360
+ def __to_column_redactions(self, column_redactions):
361
+ if column_redactions is None:
362
+ return None
363
+ return [
364
+ ColumnRedactions(column_name=entry.column_name, redaction=entry.redaction)
365
+ for entry in column_redactions
366
+ ]
367
+
368
+ def __to_token_group_redactions(self, token_group_redactions):
369
+ if token_group_redactions is None:
370
+ return None
371
+ return [
372
+ WireTokenGroupRedactions(token_group_name=entry.token_group_name, redaction=entry.redaction)
373
+ for entry in token_group_redactions
374
+ ]
375
+
376
+ def __extract_request_id(self, headers):
377
+ return headers.get(REQUEST_ID_HEADER) if headers else None
378
+
379
+ def __record_kwargs(self, record, include_data, request_id=None):
380
+ error = self.__wire_record_value(record, 'error', 'error')
381
+ kwargs = {
382
+ 'table_name': self.__wire_record_value(record, 'tableName', 'table_name'),
383
+ 'skyflow_id': self.__wire_record_value(record, 'skyflowID', 'skyflow_id'),
384
+ 'tokens': parse_tokens(self.__wire_record_value(record, 'tokens', 'tokens')),
385
+ 'hashed_data': parse_hashed_data(self.__wire_record_value(record, 'hashedData', 'hashed_data')),
386
+ 'http_code': self.__wire_record_value(record, 'httpCode', 'http_code'),
387
+ 'error': error,
388
+ 'request_id': request_id if error is not None else None,
389
+ }
390
+ if include_data:
391
+ kwargs['data'] = self.__wire_record_value(record, 'data', 'data')
392
+ return kwargs
393
+
394
+ def __to_skyflow_error(self, e):
395
+ if isinstance(e, SkyflowError):
396
+ return e
397
+ if isinstance(e, (ApiError, ParsingError)):
398
+ message, grpc_code, http_status, details = self.__parse_api_error_body(e.body)
399
+ return SkyflowError(
400
+ message=message,
401
+ http_code=e.status_code,
402
+ request_id=self.__extract_request_id(e.headers),
403
+ grpc_code=grpc_code,
404
+ http_status=http_status,
405
+ details=details,
406
+ )
407
+ return SkyflowError(
408
+ message=str(e) if e else CommonMessages.Error.GENERIC_API_ERROR.value,
409
+ http_code=None,
410
+ )
411
+
412
+ def __parse_api_error_body(self, body):
413
+ error = getattr(body, 'error', None) if body is not None and not isinstance(body, dict) else None
414
+ if error is not None and not isinstance(error, dict):
415
+ return (
416
+ getattr(error, 'message', None) or UNKNOWN_ERROR_MESSAGE,
417
+ getattr(error, 'grpc_code', None),
418
+ getattr(error, 'http_status', None),
419
+ getattr(error, 'details', None) or [],
420
+ )
421
+ if isinstance(body, dict):
422
+ records = body.get('records')
423
+ if isinstance(records, list) and records:
424
+ first = records[0] if isinstance(records[0], dict) else {}
425
+ message = first.get('error') or first.get('message') or UNKNOWN_ERROR_MESSAGE
426
+ return (message, None, None, [record for record in records if isinstance(record, dict)])
427
+ error = body.get('error')
428
+ if isinstance(error, dict):
429
+ return (
430
+ error.get('message') or UNKNOWN_ERROR_MESSAGE,
431
+ error.get('grpc_code', error.get('grpcCode')),
432
+ error.get('http_status', error.get('httpStatus')),
433
+ error.get('details') or [],
434
+ )
435
+ if error is not None:
436
+ return (str(error), None, None, [])
437
+ return (UNKNOWN_ERROR_MESSAGE, None, None, [])
438
+
439
+ def __to_get_request_data(self, records):
440
+ return [
441
+ GetRequestData(
442
+ table_name=record.table_name,
443
+ skyflow_i_ds=record.skyflow_ids or [],
444
+ **self.__omit_none(
445
+ columns=record.columns,
446
+ column_redactions=self.__to_column_redactions(record.column_redactions),
447
+ unique_values=self.__to_unique_values(record.unique_values),
448
+ ),
449
+ )
450
+ for record in records
451
+ ]
452
+
453
+ def __delete_row(self, record, request_id=None):
454
+ error = self.__wire_record_value(record, 'error', 'error')
455
+ return DeleteResponseRecord(
456
+ skyflow_id=self.__wire_record_value(record, 'skyflowID', 'skyflow_id'),
457
+ http_code=self.__wire_record_value(record, 'httpCode', 'http_code'),
458
+ error=error,
459
+ request_id=request_id if error is not None else None,
460
+ )
461
+
462
+ def __detokenize_row(self, resp, request_id=None):
463
+ error = self.__wire_record_value(resp, 'error', 'error')
464
+ return DetokenizeResponseRecord(
465
+ token=self.__wire_record_value(resp, 'token', 'token'),
466
+ value=self.__wire_record_value(resp, 'value', 'value'),
467
+ token_group_name=self.__wire_record_value(resp, 'tokenGroupName', 'token_group_name'),
468
+ metadata=parse_metadata(self.__wire_record_value(resp, 'metadata', 'metadata')),
469
+ http_code=self.__wire_record_value(resp, 'httpCode', 'http_code'),
470
+ error=error,
471
+ request_id=request_id if error is not None else None,
472
+ )
473
+
474
+
475
+
@@ -0,0 +1,30 @@
1
+ from ._upsert_options import UpsertOptions
2
+ from ._column_redactions import ColumnRedactions
3
+ from ._insert_request_record import InsertRequestRecord
4
+ from ._insert_request import InsertRequest
5
+ from ._insert_response import InsertResponse
6
+ from ._get_request_record import GetRequestRecord
7
+ from ._get_request import GetRequest
8
+ from ._get_response import GetResponse
9
+ from ._update_request_record import UpdateRequestRecord
10
+ from ._update_request import UpdateRequest
11
+ from ._update_response import UpdateResponse
12
+ from ._delete_request import DeleteRequest
13
+ from ._delete_response import DeleteResponse
14
+ from ._token_group_redactions import TokenGroupRedactions
15
+ from ._detokenize_request import DetokenizeRequest
16
+ from ._token import Token
17
+ from ._insert_response_record import InsertResponseRecord
18
+ from ._get_response_record import GetResponseRecord
19
+ from ._update_response_record import UpdateResponseRecord
20
+ from ._delete_response_record import DeleteResponseRecord
21
+ from ._detokenize_response import DetokenizeResponse
22
+ from ._detokenize_response_record import DetokenizeResponseRecord
23
+ from ._detokenize_response_record_metadata import DetokenizeResponseRecordMetadata
24
+ from ._request_context import RequestContext
25
+ from ._insert_options import InsertOptions
26
+ from ._get_options import GetOptions
27
+ from ._update_options import UpdateOptions
28
+ from ._delete_options import DeleteOptions
29
+ from ._detokenize_options import DetokenizeOptions
30
+ from skyflow.utils.enums import CustomHeaderKey
@@ -0,0 +1,4 @@
1
+ class ColumnRedactions:
2
+ def __init__(self, column_name: str, redaction: str = None):
3
+ self.column_name = column_name
4
+ self.redaction = redaction
@@ -0,0 +1,8 @@
1
+ from typing import Callable, Optional
2
+
3
+ from ._request_context import RequestContext
4
+
5
+
6
+ class DeleteOptions:
7
+ def __init__(self, interceptor: Optional[Callable[[RequestContext], None]] = None):
8
+ self.interceptor = interceptor
@@ -0,0 +1,5 @@
1
+ class DeleteRequest:
2
+ def __init__(self, table_name: str, ids: list = None, unique_values: list = None):
3
+ self.table_name = table_name
4
+ self.ids = ids
5
+ self.unique_values = unique_values
@@ -0,0 +1,9 @@
1
+ class DeleteResponse:
2
+ def __init__(self, records=None):
3
+ self.records = records
4
+
5
+ def __repr__(self):
6
+ return f"DeleteResponse(records={self.records})"
7
+
8
+ def __str__(self):
9
+ return self.__repr__()
@@ -0,0 +1,9 @@
1
+ class DeleteResponseRecord:
2
+ def __init__(self, skyflow_id=None, http_code=None, error=None, request_id=None):
3
+ self.skyflow_id = skyflow_id
4
+ self.http_code = http_code
5
+ self.error = error
6
+ self.request_id = request_id
7
+
8
+ def __repr__(self):
9
+ return f"DeleteResponseRecord(skyflow_id={self.skyflow_id}, http_code={self.http_code}, error={self.error}, request_id={self.request_id})"
@@ -0,0 +1,8 @@
1
+ from typing import Callable, Optional
2
+
3
+ from ._request_context import RequestContext
4
+
5
+
6
+ class DetokenizeOptions:
7
+ def __init__(self, interceptor: Optional[Callable[[RequestContext], None]] = None):
8
+ self.interceptor = interceptor
@@ -0,0 +1,9 @@
1
+ from typing import List
2
+
3
+ from ._token_group_redactions import TokenGroupRedactions
4
+
5
+
6
+ class DetokenizeRequest:
7
+ def __init__(self, tokens: list, token_group_redactions: List[TokenGroupRedactions] = None):
8
+ self.tokens = tokens
9
+ self.token_group_redactions = token_group_redactions
@@ -0,0 +1,9 @@
1
+ class DetokenizeResponse:
2
+ def __init__(self, records=None):
3
+ self.records = records
4
+
5
+ def __repr__(self):
6
+ return f"DetokenizeResponse(records={self.records})"
7
+
8
+ def __str__(self):
9
+ return self.__repr__()
@@ -0,0 +1,16 @@
1
+ class DetokenizeResponseRecord:
2
+ def __init__(self, token=None, value=None, token_group_name=None, metadata=None,
3
+ http_code=None, error=None, request_id=None):
4
+ self.token = token
5
+ self.value = value
6
+ self.token_group_name = token_group_name
7
+ self.metadata = metadata
8
+ self.http_code = http_code
9
+ self.error = error
10
+ self.request_id = request_id
11
+
12
+ def __repr__(self):
13
+ return ("DetokenizeResponseRecord(token={}, value={}, token_group_name={}, metadata={}, "
14
+ "http_code={}, error={}, request_id={})").format(
15
+ self.token, self.value, self.token_group_name, self.metadata,
16
+ self.http_code, self.error, self.request_id)
@@ -0,0 +1,10 @@
1
+ class DetokenizeResponseRecordMetadata:
2
+ def __init__(self, skyflow_id: str = None, table_name: str = None):
3
+ self.skyflow_id = skyflow_id
4
+ self.table_name = table_name
5
+
6
+ def __repr__(self):
7
+ return f"DetokenizeResponseRecordMetadata(skyflow_id={self.skyflow_id}, table_name={self.table_name})"
8
+
9
+ def __str__(self):
10
+ return self.__repr__()
@@ -0,0 +1,8 @@
1
+ from typing import Callable, Optional
2
+
3
+ from ._request_context import RequestContext
4
+
5
+
6
+ class GetOptions:
7
+ def __init__(self, interceptor: Optional[Callable[[RequestContext], None]] = None):
8
+ self.interceptor = interceptor
@@ -0,0 +1,17 @@
1
+ from typing import List
2
+
3
+ from ._column_redactions import ColumnRedactions
4
+
5
+
6
+ class GetRequest:
7
+ def __init__(self, table_name: str = None, skyflow_ids: list = None, unique_values: list = None, columns: list = None,
8
+ column_redactions: List[ColumnRedactions] = None, limit: int = None, offset: int = None,
9
+ records: list = None):
10
+ self.table_name = table_name
11
+ self.skyflow_ids = skyflow_ids
12
+ self.unique_values = unique_values
13
+ self.columns = columns
14
+ self.column_redactions = column_redactions
15
+ self.limit = limit
16
+ self.offset = offset
17
+ self.records = records
@@ -0,0 +1,13 @@
1
+ from typing import List
2
+
3
+ from ._column_redactions import ColumnRedactions
4
+
5
+
6
+ class GetRequestRecord:
7
+ def __init__(self, table_name: str, skyflow_ids: list = None, columns: list = None,
8
+ column_redactions: List[ColumnRedactions] = None, unique_values: list = None):
9
+ self.table_name = table_name
10
+ self.skyflow_ids = skyflow_ids
11
+ self.columns = columns
12
+ self.column_redactions = column_redactions
13
+ self.unique_values = unique_values
@@ -0,0 +1,9 @@
1
+ class GetResponse:
2
+ def __init__(self, records=None):
3
+ self.records = records
4
+
5
+ def __repr__(self):
6
+ return f"GetResponse(records={self.records})"
7
+
8
+ def __str__(self):
9
+ return self.__repr__()