Skip to content

Commit

Permalink
Merge pull request #371 from aiven/dogukancagatay/EH-766/prepare-for-…
Browse files Browse the repository at this point in the history
…v4-release

Remove/update parameters for project and card commands
  • Loading branch information
ngilles-aiven authored Nov 10, 2023
2 parents e2f7b94 + 94328e1 commit 6a220e2
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 204 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ Pipfile*
/rpms/
__pycache__/
.mypy_cache/
.pytest_cache/
.ruff_cache/
.vscode
venv
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
144 changes: 12 additions & 132 deletions aiven/client/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4310,16 +4310,16 @@ 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
@arg(
"--no-fail-if-exists",
Expand All @@ -4342,41 +4342,17 @@ def _show_projects(self, projects: Sequence[dict[str, Any]], verbose: bool = Tru
"used by source project instead of creating a new one"
),
)
@arg.country_code
@arg.billing_address
@arg.billing_extra_text
@arg.billing_currency
@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,
billing_address=self.args.billing_address,
billing_currency=self.args.billing_currency,
billing_extra_text=self.args.billing_extra_text,
account_id=self._resolve_parent_id(self.args.parent_id),
billing_group_id=self.args.billing_group_id,
card_id=self.args.card_id,
cloud=self.args.cloud,
copy_from_project=self.args.copy_from_project,
country_code=self.args.country_code,
project=self.args.project_name,
vat_id=self.args.vat_id,
billing_emails=self.args.billing_email,
tech_emails=self.args.tech_email,
use_source_project_billing_group=self.args.use_source_project_billing_group,
)
Expand Down Expand Up @@ -4413,32 +4389,18 @@ 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
@arg.billing_extra_text
@arg.billing_currency
@arg.vat_id
@arg.billing_email
@arg.tech_email
def project__update(self) -> None:
"""Update a project"""
project_name = self.get_project()
try:
project = self.client.update_project(
new_project_name=self.args.name,
account_id=self.args.account_id,
billing_address=self.args.billing_address,
billing_currency=self.args.billing_currency,
billing_extra_text=self.args.billing_extra_text,
card_id=self.args.card_id,
account_id=self._resolve_parent_id(self.args.parent_id) if self.args.parent_id else None,
cloud=self.args.cloud,
country_code=self.args.country_code,
project=project_name,
vat_id=self.args.vat_id,
billing_emails=self.args.billing_email,
tech_emails=self.args.tech_email,
)
except client.Error as ex:
Expand Down Expand Up @@ -4882,88 +4844,6 @@ def pre_run(self, func: Callable[[], int | None]) -> None:
elif not getattr(func, "optional_auth", False):
raise argx.UserError("not authenticated: please login first with 'avn user login'")

@arg.json
def card__list(self) -> None:
"""List credit cards"""
layout = [["card_id", "name", "country", "exp_year", "exp_month", "last4"]]
self.print_response(self.client.get_cards(), json=self.args.json, table_layout=layout)

def _card_get_stripe_token(
self, stripe_publishable_key: str, name: str, number: str, exp_month: int, exp_year: int, cvc: str
) -> str:
data = {
"card[name]": name,
"card[number]": number,
"card[exp_month]": exp_month,
"card[exp_year]": exp_year,
"card[cvc]": cvc,
"key": stripe_publishable_key,
}
response = requests.post("https://api.stripe.com/v1/tokens", data=data, timeout=30)
if not response.ok:
print(response.text)
response.raise_for_status()
return response.json()["id"]

@arg.json
@arg("--cvc", help="Credit card security code", required=True)
@arg("--exp-month", help="Card expiration month (1-12)", type=int, required=True)
@arg("--exp-year", help="Card expiration year", type=int, required=True)
@arg("--name", help="Name on card", required=True)
@arg("--number", help="Credit card number", type=int, required=True)
@arg("--update-project", help="Assign card to project")
def card__add(self) -> None:
"""Add a credit card"""
self.print_boxed(
[
"avn card add has been deprecated and will be removed in the next major release.",
"Please use `avn organization --organization-id organization_id card create`",
]
)

stripe_key = self.client.get_stripe_key()
stripe_token = self._card_get_stripe_token(
stripe_key,
self.args.name,
self.args.number,
self.args.exp_month,
self.args.exp_year,
self.args.cvc,
)
card = self.client.add_card(stripe_token)
if self.args.json:
self.print_response(card, json=True)

if self.args.update_project:
self.client.update_project(
project=self.args.update_project,
card_id=card["card_id"],
)

