From ddd50762cc4405d19c5223a50a87296e72f71505 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 13 Nov 2026 19:36:08 +0000 Subject: [PATCH] feat(api): update SDKs to meteroid 0.1.0 BEGIN_NESTED_COMMIT feat(api): update `GET /api/v1/licenses`, `GET /api/v1/licenses/{license_id}`, `POST /api/v1/licenses`, `POST /api/v1/licenses/{license_id}/revoke` END_NESTED_COMMIT --- api.md | 2 +- meteroid/api/licenses.py | 10 ++++++++-- meteroid/models/issue_license_request.py | 5 +++++ meteroid/models/issue_license_response.py | 3 ++- meteroid/models/license.py | 3 +++ tests/test_licenses.py | 8 ++++---- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/api.md b/api.md index 082696a..7316ae8 100644 --- a/api.md +++ b/api.md @@ -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) | diff --git a/meteroid/api/licenses.py b/meteroid/api/licenses.py index fa41445..7d67836 100644 --- a/meteroid/api/licenses.py +++ b/meteroid/api/licenses.py @@ -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, @@ -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. @@ -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, @@ -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", @@ -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, @@ -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. @@ -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, @@ -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", diff --git a/meteroid/models/issue_license_request.py b/meteroid/models/issue_license_request.py index 65974b9..e7eb149 100644 --- a/meteroid/models/issue_license_request.py +++ b/meteroid/models/issue_license_request.py @@ -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 diff --git a/meteroid/models/issue_license_response.py b/meteroid/models/issue_license_response.py index ac3032f..f20acae 100644 --- a/meteroid/models/issue_license_response.py +++ b/meteroid/models/issue_license_response.py @@ -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.""" diff --git a/meteroid/models/license.py b/meteroid/models/license.py index ea82ef5..fb9ccf2 100644 --- a/meteroid/models/license.py +++ b/meteroid/models/license.py @@ -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.""" diff --git a/tests/test_licenses.py b/tests/test_licenses.py index 17b8b22..81741d8 100644 --- a/tests/test_licenses.py +++ b/tests/test_licenses.py @@ -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"]) @@ -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, @@ -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"]) @@ -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,