Skip to content

Commit 05ed2e2

Browse files
committed
feat: add creator() factories for the Metabase asset family
MetabaseCollection, MetabaseDashboard, and MetabaseQuestion shipped without a .creator()/.create() factory, so connectors hand-build their qualifiedNames with f-strings — duplicating Atlan's QN grammar per connector (the exact anti-pattern the application-sdk conformance rule P028 flags). Add creators that own the grammar centrally: - MetabaseCollection.creator(name, connection_qualified_name, metabase_id) -> {connection_qualified_name}/collections/{metabase_id} - MetabaseDashboard.creator(...) -> {connection_qualified_name}/dashboards/{metabase_id} - MetabaseQuestion.creator(...) -> {connection_qualified_name}/questions/{metabase_id} The grammar is id-based (a Metabase system id in a typed segment) to match the qualifiedNames connectors already write in production; a name-based grammar would not match existing assets. This is a departure from the name-based house convention and is called out for maintainer review. Adds the generator method templates (methods/asset + methods/attribute) as the source of truth, the regenerated asset methods, and parametrized creator tests (creator, deprecated create warning, missing-param validation, modification).
1 parent 3be15f9 commit 05ed2e2

13 files changed

Lines changed: 611 additions & 0 deletions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
@classmethod
3+
@init_guid
4+
def creator(
5+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
6+
) -> MetabaseCollection:
7+
validate_required_fields(
8+
["name", "connection_qualified_name", "metabase_id"],
9+
[name, connection_qualified_name, metabase_id],
10+
)
11+
attributes = MetabaseCollection.Attributes.create(
12+
name=name,
13+
connection_qualified_name=connection_qualified_name,
14+
metabase_id=metabase_id,
15+
)
16+
return cls(attributes=attributes)
17+
18+
@classmethod
19+
@init_guid
20+
def create(
21+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
22+
) -> MetabaseCollection:
23+
warn(
24+
(
25+
"This method is deprecated, please use 'creator' "
26+
"instead, which offers identical functionality."
27+
),
28+
DeprecationWarning,
29+
stacklevel=2,
30+
)
31+
return cls.creator(
32+
name=name,
33+
connection_qualified_name=connection_qualified_name,
34+
metabase_id=metabase_id,
35+
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
@classmethod
3+
@init_guid
4+
def creator(
5+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
6+
) -> MetabaseDashboard:
7+
validate_required_fields(
8+
["name", "connection_qualified_name", "metabase_id"],
9+
[name, connection_qualified_name, metabase_id],
10+
)
11+
attributes = MetabaseDashboard.Attributes.create(
12+
name=name,
13+
connection_qualified_name=connection_qualified_name,
14+
metabase_id=metabase_id,
15+
)
16+
return cls(attributes=attributes)
17+
18+
@classmethod
19+
@init_guid
20+
def create(
21+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
22+
) -> MetabaseDashboard:
23+
warn(
24+
(
25+
"This method is deprecated, please use 'creator' "
26+
"instead, which offers identical functionality."
27+
),
28+
DeprecationWarning,
29+
stacklevel=2,
30+
)
31+
return cls.creator(
32+
name=name,
33+
connection_qualified_name=connection_qualified_name,
34+
metabase_id=metabase_id,
35+
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
@classmethod
3+
@init_guid
4+
def creator(
5+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
6+
) -> MetabaseQuestion:
7+
validate_required_fields(
8+
["name", "connection_qualified_name", "metabase_id"],
9+
[name, connection_qualified_name, metabase_id],
10+
)
11+
attributes = MetabaseQuestion.Attributes.create(
12+
name=name,
13+
connection_qualified_name=connection_qualified_name,
14+
metabase_id=metabase_id,
15+
)
16+
return cls(attributes=attributes)
17+
18+
@classmethod
19+
@init_guid
20+
def create(
21+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
22+
) -> MetabaseQuestion:
23+
warn(
24+
(
25+
"This method is deprecated, please use 'creator' "
26+
"instead, which offers identical functionality."
27+
),
28+
DeprecationWarning,
29+
stacklevel=2,
30+
)
31+
return cls.creator(
32+
name=name,
33+
connection_qualified_name=connection_qualified_name,
34+
metabase_id=metabase_id,
35+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
@classmethod
3+
@init_guid
4+
def create(
5+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
6+
) -> MetabaseCollection.Attributes:
7+
validate_required_fields(
8+
["name", "connection_qualified_name", "metabase_id"],
9+
[name, connection_qualified_name, metabase_id],
10+
)
11+
return MetabaseCollection.Attributes(
12+
name=name,
13+
qualified_name=f"{connection_qualified_name}/collections/{metabase_id}",
14+
connection_qualified_name=connection_qualified_name,
15+
connector_name=AtlanConnectorType.get_connector_name(
16+
connection_qualified_name
17+
),
18+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
@classmethod
3+
@init_guid
4+
def create(
5+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
6+
) -> MetabaseDashboard.Attributes:
7+
validate_required_fields(
8+
["name", "connection_qualified_name", "metabase_id"],
9+
[name, connection_qualified_name, metabase_id],
10+
)
11+
return MetabaseDashboard.Attributes(
12+
name=name,
13+
qualified_name=f"{connection_qualified_name}/dashboards/{metabase_id}",
14+
connection_qualified_name=connection_qualified_name,
15+
connector_name=AtlanConnectorType.get_connector_name(
16+
connection_qualified_name
17+
),
18+
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
@classmethod
3+
@init_guid
4+
def create(
5+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
6+
) -> MetabaseQuestion.Attributes:
7+
validate_required_fields(
8+
["name", "connection_qualified_name", "metabase_id"],
9+
[name, connection_qualified_name, metabase_id],
10+
)
11+
return MetabaseQuestion.Attributes(
12+
name=name,
13+
qualified_name=f"{connection_qualified_name}/questions/{metabase_id}",
14+
connection_qualified_name=connection_qualified_name,
15+
connector_name=AtlanConnectorType.get_connector_name(
16+
connection_qualified_name
17+
),
18+
)

pyatlan/model/assets/metabase_collection.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,61 @@
55
from __future__ import annotations
66

77
from typing import ClassVar, List, Optional
8+
from warnings import warn
89

910
from pydantic.v1 import Field, validator
1011

12+
from pyatlan.model.enums import AtlanConnectorType
1113
from pyatlan.model.fields.atlan_fields import (
1214
BooleanField,
1315
KeywordField,
1416
KeywordTextField,
1517
RelationField,
1618
)
1719

20+
from pyatlan.utils import init_guid, validate_required_fields
21+
1822
from .metabase import Metabase
1923

2024

2125
class MetabaseCollection(Metabase):
2226
"""Description"""
2327

28+
@classmethod
29+
@init_guid
30+
def creator(
31+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
32+
) -> MetabaseCollection:
33+
validate_required_fields(
34+
["name", "connection_qualified_name", "metabase_id"],
35+
[name, connection_qualified_name, metabase_id],
36+
)
37+
attributes = MetabaseCollection.Attributes.create(
38+
name=name,
39+
connection_qualified_name=connection_qualified_name,
40+
metabase_id=metabase_id,
41+
)
42+
return cls(attributes=attributes)
43+
44+
@classmethod
45+
@init_guid
46+
def create(
47+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
48+
) -> MetabaseCollection:
49+
warn(
50+
(
51+
"This method is deprecated, please use 'creator' "
52+
"instead, which offers identical functionality."
53+
),
54+
DeprecationWarning,
55+
stacklevel=2,
56+
)
57+
return cls.creator(
58+
name=name,
59+
connection_qualified_name=connection_qualified_name,
60+
metabase_id=metabase_id,
61+
)
62+
2463
type_name: str = Field(default="MetabaseCollection", allow_mutation=False)
2564

2665
@validator("type_name")
@@ -161,6 +200,24 @@ class Attributes(Metabase.Attributes):
161200
default=None, description=""
162201
) # relationship
163202

203+
@classmethod
204+
@init_guid
205+
def create(
206+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
207+
) -> MetabaseCollection.Attributes:
208+
validate_required_fields(
209+
["name", "connection_qualified_name", "metabase_id"],
210+
[name, connection_qualified_name, metabase_id],
211+
)
212+
return MetabaseCollection.Attributes(
213+
name=name,
214+
qualified_name=f"{connection_qualified_name}/collections/{metabase_id}",
215+
connection_qualified_name=connection_qualified_name,
216+
connector_name=AtlanConnectorType.get_connector_name(
217+
connection_qualified_name
218+
),
219+
)
220+
164221
attributes: MetabaseCollection.Attributes = Field(
165222
default_factory=lambda: MetabaseCollection.Attributes(),
166223
description=(

pyatlan/model/assets/metabase_dashboard.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,55 @@
55
from __future__ import annotations
66

77
from typing import ClassVar, List, Optional
8+
from warnings import warn
89

910
from pydantic.v1 import Field, validator
1011

12+
from pyatlan.model.enums import AtlanConnectorType
1113
from pyatlan.model.fields.atlan_fields import NumericField, RelationField
14+
from pyatlan.utils import init_guid, validate_required_fields
1215

1316
from .metabase import Metabase
1417

1518

1619
class MetabaseDashboard(Metabase):
1720
"""Description"""
1821

22+
@classmethod
23+
@init_guid
24+
def creator(
25+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
26+
) -> MetabaseDashboard:
27+
validate_required_fields(
28+
["name", "connection_qualified_name", "metabase_id"],
29+
[name, connection_qualified_name, metabase_id],
30+
)
31+
attributes = MetabaseDashboard.Attributes.create(
32+
name=name,
33+
connection_qualified_name=connection_qualified_name,
34+
metabase_id=metabase_id,
35+
)
36+
return cls(attributes=attributes)
37+
38+
@classmethod
39+
@init_guid
40+
def create(
41+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
42+
) -> MetabaseDashboard:
43+
warn(
44+
(
45+
"This method is deprecated, please use 'creator' "
46+
"instead, which offers identical functionality."
47+
),
48+
DeprecationWarning,
49+
stacklevel=2,
50+
)
51+
return cls.creator(
52+
name=name,
53+
connection_qualified_name=connection_qualified_name,
54+
metabase_id=metabase_id,
55+
)
56+
1957
type_name: str = Field(default="MetabaseDashboard", allow_mutation=False)
2058

2159
@validator("type_name")
@@ -92,6 +130,24 @@ class Attributes(Metabase.Attributes):
92130
default=None, description=""
93131
) # relationship
94132

133+
@classmethod
134+
@init_guid
135+
def create(
136+
cls, *, name: str, connection_qualified_name: str, metabase_id: str
137+
) -> MetabaseDashboard.Attributes:
138+
validate_required_fields(
139+
["name", "connection_qualified_name", "metabase_id"],
140+
[name, connection_qualified_name, metabase_id],
141+
)
142+
return MetabaseDashboard.Attributes(
143+
name=name,
144+
qualified_name=f"{connection_qualified_name}/dashboards/{metabase_id}",
145+
connection_qualified_name=connection_qualified_name,
146+
connector_name=AtlanConnectorType.get_connector_name(
147+
connection_qualified_name
148+
),
149+
)
150+
95151
attributes: MetabaseDashboard.Attributes = Field(
96152
default_factory=lambda: MetabaseDashboard.Attributes(),
97153
description=(

0 commit comments

Comments
 (0)