dbt-platform-helper 13.2.0__py3-none-any.whl → 13.3.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.

Potentially problematic release.


This version of dbt-platform-helper might be problematic. Click here for more details.

Files changed (35) hide show
  1. dbt_platform_helper/commands/codebase.py +10 -4
  2. dbt_platform_helper/commands/config.py +12 -314
  3. dbt_platform_helper/commands/copilot.py +10 -6
  4. dbt_platform_helper/commands/database.py +17 -9
  5. dbt_platform_helper/commands/environment.py +2 -3
  6. dbt_platform_helper/domain/codebase.py +7 -7
  7. dbt_platform_helper/domain/config.py +345 -0
  8. dbt_platform_helper/domain/copilot.py +155 -157
  9. dbt_platform_helper/domain/versioning.py +48 -7
  10. dbt_platform_helper/providers/aws/__init__.py +0 -0
  11. dbt_platform_helper/providers/aws/exceptions.py +10 -0
  12. dbt_platform_helper/providers/aws/sso_auth.py +61 -0
  13. dbt_platform_helper/providers/config.py +2 -1
  14. dbt_platform_helper/providers/config_validator.py +15 -13
  15. dbt_platform_helper/providers/io.py +2 -2
  16. dbt_platform_helper/providers/parameter_store.py +47 -0
  17. dbt_platform_helper/providers/platform_config_schema.py +17 -0
  18. dbt_platform_helper/providers/semantic_version.py +15 -88
  19. dbt_platform_helper/providers/terraform_manifest.py +1 -0
  20. dbt_platform_helper/providers/version.py +82 -24
  21. dbt_platform_helper/providers/version_status.py +80 -0
  22. dbt_platform_helper/utils/aws.py +0 -141
  23. dbt_platform_helper/utils/git.py +3 -1
  24. dbt_platform_helper/utils/tool_versioning.py +0 -84
  25. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/METADATA +2 -2
  26. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/RECORD +30 -30
  27. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/WHEEL +1 -1
  28. platform_helper.py +1 -1
  29. dbt_platform_helper/templates/svc/manifest-backend.yml +0 -69
  30. dbt_platform_helper/templates/svc/manifest-public.yml +0 -109
  31. dbt_platform_helper/utils/cloudfoundry.py +0 -14
  32. dbt_platform_helper/utils/files.py +0 -53
  33. dbt_platform_helper/utils/manifests.py +0 -18
  34. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/LICENSE +0 -0
  35. {dbt_platform_helper-13.2.0.dist-info → dbt_platform_helper-13.3.0.dist-info}/entry_points.txt +0 -0
@@ -17,7 +17,10 @@ class ConfigValidatorError(PlatformException):
17
17
  class ConfigValidator:
18
18
 
19
19
  def __init__(
20
- self, validations: Callable[[dict], None] = None, io: ClickIOProvider = ClickIOProvider()
20
+ self,
21
+ validations: Callable[[dict], None] = None,
22
+ io: ClickIOProvider = ClickIOProvider(),
23
+ session: boto3.Session = None,
21
24
  ):
22
25
  self.validations = validations or [
23
26
  self.validate_supported_redis_versions,
@@ -28,6 +31,7 @@ class ConfigValidator:
28
31
  self.validate_database_migration_input_sources,
29
32
  ]
30
33
  self.io = io
34
+ self.session = session
31
35
 
32
36
  def run_validations(self, config: dict):
33
37
  for validation in self.validations:
@@ -76,10 +80,15 @@ class ConfigValidator:
76
80
  f"{extension_type} version for environment {version_failure['environment']} is not in the list of supported {extension_type} versions: {supported_extension_versions}. Provided Version: {version_failure['version']}",
77
81
  )
78
82
 
83
+ def _get_client(self, service_name: str):
84
+ if self.session:
85
+ return self.session.client(service_name)
86
+ return boto3.client(service_name)
87
+
79
88
  def validate_supported_redis_versions(self, config):
80
89
  return self._validate_extension_supported_versions(
81
90
  config=config,
82
- aws_provider=Redis(boto3.client("elasticache")),
91
+ aws_provider=Redis(self._get_client("elasticache")),
83
92
  extension_type="redis", # TODO this is information which can live in the RedisProvider
84
93
  version_key="engine", # TODO this is information which can live in the RedisProvider
85
94
  )
