nextmv 0.18.0__py3-none-any.whl → 1.0.0.dev2__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 (175) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +8 -13
  3. nextmv/__init__.py +53 -0
  4. nextmv/_serialization.py +96 -0
  5. nextmv/base_model.py +54 -9
  6. nextmv/cli/CONTRIBUTING.md +511 -0
  7. nextmv/cli/__init__.py +0 -0
  8. nextmv/cli/cloud/__init__.py +47 -0
  9. nextmv/cli/cloud/acceptance/__init__.py +27 -0
  10. nextmv/cli/cloud/acceptance/create.py +393 -0
  11. nextmv/cli/cloud/acceptance/delete.py +68 -0
  12. nextmv/cli/cloud/acceptance/get.py +104 -0
  13. nextmv/cli/cloud/acceptance/list.py +62 -0
  14. nextmv/cli/cloud/acceptance/update.py +95 -0
  15. nextmv/cli/cloud/account/__init__.py +28 -0
  16. nextmv/cli/cloud/account/create.py +83 -0
  17. nextmv/cli/cloud/account/delete.py +60 -0
  18. nextmv/cli/cloud/account/get.py +66 -0
  19. nextmv/cli/cloud/account/update.py +70 -0
  20. nextmv/cli/cloud/app/__init__.py +35 -0
  21. nextmv/cli/cloud/app/create.py +141 -0
  22. nextmv/cli/cloud/app/delete.py +58 -0
  23. nextmv/cli/cloud/app/exists.py +44 -0
  24. nextmv/cli/cloud/app/get.py +66 -0
  25. nextmv/cli/cloud/app/list.py +61 -0
  26. nextmv/cli/cloud/app/push.py +137 -0
  27. nextmv/cli/cloud/app/update.py +124 -0
  28. nextmv/cli/cloud/batch/__init__.py +29 -0
  29. nextmv/cli/cloud/batch/create.py +454 -0
  30. nextmv/cli/cloud/batch/delete.py +68 -0
  31. nextmv/cli/cloud/batch/get.py +104 -0
  32. nextmv/cli/cloud/batch/list.py +63 -0
  33. nextmv/cli/cloud/batch/metadata.py +66 -0
  34. nextmv/cli/cloud/batch/update.py +95 -0
  35. nextmv/cli/cloud/data/__init__.py +26 -0
  36. nextmv/cli/cloud/data/upload.py +162 -0
  37. nextmv/cli/cloud/ensemble/__init__.py +31 -0
  38. nextmv/cli/cloud/ensemble/create.py +414 -0
  39. nextmv/cli/cloud/ensemble/delete.py +67 -0
  40. nextmv/cli/cloud/ensemble/get.py +65 -0
  41. nextmv/cli/cloud/ensemble/update.py +103 -0
  42. nextmv/cli/cloud/input_set/__init__.py +30 -0
  43. nextmv/cli/cloud/input_set/create.py +170 -0
  44. nextmv/cli/cloud/input_set/get.py +63 -0
  45. nextmv/cli/cloud/input_set/list.py +63 -0
  46. nextmv/cli/cloud/input_set/update.py +123 -0
  47. nextmv/cli/cloud/instance/__init__.py +35 -0
  48. nextmv/cli/cloud/instance/create.py +290 -0
  49. nextmv/cli/cloud/instance/delete.py +62 -0
  50. nextmv/cli/cloud/instance/exists.py +39 -0
  51. nextmv/cli/cloud/instance/get.py +62 -0
  52. nextmv/cli/cloud/instance/list.py +60 -0
  53. nextmv/cli/cloud/instance/update.py +216 -0
  54. nextmv/cli/cloud/managed_input/__init__.py +31 -0
  55. nextmv/cli/cloud/managed_input/create.py +146 -0
  56. nextmv/cli/cloud/managed_input/delete.py +65 -0
  57. nextmv/cli/cloud/managed_input/get.py +63 -0
  58. nextmv/cli/cloud/managed_input/list.py +60 -0
  59. nextmv/cli/cloud/managed_input/update.py +97 -0
  60. nextmv/cli/cloud/run/__init__.py +37 -0
  61. nextmv/cli/cloud/run/cancel.py +37 -0
  62. nextmv/cli/cloud/run/create.py +530 -0
  63. nextmv/cli/cloud/run/get.py +199 -0
  64. nextmv/cli/cloud/run/input.py +86 -0
  65. nextmv/cli/cloud/run/list.py +80 -0
  66. nextmv/cli/cloud/run/logs.py +167 -0
  67. nextmv/cli/cloud/run/metadata.py +67 -0
  68. nextmv/cli/cloud/run/track.py +501 -0
  69. nextmv/cli/cloud/scenario/__init__.py +29 -0
  70. nextmv/cli/cloud/scenario/create.py +451 -0
  71. nextmv/cli/cloud/scenario/delete.py +65 -0
  72. nextmv/cli/cloud/scenario/get.py +102 -0
  73. nextmv/cli/cloud/scenario/list.py +63 -0
  74. nextmv/cli/cloud/scenario/metadata.py +67 -0
  75. nextmv/cli/cloud/scenario/update.py +93 -0
  76. nextmv/cli/cloud/secrets/__init__.py +33 -0
  77. nextmv/cli/cloud/secrets/create.py +206 -0
  78. nextmv/cli/cloud/secrets/delete.py +67 -0
  79. nextmv/cli/cloud/secrets/get.py +66 -0
  80. nextmv/cli/cloud/secrets/list.py +60 -0
  81. nextmv/cli/cloud/secrets/update.py +147 -0
  82. nextmv/cli/cloud/shadow/__init__.py +33 -0
  83. nextmv/cli/cloud/shadow/create.py +184 -0
  84. nextmv/cli/cloud/shadow/delete.py +68 -0
  85. nextmv/cli/cloud/shadow/get.py +61 -0
  86. nextmv/cli/cloud/shadow/list.py +63 -0
  87. nextmv/cli/cloud/shadow/metadata.py +66 -0
  88. nextmv/cli/cloud/shadow/start.py +43 -0
  89. nextmv/cli/cloud/shadow/stop.py +43 -0
  90. nextmv/cli/cloud/shadow/update.py +95 -0
  91. nextmv/cli/cloud/upload/__init__.py +22 -0
  92. nextmv/cli/cloud/upload/create.py +39 -0
  93. nextmv/cli/cloud/version/__init__.py +33 -0
  94. nextmv/cli/cloud/version/create.py +97 -0
  95. nextmv/cli/cloud/version/delete.py +62 -0
  96. nextmv/cli/cloud/version/exists.py +39 -0
  97. nextmv/cli/cloud/version/get.py +62 -0
  98. nextmv/cli/cloud/version/list.py +60 -0
  99. nextmv/cli/cloud/version/update.py +92 -0
  100. nextmv/cli/community/__init__.py +24 -0
  101. nextmv/cli/community/clone.py +270 -0
  102. nextmv/cli/community/list.py +265 -0
  103. nextmv/cli/configuration/__init__.py +23 -0
  104. nextmv/cli/configuration/config.py +195 -0
  105. nextmv/cli/configuration/create.py +94 -0
  106. nextmv/cli/configuration/delete.py +67 -0
  107. nextmv/cli/configuration/list.py +77 -0
  108. nextmv/cli/main.py +188 -0
  109. nextmv/cli/message.py +153 -0
  110. nextmv/cli/options.py +206 -0
  111. nextmv/cli/version.py +38 -0
  112. nextmv/cloud/__init__.py +71 -17
  113. nextmv/cloud/acceptance_test.py +757 -51
  114. nextmv/cloud/account.py +406 -17
  115. nextmv/cloud/application/__init__.py +957 -0
  116. nextmv/cloud/application/_acceptance.py +419 -0
  117. nextmv/cloud/application/_batch_scenario.py +860 -0
  118. nextmv/cloud/application/_ensemble.py +251 -0
  119. nextmv/cloud/application/_input_set.py +227 -0
  120. nextmv/cloud/application/_instance.py +289 -0
  121. nextmv/cloud/application/_managed_input.py +227 -0
  122. nextmv/cloud/application/_run.py +1393 -0
  123. nextmv/cloud/application/_secrets.py +294 -0
  124. nextmv/cloud/application/_shadow.py +314 -0
  125. nextmv/cloud/application/_utils.py +54 -0
  126. nextmv/cloud/application/_version.py +303 -0
  127. nextmv/cloud/assets.py +48 -0
  128. nextmv/cloud/batch_experiment.py +294 -33
  129. nextmv/cloud/client.py +307 -66
  130. nextmv/cloud/ensemble.py +247 -0
  131. nextmv/cloud/input_set.py +120 -2
  132. nextmv/cloud/instance.py +133 -8
  133. nextmv/cloud/integration.py +533 -0
  134. nextmv/cloud/package.py +168 -53
  135. nextmv/cloud/scenario.py +410 -0
  136. nextmv/cloud/secrets.py +234 -0
  137. nextmv/cloud/shadow.py +190 -0
  138. nextmv/cloud/url.py +73 -0
  139. nextmv/cloud/version.py +132 -4
  140. nextmv/default_app/.gitignore +1 -0
  141. nextmv/default_app/README.md +32 -0
  142. nextmv/default_app/app.yaml +12 -0
  143. nextmv/default_app/input.json +5 -0
  144. nextmv/default_app/main.py +37 -0
  145. nextmv/default_app/requirements.txt +2 -0
  146. nextmv/default_app/src/__init__.py +0 -0
  147. nextmv/default_app/src/visuals.py +36 -0
  148. nextmv/deprecated.py +47 -0
  149. nextmv/input.py +861 -90
  150. nextmv/local/__init__.py +5 -0
  151. nextmv/local/application.py +1251 -0
  152. nextmv/local/executor.py +1042 -0
  153. nextmv/local/geojson_handler.py +323 -0
  154. nextmv/local/local.py +97 -0
  155. nextmv/local/plotly_handler.py +61 -0
  156. nextmv/local/runner.py +274 -0
  157. nextmv/logger.py +80 -9
  158. nextmv/manifest.py +1466 -0
  159. nextmv/model.py +241 -66
  160. nextmv/options.py +708 -115
  161. nextmv/output.py +1301 -274
  162. nextmv/polling.py +325 -0
  163. nextmv/run.py +1702 -0
  164. nextmv/safe.py +145 -0
  165. nextmv/status.py +122 -0
  166. nextmv-1.0.0.dev2.dist-info/METADATA +311 -0
  167. nextmv-1.0.0.dev2.dist-info/RECORD +170 -0
  168. {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/WHEEL +1 -1
  169. nextmv-1.0.0.dev2.dist-info/entry_points.txt +2 -0
  170. nextmv/cloud/application.py +0 -1405
  171. nextmv/cloud/manifest.py +0 -234
  172. nextmv/cloud/status.py +0 -29
  173. nextmv-0.18.0.dist-info/METADATA +0 -770
  174. nextmv-0.18.0.dist-info/RECORD +0 -25
  175. {nextmv-0.18.0.dist-info → nextmv-1.0.0.dev2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,533 @@
1
+ """
2
+ Integration module for interacting with Nextmv Cloud integrations.
3
+
4
+ This module provides functionality to interact with integrations in Nextmv
5
+ Cloud, including integration management.
6
+
7
+ Classes
8
+ -------
9
+ IntegrationType
10
+ Enum representing the type of an integration.
11
+ IntegrationProvider
12
+ Enum representing the provider of an integration.
13
+ Integration
14
+ Class representing an integration in Nextmv Cloud.
15
+
16
+ Functions
17
+ ---------
18
+ list_integrations
19
+ Function to list all integrations in Nextmv Cloud.
20
+ """
21
+
22
+ from datetime import datetime
23
+ from enum import Enum
24
+ from typing import Any
25
+
26
+ from pydantic import AliasChoices, Field
27
+
28
+ from nextmv.base_model import BaseModel
29
+ from nextmv.cloud.client import Client
30
+ from nextmv.manifest import ManifestType
31
+ from nextmv.safe import safe_id
32
+
33
+
34
+ class IntegrationType(str, Enum):
35
+ """
36
+ The type of an integration.
37
+
38
+ You can import the `IntegrationType` class directly from `cloud`:
39
+
40
+ ```python
41
+ from nextmv.cloud import IntegrationType
42
+ ```
43
+
44
+ Attributes
45
+ ----------
46
+ RUNTIME : str
47
+ Indicates a runtime integration.
48
+ DATA : str
49
+ Indicates a data integration.
50
+ """
51
+
52
+ RUNTIME = "runtime"
53
+ """Indicates a runtime integration."""
54
+ DATA = "data"
55
+ """Indicates a data integration."""
56
+
57
+
58
+ class IntegrationProvider(str, Enum):
59
+ """
60
+ The provider of an integration.
61
+
62
+ You can import the `IntegrationProvider` class directly from `cloud`:
63
+
64
+ ```python
65
+ from nextmv.cloud import IntegrationProvider
66
+ ```
67
+
68
+ Attributes
69
+ ----------
70
+ DBX : str
71
+ Indicates a Databricks integration.
72
+ UNKNOWN : str
73
+ Indicates an unknown integration provider.
74
+ """
75
+
76
+ DBX = "dbx"
77
+ """Indicates a Databricks integration."""
78
+ UNKNOWN = "unknown"
79
+ """Indicates an unknown integration provider."""
80
+
81
+
82
+ class Integration(BaseModel):
83
+ """
84
+ Represents an integration in Nextmv Cloud. An integration allows Nextmv
85
+ Cloud to communicate with external systems or services.
86
+
87
+ You can import the `Integration` class directly from `cloud`:
88
+
89
+ ```python
90
+ from nextmv.cloud import Integration
91
+ ```
92
+
93
+ You can use the `Integration.get` class method to retrieve an existing
94
+ integration from Nextmv Cloud, to ensure that all fields are properly
95
+ populated.
96
+
97
+ Parameters
98
+ ----------
99
+ integration_id : str
100
+ The unique identifier of the integration.
101
+ client : Client
102
+ Client to use for interacting with the Nextmv Cloud API.
103
+ name : str, optional
104
+ The name of the integration.
105
+ description : str, optional
106
+ An optional description of the integration.
107
+ is_global : bool, optional
108
+ Indicates whether the integration is global (available to all
109
+ applications in the account).
110
+ application_ids : list[str], optional
111
+ List of application IDs that have access to this integration.
112
+ integration_type : IntegrationType, optional
113
+ The type of the integration (runtime or data).
114
+ exec_types : list[ManifestType], optional
115
+ List of execution types supported by the integration.
116
+ provider : IntegrationProvider, optional
117
+ The provider of the integration.
118
+ provider_config : dict[str, Any], optional
119
+ Configuration specific to the integration provider.
120
+ created_at : datetime, optional
121
+ The timestamp when the integration was created.
122
+ updated_at : datetime, optional
123
+ The timestamp when the integration was last updated.
124
+ """
125
+
126
+ integration_id: str = Field(
127
+ serialization_alias="id",
128
+ validation_alias=AliasChoices("id", "integration_id"),
129
+ )
130
+ """The unique identifier of the integration."""
131
+ client: Client = Field(exclude=True)
132
+ """Client to use for interacting with the Nextmv Cloud API."""
133
+
134
+ name: str | None = None
135
+ """The name of the integration."""
136
+ description: str | None = None
137
+ """An optional description of the integration."""
138
+ is_global: bool = Field(
139
+ serialization_alias="global",
140
+ validation_alias=AliasChoices("global", "is_global"),
141
+ default=False,
142
+ )
143
+ """
144
+ Indicates whether the integration is global (available to all
145
+ applications in the account).
146
+ """
147
+ application_ids: list[str] | None = None
148
+ """
149
+ List of application IDs that have access to this integration.
150
+ """
151
+ integration_type: IntegrationType | None = Field(
152
+ serialization_alias="type",
153
+ validation_alias=AliasChoices("type", "integration_type"),
154
+ default=None,
155
+ )
156
+ """The type of the integration (runtime or data)."""
157
+ exec_types: list[ManifestType] | None = None
158
+ """List of execution types supported by the integration."""
159
+ provider: IntegrationProvider | None = None
160
+ """The provider of the integration."""
161
+ provider_config: dict[str, Any] | None = None
162
+ """Configuration specific to the integration provider."""
163
+ created_at: datetime | None = None
164
+ """The timestamp when the integration was created."""
165
+ updated_at: datetime | None = None
166
+ """The timestamp when the integration was last updated."""
167
+ endpoint: str = Field(
168
+ exclude=True,
169
+ default="v1/integrations/{id}",
170
+ )
171
+ """Base endpoint for the integration."""
172
+
173
+ def model_post_init(self, __context) -> None:
174
+ """
175
+ Validations done after model initialization.
176
+ """
177
+
178
+ self.endpoint = self.endpoint.format(id=self.integration_id)
179
+
180
+ @classmethod
181
+ def get(cls, client: Client, integration_id: str) -> "Integration":
182
+ """
183
+ Retrieve an existing integration from Nextmv Cloud.
184
+
185
+ This method should be used for validating that the integration exists,
186
+ and not rely simply on instantiating the `Integration` class. Using
187
+ this method ensures that all the fields of the `Integration` class are
188
+ properly populated.
189
+
190
+ Parameters
191
+ ----------
192
+ client : Client
193
+ Client to use for interacting with the Nextmv Cloud API.
194
+ integration_id : str
195
+ The unique identifier of the integration to retrieve.
196
+
197
+ Returns
198
+ -------
199
+ Integration
200
+ The retrieved integration instance.
201
+
202
+ Raises
203
+ ------
204
+ requests.HTTPError
205
+ If the response status code is not 2xx.
206
+
207
+ Examples
208
+ --------
209
+ >>> from nextmv.cloud import Client, Integration
210
+ >>> client = Client(api_key="your_api_key")
211
+ >>> integration = Integration.get(client=client, integration_id="your_integration_id")
212
+ >>> print(integration.to_dict())
213
+ """
214
+
215
+ response = client.request(
216
+ method="GET",
217
+ endpoint=f"v1/integrations/{integration_id}",
218
+ )
219
+ response_dict = response.json()
220
+ response_dict["client"] = client
221
+
222
+ return cls.from_dict(response_dict)
223
+
224
+ @classmethod
225
+ def new( # noqa: C901
226
+ cls,
227
+ client: Client,
228
+ name: str,
229
+ integration_type: IntegrationType | str,
230
+ exec_types: list[ManifestType | str],
231
+ provider: IntegrationProvider | str,
232
+ provider_config: dict[str, Any],
233
+ integration_id: str | None = None,
234
+ description: str | None = None,
235
+ is_global: bool = False,
236
+ application_ids: list[str] | None = None,
237
+ exist_ok: bool = False,
238
+ ) -> "Integration":
239
+ """
240
+ Create a new integration directly in Nextmv Cloud.
241
+
242
+ Parameters
243
+ ----------
244
+ client : Client
245
+ Client to use for interacting with the Nextmv Cloud API.
246
+ name : str
247
+ The name of the integration.
248
+ integration_type : IntegrationType | str
249
+ The type of the integration. Please refer to the `IntegrationType`
250
+ enum for possible values.
251
+ exec_types : list[ManifestType | str]
252
+ List of execution types supported by the integration. Please refer
253
+ to the `ManifestType` enum for possible values.
254
+ provider : IntegrationProvider | str
255
+ The provider of the integration. Please refer to the
256
+ `IntegrationProvider` enum for possible values.
257
+ provider_config : dict[str, Any]
258
+ Configuration specific to the integration provider.
259
+ integration_id : str, optional
260
+ The unique identifier of the integration. If not provided,
261
+ it will be generated automatically.
262
+ description : str, optional
263
+ An optional description of the integration.
264
+ is_global : bool, optional, default=False
265
+ Indicates whether the integration is global (available to all
266
+ applications in the account). Default is False.
267
+ application_ids : list[str], optional
268
+ List of application IDs that have access to this integration.
269
+ exist_ok : bool, default=False
270
+ If True and an integration with the same ID already exists,
271
+ return the existing integration instead of creating a new one.
272
+
273
+ Returns
274
+ -------
275
+ Integration
276
+ The created integration instance.
277
+
278
+ Raises
279
+ ------
280
+ requests.HTTPError
281
+ If the response status code is not 2xx.
282
+ ValueError
283
+ If both `is_global` is True and `application_ids` is provided.
284
+
285
+ Examples
286
+ --------
287
+ >>> from nextmv.cloud import Client, Integration, IntegrationType, IntegrationProvider, ManifestType
288
+ >>> client = Client(api_key="your_api_key")
289
+ >>> integration = Integration.new(
290
+ ... client=client,
291
+ ... name="my_integration",
292
+ ... integration_type=IntegrationType.RUNTIME,
293
+ ... exec_types=[ManifestType.PYTHON],
294
+ ... provider=IntegrationProvider.DBX,
295
+ ... provider_config={"config_key": "config_value"},
296
+ ... )
297
+ >>> print(integration.to_dict())
298
+ """
299
+
300
+ if is_global and application_ids is not None:
301
+ raise ValueError("An integration cannot be global and have specific application IDs.")
302
+ elif not is_global and application_ids is None:
303
+ raise ValueError("A non-global integration must have specific application IDs.")
304
+
305
+ if integration_id is None:
306
+ integration_id = safe_id("integration")
307
+
308
+ if exist_ok:
309
+ try:
310
+ integration = cls.get(client=client, integration_id=integration_id)
311
+ return integration
312
+ except Exception:
313
+ pass
314
+
315
+ if not isinstance(integration_type, IntegrationType):
316
+ integration_type = IntegrationType(integration_type)
317
+
318
+ if not all(isinstance(exec_type, ManifestType) for exec_type in exec_types):
319
+ exec_types = [ManifestType(exec_type) for exec_type in exec_types]
320
+
321
+ if not isinstance(provider, IntegrationProvider):
322
+ provider = IntegrationProvider(provider)
323
+
324
+ payload = {
325
+ "id": integration_id,
326
+ "name": name,
327
+ "global": is_global,
328
+ "type": integration_type.value,
329
+ "exec_types": [exec_type.value for exec_type in exec_types],
330
+ "provider": provider.value,
331
+ "provider_config": provider_config,
332
+ }
333
+
334
+ if description is not None:
335
+ payload["description"] = description
336
+
337
+ if application_ids is not None:
338
+ payload["application_ids"] = application_ids
339
+
340
+ response = client.request(
341
+ method="POST",
342
+ endpoint="v1/integrations",
343
+ payload=payload,
344
+ )
345
+ response_dict = response.json()
346
+ response_dict["client"] = client
347
+ integration = cls.from_dict(response_dict)
348
+
349
+ return integration
350
+
351
+ def delete(self) -> None:
352
+ """
353
+ Deletes the integration from Nextmv Cloud.
354
+
355
+ Raises
356
+ ------
357
+ requests.HTTPError
358
+ If the response status code is not 2xx.
359
+
360
+ Examples
361
+ --------
362
+ >>> from nextmv.cloud import Client, Integration
363
+ >>> client = Client(api_key="your_api_key")
364
+ >>> integration = Integration.get(client=client, integration_id="your_integration_id")
365
+ >>> integration.delete()
366
+ """
367
+
368
+ _ = self.client.request(
369
+ method="DELETE",
370
+ endpoint=self.endpoint,
371
+ )
372
+
373
+ def update( # noqa: C901
374
+ self,
375
+ name: str | None = None,
376
+ integration_type: IntegrationType | str | None = None,
377
+ exec_types: list[ManifestType | str] | None = None,
378
+ provider: IntegrationProvider | str | None = None,
379
+ provider_config: dict[str, Any] | None = None,
380
+ description: str | None = None,
381
+ is_global: bool | None = None,
382
+ application_ids: list[str] | None = None,
383
+ ) -> "Integration":
384
+ """
385
+ Updates the integration in Nextmv Cloud.
386
+
387
+ Parameters
388
+ ----------
389
+ name : str, optional
390
+ The new name of the integration.
391
+ integration_type : IntegrationType | str, optional
392
+ The new type of the integration. Please refer to the `IntegrationType`
393
+ enum for possible values.
394
+ exec_types : list[ManifestType | str], optional
395
+ New list of execution types supported by the integration. Please refer
396
+ to the `ManifestType` enum for possible values.
397
+ provider : IntegrationProvider | str, optional
398
+ The new provider of the integration. Please refer to the
399
+ `IntegrationProvider` enum for possible values.
400
+ provider_config : dict[str, Any], optional
401
+ New configuration specific to the integration provider.
402
+ description : str, optional
403
+ The new description of the integration.
404
+ is_global : bool, optional
405
+ Indicates whether the integration is global (available to all
406
+ applications in the account). If not provided, the current value
407
+ is preserved.
408
+ application_ids : list[str], optional
409
+ New list of application IDs that have access to this integration.
410
+
411
+ Returns
412
+ -------
413
+ Integration
414
+ The updated integration instance.
415
+
416
+ Raises
417
+ ------
418
+ requests.HTTPError
419
+ If the response status code is not 2xx.
420
+
421
+ Examples
422
+ --------
423
+ >>> from nextmv.cloud import Client, Integration
424
+ >>> client = Client(api_key="your_api_key")
425
+ >>> integration = Integration.get(client=client, integration_id="your_integration_id")
426
+ >>> updated_integration = integration.update(name="new_name")
427
+ >>> print(updated_integration.to_dict())
428
+ """
429
+
430
+ integration = self.get(client=self.client, integration_id=self.integration_id)
431
+ integration_dict = integration.to_dict()
432
+ payload = integration_dict.copy()
433
+
434
+ if name is not None:
435
+ payload["name"] = name
436
+
437
+ if integration_type is not None:
438
+ if not isinstance(integration_type, IntegrationType):
439
+ integration_type = IntegrationType(integration_type)
440
+ payload["type"] = integration_type.value
441
+
442
+ if exec_types is not None:
443
+ if not all(isinstance(exec_type, ManifestType) for exec_type in exec_types):
444
+ exec_types = [ManifestType(exec_type) for exec_type in exec_types]
445
+ payload["exec_types"] = [exec_type.value for exec_type in exec_types]
446
+
447
+ if provider is not None:
448
+ if not isinstance(provider, IntegrationProvider):
449
+ provider = IntegrationProvider(provider)
450
+ payload["provider"] = provider.value
451
+
452
+ if provider_config is not None:
453
+ payload["provider_config"] = provider_config
454
+
455
+ if description is not None:
456
+ payload["description"] = description
457
+
458
+ if is_global is not None:
459
+ payload["global"] = is_global
460
+
461
+ if application_ids is not None:
462
+ payload["application_ids"] = application_ids
463
+
464
+ # Final validation: ensure invariants are met.
465
+ if payload["global"] is True and payload.get("application_ids"):
466
+ raise ValueError(
467
+ "An integration cannot be global and have application_ids. "
468
+ "To make an integration global, call update(is_global=True, application_ids=[])."
469
+ )
470
+ if payload["global"] is False and not payload.get("application_ids"):
471
+ raise ValueError(
472
+ "A non-global integration must have specific application IDs. "
473
+ "Provide application_ids with at least one ID, or set is_global=True."
474
+ )
475
+
476
+ response = self.client.request(
477
+ method="PUT",
478
+ endpoint=self.endpoint,
479
+ payload=payload,
480
+ )
481
+ response_dict = response.json()
482
+ response_dict["client"] = self.client
483
+ integration = self.from_dict(response_dict)
484
+
485
+ return integration
486
+
487
+
488
+ def list_integrations(client: Client) -> list[Integration]:
489
+ """
490
+ List all integrations in Nextmv Cloud for the given client.
491
+
492
+ You can import the `list_integrations` method directly from `cloud`:
493
+
494
+ ```python
495
+ from nextmv.cloud import list_integrations
496
+ ```
497
+
498
+ Parameters
499
+ ----------
500
+ client : Client
501
+ Client to use for interacting with the Nextmv Cloud API.
502
+
503
+ Returns
504
+ -------
505
+ list[Integration]
506
+ List of integrations.
507
+
508
+ Raises
509
+ ------
510
+ requests.HTTPError
511
+ If the response status code is not 2xx.
512
+
513
+ Examples
514
+ --------
515
+ >>> from nextmv.cloud import Client, list_integrations
516
+ >>> client = Client(api_key="your_api_key")
517
+ >>> integrations = list_integrations(client=client)
518
+ >>> for integration in integrations:
519
+ ... print(integration.to_dict())
520
+ """
521
+
522
+ response = client.request(
523
+ method="GET",
524
+ endpoint="v1/integrations",
525
+ )
526
+ response_dict = response.json()
527
+ integrations = []
528
+ for integration_data in response_dict.get("items", []):
529
+ integration_data["client"] = client
530
+ integration = Integration.from_dict(integration_data)
531
+ integrations.append(integration)
532
+
533
+ return integrations