Skip to content

Commit 72117aa

Browse files
authored
fix(manifest-connectors): resolve parsing errors during version comparisons (#38)
1 parent 4592368 commit 72117aa

File tree

3 files changed

+58
-26
lines changed

3 files changed

+58
-26
lines changed

.github/workflows/connector-tests.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ jobs:
7272
cdk_extra: n/a
7373
- connector: source-shopify
7474
cdk_extra: n/a
75+
- connector: source-chargebee
76+
cdk_extra: n/a
7577
# Currently not passing CI (unrelated)
7678
# - connector: source-zendesk-support
7779
# cdk_extra: n/a

airbyte_cdk/sources/declarative/manifest_declarative_source.py

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
import json
66
import logging
77
import pkgutil
8-
import re
98
from copy import deepcopy
109
from importlib import metadata
11-
from typing import Any, Dict, Iterator, List, Mapping, Optional, Tuple
10+
from typing import Any, Dict, Iterator, List, Mapping, Optional
11+
from packaging.version import Version, InvalidVersion
1212

1313
import yaml
1414
from airbyte_cdk.models import (
@@ -245,45 +245,54 @@ def _validate_source(self) -> None:
245245
"Validation against json schema defined in declarative_component_schema.yaml schema failed"
246246
) from e
247247

248-
cdk_version = metadata.version("airbyte_cdk")
249-
cdk_major, cdk_minor, cdk_patch = self._get_version_parts(cdk_version, "airbyte-cdk")
250-
manifest_version = self._source_config.get("version")
251-
if manifest_version is None:
248+
cdk_version_str = metadata.version("airbyte_cdk")
249+
cdk_version = self._parse_version(cdk_version_str, "airbyte-cdk")
250+
manifest_version_str = self._source_config.get("version")
251+
if manifest_version_str is None:
252252
raise RuntimeError(
253253
"Manifest version is not defined in the manifest. This is unexpected since it should be a required field. Please contact support."
254254
)
255-
manifest_major, manifest_minor, manifest_patch = self._get_version_parts(
256-
manifest_version, "manifest"
257-
)
255+
manifest_version = self._parse_version(manifest_version_str, "manifest")
258256

259-
if cdk_version.startswith("0.0.0"):
257+
if (cdk_version.major, cdk_version.minor, cdk_version.micro) == (0, 0, 0):
260258
# Skipping version compatibility check on unreleased dev branch
261259
pass
262-
elif cdk_major < manifest_major or (
263-
cdk_major == manifest_major and cdk_minor < manifest_minor
260+
elif (cdk_version.major, cdk_version.minor) < (
261+
manifest_version.major,
262+
manifest_version.minor,
264263
):
265264
raise ValidationError(
266-
f"The manifest version {manifest_version} is greater than the airbyte-cdk package version ({cdk_version}). Your "
265+
f"The manifest version {manifest_version!s} is greater than the airbyte-cdk package version ({cdk_version!s}). Your "
267266
f"manifest may contain features that are not in the current CDK version."
268267
)
269-
elif manifest_major == 0 and manifest_minor < 29:
268+
elif (manifest_version.major, manifest_version.minor) < (0, 29):
270269
raise ValidationError(
271270
f"The low-code framework was promoted to Beta in airbyte-cdk version 0.29.0 and contains many breaking changes to the "
272-
f"language. The manifest version {manifest_version} is incompatible with the airbyte-cdk package version "
273-
f"{cdk_version} which contains these breaking changes."
271+
f"language. The manifest version {manifest_version!s} is incompatible with the airbyte-cdk package version "
272+
f"{cdk_version!s} which contains these breaking changes."
274273
)
275274

276275
@staticmethod
277-
def _get_version_parts(version: str, version_type: str) -> Tuple[int, int, int]:
278-
"""
279-
Takes a semantic version represented as a string and splits it into a tuple of its major, minor, and patch versions.
276+
def _parse_version(
277+
version: str,
278+
version_type: str,
279+
) -> Version:
280+
"""Takes a semantic version represented as a string and splits it into a tuple.
281+
282+
The fourth part (prerelease) is not returned in the tuple.
283+
284+
Returns:
285+
Version: the parsed version object
280286
"""
281-
version_parts = re.split(r"\.", version)
282-
if len(version_parts) != 3 or not all([part.isdigit() for part in version_parts]):
287+
try:
288+
parsed_version = Version(version)
289+
except InvalidVersion as ex:
283290
raise ValidationError(
284-
f"The {version_type} version {version} specified is not a valid version format (ex. 1.2.3)"
285-
)
286-
return tuple(int(part) for part in version_parts) # type: ignore # We already verified there were 3 parts and they are all digits
291+
f"The {version_type} version '{version}' is not a valid version format."
292+
) from ex
293+
else:
294+
# No exception
295+
return parsed_version
287296

288297
def _stream_configs(self, manifest: Mapping[str, Any]) -> List[Dict[str, Any]]:
289298
# This has a warning flag for static, but after we finish part 4 we'll replace manifest with self._source_config

unit_tests/sources/declarative/test_manifest_declarative_source.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,22 @@ def test_source_with_missing_version_fails(self):
644644
id="manifest_version_has_invalid_minor_format",
645645
),
646646
pytest.param(
647-
"0.29.0", "0.29.0.1", ValidationError, id="manifest_version_has_extra_version_parts"
647+
"0.29.0",
648+
"0.29.0rc1",
649+
None,
650+
id="manifest_version_is_release_candidate",
651+
),
652+
pytest.param(
653+
"0.29.0rc1",
654+
"0.29.0",
655+
None,
656+
id="cdk_version_is_release_candidate",
657+
),
658+
pytest.param(
659+
"0.29.0",
660+
"0.29.0.0.3", # packaging library does not complain and the parts are ignored during comparisons.
661+
None,
662+
id="manifest_version_has_extra_version_parts",
648663
),
649664
pytest.param(
650665
"0.29.0", "5.0", ValidationError, id="manifest_version_has_too_few_version_parts"
@@ -655,7 +670,13 @@ def test_source_with_missing_version_fails(self):
655670
],
656671
)
657672
@patch("importlib.metadata.version")
658-
def test_manifest_versions(self, version, cdk_version, manifest_version, expected_error):
673+
def test_manifest_versions(
674+
self,
675+
version,
676+
cdk_version,
677+
manifest_version,
678+
expected_error,
679+
) -> None:
659680
# Used to mock the metadata.version() for test scenarios which normally returns the actual version of the airbyte-cdk package
660681
version.return_value = cdk_version
661682

0 commit comments

Comments
 (0)