Skip to content

Commit ebc854d

Browse files
author
Oleksandr Bazarnov
committed
updated version checks and error messaging
1 parent 88f9e30 commit ebc854d

File tree

3 files changed

+66
-23
lines changed

3 files changed

+66
-23
lines changed

airbyte_cdk/manifest_migrations/manifest_migration.py

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from abc import abstractmethod
55
from typing import Any, Dict
66

7+
from packaging.version import Version
8+
79
ManifestType = Dict[str, Any]
810

911

@@ -36,13 +38,13 @@ def migrate(self, manifest: ManifestType) -> None:
3638
"""
3739

3840
@property
39-
def migration_version(self) -> str:
41+
def migration_version(self) -> Version:
4042
"""
4143
Get the migration version.
4244
43-
:return: The migration version as a string
45+
:return: The migration version as a Version object
4446
"""
45-
return self._get_migration_version()
47+
return Version(self._get_migration_version())
4648

4749
def _is_component(self, obj: Dict[str, Any]) -> bool:
4850
"""
@@ -53,18 +55,15 @@ def _is_component(self, obj: Dict[str, Any]) -> bool:
5355
"""
5456
return TYPE_TAG in obj.keys()
5557

56-
def _is_migratable(self, obj: Dict[str, Any]) -> bool:
58+
def _is_migratable_type(self, obj: Dict[str, Any]) -> bool:
5759
"""
5860
Check if the object is a migratable component,
5961
based on the Type of the component and the migration version.
6062
6163
:param obj: The object to check
6264
:return: True if the object is a migratable component, False otherwise
6365
"""
64-
return (
65-
obj[TYPE_TAG] not in NON_MIGRATABLE_TYPES
66-
and self._get_manifest_version(obj) <= self.migration_version
67-
)
66+
return obj[TYPE_TAG] not in NON_MIGRATABLE_TYPES
6867

6968
def _process_manifest(self, obj: Any) -> None:
7069
"""
@@ -89,7 +88,7 @@ def _process_manifest(self, obj: Any) -> None:
8988
# Check if the object is a component
9089
if self._is_component(obj):
9190
# Check if the object is allowed to be migrated
92-
if not self._is_migratable(obj):
91+
if not self._is_migratable_type(obj):
9392
return
9493

9594
# Check if the object should be migrated
@@ -106,15 +105,6 @@ def _process_manifest(self, obj: Any) -> None:
106105
for item in obj:
107106
self._process_manifest(item)
108107

109-
def _get_manifest_version(self, manifest: ManifestType) -> str:
110-
"""
111-
Get the manifest version from the manifest.
112-
113-
:param manifest: The manifest to get the version from
114-
:return: The manifest version
115-
"""
116-
return str(manifest.get("version", "0.0.0"))
117-
118108
def _get_migration_version(self) -> str:
119109
"""
120110
Get the migration version from the class name.

airbyte_cdk/manifest_migrations/migration_handler.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
#
44

55
import copy
6+
import logging
67
from typing import Type
78

9+
from packaging.version import Version
10+
811
from airbyte_cdk.manifest_migrations.exceptions import (
912
ManifestMigrationException,
1013
)
@@ -16,6 +19,8 @@
1619
MIGRATIONS,
1720
)
1821

22+
LOGGER = logging.getLogger("airbyte.cdk.manifest_migrations")
23+
1924

2025
class ManifestMigrationHandler:
2126
"""
@@ -25,6 +30,7 @@ class ManifestMigrationHandler:
2530
def __init__(self, manifest: ManifestType) -> None:
2631
self._manifest = manifest
2732
self._migrated_manifest: ManifestType = copy.deepcopy(self._manifest)
33+
self._manifest_version: Version = self._get_manifest_version()
2834

