kodexa 7.4.414560697286__py3-none-any.whl → 7.4.414561019241__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.
@@ -301,3 +301,152 @@ class ManifestManager:
301
301
  f"Undeployed {undeployed_count} components."
302
302
  )
303
303
 
304
+ def sync_from_instance(self, manifest_path: str, org_slug: str | None = None):
305
+ """
306
+ Syncs resources from a Kodexa instance to the filesystem based on a manifest.
307
+
308
+ This reads a manifest file and for each resource defined:
309
+ 1. Retrieves the resource from the Kodexa instance
310
+ 2. Saves it to the corresponding file on the filesystem
311
+
312
+ Args:
313
+ manifest_path (str): The path to the manifest file.
314
+ org_slug (str | None): The organization slug to sync from.
315
+ If None, uses the default org from client.
316
+ """
317
+ target_org_slug = org_slug or self.kodexa_client.get_org_slug()
318
+ if not target_org_slug:
319
+ msg = "Organization slug must be provided or set in the client."
320
+ raise ValueError(msg)
321
+
322
+ logger.info(
323
+ f"Starting sync from instance to filesystem using manifest "
324
+ f"{manifest_path} for organization {target_org_slug}"
325
+ )
326
+ resource_paths = self.read_manifest(manifest_path)
327
+ abs_manifest_path = os.path.abspath(manifest_path)
328
+ manifest_dir = os.path.dirname(abs_manifest_path)
329
+ original_cwd = os.getcwd()
330
+ synced_count = 0
331
+
332
+ try:
333
+ # Change to manifest directory to resolve relative paths
334
+ os.chdir(manifest_dir)
335
+
336
+ for resource_pattern in resource_paths:
337
+ resource_files = glob.glob(resource_pattern, recursive=True)
338
+
339
+ if not resource_files:
340
+ logger.warning(
341
+ f"No files found matching pattern '{resource_pattern}' "
342
+ f"relative to {manifest_dir}"
343
+ )
344
+ continue
345
+
346
+ for rel_path in resource_files:
347
+ # Resolve absolute path based on current directory
348
+ abs_path = os.path.abspath(rel_path)
349
+ logger.info(f"Processing for sync: {abs_path}")
350
+
351
+ try:
352
+ # Read the file to get component information
353
+ with open(abs_path, 'r') as f:
354
+ path_lower = abs_path.lower()
355
+ if path_lower.endswith(".json"):
356
+ file_obj = json.load(f)
357
+ elif path_lower.endswith((".yaml", ".yml")):
358
+ file_obj = yaml.safe_load(f)
359
+ else:
360
+ logger.warning(
361
+ f"Skipping unsupported file type: {abs_path}"
362
+ )
363
+ continue
364
+
365
+ components = []
366
+ if isinstance(file_obj, list):
367
+ logger.info(
368
+ f"Found {len(file_obj)} components in {abs_path}"
369
+ )
370
+ components.extend(file_obj)
371
+ elif isinstance(file_obj, dict):
372
+ components.append(file_obj)
373
+ else:
374
+ logger.warning(
375
+ f"Skipping unexpected object type in {abs_path}"
376
+ )
377
+ continue
378
+
379
+ for comp_obj in components:
380
+ # Ensure component object is a dictionary
381
+ if not isinstance(comp_obj, dict):
382
+ logger.warning(
383
+ f"Skipping non-dictionary item in {abs_path}"
384
+ )
385
+ continue
386
+
387
+ slug = comp_obj.get('slug')
388
+ version = comp_obj.get('version')
389
+
390
+ if not slug or not version:
391
+ logger.warning(
392
+ "Skipping component (missing slug/version) in %s",
393
+ abs_path
394
+ )
395
+ continue
396
+
397
+ try:
398
+ # Get the component from the Kodexa instance
399
+ logger.info(
400
+ f"Retrieving component {slug}:{version} "
401
+ f"from org {target_org_slug}"
402
+ )
403
+ component = self.kodexa_client.get_object(
404
+ target_org_slug, slug, version
405
+ )
406
+
407
+ if not component:
408
+ logger.warning(
409
+ f"Component {slug}:{version} not found "
410
+ f"in org {target_org_slug}"
411
+ )
412
+ continue
413
+
414
+ # Serialize the component to data
415
+ updated_obj = component.serialize()
416
+
417
+ # Write the updated data back to the file
418
+ write_format = "json"
419
+ if path_lower.endswith((".yaml", ".yml")):
420
+ write_format = "yaml"
421
+
422
+ with open(abs_path, 'w') as f:
423
+ if write_format == "json":
424
+ json.dump(updated_obj, f, indent=2)
425
+ else:
426
+ yaml.dump(updated_obj, f)
427
+
428
+ logger.info(
429
+ f"Synced component {slug}:{version} to {abs_path}"
430
+ )
431
+ synced_count += 1
432
+
433
+ except Exception as e:
434
+ logger.error(
435
+ "Failed to sync component %s:%s: %s",
436
+ slug, version, e, exc_info=True
437
+ )
438
+ except Exception as e:
439
+ logger.error(
440
+ "Failed to process file %s for sync: %s",
441
+ abs_path, e, exc_info=True
442
+ )
443
+
444
+ finally:
445
+ # Return to original working directory
446
+ os.chdir(original_cwd)
447
+
448
+ logger.info(
449
+ f"Sync completed from instance to filesystem using manifest "
450
+ f"{manifest_path}. Synced {synced_count} components."
451
+ )
452
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: kodexa
3
- Version: 7.4.414560697286
3
+ Version: 7.4.414561019241
4
4
  Summary: Python SDK for the Kodexa Platform
