nextmv 0.39.0.dev1__py3-none-any.whl → 1.0.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.
Files changed (161) hide show
  1. nextmv/__about__.py +1 -1
  2. nextmv/__entrypoint__.py +1 -2
  3. nextmv/__init__.py +2 -4
  4. nextmv/cli/CONTRIBUTING.md +583 -0
  5. nextmv/cli/cloud/__init__.py +49 -0
  6. nextmv/cli/cloud/acceptance/__init__.py +27 -0
  7. nextmv/cli/cloud/acceptance/create.py +391 -0
  8. nextmv/cli/cloud/acceptance/delete.py +64 -0
  9. nextmv/cli/cloud/acceptance/get.py +103 -0
  10. nextmv/cli/cloud/acceptance/list.py +62 -0
  11. nextmv/cli/cloud/acceptance/update.py +95 -0
  12. nextmv/cli/cloud/account/__init__.py +28 -0
  13. nextmv/cli/cloud/account/create.py +83 -0
  14. nextmv/cli/cloud/account/delete.py +59 -0
  15. nextmv/cli/cloud/account/get.py +66 -0
  16. nextmv/cli/cloud/account/update.py +70 -0
  17. nextmv/cli/cloud/app/__init__.py +35 -0
  18. nextmv/cli/cloud/app/create.py +140 -0
  19. nextmv/cli/cloud/app/delete.py +57 -0
  20. nextmv/cli/cloud/app/exists.py +44 -0
  21. nextmv/cli/cloud/app/get.py +66 -0
  22. nextmv/cli/cloud/app/list.py +61 -0
  23. nextmv/cli/cloud/app/push.py +432 -0
  24. nextmv/cli/cloud/app/update.py +124 -0
  25. nextmv/cli/cloud/batch/__init__.py +29 -0
  26. nextmv/cli/cloud/batch/create.py +452 -0
  27. nextmv/cli/cloud/batch/delete.py +64 -0
  28. nextmv/cli/cloud/batch/get.py +104 -0
  29. nextmv/cli/cloud/batch/list.py +63 -0
  30. nextmv/cli/cloud/batch/metadata.py +66 -0
  31. nextmv/cli/cloud/batch/update.py +95 -0
  32. nextmv/cli/cloud/data/__init__.py +26 -0
  33. nextmv/cli/cloud/data/upload.py +162 -0
  34. nextmv/cli/cloud/ensemble/__init__.py +33 -0
  35. nextmv/cli/cloud/ensemble/create.py +413 -0
  36. nextmv/cli/cloud/ensemble/delete.py +63 -0
  37. nextmv/cli/cloud/ensemble/get.py +65 -0
  38. nextmv/cli/cloud/ensemble/list.py +63 -0
  39. nextmv/cli/cloud/ensemble/update.py +103 -0
  40. nextmv/cli/cloud/input_set/__init__.py +32 -0
  41. nextmv/cli/cloud/input_set/create.py +168 -0
  42. nextmv/cli/cloud/input_set/delete.py +64 -0
  43. nextmv/cli/cloud/input_set/get.py +63 -0
  44. nextmv/cli/cloud/input_set/list.py +63 -0
  45. nextmv/cli/cloud/input_set/update.py +123 -0
  46. nextmv/cli/cloud/instance/__init__.py +35 -0
  47. nextmv/cli/cloud/instance/create.py +289 -0
  48. nextmv/cli/cloud/instance/delete.py +61 -0
  49. nextmv/cli/cloud/instance/exists.py +39 -0
  50. nextmv/cli/cloud/instance/get.py +62 -0
  51. nextmv/cli/cloud/instance/list.py +60 -0
  52. nextmv/cli/cloud/instance/update.py +216 -0
  53. nextmv/cli/cloud/managed_input/__init__.py +31 -0
  54. nextmv/cli/cloud/managed_input/create.py +144 -0
  55. nextmv/cli/cloud/managed_input/delete.py +64 -0
  56. nextmv/cli/cloud/managed_input/get.py +63 -0
  57. nextmv/cli/cloud/managed_input/list.py +60 -0
  58. nextmv/cli/cloud/managed_input/update.py +97 -0
  59. nextmv/cli/cloud/run/__init__.py +37 -0
  60. nextmv/cli/cloud/run/cancel.py +37 -0
  61. nextmv/cli/cloud/run/create.py +524 -0
  62. nextmv/cli/cloud/run/get.py +199 -0
  63. nextmv/cli/cloud/run/input.py +86 -0
  64. nextmv/cli/cloud/run/list.py +80 -0
  65. nextmv/cli/cloud/run/logs.py +166 -0
  66. nextmv/cli/cloud/run/metadata.py +67 -0
  67. nextmv/cli/cloud/run/track.py +500 -0
  68. nextmv/cli/cloud/scenario/__init__.py +29 -0
  69. nextmv/cli/cloud/scenario/create.py +451 -0
  70. nextmv/cli/cloud/scenario/delete.py +61 -0
  71. nextmv/cli/cloud/scenario/get.py +102 -0
  72. nextmv/cli/cloud/scenario/list.py +63 -0
  73. nextmv/cli/cloud/scenario/metadata.py +67 -0
  74. nextmv/cli/cloud/scenario/update.py +93 -0
  75. nextmv/cli/cloud/secrets/__init__.py +33 -0
  76. nextmv/cli/cloud/secrets/create.py +206 -0
  77. nextmv/cli/cloud/secrets/delete.py +63 -0
  78. nextmv/cli/cloud/secrets/get.py +66 -0
  79. nextmv/cli/cloud/secrets/list.py +60 -0
  80. nextmv/cli/cloud/secrets/update.py +144 -0
  81. nextmv/cli/cloud/shadow/__init__.py +33 -0
  82. nextmv/cli/cloud/shadow/create.py +184 -0
  83. nextmv/cli/cloud/shadow/delete.py +64 -0
  84. nextmv/cli/cloud/shadow/get.py +61 -0
  85. nextmv/cli/cloud/shadow/list.py +63 -0
  86. nextmv/cli/cloud/shadow/metadata.py +66 -0
  87. nextmv/cli/cloud/shadow/start.py +43 -0
  88. nextmv/cli/cloud/shadow/stop.py +53 -0
  89. nextmv/cli/cloud/shadow/update.py +96 -0
  90. nextmv/cli/cloud/switchback/__init__.py +33 -0
  91. nextmv/cli/cloud/switchback/create.py +151 -0
  92. nextmv/cli/cloud/switchback/delete.py +64 -0
  93. nextmv/cli/cloud/switchback/get.py +62 -0
  94. nextmv/cli/cloud/switchback/list.py +63 -0
  95. nextmv/cli/cloud/switchback/metadata.py +68 -0
  96. nextmv/cli/cloud/switchback/start.py +43 -0
  97. nextmv/cli/cloud/switchback/stop.py +53 -0
  98. nextmv/cli/cloud/switchback/update.py +96 -0
  99. nextmv/cli/cloud/upload/__init__.py +22 -0
  100. nextmv/cli/cloud/upload/create.py +39 -0
  101. nextmv/cli/cloud/version/__init__.py +33 -0
  102. nextmv/cli/cloud/version/create.py +96 -0
  103. nextmv/cli/cloud/version/delete.py +61 -0
  104. nextmv/cli/cloud/version/exists.py +39 -0
  105. nextmv/cli/cloud/version/get.py +62 -0
  106. nextmv/cli/cloud/version/list.py +60 -0
  107. nextmv/cli/cloud/version/update.py +92 -0
  108. nextmv/cli/community/__init__.py +24 -0
  109. nextmv/cli/community/clone.py +86 -0
  110. nextmv/cli/community/list.py +200 -0
  111. nextmv/cli/configuration/__init__.py +23 -0
  112. nextmv/cli/configuration/config.py +228 -0
  113. nextmv/cli/configuration/create.py +94 -0
  114. nextmv/cli/configuration/delete.py +67 -0
  115. nextmv/cli/configuration/list.py +77 -0
  116. nextmv/cli/confirm.py +34 -0
  117. nextmv/cli/main.py +161 -3
  118. nextmv/cli/message.py +170 -0
  119. nextmv/cli/options.py +220 -0
  120. nextmv/cli/version.py +22 -2
  121. nextmv/cloud/__init__.py +17 -38
  122. nextmv/cloud/acceptance_test.py +20 -83
  123. nextmv/cloud/account.py +269 -30
  124. nextmv/cloud/application/__init__.py +898 -0
  125. nextmv/cloud/application/_acceptance.py +424 -0
  126. nextmv/cloud/application/_batch_scenario.py +845 -0
  127. nextmv/cloud/application/_ensemble.py +251 -0
  128. nextmv/cloud/application/_input_set.py +263 -0
  129. nextmv/cloud/application/_instance.py +289 -0
  130. nextmv/cloud/application/_managed_input.py +227 -0
  131. nextmv/cloud/application/_run.py +1393 -0
  132. nextmv/cloud/application/_secrets.py +294 -0
  133. nextmv/cloud/application/_shadow.py +320 -0
  134. nextmv/cloud/application/_switchback.py +332 -0
  135. nextmv/cloud/application/_utils.py +54 -0
  136. nextmv/cloud/application/_version.py +304 -0
  137. nextmv/cloud/batch_experiment.py +6 -2
  138. nextmv/cloud/community.py +446 -0
  139. nextmv/cloud/instance.py +11 -1
  140. nextmv/cloud/integration.py +8 -5
  141. nextmv/cloud/package.py +50 -9
  142. nextmv/cloud/shadow.py +254 -0
  143. nextmv/cloud/switchback.py +228 -0
  144. nextmv/deprecated.py +5 -3
  145. nextmv/input.py +20 -88
  146. nextmv/local/application.py +3 -15
  147. nextmv/local/runner.py +1 -1
  148. nextmv/model.py +50 -11
  149. nextmv/options.py +11 -256
  150. nextmv/output.py +0 -62
  151. nextmv/polling.py +54 -16
  152. nextmv/run.py +84 -37
  153. nextmv/status.py +1 -51
  154. {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/METADATA +37 -11
  155. nextmv-1.0.0.dist-info/RECORD +185 -0
  156. nextmv-1.0.0.dist-info/entry_points.txt +2 -0
  157. nextmv/cloud/application.py +0 -4204
  158. nextmv-0.39.0.dev1.dist-info/RECORD +0 -55
  159. nextmv-0.39.0.dev1.dist-info/entry_points.txt +0 -2
  160. {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/WHEEL +0 -0
  161. {nextmv-0.39.0.dev1.dist-info → nextmv-1.0.0.dist-info}/licenses/LICENSE +0 -0
nextmv/cli/version.py CHANGED
@@ -6,7 +6,7 @@ import typer
6
6
 
7
7
  from nextmv.__about__ import __version__
8
8
 
9
- # Version subcommand application.
9
+ # Set up subcommand application.
10
10
  app = typer.Typer()
11
11
 
12
12
 
@@ -14,5 +14,25 @@ app = typer.Typer()
14
14
  def version() -> None:
15
15
  """
16
16
  Show the current version of the Nextmv CLI.
17
+
18
+ [bold][underline]Examples[/underline][/bold]
19
+
20
+ - Show the version.
21
+ $ [dim]nextmv version[/dim]
22
+ """
23
+
24
+ version_callback(True)
25
+
26
+
27
+ def version_callback(value: bool):
28
+ """
29
+ Callback function to display the version.
30
+
31
+ Parameters
32
+ ----------
33
+ value : bool
34
+ If True, print the version and exit.
17
35
  """
18
- print(__version__)
36
+ if value:
37
+ print(__version__)
38
+ raise typer.Exit()
nextmv/cloud/__init__.py CHANGED
@@ -1,41 +1,5 @@
1
1
  """Functionality for interacting with the Nextmv Cloud."""
2
2
 
3
- # These imports are kept for backwards compatibility but the preferred import path is
4
- # from nextmv directly. These imports will be removed in a future release.
5
- from nextmv.manifest import MANIFEST_FILE_NAME as MANIFEST_FILE_NAME
6
- from nextmv.manifest import Manifest as Manifest
7
- from nextmv.manifest import ManifestBuild as ManifestBuild
8
- from nextmv.manifest import ManifestContent as ManifestContent
9
- from nextmv.manifest import ManifestContentMultiFile as ManifestContentMultiFile
10
- from nextmv.manifest import ManifestContentMultiFileInput as ManifestContentMultiFileInput
11
- from nextmv.manifest import ManifestContentMultiFileOutput as ManifestContentMultiFileOutput
12
- from nextmv.manifest import ManifestOption as ManifestOption
13
- from nextmv.manifest import ManifestPython as ManifestPython
14
- from nextmv.manifest import ManifestPythonModel as ManifestPythonModel
15
- from nextmv.manifest import ManifestRuntime as ManifestRuntime
16
- from nextmv.manifest import ManifestType as ManifestType
17
- from nextmv.polling import PollingOptions as PollingOptions
18
- from nextmv.run import ErrorLog as ErrorLog
19
- from nextmv.run import ExternalRunResult as ExternalRunResult
20
- from nextmv.run import Format as Format
21
- from nextmv.run import FormatInput as FormatInput
22
- from nextmv.run import FormatOutput as FormatOutput
23
- from nextmv.run import Metadata as Metadata
24
- from nextmv.run import RunConfiguration as RunConfiguration
25
- from nextmv.run import RunInformation as RunInformation
26
- from nextmv.run import RunLog as RunLog
27
- from nextmv.run import RunQueuing as RunQueuing
28
- from nextmv.run import RunResult as RunResult
29
- from nextmv.run import RunType as RunType
30
- from nextmv.run import RunTypeConfiguration as RunTypeConfiguration
31
- from nextmv.run import TrackedRun as TrackedRun
32
- from nextmv.run import TrackedRunStatus as TrackedRunStatus
33
- from nextmv.run import run_duration as run_duration
34
- from nextmv.safe import safe_id as safe_id
35
- from nextmv.safe import safe_name_and_id as safe_name_and_id
36
- from nextmv.status import Status as Status
37
- from nextmv.status import StatusV2 as StatusV2
38
-
39
3
  from .acceptance_test import AcceptanceTest as AcceptanceTest
40
4
  from .acceptance_test import AcceptanceTestResults as AcceptanceTestResults
41
5
  from .acceptance_test import Comparison as Comparison
@@ -51,12 +15,13 @@ from .acceptance_test import MetricToleranceType as MetricToleranceType
51
15
  from .acceptance_test import MetricType as MetricType
52
16
  from .acceptance_test import ResultStatistics as ResultStatistics
53
17
  from .acceptance_test import StatisticType as StatisticType
54
- from .acceptance_test import ToleranceType as ToleranceType
55
18
  from .account import Account as Account
19
+ from .account import AccountMember as AccountMember
56
20
  from .account import Queue as Queue
57
21
  from .account import QueuedRun as QueuedRun
58
22
  from .application import Application as Application
59
- from .application import poll as poll
23
+ from .application import ApplicationType as ApplicationType
24
+ from .application import list_applications as list_applications
60
25
  from .assets import RunAsset as RunAsset
61
26
  from .batch_experiment import BatchExperiment as BatchExperiment
62
27
  from .batch_experiment import BatchExperimentInformation as BatchExperimentInformation
@@ -65,6 +30,9 @@ from .batch_experiment import BatchExperimentRun as BatchExperimentRun
65
30
  from .batch_experiment import ExperimentStatus as ExperimentStatus
66
31
  from .client import Client as Client
67
32
  from .client import get_size as get_size
33
+ from .community import CommunityApp as CommunityApp
34
+ from .community import clone_community_app as clone_community_app
35
+ from .community import list_community_apps as list_community_apps
68
36
  from .ensemble import EnsembleDefinition as EnsembleDefinition
69
37
  from .ensemble import EvaluationRule as EvaluationRule
70
38
  from .ensemble import RuleObjective as RuleObjective
@@ -87,6 +55,17 @@ from .secrets import Secret as Secret
87
55
  from .secrets import SecretsCollection as SecretsCollection
88
56
  from .secrets import SecretsCollectionSummary as SecretsCollectionSummary
89
57
  from .secrets import SecretType as SecretType
58
+ from .shadow import ShadowTest as ShadowTest
59
+ from .shadow import ShadowTestMetadata as ShadowTestMetadata
60
+ from .shadow import StartEvents as StartEvents
61
+ from .shadow import StopIntent as StopIntent
62
+ from .shadow import TerminationEvents as TerminationEvents
63
+ from .shadow import TestComparison as TestComparison
64
+ from .switchback import SwitchbackPlan as SwitchbackPlan
65
+ from .switchback import SwitchbackPlanUnit as SwitchbackPlanUnit
66
+ from .switchback import SwitchbackTest as SwitchbackTest
67
+ from .switchback import SwitchbackTestMetadata as SwitchbackTestMetadata
68
+ from .switchback import TestComparisonSingle as TestComparisonSingle
90
69
  from .url import DownloadURL as DownloadURL
91
70
  from .url import UploadURL as UploadURL
92
71
  from .version import Version as Version
@@ -13,7 +13,7 @@ StatisticType : Enum
13
13
  Type of statistical process for collapsing multiple values of a metric.
14
14
  Comparison : Enum
15
15
  Comparison operators to use for comparing two metrics.
16
- ToleranceType : Enum
16
+ MetricToleranceType : Enum
17
17
  Type of tolerance used for a metric.
18
18
  ExperimentStatus : Enum
19
19
  Status of an acceptance test experiment.
@@ -46,7 +46,6 @@ from enum import Enum
46
46
 
47
47
  from nextmv.base_model import BaseModel
48
48
  from nextmv.cloud.batch_experiment import ExperimentStatus
49
- from nextmv.deprecated import deprecated
50
49
 
51
50
 
52
51
  class MetricType(str, Enum):
@@ -212,69 +211,6 @@ class Comparison(str, Enum):
212
211
  """Not equal to metric type."""
213
212
 
214
213
 
215
- class ToleranceType(str, Enum):
216
- """
217
- !!! warning
218
- `ToleranceType` is deprecated, use `MetricToleranceType` instead.
219
-
220
- Type of tolerance used for a metric.
221
-
222
- You can import the `ToleranceType` class directly from `cloud`:
223
-
224
- ```python
225
- from nextmv.cloud import ToleranceType
226
- ```
227
-
228
- This enumeration defines the different types of tolerances that can be used
229
- when comparing metrics in acceptance tests.
230
-
231
- Attributes
232
- ----------
233
- undefined : str
234
- Undefined tolerance type (empty string).
235
- absolute : str
236
- Absolute tolerance type, using a fixed value.
237
- relative : str
238
- Relative tolerance type, using a percentage.
239
-
240
- Examples
241
- --------
242
- >>> from nextmv.cloud import ToleranceType
243
- >>> tol_type = ToleranceType.absolute
244
- >>> tol_type
245
- <ToleranceType.absolute: 'absolute'>
246
- """
247
-
248
- undefined = ""
249
- """ToleranceType is deprecated, please use MetricToleranceType instead.
250
- Undefined tolerance type."""
251
- absolute = "absolute"
252
- """ToleranceType is deprecated, please use MetricToleranceType instead.
253
- Absolute tolerance type."""
254
- relative = "relative"
255
- """ToleranceType is deprecated, please use MetricToleranceType instead.
256
- Relative tolerance type."""
257
-
258
-
259
- # Override __getattribute__ to emit deprecation warnings when enum values are accessed
260
- _original_getattribute = ToleranceType.__class__.__getattribute__
261
-
262
-
263
- def _deprecated_getattribute(cls, name: str):
264
- # Only emit deprecation warning if this is specifically the ToleranceType class
265
- if cls is ToleranceType and name in ("undefined", "absolute", "relative"):
266
- deprecated(
267
- f"ToleranceType.{name}",
268
- "ToleranceType is deprecated and will be removed in a future version. "
269
- "Please use MetricToleranceType instead",
270
- )
271
-
272
- return _original_getattribute(cls, name)
273
-
274
-
275
- ToleranceType.__class__.__getattribute__ = _deprecated_getattribute
276
-
277
-
278
214
  class MetricToleranceType(str, Enum):
279
215
  """
280
216
  Type of tolerance used for a metric.
@@ -888,20 +824,20 @@ class AcceptanceTest(BaseModel):
888
824
  Name of the acceptance test.
889
825
  description : str
890
826
  Description of the acceptance test.
891
- app_id : str
827
+ created_at : datetime
828
+ Creation date of the acceptance test.
829
+ updated_at : datetime
830
+ Last update date of the acceptance test.
831
+ app_id : str, optional
892
832
  ID of the app that owns the acceptance test.
893
- experiment_id : str
833
+ experiment_id : str, optional
894
834
  ID of the batch experiment underlying the acceptance test.
895
- control : ComparisonInstance
835
+ control : ComparisonInstance, optional
896
836
  Control instance of the acceptance test.
897
- candidate : ComparisonInstance
837
+ candidate : ComparisonInstance, optional
898
838
  Candidate instance of the acceptance test.
899
- metrics : list[Metric]
839
+ metrics : list[Metric], optional
900
840
  Metrics to evaluate in the acceptance test.
901
- created_at : datetime
902
- Creation date of the acceptance test.
903
- updated_at : datetime
904
- Last update date of the acceptance test.
905
841
  status : ExperimentStatus, optional
906
842
  Status of the acceptance test.
907
843
  results : AcceptanceTestResults, optional
@@ -942,20 +878,21 @@ class AcceptanceTest(BaseModel):
942
878
  """Name of the acceptance test."""
943
879
  description: str
944
880
  """Description of the acceptance test."""
945
- app_id: str
881
+ created_at: datetime
882
+ """Creation date of the acceptance test."""
883
+ updated_at: datetime
884
+ """Last update date of the acceptance test."""
885
+
886
+ app_id: str | None = None
946
887
  """ID of the app that owns the acceptance test."""
947
- experiment_id: str
888
+ experiment_id: str | None = None
948
889
  """ID of the batch experiment underlying in the acceptance test."""
949
- control: ComparisonInstance
890
+ control: ComparisonInstance | None = None
950
891
  """Control instance of the acceptance test."""
951
- candidate: ComparisonInstance
892
+ candidate: ComparisonInstance | None = None
952
893
  """Candidate instance of the acceptance test."""
953
- metrics: list[Metric]
894
+ metrics: list[Metric] | None = None
954
895
  """Metrics of the acceptance test."""
955
- created_at: datetime
956
- """Creation date of the acceptance test."""
957
- updated_at: datetime
958
- """Last update date of the acceptance test."""
959
896
  status: ExperimentStatus | None = ExperimentStatus.UNKNOWN
960
897
  """Status of the acceptance test."""
961
898
  results: AcceptanceTestResults | None = None
nextmv/cloud/account.py CHANGED
@@ -15,16 +15,18 @@ Account
15
15
  The Nextmv Platform account with API access methods.
16
16
  """
17
17
 
18
- from dataclasses import dataclass
19
18
  from datetime import datetime
20
19
 
20
+ from pydantic import AliasChoices, Field
21
+
21
22
  from nextmv.base_model import BaseModel
22
23
  from nextmv.cloud.client import Client
23
- from nextmv.status import Status, StatusV2
24
+ from nextmv.status import StatusV2
24
25
 
25
26
 
26
27
  class QueuedRun(BaseModel):
27
- """A run that is pending to be executed in the account.
28
+ """
29
+ A run that is pending to be executed in the account.
28
30
 
29
31
  You can import the `QueuedRun` class directly from `cloud`:
30
32
 
@@ -55,8 +57,6 @@ class QueuedRun(BaseModel):
55
57
  ID of the application version used for the run.
56
58
  execution_class : str
57
59
  Execution class used for the run.
58
- status : Status
59
- Deprecated: use status_v2.
60
60
  status_v2 : StatusV2
61
61
  Status of the run.
62
62
 
@@ -72,7 +72,6 @@ class QueuedRun(BaseModel):
72
72
  ... "application_instance_id": "appins-123456",
73
73
  ... "application_version_id": "appver-123456",
74
74
  ... "execution_class": "standard",
75
- ... "status": "RUNNING",
76
75
  ... "status_v2": "RUNNING"
77
76
  ... })
