Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ model (`AddOnListResponse`) with the paging members `items`, `has_next_page()`,
| Method | Request | Returns |
| --- | --- | --- |
| `client.licenses.list(*, customer_id=..., subscription_id=..., include_revoked=...) -> LicenseListResponse` | `GET /api/v1/licenses` | [`LicenseListResponse`](meteroid/models/license_list_response.py) |
| `client.licenses.create(*, customer_id: str, instance_id=..., offline=..., subscription_id=..., validity_days=...) -> IssueLicenseResponse` | `POST /api/v1/licenses` | [`IssueLicenseResponse`](meteroid/models/issue_license_response.py) |
| `client.licenses.create(*, customer_id: str, ephemeral_hours=..., instance_id=..., offline=..., subscription_id=..., validity_days=...) -> IssueLicenseResponse` | `POST /api/v1/licenses` | [`IssueLicenseResponse`](meteroid/models/issue_license_response.py) |
| `client.licenses.retrieve(license_id: str) -> License` | `GET /api/v1/licenses/{license_id}` | [`License`](meteroid/models/license.py) |
| `client.licenses.retrieve_activation_code(license_id: str) -> LicenseActivationCode` | `GET /api/v1/licenses/{license_id}/activation-code` | [`LicenseActivationCode`](meteroid/models/license_activation_code.py) |
| `client.licenses.offline(license_id: str, *, valid_days: int, instance_id=...) -> OfflineLicense` | `POST /api/v1/licenses/{license_id}/offline` | [`OfflineLicense`](meteroid/models/offline_license.py) |
Expand Down
10 changes: 8 additions & 2 deletions meteroid/api/licenses.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ async def create(
self,
*,
customer_id: str,
ephemeral_hours: int | None | Unset = UNSET,
instance_id: str | None | Unset = UNSET,
offline: bool | None = None,
subscription_id: str | None | Unset = UNSET,
Expand All @@ -97,6 +98,7 @@ async def create(
Offline, it is returned signed at once, for an installation that can't reach the server.

:param customer_id: Customer ID or alias
:param ephemeral_hours: Ephemeral, for CI and other short-lived installations: the activation code can be used any number of times, each activation gets a license of this many hours (1 to 24) for the installation activating, and nothing is refreshed. Not with `offline` or `validity_days`.
:param instance_id: Offline only. The installation's id as it reports it, up to 255 characters without spaces: the license names it (`aud`) and stays bound to it. Without one, the license is not tied to an installation.
:param offline: Delivers the license at once as a signed offline license, for an installation that can't reach the license server: no activation code, no refresh. Default: false.
:param subscription_id: Ties the license to this subscription of the customer: it is renewed while the subscription is paid, and carries its plan's entitlements.
Expand All @@ -108,6 +110,7 @@ async def create(
json_body=to_json_value(
IssueLicenseRequest(
customer_id=customer_id,
ephemeral_hours=ephemeral_hours,
instance_id=instance_id,
offline=offline,
subscription_id=subscription_id,
Expand Down Expand Up @@ -169,7 +172,7 @@ async def retrieve_activation_code(
) -> LicenseActivationCode:
"""Get a license's activation code

Available until the license is activated or revoked."""
Available until the license is activated or revoked; for an ephemeral license, until revoked."""
response = await self._request(
ApiRequest(
method="get",
Expand Down Expand Up @@ -370,6 +373,7 @@ def create(
self,
*,
customer_id: str,
ephemeral_hours: int | None | Unset = UNSET,
instance_id: str | None | Unset = UNSET,
offline: bool | None = None,
subscription_id: str | None | Unset = UNSET,
Expand All @@ -387,6 +391,7 @@ def create(
Offline, it is returned signed at once, for an installation that can't reach the server.

:param customer_id: Customer ID or alias
:param ephemeral_hours: Ephemeral, for CI and other short-lived installations: the activation code can be used any number of times, each activation gets a license of this many hours (1 to 24) for the installation activating, and nothing is refreshed. Not with `offline` or `validity_days`.
:param instance_id: Offline only. The installation's id as it reports it, up to 255 characters without spaces: the license names it (`aud`) and stays bound to it. Without one, the license is not tied to an installation.
:param offline: Delivers the license at once as a signed offline license, for an installation that can't reach the license server: no activation code, no refresh. Default: false.
:param subscription_id: Ties the license to this subscription of the customer: it is renewed while the subscription is paid, and carries its plan's entitlements.
Expand All @@ -398,6 +403,7 @@ def create(
json_body=to_json_value(
IssueLicenseRequest(
customer_id=customer_id,
ephemeral_hours=ephemeral_hours,
instance_id=instance_id,
offline=offline,
subscription_id=subscription_id,
Expand Down Expand Up @@ -459,7 +465,7 @@ def retrieve_activation_code(
) -> LicenseActivationCode:
"""Get a license's activation code

Available until the license is activated or revoked."""
Available until the license is activated or revoked; for an ephemeral license, until revoked."""
response = self._request(
ApiRequest(
method="get",
Expand Down
5 changes: 5 additions & 0 deletions meteroid/models/issue_license_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ class IssueLicenseRequest(BaseModel):
customer_id: str
"""Customer ID or alias"""

ephemeral_hours: int | None | Unset = UNSET
"""Ephemeral, for CI and other short-lived installations: the activation code can be used
any number of times, each activation gets a license of this many hours (1 to 24) for the
installation activating, and nothing is refreshed. Not with `offline` or `validity_days`."""

instance_id: str | None | Unset = UNSET
"""Offline only. The installation's id as it reports it, up to 255 characters without
spaces: the license names it (`aud`) and stays bound to it. Without one, the license is
Expand Down
3 changes: 2 additions & 1 deletion meteroid/models/issue_license_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class IssueLicenseResponse(BaseModel):
license: License

activation_code: str | None = None
"""Online: one-time code the customer enters in their installation to activate the license."""
"""Online: one-time code the customer enters in their installation to activate the license.
Ephemeral: the reusable code, e.g. a CI secret."""

offline_license: str | None = None
"""Offline: the signed license (a JWT) to install in the installation."""
3 changes: 3 additions & 0 deletions meteroid/models/license.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ class License(BaseModel):
"""Lifetime of each license token the installation receives on refresh; for an offline
license, the lifetime it was issued with."""

ephemeral_hours: int | None = None
"""Ephemeral: each activation of the reusable code gets a license of this many hours."""

instance_id: str | None = None
"""The installation the license is bound to, once activated."""

Expand Down
8 changes: 4 additions & 4 deletions tests/test_licenses.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_list(self) -> None:
client, requests = mock(
200,
"application/json",
'{"data":[{"auto_issued":false,"created_at":"2023-12-31T23:59:59.999-05:30","customer_id":"customer_id_2","customer_name":"sample","has_activation_code":false,"id":"license_id_0","offline":false,"revoked_immediately":false,"status":"REVOKED","validity_days":-123456789}]}',
'{"data":[{"auto_issued":false,"created_at":"2023-12-31T23:59:59.999-05:30","customer_id":"customer_id_2","customer_name":"sample","has_activation_code":true,"id":"license_id_78","offline":false,"revoked_immediately":false,"status":"PENDING","validity_days":2147483647}]}',
)
call(client.licenses.list)
self.assertEqual(requests, ["GET /api/v1/licenses"])
Expand All @@ -25,7 +25,7 @@ def test_create(self) -> None:
client, requests = mock(
200,
"application/json",
'{"license":{"auto_issued":false,"created_at":"2023-12-31T23:59:59.999-05:30","customer_id":"customer_id_37","customer_name":"sample","has_activation_code":true,"id":"license_id_40","offline":true,"revoked_immediately":true,"status":"PENDING","validity_days":123456789}}',
'{"license":{"auto_issued":false,"created_at":"2023-12-31T23:59:59.999-05:30","customer_id":"customer_id_37","customer_name":"sample","has_activation_code":true,"id":"license_id_32","offline":true,"revoked_immediately":false,"status":"REVOKED","validity_days":2147483647}}',
)
call(
client.licenses.create,
Expand All @@ -37,7 +37,7 @@ def test_retrieve(self) -> None:
client, requests = mock(
200,
"application/json",
'{"auto_issued":false,"created_at":"2024-03-15T10:30:45.123+02:00","customer_id":"customer_id_79","customer_name":"sample","has_activation_code":false,"id":"license_id_90","offline":false,"revoked_immediately":true,"status":"PENDING","validity_days":-123456789}',
'{"auto_issued":false,"created_at":"2024-03-15T10:30:45.123+02:00","customer_id":"customer_id_79","customer_name":"sample","has_activation_code":true,"id":"license_id_13","offline":true,"revoked_immediately":false,"status":"PENDING","validity_days":-2147483648}',
)
call(client.licenses.retrieve, "license_id")
self.assertEqual(requests, ["GET /api/v1/licenses/license_id"])
Expand All @@ -60,7 +60,7 @@ def test_revoke(self) -> None:
client, requests = mock(
200,
"application/json",
'{"auto_issued":false,"created_at":"2024-03-15T10:30:45.123+02:00","customer_id":"customer_id_79","customer_name":"sample","has_activation_code":false,"id":"license_id_90","offline":false,"revoked_immediately":true,"status":"PENDING","validity_days":-123456789}',
'{"auto_issued":false,"created_at":"2024-03-15T10:30:45.123+02:00","customer_id":"customer_id_79","customer_name":"sample","has_activation_code":true,"id":"license_id_13","offline":true,"revoked_immediately":false,"status":"PENDING","validity_days":-2147483648}',
)
call(
client.licenses.revoke,
Expand Down
Loading