@arg.json
@arg("card-id", help="Card ID")
@arg("--exp-month", help="Card expiration month (1-12)", type=int)
@arg("--exp-year", help="Card expiration year", type=int)
@arg("--name", help="Name on card")
def card__update(self) -> None:
"""Update credit card information"""
card = self.client.update_card(
card_id=getattr(self.args, "card-id"),
exp_month=self.args.exp_month,
exp_year=self.args.exp_year,
name=self.args.name,
)
if self.args.json:
self.print_response(card, json=True)

@arg.json
@arg("card-id", help="Card ID")
def card__remove(self) -> None:
"""Remove a credit card"""
result = self.client.remove_card(card_id=getattr(self.args, "card-id"))
if self.args.json:
self.print_response(result, json=True)

@arg.json
@arg.project
def credits__list(self) -> None:
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
74 changes: 2 additions & 72 deletions aiven/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1639,43 +1639,22 @@ 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,
copy_from_project: str | None = None,
country_code: str | None = None,
billing_address: str | None = None,
billing_currency: str | None = None,
billing_extra_text: str | None = None,
vat_id: str | None = None,
billing_emails: Sequence[str] | None = None,
tech_emails: Sequence[str] | None = None,
use_source_project_billing_group: bool | None = None,
) -> Mapping:
body: dict[str, Any] = {
"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:
body["copy_from_project"] = copy_from_project
if country_code is not None:
body["country_code"] = country_code
if billing_address is not None:
body["billing_address"] = billing_address
if billing_currency is not None:
body["billing_currency"] = billing_currency
if billing_extra_text is not None:
body["billing_extra_text"] = billing_extra_text
if vat_id is not None:
body["vat_id"] = vat_id
if billing_emails is not None:
body["billing_emails"] = [{"email": email} for email in billing_emails]
if tech_emails is not None:
body["tech_emails"] = [{"email": email} for email in tech_emails]
if use_source_project_billing_group is not None:
Expand All @@ -1697,37 +1676,16 @@ def update_project(
project: str,
new_project_name: str | None = None,
account_id: str | None = None,
card_id: str | None = None,
cloud: str | None = None,
country_code: str | None = None,
billing_address: str | None = None,
billing_currency: str | None = None,
billing_extra_text: str | None = None,
vat_id: str | None = None,
billing_emails: Sequence[str] | None = None,
tech_emails: Sequence[str] | None = None,
) -> Mapping:
body: dict[str, Any] = {}
if new_project_name is not None:
body["project_name"] = new_project_name
if account_id is not None:
body["account_id"] = account_id
if card_id is not None:
body["card_id"] = card_id
if cloud is not None:
body["cloud"] = cloud
if country_code is not None:
body["country_code"] = country_code
if billing_address is not None:
body["billing_address"] = billing_address
if billing_currency is not None:
body["billing_currency"] = billing_currency
if billing_extra_text is not None:
body["billing_extra_text"] = billing_extra_text
if vat_id is not None:
body["vat_id"] = vat_id
if billing_emails is not None:
body["billing_emails"] = [{"email": email} for email in billing_emails]
if tech_emails is not None:
body["tech_emails"] = [{"email": email} for email in tech_emails]

Expand Down Expand Up @@ -1833,34 +1791,6 @@ def get_events(self, project: str, limit: int = 100) -> Sequence[dict[str, Any]]
result_key="events",
)

def get_cards(self) -> Sequence[dict[str, Any]]:
return self.verify(self.get, "/card", result_key="cards")

def add_card(self, stripe_token: str) -> Mapping:
request = {
"stripe_token": stripe_token,
}
return self.verify(self.post, "/card", body=request, result_key="card")

def update_card(self, card_id: str, **kwargs: Any) -> Mapping:
keys = {"exp_month", "exp_year", "name"}
wrong = set(kwargs) - keys
assert not wrong, "invalid arguments to update_card: {!r}".format(wrong)
request: dict[str, Any] = {}
for key in keys:
value = kwargs.get(key)
if value is not None:
expected: type = int if key in {"exp_month", "exp_year"} else str

assert isinstance(value, expected), "expected '{}' type for argument '{}'".format(expected, key)

request[key] = value

return self.verify(self.put, self.build_path("card", card_id), body=request, result_key="card")

def remove_card(self, card_id: str) -> Mapping:
return self.verify(self.delete, self.build_path("card", card_id))

def get_stripe_key(self) -> str:
return self.verify(self.get, self.build_path("config", "stripe_key"), result_key="stripe_key")

Expand Down
Loading

0 comments on commit 6a220e2

Please sign in to comment.