78
77
  >>> print(queued_run.name)
@@ -97,14 +96,13 @@ class QueuedRun(BaseModel):
97
96
  """ID of the application version used for the run."""
98
97
  execution_class: str
99
98
  """Execution class used for the run."""
100
- status: Status
101
- """Deprecated: use status_v2."""
102
99
  status_v2: StatusV2
103
100
  """Status of the run."""
104
101
 
105
102
 
106
103
  class Queue(BaseModel):
107
- """A queue is a list of runs that are pending to be executed, or currently
104
+ """
105
+ A queue is a list of runs that are pending to be executed, or currently
108
106
  being executed, in the account.
109
107
 
110
108
  You can import the `Queue` class directly from `cloud`:
@@ -123,7 +121,9 @@ class Queue(BaseModel):
123
121
 
124
122
  Examples
125
123
  --------
126
- >>> account = Account(client=Client(api_key="your-api-key"))
124
+ >>> from nextmv.cloud import Client, Account
125
+ >>> client = Client(api_key="your-api-key")
126
+ >>> account = Account.get(client=client, account_id="your-account-id")
127
127
  >>> queue = account.queue()
128
128
  >>> print(f"Number of runs in queue: {len(queue.runs)}")
129
129
  Number of runs in queue: 5
@@ -137,9 +137,54 @@ class Queue(BaseModel):
137
137
  """List of runs in the queue."""
