atlas-init 0.1.1__py3-none-any.whl → 0.1.8__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.
Files changed (73) hide show
  1. atlas_init/__init__.py +3 -3
  2. atlas_init/atlas_init.yaml +18 -1
  3. atlas_init/cli.py +62 -70
  4. atlas_init/cli_cfn/app.py +40 -117
  5. atlas_init/cli_cfn/{cfn.py → aws.py} +129 -14
  6. atlas_init/cli_cfn/cfn_parameter_finder.py +89 -6
  7. atlas_init/cli_cfn/example.py +203 -0
  8. atlas_init/cli_cfn/files.py +63 -0
  9. atlas_init/cli_helper/run.py +18 -2
  10. atlas_init/cli_helper/tf_runner.py +4 -6
  11. atlas_init/cli_root/__init__.py +0 -0
  12. atlas_init/cli_root/trigger.py +153 -0
  13. atlas_init/cli_tf/app.py +211 -4
  14. atlas_init/cli_tf/changelog.py +103 -0
  15. atlas_init/cli_tf/debug_logs.py +221 -0
  16. atlas_init/cli_tf/debug_logs_test_data.py +253 -0
  17. atlas_init/cli_tf/github_logs.py +229 -0
  18. atlas_init/cli_tf/go_test_run.py +194 -0
  19. atlas_init/cli_tf/go_test_run_format.py +31 -0
  20. atlas_init/cli_tf/go_test_summary.py +144 -0
  21. atlas_init/cli_tf/hcl/__init__.py +0 -0
  22. atlas_init/cli_tf/hcl/cli.py +161 -0
  23. atlas_init/cli_tf/hcl/cluster_mig.py +348 -0
  24. atlas_init/cli_tf/hcl/parser.py +140 -0
  25. atlas_init/cli_tf/schema.py +222 -18
  26. atlas_init/cli_tf/schema_go_parser.py +236 -0
  27. atlas_init/cli_tf/schema_table.py +150 -0
  28. atlas_init/cli_tf/schema_table_models.py +155 -0
  29. atlas_init/cli_tf/schema_v2.py +599 -0
  30. atlas_init/cli_tf/schema_v2_api_parsing.py +298 -0
  31. atlas_init/cli_tf/schema_v2_sdk.py +361 -0
  32. atlas_init/cli_tf/schema_v3.py +222 -0
  33. atlas_init/cli_tf/schema_v3_sdk.py +279 -0
  34. atlas_init/cli_tf/schema_v3_sdk_base.py +68 -0
  35. atlas_init/cli_tf/schema_v3_sdk_create.py +216 -0
  36. atlas_init/humps.py +253 -0
  37. atlas_init/repos/cfn.py +6 -1
  38. atlas_init/repos/path.py +3 -3
  39. atlas_init/settings/config.py +14 -4
  40. atlas_init/settings/env_vars.py +16 -1
  41. atlas_init/settings/path.py +12 -1
  42. atlas_init/settings/rich_utils.py +2 -0
  43. atlas_init/terraform.yaml +77 -1
  44. atlas_init/tf/.terraform.lock.hcl +59 -83
  45. atlas_init/tf/always.tf +7 -0
  46. atlas_init/tf/main.tf +3 -0
  47. atlas_init/tf/modules/aws_s3/provider.tf +1 -1
  48. atlas_init/tf/modules/aws_vars/aws_vars.tf +2 -0
  49. atlas_init/tf/modules/aws_vpc/provider.tf +4 -1
  50. atlas_init/tf/modules/cfn/cfn.tf +47 -33
  51. atlas_init/tf/modules/cfn/kms.tf +54 -0
  52. atlas_init/tf/modules/cfn/resource_actions.yaml +1 -0
  53. atlas_init/tf/modules/cfn/variables.tf +31 -0
  54. atlas_init/tf/modules/cloud_provider/cloud_provider.tf +1 -0
  55. atlas_init/tf/modules/cloud_provider/provider.tf +1 -1
  56. atlas_init/tf/modules/cluster/cluster.tf +34 -24
  57. atlas_init/tf/modules/cluster/provider.tf +1 -1
  58. atlas_init/tf/modules/federated_vars/federated_vars.tf +3 -0
  59. atlas_init/tf/modules/federated_vars/provider.tf +1 -1
  60. atlas_init/tf/modules/project_extra/project_extra.tf +15 -1
  61. atlas_init/tf/modules/stream_instance/stream_instance.tf +1 -1
  62. atlas_init/tf/modules/vpc_peering/vpc_peering.tf +1 -1
  63. atlas_init/tf/modules/vpc_privatelink/versions.tf +1 -1
  64. atlas_init/tf/outputs.tf +11 -3
  65. atlas_init/tf/providers.tf +2 -1
  66. atlas_init/tf/variables.tf +12 -0
  67. atlas_init/typer_app.py +76 -0
  68. {atlas_init-0.1.1.dist-info → atlas_init-0.1.8.dist-info}/METADATA +36 -18
  69. atlas_init-0.1.8.dist-info/RECORD +91 -0
  70. {atlas_init-0.1.1.dist-info → atlas_init-0.1.8.dist-info}/WHEEL +1 -1
  71. atlas_init-0.1.1.dist-info/RECORD +0 -62
  72. /atlas_init/tf/modules/aws_vpc/{aws-vpc.tf → aws_vpc.tf} +0 -0
  73. {atlas_init-0.1.1.dist-info → atlas_init-0.1.8.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,222 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import Enum, StrEnum
4
+ from typing import TypeAlias
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+ from atlas_init.humps import pascalize
9
+
10
+
11
+ def lowercase_no_snake(name: str) -> str:
12
+ return name.lower().replace("_", "")
13
+
14
+
15
+ def default_config_dict() -> ConfigDict:
16
+ return ConfigDict(populate_by_name=True, alias_generator=lowercase_no_snake)
17
+
18
+
19
+ class BaseModelLocal(BaseModel):
20
+ model_config = default_config_dict()
21
+
22
+
23
+ class ElemType(int, Enum):
24
+ BOOL = 0
25
+ FLOAT64 = 1
26
+ INT64 = 2
27
+ NUMBER = 3
28
+ STRING = 4
29
+ UNKNOWN = 5
30
+
31
+
32
+ class CustomDefault(BaseModelLocal):
33
+ definition: str
34
+ imports: list[str]
35
+
36
+
37
+ SnakeCaseString = str
38
+
39
+
40
+ class ComputedOptionalRequired(StrEnum):
41
+ computed = "computed"
42
+ computed_optional = "computed_optional"
43
+ optional = "optional"
44
+ required = "required"
45
+ unset = ""
46
+
47
+
48
+ class BoolAttribute(BaseModelLocal):
49
+ default: bool | None = None
50
+
51
+
52
+ class Float64Attribute(BaseModelLocal):
53
+ default: float | None = None
54
+
55
+
56
+ class Int64Attribute(BaseModelLocal):
57
+ default: int | None = None
58
+
59
+
60
+ class MapAttribute(BaseModelLocal):
61
+ default: CustomDefault | None = None
62
+ element_type: ElemType
63
+
64
+
65
+ class NestedAttributeObject(BaseModelLocal):
66
+ attributes: list[Attribute]
67
+
68
+
69
+ class MapNestedAttribute(BaseModelLocal):
70
+ default: CustomDefault | None = None
71
+ nested_object: NestedAttributeObject
72
+
73
+
74
+ class NumberAttribute(BaseModelLocal):
75
+ default: CustomDefault | None = None
76
+
77
+
78
+ class SetAttribute(BaseModelLocal):
79
+ default: CustomDefault | None = None
80
+ element_type: ElemType
81
+
82
+
83
+ class SetNestedAttribute(BaseModelLocal):
84
+ default: CustomDefault | None = None
85
+ nested_object: NestedAttributeObject
86
+
87
+
88
+ class SingleNestedAttribute(BaseModelLocal):
89
+ default: CustomDefault | None = None
90
+ nested_object: NestedAttributeObject
91
+
92
+
93
+ class StringAttribute(BaseModelLocal):
94
+ default: str | None = None
95
+
96
+
97
+ class ListAttribute(BaseModelLocal):
98
+ default: CustomDefault | None = None
99
+ element_type: ElemType
100
+
101
+
102
+ class ListNestedAttribute(BaseModelLocal):
103
+ default: CustomDefault | None = None
104
+ nested_object: NestedAttributeObject
105
+
106
+
107
+ class Operation(int, Enum):
108
+ CREATE = 0
109
+ UPDATE = 1
110
+ READ = 2
111
+ DELETE = 3
112
+
113
+
114
+ class TimeoutsAttribute(BaseModelLocal):
115
+ configurable_timeouts: list[Operation] = Field(default_factory=list, alias="configurabletimeouts")
116
+
117
+
118
+ NestedModelT: TypeAlias = SetNestedAttribute | ListNestedAttribute | MapNestedAttribute | SingleNestedAttribute
119
+
120
+
121
+ class Attribute(BaseModelLocal):
122
+ list: ListAttribute | None = None
123
+ float64: Float64Attribute | None = None
124
+ string: StringAttribute | None = None
125
+ bool: BoolAttribute | None = None
126
+ map: MapAttribute | None = None
127
+ number: NumberAttribute | None = None
128
+ set: SetAttribute | None = None
129
+ int64: Int64Attribute | None = None
130
+
131
+ set_nested: SetNestedAttribute | None = None
132
+ list_nested: ListNestedAttribute | None = None
133
+ map_nested: MapNestedAttribute | None = None
134
+ single_nested: SingleNestedAttribute | None = None
135
+
136
+ timeouts: TimeoutsAttribute | None = None
137
+ description: str | None = None
138
+ name: SnakeCaseString
139
+ deprecation_message: str | None = None
140
+ sensitive: bool = False
141
+
142
+ computed_optional_required: ComputedOptionalRequired | None = Field(default=None, alias="computedoptionalrequired")
143
+
144
+ @property
145
+ def name_pascal(self):
146
+ return pascalize(self.name)
147
+
148
+ @property
149
+ def is_nested(self) -> bool:
150
+ return any([self.single_nested, self.list_nested, self.map_nested, self.set_nested])
151
+
152
+ @property
153
+ def nested_model(self) -> NestedModelT:
154
+ assert self.is_nested
155
+ return self.single_nested or self.list_nested or self.map_nested or self.set_nested # type: ignore
156
+
157
+ @property
158
+ def is_attribute(self) -> bool:
159
+ return self.computed_optional_required != ComputedOptionalRequired.unset
160
+
161
+ @property
162
+ def nested_attributes(self) -> list[Attribute]:
163
+ return self.nested_model.nested_object.attributes
164
+
165
+ @property
166
+ def is_optional(self) -> bool:
167
+ return self.computed_optional_required in {
168
+ ComputedOptionalRequired.optional,
169
+ ComputedOptionalRequired.computed_optional,
170
+ ComputedOptionalRequired.computed,
171
+ }
172
+
173
+ @property
174
+ def is_required(self) -> bool:
175
+ return self.computed_optional_required == ComputedOptionalRequired.required
176
+
177
+ @property
178
+ def go_type(self) -> str:
179
+ assert not self.is_nested
180
+ if self.bool:
181
+ return "bool"
182
+ if self.float64:
183
+ return "float64"
184
+ if self.int64:
185
+ return "int64"
186
+ if self.number:
187
+ return "number"
188
+ if self.string:
189
+ return "string"
190
+ if self.list:
191
+ raise NotImplementedError(f"list {self.name}")
192
+ if self.map:
193
+ if self.map.element_type == ElemType.STRING:
194
+ return "map[string]string"
195
+ raise NotImplementedError(f"map {self.name}")
196
+ if self.set:
197
+ raise NotImplementedError(f"set {self.name}")
198
+ raise NotImplementedError(f"unknown type: {self.name}")
199
+
200
+ @property
201
+ def go_type_optional(self) -> str:
202
+ assert not self.is_nested, "should find go_type_optional for a nested attribute"
203
+ return f"*{self.go_type}" if self.is_optional else self.go_type
204
+
205
+
206
+ class Schema(BaseModelLocal):
207
+ description: str | None = None
208
+ deprecation_message: str | None = None
209
+ attributes: list[Attribute]
210
+
211
+
212
+ class Resource(BaseModelLocal):
213
+ schema: Schema
214
+ name: SnakeCaseString
215
+
216
+ @property
217
+ def use_timeout(self) -> bool:
218
+ return any(a.timeouts for a in self.schema.attributes)
219
+
220
+
221
+ ResourceSchemaV3 = Resource
222
+ TF_MODEL_NAME = "TFModel"
@@ -0,0 +1,279 @@
1
+ import logging
2
+ from functools import singledispatch
3
+ from queue import Queue
4
+
5
+ from atlas_init.cli_tf.schema_v2 import (
6
+ extend_import_urls,
7
+ go_fmt,
8
+ import_lines,
9
+ package_name,
10
+ )
11
+ from atlas_init.cli_tf.schema_v2_sdk import GoVarName, SDKAttribute, SDKModel
12
+ from atlas_init.cli_tf.schema_v3 import (
13
+ TF_MODEL_NAME,
14
+ Attribute,
15
+ ListNestedAttribute,
16
+ Resource,
17
+ SingleNestedAttribute,
18
+ )
19
+ from atlas_init.cli_tf.schema_v3_sdk_base import (
20
+ AllowedMissingAttributeError,
21
+ SDKAndSchemaAttribute,
22
+ find_attribute,
23
+ name_schema_struct,
24
+ name_struct_attribute,
25
+ schema_attributes,
26
+ )
27
+ from atlas_init.humps import camelize, pascalize
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ def generate_model_go(resource: Resource, sdk_model: SDKModel) -> str:
33
+ func_lines = sdk_to_tf_func(resource, sdk_model)
34
+ import_urls = set()
35
+ extend_import_urls(import_urls, func_lines)
36
+ unformatted = "\n".join(
37
+ [
38
+ f"package {package_name(resource.name)}",
39
+ "",
40
+ *import_lines(import_urls),
41
+ "",
42
+ *func_lines,
43
+ ]
44
+ )
45
+ return go_fmt(resource.name, unformatted)
46
+
47
+
48
+ def sdk_to_tf_func(resource: Resource, sdk_model: SDKModel) -> list[str]:
49
+ lines = []
50
+ timeouts_signature = ", timeout timeouts.Value" if resource.use_timeout else ""
51
+ lines.append(
52
+ f"func New{TF_MODEL_NAME}(ctx context.Context, {GoVarName.INPUT} *admin.{sdk_model.name}{timeouts_signature}) (*{TF_MODEL_NAME}, diag.Diagnostics) {{"
53
+ )
54
+ nested_attributes, call_lines = call_nested_functions(resource, sdk_model.list_nested_attributes())
55
+ lines.extend(call_lines)
56
+ timeouts_set = [" Timeouts: timeout,"] if resource.use_timeout else []
57
+ lines.extend(
58
+ [
59
+ f" return &{TF_MODEL_NAME}{{",
60
+ *tf_struct_create(resource, sdk_model),
61
+ *timeouts_set,
62
+ " }, nil", # close return
63
+ "}\n", # close function
64
+ ]
65
+ )
66
+ lines.extend(process_nested_attributes(nested_attributes))
67
+ return lines
68
+
69
+
70
+ def as_var_name(attr: SDKAttribute) -> str:
71
+ return camelize(attr.json_name)
72
+
73
+
74
+ def call_nested_functions(
75
+ root: Resource | Attribute,
76
+ nested_attributes: list[SDKAttribute],
77
+ *,
78
+ return_on_error: bool = True,
79
+ sdk_var_name: GoVarName = GoVarName.INPUT,
80
+ ) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
81
+ lines = []
82
+ schema_nested_attributes = schema_attributes(root)
83
+ nested_generations = []
84
+ for sdk_attribute in nested_attributes:
85
+ try:
86
+ schema_attribute = find_attribute(schema_nested_attributes, sdk_attribute.tf_name, root.name)
87
+ except AllowedMissingAttributeError as e:
88
+ logger.info(f"skipping {e!r}")
89
+ continue
90
+ var_name = as_var_name(sdk_attribute)
91
+ lines.append(
92
+ f"{var_name} := New{_name_custom_object_type(schema_attribute.name)}(ctx, {sdk_var_name}.{sdk_attribute.struct_name}, {GoVarName.DIAGS})"
93
+ )
94
+ nested_generations.append(SDKAndSchemaAttribute(sdk_attribute, schema_attribute))
95
+ if lines and return_on_error:
96
+ lines.insert(0, "diags := &diag.Diagnostics{}")
97
+ lines.extend(
98
+ [
99
+ " if diags.HasError() {",
100
+ " return nil, *diags",
101
+ " }",
102
+ ]
103
+ )
104
+
105
+ return nested_generations, lines
106
+
107
+
108
+ _sdk_to_tf_funcs = {
109
+ ("*string", "string"): lambda sdk_ref: f"types.StringPointerValue({sdk_ref})",
110
+ ("string", "string"): lambda sdk_ref: f"types.StringValue({sdk_ref})",
111
+ ("*int64", "int64"): lambda sdk_ref: f"types.Int64PointerValue({sdk_ref})",
112
+ ("int64", "int64"): lambda sdk_ref: f"types.Int64Value({sdk_ref})",
113
+ (
114
+ "*int",
115
+ "int64",
116
+ ): lambda sdk_ref: f"types.Int64PointerValue(conversion.IntPtrToInt64Ptr({sdk_ref}))",
117
+ ("*float64", "float64"): lambda sdk_ref: f"types.Float64PointerValue({sdk_ref})",
118
+ ("float64", "float64"): lambda sdk_ref: f"types.Float64Value({sdk_ref})",
119
+ ("*bool", "bool"): lambda sdk_ref: f"types.BoolPointerValue({sdk_ref})",
120
+ ("bool", "bool"): lambda sdk_ref: f"types.BoolValue({sdk_ref})",
121
+ (
122
+ "*map[string]string",
123
+ "map[string]string",
124
+ ): lambda sdk_ref: f"conversion.ToTFMapOfString({GoVarName.CTX}, {GoVarName.DIAGS}, {sdk_ref})",
125
+ (
126
+ "map[string]string",
127
+ "map[string]string",
128
+ ): lambda sdk_ref: f"conversion.ToTFMapOfString({GoVarName.CTX}, {GoVarName.DIAGS}, &{sdk_ref})",
129
+ (
130
+ "*time.Time",
131
+ "string",
132
+ ): lambda sdk_ref: f"types.StringPointerValue(conversion.TimePtrToStringPtr({sdk_ref}))",
133
+ (
134
+ "time.Time",
135
+ "string",
136
+ ): lambda sdk_ref: f"types.StringValue(conversion.TimeToString({sdk_ref}))",
137
+ }
138
+
139
+
140
+ def sdk_to_tf_attribute_value(
141
+ schema_attribute: Attribute,
142
+ sdk_attribute: SDKAttribute,
143
+ variable_name: GoVarName = GoVarName.INPUT,
144
+ ) -> str:
145
+ key = (sdk_attribute.go_type, schema_attribute.go_type)
146
+ if key in _sdk_to_tf_funcs:
147
+ return _sdk_to_tf_funcs[key](f"{variable_name}.{sdk_attribute.struct_name}")
148
+ raise ValueError(f"Could not find conversion function for {key}")
149
+
150
+
151
+ # sdk_to_tf_attribute_value(schema_attribute, sdk_attribute, sdk_var_name)
152
+ def tf_struct_create(
153
+ root: Resource | Attribute,
154
+ sdk_model: SDKModel,
155
+ sdk_var_name: GoVarName = GoVarName.INPUT,
156
+ ) -> list[str]:
157
+ lines = []
158
+ for attr in schema_attributes(root):
159
+ if attr.is_nested:
160
+ local_var = sdk_model.lookup_tf_name(attr.name)
161
+ lines.append(f"{name_struct_attribute(attr.name)}: {as_var_name(local_var)},")
162
+ elif attr.is_attribute:
163
+ local_var = sdk_model.lookup_tf_name(attr.name)
164
+ lines.append(
165
+ f"{name_struct_attribute(attr.name)}: {sdk_to_tf_attribute_value(attr, local_var, sdk_var_name)},"
166
+ )
167
+ return lines
168
+
169
+
170
+ def process_nested_attributes(
171
+ nested_attributes: list[SDKAndSchemaAttribute],
172
+ ) -> list[str]:
173
+ lines = []
174
+ queue = Queue()
175
+
176
+ def add_nested_to_queue(attributes: list[SDKAndSchemaAttribute]):
177
+ for nested in attributes:
178
+ logger.info(f"found nested attribute: {nested.schema_attribute.name}")
179
+ queue.put(nested)
180
+
181
+ add_nested_to_queue(nested_attributes)
182
+ while not queue.empty():
183
+ sdk_attribute, schema_attribute = queue.get()
184
+ more_nested_attributes, nested_lines = convert_nested_attribute(
185
+ schema_attribute.nested_model, schema_attribute, sdk_attribute
186
+ )
187
+ lines.extend(nested_lines)
188
+ add_nested_to_queue(more_nested_attributes)
189
+ return lines
190
+
191
+
192
+ def _name_custom_object_type(name: str) -> str:
193
+ return f"{pascalize(name)}ObjType"
194
+
195
+
196
+ @singledispatch
197
+ def convert_nested_attribute(
198
+ nested_model: object, schema_attribute: Attribute, _: SDKAttribute
199
+ ) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
200
+ raise NotImplementedError(f"unsupported nested attribute: {schema_attribute.name} of type {type(nested_model)}")
201
+
202
+
203
+ @convert_nested_attribute.register
204
+ def _convert_single_nested_attribute(
205
+ _: SingleNestedAttribute,
206
+ schema_attribute: Attribute,
207
+ sdk_attribute: SDKAttribute,
208
+ ) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
209
+ object_type_name = _name_custom_object_type(schema_attribute.name)
210
+ lines: list[str] = [
211
+ f"func New{object_type_name}(ctx context.Context, {GoVarName.INPUT} *admin.{sdk_attribute.struct_type_name}, diags *diag.Diagnostics) types.Object {{",
212
+ f" if {GoVarName.INPUT} == nil {{",
213
+ f" return types.ObjectNull({object_type_name}.AttrTypes)",
214
+ " }",
215
+ ]
216
+
217
+ nested_attributes, call_lines = call_nested_functions(
218
+ schema_attribute, sdk_attribute.list_nested_attributes(), return_on_error=False
219
+ )
220
+ lines.extend(call_lines)
221
+ struct_name = name_schema_struct(schema_attribute.name)
222
+ lines.extend(
223
+ [
224
+ f" tfModel := {struct_name}{{",
225
+ *tf_struct_create(schema_attribute, sdk_attribute.as_sdk_model()),
226
+ " }",
227
+ f" objType, diagsLocal := types.ObjectValueFrom(ctx, {object_type_name}.AttrTypes, tfModel)",
228
+ f" {GoVarName.DIAGS}.Append(diagsLocal...)",
229
+ " return objType",
230
+ "}\n",
231
+ ]
232
+ )
233
+ return nested_attributes, lines
234
+
235
+
236
+ @convert_nested_attribute.register
237
+ def _convert_list_nested_attriute(
238
+ _: ListNestedAttribute,
239
+ schema_attribute: Attribute,
240
+ sdk_attribute: SDKAttribute,
241
+ ) -> tuple[list[SDKAndSchemaAttribute], list[str]]:
242
+ object_type_name = _name_custom_object_type(schema_attribute.name)
243
+ lines: list[str] = [
244
+ f"func New{object_type_name}(ctx context.Context, {GoVarName.INPUT} *[]admin.{sdk_attribute.struct_type_name}, diags *diag.Diagnostics) types.List {{",
245
+ f" if {GoVarName.INPUT} == nil {{",
246
+ f" return types.ListNull({object_type_name})",
247
+ " }",
248
+ ]
249
+ struct_name = name_schema_struct(schema_attribute.name)
250
+ lines.extend(
251
+ [
252
+ f" tfModels := make([]{struct_name}, len(*{GoVarName.INPUT}))",
253
+ f" for i, item := range *{GoVarName.INPUT} {{",
254
+ ]
255
+ )
256
+ nested_attributes, call_lines = call_nested_functions(
257
+ schema_attribute,
258
+ sdk_attribute.list_nested_attributes(),
259
+ return_on_error=False,
260
+ sdk_var_name=GoVarName.ITEM,
261
+ )
262
+ lines.extend([f" {line}" for line in call_lines])
263
+ lines.extend(
264
+ [
265
+ f" tfModels[i] = {struct_name}{{",
266
+ *tf_struct_create(
267
+ schema_attribute,
268
+ sdk_attribute.as_sdk_model(),
269
+ sdk_var_name=GoVarName.ITEM,
270
+ ),
271
+ " }",
272
+ " }",
273
+ f" listType, diagsLocal := types.ListValueFrom(ctx, {object_type_name}, tfModels)",
274
+ f" {GoVarName.DIAGS}.Append(diagsLocal...)",
275
+ " return listType",
276
+ "}\n",
277
+ ]
278
+ )
279
+ return nested_attributes, lines
@@ -0,0 +1,68 @@
1
+ from functools import singledispatch
2
+ from typing import NamedTuple
3
+
4
+ from atlas_init.cli_tf.schema_v2_sdk import SDKAttribute
5
+ from atlas_init.cli_tf.schema_v3 import Attribute, Resource
6
+ from atlas_init.humps import pascalize
7
+
8
+
9
+ class SDKAndSchemaAttribute(NamedTuple):
10
+ sdk_attribute: SDKAttribute
11
+ schema_attribute: Attribute
12
+
13
+
14
+ _allowed_missing_attributes: set[str] = set()
15
+
16
+
17
+ def set_allowed_missing_attributes(allowed: set[str]):
18
+ global _allowed_missing_attributes # noqa: PLW0603 `Using the global statement to update `_allowed_missing_attributes` is discouraged`
19
+ _allowed_missing_attributes = allowed
20
+
21
+
22
+ class AllowedMissingAttributeError(Exception):
23
+ def __init__(self, name: str, *args: object) -> None:
24
+ self.name = name
25
+ super().__init__(*args)
26
+
27
+
28
+ def find_attribute(attributes: list[Attribute], name: str, root_name: str) -> Attribute:
29
+ for schema_attribute in attributes:
30
+ if name == schema_attribute.name:
31
+ return schema_attribute
32
+ if name in _allowed_missing_attributes:
33
+ raise AllowedMissingAttributeError(name)
34
+ raise ValueError(f"could not find schema attribute for {name} on resource: {root_name}")
35
+
36
+
37
+ @singledispatch
38
+ def schema_attributes(root: object) -> list[Attribute]:
39
+ raise NotImplementedError(f"unsupported root type: {type(root)} to find schema attributes")
40
+
41
+
42
+ @schema_attributes.register
43
+ def _resource_attributes(root: Resource) -> list[Attribute]:
44
+ return root.schema.attributes
45
+
46
+
47
+ @schema_attributes.register
48
+ def _attribute_nested(root: Attribute) -> list[Attribute]:
49
+ return root.nested_attributes
50
+
51
+
52
+ def name_schema_struct(name: str) -> str:
53
+ return f"TF{pascalize(name)}Model"
54
+
55
+
56
+ def name_struct_attribute(name: str) -> str:
57
+ default = pascalize(name)
58
+ if override := _name_attribute_overrides.get(default):
59
+ return override
60
+ return default
61
+
62
+
63
+ _name_attribute_overrides = {}
64
+
65
+
66
+ def set_name_attribute_overrides(overrides: dict[str, str]):
67
+ global _name_attribute_overrides # noqa: PLW0603 `Using the global statement to update `_name_attribute_overrides` is discouraged`
68
+ _name_attribute_overrides = overrides