Skip to content

Commit 11e04da

Browse files
committed
APP-6565: Added support for dynamic extension of AtlanConnectorType for custom connectors
- Enabled adding custom connector types at runtime - Ensured compatibility with string behavior and existing enum usage - Added helper methods to list names and values
1 parent 7340bbf commit 11e04da

File tree

3 files changed

+75
-16
lines changed

3 files changed

+75
-16
lines changed

pyatlan/cache/connection_cache.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,18 @@ def __init__(
175175
elif isinstance(connection, str):
176176
tokens = connection.split("/")
177177
if len(tokens) > 1:
178-
self.type = AtlanConnectorType(tokens[0]).value # type: ignore[call-arg]
179-
self.name = connection[len(tokens[0]) + 1 :] # noqa
178+
# Try enum conversion; fallback to custom connector if it fails
179+
try:
180+
self.type = AtlanConnectorType(tokens[0]).value # type: ignore[call-arg]
181+
self.name = connection[len(tokens[0]) + 1 :] # noqa
182+
except ValueError:
183+
custom_connector = AtlanConnectorType.CREATE_CUSTOM(
184+
# Ensure the enum name is converted to UPPER_SNAKE_CASE from kebab-case
185+
name=tokens[0].replace("-", "_").upper(),
186+
value=tokens[0],
187+
)
188+
self.type = custom_connector.value
189+
self.name = connection[len(tokens[0]) + 1 :]
180190

181191
def __hash__(self):
182192
return hash((self.name, self.type))

pyatlan/model/enums.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,21 @@ class AtlanConnectionCategory(str, Enum):
140140
CUSTOM = "custom"
141141

142142

143-
class AtlanConnectorType(str, Enum):
143+
class AtlanConnectorType(str, Enum, metaclass=utils.ExtendableEnumMeta):
144144
category: AtlanConnectionCategory
145145

146+
@classmethod
147+
def get_values(cls):
148+
return [member.value for member in cls._member_map_.values()]
149+
150+
@classmethod
151+
def get_names(cls):
152+
return list(cls._member_map_.keys())
153+
154+
@classmethod
155+
def get_items(cls):
156+
return [(name, member.value) for name, member in cls._member_map_.items()]
157+
146158
@classmethod
147159
def _get_connector_type_from_qualified_name(
148160
cls, qualified_name: str
@@ -152,12 +164,17 @@ def _get_connector_type_from_qualified_name(
152164
raise ValueError(
153165
f"Qualified name '{qualified_name}' does not contain enough segments."
154166
)
155-
connector_type_key = tokens[1].upper()
156-
# Check if the connector_type_key exists in AtlanConnectorType
167+
168+
connector_value = tokens[1]
169+
# Ensure the enum name is converted to UPPER_SNAKE_CASE from kebab-case
170+
connector_type_key = tokens[1].replace("-", "_").upper()
171+
172+
# Check if the connector_type_key exists in AtlanConnectorType;
173+
# if so, return it directly. Otherwise, it may be a custom type.
157174
if connector_type_key not in AtlanConnectorType.__members__:
158-
raise ValueError(
159-
f"Could not determine AtlanConnectorType from '{qualified_name}'; "
160-
f"'{connector_type_key}' is not a valid connector type."
175+
return AtlanConnectorType.CREATE_CUSTOM(
176+
name=connector_type_key,
177+
value=connector_value,
161178
)
162179
return AtlanConnectorType[connector_type_key]
163180

@@ -169,6 +186,12 @@ def __new__(
169186
obj.category = category
170187
return obj
171188

189+
@classmethod
190+
def CREATE_CUSTOM(
191+
cls, name: str, value: str, category=AtlanConnectionCategory.CUSTOM
192+
) -> "AtlanConnectorType":
193+
return cls.add_value(name, value, category)
194+
172195
def to_qualified_name(self):
173196
return f"default/{self.value}/{int(utils.get_epoch_timestamp())}"
174197

@@ -193,14 +216,22 @@ def get_connector_name(
193216
fields = qualified_name.split("/")
194217
if len(fields) != qualified_name_len:
195218
raise ValueError(err)
219+
220+
connector_value = fields[1]
221+
# Try enum conversion; fallback to custom connector if it fails
196222
try:
197-
connector_name = AtlanConnectorType(fields[1]).value # type:ignore
198-
if attribute_name != "connection_qualified_name":
199-
connection_qn = f"{fields[0]}/{fields[1]}/{fields[2]}"
200-
return connection_qn, connector_name
201-
return connector_name
202-
except ValueError as e:
203-
raise ValueError(err) from e
223+
connector_name = AtlanConnectorType(connector_value).value # type: ignore
224+
except ValueError:
225+
custom_connection = AtlanConnectorType.CREATE_CUSTOM(
226+
# Ensure the enum name is converted to UPPER_SNAKE_CASE from kebab-case
227+
name=connector_value.replace("-", "_").upper(),
228+
value=connector_value,
229+
)
230+
connector_name = custom_connection.value
231+
if attribute_name != "connection_qualified_name":
232+
connection_qn = f"{fields[0]}/{fields[1]}/{fields[2]}"
233+
return connection_qn, connector_name
234+
return connector_name
204235

205236
SNOWFLAKE = ("snowflake", AtlanConnectionCategory.WAREHOUSE)
206237
TABLEAU = ("tableau", AtlanConnectionCategory.BI)

pyatlan/utils.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import time
1111
from contextvars import ContextVar
1212
from datetime import datetime
13-
from enum import Enum
13+
from enum import Enum, EnumMeta
1414
from functools import reduce, wraps
1515
from typing import Any, Dict, List, Mapping, Optional
1616

@@ -471,3 +471,21 @@ def validate_single_required_field(field_names: List[str], values: List[Any]):
471471
raise ValueError(
472472
f"Only one of the following parameters are allowed: {', '.join(names)}"
473473
)
474+
475+
476+
class ExtendableEnumMeta(EnumMeta):
477+
def __init__(cls, name, bases, namespace):
478+
super().__init__(name, bases, namespace)
479+
cls._additional_members = {}
480+
481+
def add_value(cls, name, value, category=None):
482+
member = str.__new__(cls, value)
483+
member._name_ = name
484+
member._value_ = value
485+
member.category = category
486+
487+
cls._member_map_[name] = member
488+
cls._value2member_map_[value] = member
489+
cls._member_names_.append(name)
490+
cls._additional_members[name] = member
491+
return member

0 commit comments

Comments
 (0)