138
138
 
139
139
 
140
- @dataclass
141
- class Account:
142
- """The Nextmv Platform account.
140
+ class AccountMember(BaseModel):
141
+ """
142
+ A member of a Nextmv Cloud account (organization).
143
+
144
+ You can import the `AccountMember` class directly from `cloud`:
145
+
146
+ ```python
147
+ from nextmv.cloud import AccountMember
148
+ ```
149
+
150
+ Represents an individual member of an organization in Nextmv Cloud,
151
+ including their role and invitation status.
152
+
153
+ Attributes
154
+ ----------
155
+ email : str | None
156
+ Email of the account member.
157
+ role : str | None
158
+ Role of the account member.
159
+ pending_invite : bool | None
160
+ Whether the member has a pending invite.
161
+
162
+ Examples
163
+ --------
164
+ >>> member = AccountMember.from_dict({
165
+ ... "email": "peter.rabbit@carrotexpress.com",
166
+ ... "role": "admin",
167
+ ... "pending_invite": False
168
+ ... })
169
+ >>> print(f"{member.email} - {member.role}")
170
+ peter.rabbit@carrotexpress.com - admin
171
+ """
172
+
173
+ email: str | None = None
174
+ """Email of the account member."""
175
+ role: str | None = None
176
+ """Role of the account member."""
177
+ pending_invite: bool | None = None
178
+ """Whether the member has a pending invite."""
179
+
180
+
181
+ class Account(BaseModel):
182
+ """
183
+ The Nextmv Cloud account (organization).
184
+
185
+ To handle managed accounts, SSO must be configured for your organization.
186
+ Please contact [Nextmv support](https://www.nextmv.io/contact) for
187
+ assistance.
143
188
 
144
189
  You can import the `Account` class directly from `cloud`:
145
190
 
@@ -147,41 +192,191 @@ class Account:
147
192
  from nextmv.cloud import Account
148
193
  ```
149
194
 
150
- This class provides access to account-level operations in the Nextmv Platform,
195
+ This class provides access to account-level operations in the Nextmv Cloud,
151
196
  such as retrieving the queue of runs.
152
197
 
153
- Parameters
154
- ----------
155
- client : Client
156
- Client to use for interacting with the Nextmv Cloud API.
157
- endpoint : str, optional
158
- Base endpoint for the account, by default "v1/account"
198
+ Note: It is recommended to use `Account.get()` or `Account.new()`
199
+ instead of direct initialization to ensure proper setup.
159
200
 
160
- Attributes
201
+ Parameters
161
202
  ----------
162
203
  client : Client
163
204
  Client to use for interacting with the Nextmv Cloud API.
164
- endpoint : str
165
- Base endpoint for the account.
205
+ account_id : str, optional
206
+ ID of the account (organization).
207
+ name : str, optional
208
+ Name of the account (organization).
209
+ members : list[AccountMember], optional
210
+ List of members in the account (organization).
211
+ account_endpoint : str, default="v1/account"
212
+ Base endpoint for the account (SDK-specific).
213
+ organization_endpoint : str, default="v1/organization/{organization_id}"
214
+ Base endpoint for organization operations (SDK-specific).
166
215
 
167
216
  Examples
168
217
  --------
169
218
  >>> from nextmv.cloud import Client, Account
170
219
  >>> client = Client(api_key="your-api-key")
171
- >>> account = Account(client=client)
220
+ >>> # Retrieve an existing account
221
+ >>> account = Account.get(client=client, account_id="your-account-id")
222
+ >>> print(f"Account name: {account.name}")
223
+ Account name: Bunny Logistics
224
+ >>> # Create a new account
225
+ >>> new_account = Account.new(client=client, name="Hare Delivery Co", admins=["admin@example.com"])
226
+ >>> # Get the queue of runs
172
227
  >>> queue = account.queue()
173
228
  >>> print(f"Number of runs in queue: {len(queue.runs)}")
174
229
  Number of runs in queue: 3
175
230
  """
176
231
 
177
- client: Client
232
+ # Actual API attributes of an account.
233
+ account_id: str | None = Field(
234
+ serialization_alias="id",
235
+ validation_alias=AliasChoices("id", "account_id"),
236
+ default=None,
237
+ )
238
+ """ID of the account (organization)."""
239
+ name: str | None = None
240
+ """Name of the account (organization)."""
241
+ members: list[AccountMember] | None = None
242
+ """List of members in the account (organization)."""
243
+
244
+ # SDK-specific attributes for convenience when using methods.
245
+ client: Client = Field(exclude=True)
178
246
  """Client to use for interacting with the Nextmv Cloud API."""
179
-
180
- endpoint: str = "v1/account"
247
+ account_endpoint: str = Field(exclude=True, default="v1/account")
181
248
  """Base endpoint for the account."""
249
+ organization_endpoint: str = Field(exclude=True, default="v1/organization/{organization_id}")
250
+
251
+ def model_post_init(self, __context) -> None:
252
+ """
253
+ Initialize the organization_endpoint attribute.
254
+
255
+ This method is automatically called after class initialization to
256
+ format the organization_endpoint URL with the account ID.
257
+ """
258
+
259
+ self.organization_endpoint = self.organization_endpoint.format(organization_id=self.account_id)
260
+
261
+ @classmethod
262
+ def get(cls, client: Client, account_id: str) -> "Account":
263
+ """
264
+ Retrieve an account directly from Nextmv Cloud.
265
+
266
+ This function is useful if you want to populate an `Account` class
267
+ by fetching the attributes directly from Nextmv Cloud.
268
+
269
+ Parameters
270
+ ----------
271
+ client : Client
272
+ Client to use for interacting with the Nextmv Cloud API.
273
+ account_id : str
274
+ ID of the account to retrieve.
275
+
276
+ Returns
277
+ -------
278
+ Account
279
+ The requested account.
280
+
281
+ Raises
282
+ ------
283
+ requests.HTTPError
284
+ If the response status code is not 2xx.
285
+
286
+ Examples
287
+ --------
288
+ >>> from nextmv.cloud import Client, Account
289
+ >>> client = Client(api_key="your-api-key")
290
+ >>> account = Account.get(client=client, account_id="bunny-logistics")
291
+ >>> print(f"Account: {account.name}")
292
+ Account: Bunny Logistics
293
+ >>> print(f"Members: {len(account.members)}")
294
+ Members: 3
295
+ """
296
+
297
+ response = client.request(
298
+ method="GET",
299
+ endpoint=f"v1/organization/{account_id}",
300
+ )
301
+
302
+ return cls.from_dict({"client": client} | response.json())
303
+
304
+ @classmethod
305
+ def new(
306
+ cls,
307
+ client: Client,
308
+ name: str,
309
+ admins: list[str],
310
+ ) -> "Account":
311
+ """
312
+ Create a new account (organization) directly in Nextmv Cloud.
313
+
314
+ To create managed accounts, SSO must be configured for your
315
+ organization. Please contact [Nextmv
316
+ support](https://www.nextmv.io/contact) for assistance.
317
+
318
+ Parameters
319
+ ----------
320
+ client : Client
321
+ Client to use for interacting with the Nextmv Cloud API.
322
+ name : str
323
+ Name of the new account.
324
+ admins : list[str]
325
+ List of admin user emails for the new account.
326
+
327
+ Returns
328
+ -------
329
+ Account
330
+ The newly created account.
331
+
332
+ Examples
333
+ --------
334
+ >>> from nextmv.cloud import Client
335
+ >>> client = Client(api_key="your-api-key")
336
+ >>> account = Account.new(client=client, name="My New Account", admins=["admin@example.com"])
337
+ """
338
+
339
+ if len(admins) == 0:
340
+ raise ValueError("at least one admin email must be provided to create an account")
341
+
342
+ payload = {
343
+ "name": name,
344
+ "admins": admins,
345
+ }
346
+
347
+ response = client.request(
348
+ method="POST",
349
+ endpoint="v1/organization",
350
+ payload=payload,
351
+ )
352
+
353
+ return cls.from_dict({"client": client} | response.json())
354
+
355
+ def delete(self) -> None:
356
+ """
357
+ Delete the account.
358
+
359
+ Permanently removes the account (organization) from Nextmv Cloud. You
360
+ must have the administrator role on that account in order to delete it.
361
+
362
+ Raises
363
+ ------
364
+ requests.HTTPError
365
+ If the response status code is not 2xx.
366
+
367
+ Examples
368
+ --------
369
+ >>> account.delete() # Permanently deletes the account
370
+ """
371
+
372
+ _ = self.client.request(
373
+ method="DELETE",
374
+ endpoint=self.organization_endpoint,
375
+ )
182
376
 
183
377
  def queue(self) -> Queue:
184
- """Get the queue of runs in the account.
378
+ """
379
+ Get the queue of runs in the account.
185
380
 
186
381
  Retrieves the current list of runs that are pending or being executed
187
382
  in the Nextmv account.
@@ -198,7 +393,9 @@ class Account:
198
393
 
199
394
  Examples
200
395
  --------
201
- >>> account = Account(client=Client(api_key="your-api-key"))
396
+ >>> from nextmv.cloud import Client, Account
397
+ >>> client = Client(api_key="your-api-key")
398
+ >>> account = Account.get(client=client, account_id="your-account-id")
202
399
  >>> queue = account.queue()
203
400
  >>> for run in queue.runs:
204
401
  ... print(f"Run {run.id}: {run.name} - Status: {run.status_v2}")
@@ -207,7 +404,49 @@ class Account:
207
404
  """
208
405
  response = self.client.request(
209
406
  method="GET",
210
- endpoint=self.endpoint + "/queue",
407
+ endpoint=self.account_endpoint + "/queue",
211
408
  )
212
409
 
213
410
  return Queue.from_dict(response.json())
411
+
412
+ def update(self, name: str) -> "Account":
413
+ """
414
+ Update the account.
415
+
416
+ Parameters
417
+ ----------
418
+ name : str
419
+ Name of the account.
420
+
421
+ Returns
422
+ -------
423
+ Account
424
+ The updated account.
425
+
426
+ Raises
427
+ ------
428
+ requests.HTTPError
429
+ If the response status code is not 2xx.
430
+
431
+ Examples
432
+ --------
433
+ >>> from nextmv.cloud import Client, Account
434
+ >>> client = Client(api_key="your-api-key")
435
+ >>> account = Account.get(client=client, account_id="bunny-logistics")
436
+ >>> updated_account = account.update(name="Bunny Express Logistics")
437
+ >>> print(updated_account.name)
438
+ Bunny Express Logistics
439
+ """
440
+
441
+ account = self.get(client=self.client, account_id=self.account_id)
442
+ account_dict = account.to_dict()
443
+ payload = account_dict.copy()
444
+ payload["name"] = name
445
+
446
+ response = self.client.request(
447
+ method="PUT",
448
+ endpoint=self.organization_endpoint,
449
+ payload=payload,
450
+ )
451
+
452
+ return Account.from_dict({"client": self.client} | response.json())