@@ -87,7 +96,7 @@ class ConfigValidator:
87
96
  def validate_supported_opensearch_versions(self, config):
88
97
  return self._validate_extension_supported_versions(
89
98
  config=config,
90
- aws_provider=Opensearch(boto3.client("opensearch")),
99
+ aws_provider=Opensearch(self._get_client("opensearch")),
91
100
  extension_type="opensearch", # TODO this is information which can live in the OpensearchProvider
92
101
  version_key="engine", # TODO this is information which can live in the OpensearchProvider
93
102
  )
@@ -206,21 +215,14 @@ class ConfigValidator:
206
215
  f"database_copy 'to' parameter must be a valid environment ({all_envs_string}) but was '{to_env}' in extension '{extension_name}'."
207
216
  )
208
217
 
218
+ # TODO - The from_account and to_account properties are deprecated and will be removed when terraform-platform-modules is merged with platform-tools
209
219
  if from_account != to_account:
210
- if "from_account" not in section:
211
- errors.append(
212
- f"Environments '{from_env}' and '{to_env}' are in different AWS accounts. The 'from_account' parameter must be present."
213
- )
214
- elif section["from_account"] != from_account:
220
+ if "from_account" in section and section["from_account"] != from_account:
215
221
  errors.append(
216
222
  f"Incorrect value for 'from_account' for environment '{from_env}'"
217
223
  )
218
224
 
219
- if "to_account" not in section:
220
- errors.append(
221
- f"Environments '{from_env}' and '{to_env}' are in different AWS accounts. The 'to_account' parameter must be present."
222
- )
223
- elif section["to_account"] != to_account:
225
+ if "to_account" in section and section["to_account"] != to_account:
224
226
  errors.append(
225
227
  f"Incorrect value for 'to_account' for environment '{to_env}'"
226
228
  )
@@ -13,8 +13,8 @@ class ClickIOProvider:
13
13
  def error(self, message: str):
14
14
  click.secho(f"Error: {message}", fg="red")
15
15
 
16
- def info(self, message: str):
17
- click.secho(message)
16
+ def info(self, message: str, **kwargs):
17
+ click.secho(message, **kwargs)
18
18
 
19
19
  def input(self, message: str) -> str:
20
20
  return click.prompt(message)
@@ -0,0 +1,47 @@
1
+ import boto3
2
+
3
+ from dbt_platform_helper.platform_exception import PlatformException
4
+
5
+
6
+ class ParameterStore:
7
+ def __init__(self, ssm_client: boto3.client):
8
+ self.ssm_client = ssm_client
9
+
10
+ def get_ssm_parameter_by_name(self, parameter_name: str) -> dict:
11
+ """
12
+ Retrieves the latest version of a parameter from parameter store for a
13
+ given name/arn.
14
+
15
+ Args:
16
+ path (str): The parameter name to retrieve the parameter value for.
17
+ Returns:
18
+ dict: A dictionary representation of your ssm parameter
19
+ """
20
+
21
+ return self.ssm_client.get_parameter(Name=parameter_name)["Parameter"]
22
+
23
+ def get_ssm_parameters_by_path(self, path: str) -> list:
24
+ """
25
+ Retrieves all SSM parameters for a given path from parameter store.
26
+
27
+ Args:
28
+ path (str): The parameter path to retrieve the parameters for. e.g. /copilot/applications/
29
+ Returns:
30
+ list: A list of dictionaries containing all SSM parameters under the provided path.
31
+ """
32
+
33
+ parameters = []
34
+ paginator = self.ssm_client.get_paginator("get_parameters_by_path")
35
+ page_iterator = paginator.paginate(Path=path, Recursive=True)
36
+
37
+ for page in page_iterator:
38
+ parameters.extend(page.get("Parameters", []))
39
+
40
+ if parameters:
41
+ return parameters
42
+ else:
43
+ raise ParameterNotFoundForPathException()
44
+
45
+
46
+ class ParameterNotFoundForPathException(PlatformException):
47
+ """Exception raised when no parameters are found for a given path."""
@@ -28,6 +28,7 @@ class PlatformConfigSchema:
28
28
  PlatformConfigSchema.__monitoring_schema(),
29
29
  PlatformConfigSchema.__opensearch_schema(),
30
30
  PlatformConfigSchema.__postgres_schema(),
