Skip to content

Commit

Permalink
Replace account-id with parent-id parameter for project create/update…
Browse files Browse the repository at this point in the history
… [EH-766]

- Deprecated `account-id` parameter is removed from `project create/update`.
- `parent-id` replaces `account-id`.
- `parent-id` is made required when creating projects.

[EH-766]
  • Loading branch information
dogukancagatay committed Nov 10, 2023
1 parent 5665bce commit a3e7355
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 25 deletions.
16 changes: 16 additions & 0 deletions aiven/client/argx.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,22 @@ def __call__(


class NextReleaseDeprecationNotice(ArgumentDeprecationNotice):
"""Action class for CLI parameters that will be deprecated in the next major release.
Parameters:
deprecation_message_hint: (Optional) Shows the message when you use the parameter.
deprecation_help_hint: (Optional) Shows the message on help text.
Example Usage:
@arg(
"--account-id",
help="Account ID of the project",
action=argx.NextReleaseDeprecationNotice,
deprecation_message_hint="Please use `--parent-id` instead, which will be mandatory in the next release.",
deprecation_help_hint="Will be replaced by `--parent-id` in the next release.",
)
"""

message = "Argument `%s` is deprecated and will be removed in the next release."


Expand Down
34 changes: 12 additions & 22 deletions aiven/client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4310,14 +4310,15 @@ def _show_projects(self, projects: Sequence[dict[str, Any]], verbose: bool = Tru
layout = [["project_name", "default_cloud", "credit_card"]]
self.print_response(projects, json=getattr(self.args, "json", False), table_layout=layout)

def _resolve_parent_id(self, parent_id: str) -> str:
"""Resolves parent id to an account id, if parent id was an organization id"""

if parent_id.startswith("org"):
org_info = self.client.get_organization(parent_id)
return org_info["account_id"]
return parent_id

@arg("project_name", help="Project name")
@arg(
"--account-id",
help="Account ID of the project",
action=argx.NextReleaseDeprecationNotice,
deprecation_message_hint="Please use `--parent-id` instead, which will be mandatory in the next release.",
deprecation_help_hint="Will be replaced by `--parent-id` in the next release.",
)
@arg("--billing-group-id", help="Billing group ID of the project")
@arg.card_id
@arg.cloud
Expand Down Expand Up @@ -4349,23 +4350,12 @@ def _show_projects(self, projects: Sequence[dict[str, Any]], verbose: bool = Tru
@arg.vat_id
@arg.billing_email
@arg.tech_email
@arg.parent_id
@arg.parent_id_mandatory
def project__create(self) -> None:
"""Create a project"""

if self.args.parent_id is not None and self.args.account_id is not None:
raise argx.UserError("`--parent-id` and `--account-id` cannot be specified together.")

account_id = self.args.parent_id or self.args.account_id

# If parent id was an organization id, resolve to root account id
if account_id.startswith("org"):
org_info = self.client.get_organization(self.args.parent_id)
account_id = org_info["account_id"]

try:
project = self.client.create_project(
account_id=account_id,
account_id=self._resolve_parent_id(self.args.parent_id),
billing_address=self.args.billing_address,
billing_currency=self.args.billing_currency,
billing_extra_text=self.args.billing_extra_text,
Expand Down Expand Up @@ -4413,8 +4403,8 @@ def project__list(self) -> None:

@arg.project
@arg("--name", help="New project name")
@arg("--account-id", help="Account ID of the project")
@arg("--card-id", help="Card ID")
@arg.parent_id
@arg.cloud
@arg.country_code
@arg.billing_address
Expand All @@ -4429,7 +4419,7 @@ def project__update(self) -> None:
try:
project = self.client.update_project(
new_project_name=self.args.name,
account_id=self.args.account_id,
account_id=self._resolve_parent_id(self.args.parent_id) if self.args.parent_id else None,
billing_address=self.args.billing_address,
billing_currency=self.args.billing_currency,
billing_extra_text=self.args.billing_extra_text,
Expand Down
1 change: 1 addition & 0 deletions aiven/client/cliarg.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def wrapped(self: CommandLineTool) -> T:
arg.organization_id = arg("--organization-id", required=True, help="Organization identifier")
arg.organization_id_positional = arg("organization_id", help="Organization identifier")
arg.parent_id = arg("--parent-id", help="Organization or account identifier")
arg.parent_id_mandatory = arg("--parent-id", required=True, help="Organization or account identifier")
arg.partitions = arg("--partitions", type=int, required=True, help="Number of partitions")
arg.project = arg(
"--project",
Expand Down
5 changes: 2 additions & 3 deletions aiven/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1639,7 +1639,7 @@ def get_account_authentication_methods(self, account_id: str) -> Sequence[dict[s
def create_project(
self,
project: str,
account_id: str | None = None,
account_id: str,
billing_group_id: str | None = None,
card_id: str | None = None,
cloud: str | None = None,
Expand All @@ -1657,9 +1657,8 @@ def create_project(
"card_id": card_id,
"cloud": cloud,
"project": project,
"account_id": account_id,
}
if account_id is not None:
body["account_id"] = account_id
if billing_group_id is not None:
body["billing_group_id"] = billing_group_id
if copy_from_project is not None:
Expand Down
204 changes: 204 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import string
import uuid

EXIT_CODE_INVALID_USAGE = 2


def test_cli() -> None:
with pytest.raises(SystemExit) as excinfo:
Expand Down Expand Up @@ -1291,3 +1293,205 @@ def test_organizations_list(capsys: CaptureFixture[str]) -> None:
captured = capsys.readouterr()
assert "My Org" in captured.out
assert "business" in captured.out


def test_project_create__parent_id_required() -> None:
aiven_client = mock.Mock(spec_set=AivenClient)

with pytest.raises(SystemExit) as excinfo:
build_aiven_cli(aiven_client).run(
args=[
"project",
"create",
"new-project",
]
)
assert excinfo.value.code == EXIT_CODE_INVALID_USAGE


def test_project_create__parent_id_requested_correctly() -> None:
aiven_client = mock.Mock(spec_set=AivenClient)
account_id = "a1231231"
project_name = "new-project"

aiven_client.create_project.return_value = {
"project_id": "p123123124",
"project_name": project_name,
"default_cloud": "my-default-cloud",
"billing_currency": "USD",
"vat_id": "",
"billing_extra_text": "",
}

build_aiven_cli(aiven_client).run(
args=[
"project",
"create",
"--parent-id",
account_id,
project_name,
]
)
aiven_client.create_project.assert_called_with(
account_id=account_id,
project=project_name,
billing_address=None,
billing_currency=None,
billing_extra_text=None,
billing_group_id=None,
card_id=None,
cloud=None,
copy_from_project=None,
country_code=None,
vat_id=None,
billing_emails=None,
tech_emails=None,
use_source_project_billing_group=False,
)


def test_project_create__parent_id_as_org_id_requested_correctly() -> None:
aiven_client = mock.Mock(spec_set=AivenClient)
organization_id = "org2131231"
account_id = "a1231231"
project_name = "new-project-name"

aiven_client.get_organization.return_value = {
"organization_id": organization_id,
"organization_name": "My Org",
"account_id": account_id,
"tier": "business",
"create_time": "2023-07-13T08:00:45Z",
"update_time": "2023-07-13T08:00:45Z",
}

aiven_client.create_project.return_value = {
"project_id": "p123123124",
"project_name": project_name,
"default_cloud": "my-default-cloud",
"billing_currency": "USD",
"vat_id": "",
"billing_extra_text": "",
}

build_aiven_cli(aiven_client).run(
args=[
"project",
"create",
"--parent-id",
organization_id,
project_name,
]
)
aiven_client.create_project.assert_called_with(
account_id=account_id,
project=project_name,
billing_address=None,
billing_currency=None,
billing_extra_text=None,
billing_group_id=None,
card_id=None,
cloud=None,
copy_from_project=None,
country_code=None,
vat_id=None,
billing_emails=None,
tech_emails=None,
use_source_project_billing_group=False,
)


def test_project_update__parent_id_requested_correctly() -> None:
aiven_client = mock.Mock(spec_set=AivenClient)
account_id = "a1231231"
project_name = "my-project-name"
new_project_name = "new-project-name"

aiven_client.update_project.return_value = {
"project_id": "p123123124",
"project_name": new_project_name,
"default_cloud": "my-default-cloud",
"billing_currency": "USD",
"vat_id": "",
"billing_extra_text": "",
}

build_aiven_cli(aiven_client).run(
args=[
"project",
"update",
"--project",
project_name,
"--parent-id",
account_id,
"--name",
new_project_name,
]
)
aiven_client.update_project.assert_called_with(
new_project_name=new_project_name,
account_id=account_id,
billing_address=None,
billing_currency=None,
billing_extra_text=None,
card_id=None,
cloud=None,
country_code=None,
project=project_name,
vat_id=None,
billing_emails=None,
tech_emails=None,
)


def test_project_update__parent_id_as_org_id_requested_correctly() -> None:
aiven_client = mock.Mock(spec_set=AivenClient)
organization_id = "org2131231"
account_id = "a1231231"
project_name = "my-project-name"
new_project_name = "new-project-name"

aiven_client.get_organization.return_value = {
"organization_id": organization_id,
"organization_name": "My Org",
"account_id": account_id,
"tier": "business",
"create_time": "2023-07-13T08:00:45Z",
"update_time": "2023-07-13T08:00:45Z",
}

aiven_client.update_project.return_value = {
"project_id": "p123123124",
"project_name": new_project_name,
"default_cloud": "my-default-cloud",
"billing_currency": "USD",
"vat_id": "",
"billing_extra_text": "",
}

build_aiven_cli(aiven_client).run(
args=[
"project",
"update",
"--project",
project_name,
"--parent-id",
organization_id,
"--name",
new_project_name,
]
)
aiven_client.update_project.assert_called_with(
new_project_name=new_project_name,
account_id=account_id,
billing_address=None,
billing_currency=None,
billing_extra_text=None,
card_id=None,
cloud=None,
country_code=None,
project=project_name,
vat_id=None,
billing_emails=None,
tech_emails=None,
)

0 comments on commit a3e7355

Please sign in to comment.