dc-python-sdk 1.5.44__tar.gz → 1.5.46__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.
Files changed (35) hide show
  1. {dc_python_sdk-1.5.44/src/dc_python_sdk.egg-info → dc_python_sdk-1.5.46}/PKG-INFO +1 -1
  2. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/pyproject.toml +1 -1
  3. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/setup.cfg +1 -1
  4. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46/src/dc_python_sdk.egg-info}/PKG-INFO +1 -1
  5. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/models/pipeline_details.py +4 -1
  6. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/pipeline.py +81 -1
  7. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/api.py +18 -0
  8. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/aws.py +0 -3
  9. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/LICENSE +0 -0
  10. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/README.md +0 -0
  11. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_python_sdk.egg-info/SOURCES.txt +0 -0
  12. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_python_sdk.egg-info/dependency_links.txt +0 -0
  13. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_python_sdk.egg-info/entry_points.txt +0 -0
  14. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_python_sdk.egg-info/requires.txt +0 -0
  15. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_python_sdk.egg-info/top_level.txt +0 -0
  16. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/__init__.py +0 -0
  17. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/app.py +0 -0
  18. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/cli.py +0 -0
  19. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/errors.py +0 -0
  20. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/handler.py +0 -0
  21. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/__init__.py +0 -0
  22. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/ai.py +0 -0
  23. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/ai_http.py +0 -0
  24. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/mapping.py +0 -0
  25. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/models/__init__.py +0 -0
  26. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/models/enums.py +0 -0
  27. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/models/errors.py +0 -0
  28. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/models/log_templates.py +0 -0
  29. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/server.py +0 -0
  30. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/__init__.py +0 -0
  31. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/environment.py +0 -0
  32. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/loader.py +0 -0
  33. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/logger.py +0 -0
  34. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/src/services/session.py +0 -0
  35. {dc_python_sdk-1.5.44 → dc_python_sdk-1.5.46}/src/dc_sdk/types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dc-python-sdk
3
- Version: 1.5.44
3
+ Version: 1.5.46
4
4
  Summary: Data Connector Python SDK
5
5
  Home-page: https://github.com/data-connector/dc-python-sdk
6
6
  Author: DataConnector
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dc-python-sdk"
7
- version = "1.5.44"
7
+ version = "1.5.46"
8
8
  description = "Data Connector Python SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.6"
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = dc-python-sdk
3
- version = 1.5.44
3
+ version = 1.5.46
4
4
  author = DataConnector
5
5
  author_email = josh@dataconnector.com
6
6
  description = A small example package
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dc-python-sdk
3
- Version: 1.5.44
3
+ Version: 1.5.46
4
4
  Summary: Data Connector Python SDK
5
5
  Home-page: https://github.com/data-connector/dc-python-sdk
6
6
  Author: DataConnector
@@ -32,9 +32,12 @@ class PipelineDetails:
32
32
  self.destination_ecs_task_version_nbr = row_data['destination_ecs_task_version_nbr']
33
33
  self.options = json.loads(row_data['pipeline_object_options_json']) if 'pipeline_object_options_json' in row_data and row_data['pipeline_object_options_json'] else dict()
34
34
  self.max_allowed_retrieval = row_data.get('max_allowed_retrieval')
35
- self.primary_key_column_nm = row_data.get('primary_key_column_nm')
36
35
  self.destination_credential_information = row_data.get('destination_credential_information')
37
36
  self.source_credential_information = row_data.get('source_credential_information')
37
+ self.primary_key_column_nm = row_data.get('primary_key_column_nm')
38
+ self.updated_date_column_nm = row_data.get('updated_date_column_nm')
39
+ self.sync_cursor_dsc = row_data.get('sync_cursor_dsc')
40
+ self.pending_sync_cursor_dsc = row_data.get('pending_sync_cursor_dsc')
38
41
 
39
42
  def increment_stage(self):
40
43
  self.stage += 1
@@ -35,6 +35,7 @@ class PipelineConductor:
35
35
  self.mapping = kwargs.get("mapping")
36
36
  self.pipeline_object_id = kwargs.get("pipeline_mapping_id")
37
37
  self.successful_keys = []
38
+ self.extracted_sync_cursor = None
38
39
  self.config = PipelineEnvironment
39
40
  self.api = api or DataConnectorAPI()
40
41
  self.aws = aws or AwsService(PipelineEnvironment.aws_s3_bucket)
@@ -46,7 +47,7 @@ class PipelineConductor:
46
47
 
47
48
  # get connector credentials
48
49
  Connector = load_connector()
49
- self.connector = Connector(self.credentials)
50
+ self.connector = self._create_connector(Connector, self.credentials)
50
51
 
51
52
  self.row_count = 0
52
53
  self.log_templates = self._get_log_messages()
@@ -131,6 +132,7 @@ class PipelineConductor:
131
132
 
132
133
  while "next_page" in results and results["next_page"] != None:
133
134
  if "data" in results and results["data"] != None and results["data"] != []:
135
+ self._update_extracted_sync_cursor(results.get("metadata"))
134
136
  limit_reached = self._process_rows(results["data"], max_allowed)
135
137
  if limit_reached:
136
138
  break
@@ -145,9 +147,15 @@ class PipelineConductor:
145
147
  results = self.connector.get_data(self.pipeline_details.source_object_id, self._get_field_ids(), n_rows=nrows, filters=self._get_filters(), options=self.pipeline_details.options, next_page=results["next_page"])