31
+ PlatformConfigSchema.__datadog_schema(),
31
32
  PlatformConfigSchema.__prometheus_policy_schema(),
32
33
  PlatformConfigSchema.__redis_schema(),
33
34
  PlatformConfigSchema.__s3_bucket_schema(),
@@ -48,6 +49,7 @@ class PlatformConfigSchema:
48
49
  "postgres": Schema(PlatformConfigSchema.__postgres_schema()),
49
50
  "prometheus-policy": Schema(PlatformConfigSchema.__prometheus_policy_schema()),
50
51
  "redis": Schema(PlatformConfigSchema.__redis_schema()),
52
+ "datadog": Schema(PlatformConfigSchema.__datadog_schema()),
51
53
  "s3": Schema(PlatformConfigSchema.__s3_bucket_schema()),
52
54
  "s3-policy": Schema(PlatformConfigSchema.__s3_bucket_policy_schema()),
53
55
  "subscription-filter": PlatformConfigSchema.__no_configuration_required_schema(
@@ -434,6 +436,21 @@ class PlatformConfigSchema:
434
436
 
435
437
  return True
436
438
 
439
+ @staticmethod
440
+ def __datadog_schema() -> dict:
441
+ return {
442
+ "type": "datadog",
443
+ Optional("environments"): {
444
+ Optional(PlatformConfigSchema.__valid_environment_name()): {
445
+ "team_name": str,
446
+ "contact_name": str,
447
+ "contact_email": str,
448
+ "documentation_url": str,
449
+ "services_to_monitor": list,
450
+ }
451
+ },
452
+ }
453
+
437
454
  @staticmethod
438
455
  def __s3_bucket_schema() -> dict:
439
456
  def _valid_s3_bucket_arn(key):
@@ -1,12 +1,6 @@
1
1
  import re
2
- from dataclasses import dataclass
3
- from dataclasses import field
4
- from typing import Dict
5
- from typing import Optional
6
2
  from typing import Union
7
3
 
8
- from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
9
- from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
10
4
  from dbt_platform_helper.providers.validation import ValidationException
11
5
 
12
6
 
@@ -42,9 +36,13 @@ class SemanticVersion:
42
36
  return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
43
37
 
44
38
  def __eq__(self, other) -> bool:
39
+ if other is None:
40
+ return False
45
41
  return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch)
46
42
 
47
43
  def validate_compatibility_with(self, other):
44
+ if other is None:
45
+ raise ValidationException("Cannot compare NoneType")
48
46
  if (self.major == 0 and other.major == 0) and (
49
47
  self.minor != other.minor or self.patch != other.patch
50
48
  ):
@@ -57,93 +55,22 @@ class SemanticVersion:
57
55
  raise IncompatibleMinorVersionException(str(self), str(other))
58
56
 
59
57
  @staticmethod
60
- def from_string(version_string: Union[str, None]):
58
+ def _cast_to_int_with_fallback(input, fallback=-1):
59
+ try:
60
+ return int(input)
61
+ except ValueError:
62
+ return fallback
63
+
64
+ @classmethod
65
+ def from_string(self, version_string: Union[str, None]):
61
66
  if version_string is None:
62
67
  return None
63
68
 
64
- version_plain = version_string.replace("v", "")
65
- version_segments = re.split(r"[.\-]", version_plain)
69
+ version_segments = re.split(r"[.\-]", version_string.replace("v", ""))
66
70
 
67
71
  if len(version_segments) != 3:
68
72
  return None
69
73
 
70
- output_version = [0, 0, 0]
71
- for index, segment in enumerate(version_segments):
72
- try:
73
- output_version[index] = int(segment)
74
- except ValueError:
75
- output_version[index] = -1
74
+ major, minor, patch = [self._cast_to_int_with_fallback(s) for s in version_segments]
76
75
 