5
5
  Author: Austin Redenbaugh
6
6
  Author-email: austin@kodexa.com
@@ -22,7 +22,7 @@ kodexa/platform/__init__.py,sha256=1O3oiWMg292NPL_NacKDnK1T3_R6cMorrPRue_9e-O4,2
22
22
  kodexa/platform/client.py,sha256=D1J5e4URBSVafEo07Yb9QMMdXcmJFA0g4Maa6TcNYNc,236029
23
23
  kodexa/platform/interaction.py,sha256=6zpcwXKNZstUGNS6m4JsoRXAqCZPJHWI-ZN3co8nnF0,1055
24
24
  kodexa/platform/kodexa.py,sha256=foFtSakAHRbYhJQ_JQBOnrxVGVoLhsY8LKZlI3BVdtQ,35107
25
- kodexa/platform/manifest.py,sha256=0aL62HfzRShKnH4yM8bB-0qjzNLMy_EzJwIEazzilHo,12817
25
+ kodexa/platform/manifest.py,sha256=dtb9ESc8xGjlWCsat9y-Pv0bQ6j1LGr4Mb83_ybQHoE,19677
26
26
  kodexa/selectors/__init__.py,sha256=xA9-4vpyaAZWPSk3bh2kvDLkdv6XEmm7PjFbpziiTIk,100
27
27
  kodexa/selectors/ast.py,sha256=K0JUY2tYYCmDlh4uz5N8Jh2zInDremlnriPsY3dx0AI,13523
28
28
  kodexa/selectors/core.py,sha256=kkt02DN20gXeaDGoGubPPeeTV7rCr4sxTyELrI0l1YU,3691
@@ -45,7 +45,7 @@ kodexa/testing/test_utils.py,sha256=v44p__gE7ia67W7WeHN2HBFCWSCUrCZt7G4xBNCmwf8,
45
45
  kodexa/training/__init__.py,sha256=xs2L62YpRkIRfslQwtQZ5Yxjhm7sLzX2TrVX6EuBnZQ,52
46
46
  kodexa/training/train_utils.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  kodexa/utils/__init__.py,sha256=Pnim1o9_db5YEnNvDTxpM7HG-qTlL6n8JwFwOafU9wo,5928
48
- kodexa-7.4.414560697286.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
49
- kodexa-7.4.414560697286.dist-info/METADATA,sha256=CcDwZ-q8AardDy7JACH1FmZANpBD6mbuwsMwVgXZv-o,2813
50
- kodexa-7.4.414560697286.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
51
- kodexa-7.4.414560697286.dist-info/RECORD,,
48
+ kodexa-7.4.414561019241.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
49
+ kodexa-7.4.414561019241.dist-info/METADATA,sha256=CyCa37DXYeYF9jpQqPjpQQqUdYhB18YTMF3Ma8xanAE,2813
50
+ kodexa-7.4.414561019241.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
51
+ kodexa-7.4.414561019241.dist-info/RECORD,,