146
148
 
147
149
  if "data" in results and results["data"] != None and results["data"] != []:
150
+ self._update_extracted_sync_cursor(results.get("metadata"))
148
151
  self._process_rows(results["data"], max_allowed)
149
152
  elif results["data"] != []:
150
153
  self.internal_log(self.log_templates.INTERNAL_GET_DATA_FETCHED.format(0))
154
+ else:
155
+ self._update_extracted_sync_cursor(results.get("metadata"))
156
+
157
+ if self._should_track_sync_cursor():
158
+ self._save_pending_sync_cursor()
151
159
 
152
160
  self.log(self.log_templates.GET_DATA_FINISH.format(self.row_count, self.pipeline_details.source_object_id))
153
161
 
@@ -189,6 +197,78 @@ class PipelineConductor:
189
197
  else:
190
198
  raise errors.LoadDataError("Loading data failed.")
191
199
  self.log(self.log_templates.LOAD_DATA_FINISHED.format(self.row_count, self.pipeline_details.destination_object_id))
200
+ self._persist_sync_cursor_after_load()
201
+
202
+ def _create_connector(self, connector_cls, credentials):
203
+ init_params = inspect.signature(connector_cls.__init__).parameters
204
+ if "pipeline_context" in init_params:
205
+ return connector_cls(credentials, pipeline_context=self._get_pipeline_context())
206
+ return connector_cls(credentials)
207
+
208
+ def _normalize_update_method(self, update_method):
209
+ if update_method is None:
210
+ return None
211
+ if isinstance(update_method, int):
212
+ return update_method
213
+ if isinstance(update_method, str) and update_method.isdigit():
214
+ return int(update_method)
215
+ return update_method
216
+
217
+ def _get_sync_cursor(self):
218
+ return getattr(self.pipeline_details, "sync_cursor_dsc", None)
219
+
220
+ def _get_pipeline_context(self):
221
+ if self.task != "SOURCE":
222
+ return None
223
+
224
+ update_method = self._normalize_update_method(
225
+ getattr(self.pipeline_details, "update_method_cd", None)
226
+ )
227
+
228
+ return {
229
+ "update_method": update_method,
230
+ "update_date_column_nm": getattr(
231
+ self.pipeline_details, "updated_date_column_nm", None
232
+ ),
233
+ "sync_cursor": self._get_sync_cursor(),
234
+ }
235
+
236
+ def _should_track_sync_cursor(self):
237
+ if self.task != "SOURCE":
238
+ return False
239
+
240
+ update_method = self._normalize_update_method(
241
+ getattr(self.pipeline_details, "update_method_cd", None)
242
+ )
243
+ updated_date_column_nm = getattr(
244
+ self.pipeline_details, "updated_date_column_nm", None
245
+ )
246
+ return update_method == 3 and updated_date_column_nm
247
+
248
+ def _update_extracted_sync_cursor(self, metadata):
249
+ if not self._should_track_sync_cursor() or not metadata:
250
+ return
251
+
252
+ sync_cursor = metadata.get("sync_cursor")
253
+ if sync_cursor is None:
254
+ return
255
+
256
+ if self.extracted_sync_cursor is None or sync_cursor > self.extracted_sync_cursor:
257
+ self.extracted_sync_cursor = sync_cursor
258
+
259
+ def _save_pending_sync_cursor(self):
260
+ if self.extracted_sync_cursor is None:
261
+ return
262
+
263
+ if self.mode == "prod":
264
+ self.api.save_pending_sync_cursor(self.pipeline_id, self.extracted_sync_cursor)
265
+
266
+ def _persist_sync_cursor_after_load(self):
267
+ if self.task != "DESTINATION":
268
+ return
269
+
270
+ if self.mode == "prod":
271
+ self.api.commit_sync_cursor(self.pipeline_id)
192
272
 
193
273
  def _call_connector_load_data(
194
274
  self,
@@ -113,6 +113,24 @@ class DataConnectorAPI:
113
113
 
114
114
  self.post(f"{pipeline_id}/mapping", body)
115
115
 
116
+ def save_pending_sync_cursor(self, pipeline_id, pending_sync_cursor):
117
+ self.put(
118
+ f"{pipeline_id}/pending-sync-cursor",
119
+ {"pending_sync_cursor_dsc": pending_sync_cursor},
120
+ )
121
+
122
+ def commit_sync_cursor(self, pipeline_id):
123
+ self.put(
124
+ f"{pipeline_id}/sync-cursor",
125
+ {"commit": True},
126
+ )
127
+
128
+ def save_sync_cursor(self, pipeline_id, sync_cursor):
129
+ self.put(
130
+ f"{pipeline_id}/sync-cursor",
131
+ {"sync_cursor_dsc": sync_cursor},
132
+ )
133
+
116
134
  def _request(self, method, endpoint, body=None, max_attempts=4):
117
135
  endpoint = endpoint.lstrip("/")
118
136
  url = f"{self.pipelines_base_url}/{endpoint}"
@@ -164,9 +164,6 @@ class AwsService:
164
164
  )
165
165
 
166
166
 
167
-
168
-
169
-
170
167
  class EncryptionService:
171
168
  def decrypt_customer_data(
172
169
  self,
File without changes
File without changes