77
- return SemanticVersion(output_version[0], output_version[1], output_version[2])
78
-
79
-
80
- @dataclass
81
- class VersionStatus:
82
- installed: SemanticVersion = None
83
- latest: SemanticVersion = None
84
-
85
- def __str__(self):
86
- attrs = {
87
- key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
88
- }
89
- attrs_str = ", ".join(f"{key}: {value}" for key, value in attrs.items())
90
- return f"{self.__class__.__name__}: {attrs_str}"
91
-
92
- def is_outdated(self):
93
- return self.installed != self.latest
94
-
95
- def validate(self):
96
- pass
97
-
98
-
99
- @dataclass
100
- class PlatformHelperVersionStatus(VersionStatus):
101
- installed: Optional[SemanticVersion] = None
102
- latest: Optional[SemanticVersion] = None
103
- deprecated_version_file: Optional[SemanticVersion] = None
104
- platform_config_default: Optional[SemanticVersion] = None
105
- pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
106
-
107
- def __str__(self):
108
- semantic_version_attrs = {
109
- key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
110
- }
111
-
112
- class_str = ", ".join(f"{key}: {value}" for key, value in semantic_version_attrs.items())
113
-
114
- if self.pipeline_overrides.items():
115
- pipeline_overrides_str = "pipeline_overrides: " + ", ".join(
116
- f"{key}: {value}" for key, value in self.pipeline_overrides.items()
117
- )
118
- class_str = ", ".join([class_str, pipeline_overrides_str])
119
-
120
- return f"{self.__class__.__name__}: {class_str}"
121
-
122
- def validate(self) -> dict:
123
- if self.platform_config_default and not self.deprecated_version_file:
124
- return {}
125
-
126
- warnings = []
127
- errors = []
128
-
129
- missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
130
- deprecation_message = (
131
- f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
132
- )
133
-
134
- if self.platform_config_default and self.deprecated_version_file:
135
- warnings.append(deprecation_message)
136
-
137
- if not self.platform_config_default and self.deprecated_version_file:
138
- warnings.append(deprecation_message)
139
- warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
140
-
141
- if not self.platform_config_default and not self.deprecated_version_file:
142
- message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
143
- message += f"{missing_default_version_message}{self.installed}\n"
144
- errors.append(message)
145
-
146
- return {
147
- "warnings": warnings,
148
- "errors": errors,
149
- }
76
+ return SemanticVersion(major, minor, patch)
@@ -128,6 +128,7 @@ class TerraformManifestProvider:
128
128
  "codebase": "${each.key}",
129
129
  "repository": "${each.value.repository}",
130
130
  "deploy_repository": f"{deploy_repository}",
131
+ "deploy_repository_branch": '${lookup(each.value, "deploy_repository_branch", "main")}',
131
132
  "additional_ecr_repository": '${lookup(each.value, "additional_ecr_repository", null)}',
132
133
  "pipelines": '${lookup(each.value, "pipelines", [])}',
133
134
  "services": "${each.value.services}",
@@ -1,17 +1,31 @@
1
+ import re
2
+ import subprocess
1
3
  from abc import ABC
4
+ from abc import abstractmethod
2
5
  from importlib.metadata import PackageNotFoundError
3
6
  from importlib.metadata import version
4
7
  from pathlib import Path
8
+ from typing import Union
5
9
 
6
- import requests
10
+ from requests import Session
11
+ from requests.adapters import HTTPAdapter
12
+ from urllib3.util import Retry
7
13
 
8
14
  from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
9
15
  from dbt_platform_helper.platform_exception import PlatformException
16
+ from dbt_platform_helper.providers.io import ClickIOProvider
10
17
  from dbt_platform_helper.providers.semantic_version import SemanticVersion
11
18
  from dbt_platform_helper.providers.yaml_file import FileProviderException
12
19
  from dbt_platform_helper.providers.yaml_file import YamlFileProvider
13
20
 
14
21
 
22
+ def set_up_retry():
23
+ session = Session()
24
+ retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[403, 500, 502, 503, 504])
25
+ session.mount("https://", HTTPAdapter(max_retries=retries))
26
+ return session
27
+
28
+
15
29
  class InstalledVersionProviderException(PlatformException):
16
30
  pass
17
31
 
@@ -25,50 +39,67 @@ class InstalledToolNotFoundException(InstalledVersionProviderException):
25
39
 
26
40
 
27
41
  class VersionProvider(ABC):
28
- pass
42
+ @abstractmethod
43
+ def get_semantic_version() -> SemanticVersion:
44
+ raise NotImplementedError("Must be implemented in subclasses")
29
45
 
30
46
 
31
47
  class InstalledVersionProvider:
32
48
  @staticmethod
33
- def get_installed_tool_version(tool_name: str) -> SemanticVersion:
49
+ def get_semantic_version(tool_name: str) -> SemanticVersion:
34
50
  try:
35
51
  return SemanticVersion.from_string(version(tool_name))
36
52
  except PackageNotFoundError:
37
53
  raise InstalledToolNotFoundException(tool_name)
38
54
 
39
55
 
40
- # TODO add timeouts and exception handling for requests
41
- # TODO Alternatively use the gitpython package?
42
- class GithubVersionProvider(VersionProvider):
56
+ class GithubLatestVersionProvider(VersionProvider):
43
57
  @staticmethod
44
- def get_latest_version(repo_name: str, tags: bool = False) -> SemanticVersion:
45
- if tags:
46
- tags_list = requests.get(f"https://api.github.com/repos/{repo_name}/tags").json()
47
- versions = [SemanticVersion.from_string(v["name"]) for v in tags_list]
48
- versions.sort(reverse=True)
49
- return versions[0]
58
+ def get_semantic_version(
59
+ repo_name: str, tags: bool = False, request_session=set_up_retry(), io=ClickIOProvider()
60
+ ) -> Union[SemanticVersion, None]:
61
+
62
+ semantic_version = None
63
+ try:
64
+ if tags:
65
+ response = request_session.get(f"https://api.github.com/repos/{repo_name}/tags")
50
66
 
51
- package_info = requests.get(
52
- f"https://api.github.com/repos/{repo_name}/releases/latest"
53
- ).json()
54
- return SemanticVersion.from_string(package_info["tag_name"])
67
+ versions = [SemanticVersion.from_string(v["name"]) for v in response.json()]
68
+ versions.sort(reverse=True)
69
+ semantic_version = versions[0]
70
+ else:
71
+ package_info = request_session.get(
72
+ f"https://api.github.com/repos/{repo_name}/releases/latest"
73
+ ).json()
74
+ semantic_version = SemanticVersion.from_string(package_info["tag_name"])
75
+ except Exception as e:
76
+ io.error(f"Exception occured when calling Github with:\n{str(e)}")
55
77
 
78
+ return semantic_version
56
79
 
57
- class PyPiVersionProvider(VersionProvider):
80
+
81
+ class PyPiLatestVersionProvider(VersionProvider):
58
82
  @staticmethod
59
- def get_latest_version(project_name: str) -> SemanticVersion:
60
- package_info = requests.get(f"https://pypi.org/pypi/{project_name}/json").json()
61
- released_versions = package_info["releases"].keys()
62
- parsed_released_versions = [SemanticVersion.from_string(v) for v in released_versions]
63
- parsed_released_versions.sort(reverse=True)
64
- return parsed_released_versions[0]
83
+ def get_semantic_version(
84
+ project_name: str, request_session=set_up_retry(), io=ClickIOProvider()
85
+ ) -> Union[SemanticVersion, None]:
86
+ semantic_version = None
87
+ try:
88
+ package_info = request_session.get(f"https://pypi.org/pypi/{project_name}/json").json()
89
+ released_versions = package_info["releases"].keys()
90
+ parsed_released_versions = [SemanticVersion.from_string(v) for v in released_versions]
91
+ parsed_released_versions.sort(reverse=True)
92
+ semantic_version = parsed_released_versions[0]
93
+ except Exception as e:
94
+ io.error(f"Exception occured when calling PyPi with:\n{str(e)}")
95
+ return semantic_version
65
96
 
66
97
 
67
98
  class DeprecatedVersionFileVersionProvider(VersionProvider):
68
99
  def __init__(self, file_provider: YamlFileProvider):
69
100
  self.file_provider = file_provider or YamlFileProvider
70
101
 
71
- def get_required_version(self) -> SemanticVersion:
102
+ def get_semantic_version(self) -> Union[SemanticVersion, None]:
72
103
  deprecated_version_file = Path(PLATFORM_HELPER_VERSION_FILE)
73
104
  try:
74
105
  loaded_version = self.file_provider.load(deprecated_version_file)
@@ -76,3 +107,30 @@ class DeprecatedVersionFileVersionProvider(VersionProvider):
76
107
  except FileProviderException:
77
108
  version_from_file = None
78
109
  return version_from_file