2935
def apply_migrations(self) -> ManifestType:
3036
"""
@@ -57,6 +63,22 @@ def _handle_migration(self, migration_class: Type[ManifestMigration]) -> None:
5763
ManifestMigrationException: If the migration process encounters any errors.
5864
"""
5965
try:
60-
migration_class()._process_manifest(self._migrated_manifest)
66+
migration_instance = migration_class()
67+
# check if the migration is supported for the given manifest version
68+
if self._manifest_version <= migration_instance.migration_version:
69+
migration_instance._process_manifest(self._migrated_manifest)
70+
else:
71+
LOGGER.info(
72+
f"Manifest migration: `{migration_class.__name__}` is not supported for the given manifest version `{self._manifest_version}`.",
73+
)
6174
except Exception as e:
62-
raise ManifestMigrationException(f"Failed to migrate the manifest: {e}") from e
75+
raise ManifestMigrationException(str(e)) from e
76+
77+
def _get_manifest_version(self) -> Version:
78+
"""
79+
Get the manifest version from the manifest.
80+
81+
:param manifest: The manifest to get the version from
82+
:return: The manifest version
83+
"""
84+
return Version(str(self._migrated_manifest.get("version", "0.0.0")))

airbyte_cdk/manifest_migrations/migrations_registry.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,45 @@
1717

1818

1919
def _migration_order_key(cls: object) -> int:
20+
"""
21+
Determines the migration order key for a given migration class based on its module name.
22+
23+
The function expects the module name to end with a double underscore followed by an integer (e.g., '__0').
24+
This integer is extracted and returned as the migration order key.
25+
26+
Args:
27+
cls (object): The migration class whose module name encodes the migration order.
28+
29+
Returns:
30+
int: The migration order extracted from the module name.
31+
32+
Raises:
33+
ValueError: If the module name does not contain the expected order suffix.
34+
"""
2035
# Extract the migration order from the module name, e.g., http_requester_url_base_to_url_v6_45_2__0
2136
# The order is the integer after the double underscore at the end of the module name
2237
module_name = cls.__module__.split(".")[-1]
2338
match = re.search(r"__(\d+)$", module_name)
24-
return int(match.group(1)) if match else 0
39+
if match:
40+
return int(match.group(1))
41+
else:
42+
message = f"Migration `{cls.__module__}` doesn't have the `order` in the module name: {module_name}. Did you miss to add `__<order>` to the module name?"
43+
raise ValueError(message)
2544

2645

2746
def _discover_migrations() -> List[Type[ManifestMigration]]:
47+
"""
48+
Discovers and returns a sorted list of all ManifestMigration subclasses available in the migrations package.
49+
This function inspects the main migrations package and its submodules to find all classes that are subclasses of ManifestMigration,
50+
excluding the ManifestMigration base class itself and any duplicates. The discovered migration classes are then sorted using the
51+
_migration_order_key function to ensure they are returned in the correct order.
52+
53+
Returns:
54+
List[Type[ManifestMigration]]: A list of discovered ManifestMigration subclasses, sorted by migration order.
55+
"""
56+
2857
migration_classes = []
29-
for name, obj in inspect.getmembers(sys.modules[migrations_pkg.__name__], inspect.isclass):
58+
for _, obj in inspect.getmembers(sys.modules[migrations_pkg.__name__], inspect.isclass):
3059
if (
3160
issubclass(obj, ManifestMigration)
3261
and obj is not ManifestMigration
@@ -37,7 +66,7 @@ def _discover_migrations() -> List[Type[ManifestMigration]]:
3766
for _, module_name, _ in pkgutil.iter_modules(migrations_pkg.__path__):
3867
module = sys.modules.get(f"{migrations_pkg.__name__}.{module_name}")
3968
if module:
40-
for name, obj in inspect.getmembers(module, inspect.isclass):
69+
for _, obj in inspect.getmembers(module, inspect.isclass):
4170
if (
4271
issubclass(obj, ManifestMigration)
4372
and obj is not ManifestMigration
@@ -47,7 +76,9 @@ def _discover_migrations() -> List[Type[ManifestMigration]]:
4776

4877
# Sort by migration order key
4978
migration_classes.sort(key=_migration_order_key)
79+
5080
return migration_classes
5181

5282

83+
# registered migrations
5384
MIGRATIONS: List[Type[ManifestMigration]] = _discover_migrations()

0 commit comments

Comments
 (0)