110
+
111
+
112
+ class AWSCLIInstalledVersionProvider(VersionProvider):
113
+ @staticmethod
114
+ def get_semantic_version() -> Union[SemanticVersion, None]:
115
+ installed_aws_version = None
116
+ try:
117
+ response = subprocess.run("aws --version", capture_output=True, shell=True)
118
+ matched = re.match(r"aws-cli/([0-9.]+)", response.stdout.decode("utf8"))
119
+ installed_aws_version = matched.group(1)
120
+ except (ValueError, AttributeError):
121
+ pass
122
+ return SemanticVersion.from_string(installed_aws_version)
123
+
124
+
125
+ class CopilotInstalledVersionProvider(VersionProvider):
126
+ @staticmethod
127
+ def get_semantic_version() -> Union[SemanticVersion, None]:
128
+ copilot_version = None
129
+
130
+ try:
131
+ response = subprocess.run("copilot --version", capture_output=True, shell=True)
132
+ [copilot_version] = re.findall(r"[0-9.]+", response.stdout.decode("utf8"))
133
+ except ValueError:
134
+ pass
135
+
136
+ return SemanticVersion.from_string(copilot_version)
@@ -0,0 +1,80 @@
1
+ from dataclasses import dataclass
2
+ from dataclasses import field
3
+ from typing import Dict
4
+ from typing import Optional
5
+
6
+ from dbt_platform_helper.constants import PLATFORM_CONFIG_FILE
7
+ from dbt_platform_helper.constants import PLATFORM_HELPER_VERSION_FILE
8
+ from dbt_platform_helper.providers.semantic_version import SemanticVersion
9
+
10
+
11
+ @dataclass
12
+ class VersionStatus:
13
+ installed: SemanticVersion = None
14
+ latest: SemanticVersion = None
15
+
16
+ def __str__(self):
17
+ attrs = {
18
+ key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
19
+ }
20
+ attrs_str = ", ".join(f"{key}: {value}" for key, value in attrs.items())
21
+ return f"{self.__class__.__name__}: {attrs_str}"
22
+
23
+ def is_outdated(self):
24
+ return self.installed != self.latest
25
+
26
+ def validate(self):
27
+ pass
28
+
29
+
30
+ @dataclass
31
+ class PlatformHelperVersionStatus(VersionStatus):
32
+ installed: Optional[SemanticVersion] = None
33
+ latest: Optional[SemanticVersion] = None
34
+ deprecated_version_file: Optional[SemanticVersion] = None
35
+ platform_config_default: Optional[SemanticVersion] = None
36
+ pipeline_overrides: Optional[Dict[str, str]] = field(default_factory=dict)
37
+
38
+ def __str__(self):
39
+ semantic_version_attrs = {
40
+ key: value for key, value in vars(self).items() if isinstance(value, SemanticVersion)
41
+ }
42
+
43
+ class_str = ", ".join(f"{key}: {value}" for key, value in semantic_version_attrs.items())
44
+
45
+ if self.pipeline_overrides.items():
46
+ pipeline_overrides_str = "pipeline_overrides: " + ", ".join(
47
+ f"{key}: {value}" for key, value in self.pipeline_overrides.items()
48
+ )
49
+ class_str = ", ".join([class_str, pipeline_overrides_str])
50
+
51
+ return f"{self.__class__.__name__}: {class_str}"
52
+
53
+ def validate(self) -> dict:
54
+ if self.platform_config_default and not self.deprecated_version_file:
55
+ return {}
56
+
57
+ warnings = []
58
+ errors = []
59
+
60
+ missing_default_version_message = f"Create a section in the root of '{PLATFORM_CONFIG_FILE}':\n\ndefault_versions:\n platform-helper: "
61
+ deprecation_message = (
62
+ f"Please delete '{PLATFORM_HELPER_VERSION_FILE}' as it is now deprecated."
63
+ )
64
+
65
+ if self.platform_config_default and self.deprecated_version_file:
66
+ warnings.append(deprecation_message)
67
+
68
+ if not self.platform_config_default and self.deprecated_version_file:
69
+ warnings.append(deprecation_message)
70
+ warnings.append(f"{missing_default_version_message}{self.deprecated_version_file}\n")
71
+
72
+ if not self.platform_config_default and not self.deprecated_version_file:
73
+ message = f"Cannot get dbt-platform-helper version from '{PLATFORM_CONFIG_FILE}'.\n"
74
+ message += f"{missing_default_version_message}{self.installed}\n"
75
+ errors.append(message)
76
+
77
+ return {
78
+ "warnings": warnings,
79
+ "errors": errors,
80
+ }