From d8b2dd7bd8442067babe3cafb9304098854f03f2 Mon Sep 17 00:00:00 2001 From: speakeasybot Date: Thu, 13 Aug 2026 08:16:15 +0000 Subject: [PATCH 1/2] ## Python SDK Changes: * `gr4vy.roles.list()`: **Added** --- .speakeasy/gen.lock | 109 ++++++--- .speakeasy/gen.yaml | 2 +- .speakeasy/workflow.lock | 10 +- README.md | 4 + RELEASES.md | 12 +- docs/models/collectionrole.md | 11 + docs/models/listrolesrequest.md | 9 + docs/models/listrolesresponse.md | 8 + docs/models/permissionset.md | 8 +- docs/models/role.md | 10 +- docs/sdks/roles/README.md | 62 ++++++ pyproject.toml | 2 +- src/gr4vy/_version.py | 4 +- src/gr4vy/models/__init__.py | 19 ++ src/gr4vy/models/collection_role_.py | 58 +++++ src/gr4vy/models/list_rolesop.py | 65 ++++++ src/gr4vy/models/permissionset.py | 6 + src/gr4vy/models/role.py | 10 + src/gr4vy/roles.py | 322 +++++++++++++++++++++++++++ src/gr4vy/sdk.py | 3 + 20 files changed, 690 insertions(+), 44 deletions(-) create mode 100644 docs/models/collectionrole.md create mode 100644 docs/models/listrolesrequest.md create mode 100644 docs/models/listrolesresponse.md create mode 100644 docs/sdks/roles/README.md create mode 100644 src/gr4vy/models/collection_role_.py create mode 100644 src/gr4vy/models/list_rolesop.py create mode 100644 src/gr4vy/roles.py diff --git a/.speakeasy/gen.lock b/.speakeasy/gen.lock index f71dde17..d1c420c7 100644 --- a/.speakeasy/gen.lock +++ b/.speakeasy/gen.lock @@ -1,19 +1,19 @@ lockVersion: 2.0.0 id: 833c6a67-4a4a-4ef1-ba24-165a29264e4a management: - docChecksum: 81072eb4ed060199ba7a3bb8d7835d5d + docChecksum: a0e4357ef0dfe06673c7dbdf7634a215 docVersion: 1.0.0 speakeasyVersion: 1.793.1 generationVersion: 2.928.0 - releaseVersion: 2.3.10 - configChecksum: 5ecdde6a5327f5aacdf5cbc0a9a5bd66 + releaseVersion: 2.3.11 + configChecksum: edcac915d2f069d49f7510c776b4b5da repoURL: https://github.com/gr4vy/gr4vy-python.git installationURL: https://github.com/gr4vy/gr4vy-python.git published: true persistentEdits: - generation_id: caba5257-2928-4306-a15c-678b46d74de0 - pristine_commit_hash: 0dbe9412f117af9fc2eca9794b9942cee1e34437 - pristine_tree_hash: fd6a0fc6c0c242a6a763e2dd935b60f3b806abbd + generation_id: 81b9703a-ed43-488c-a98e-1b381a1e5cb9 + pristine_commit_hash: 786b18d17ffc3f9f1e3e76c62f9afa6991c3e90c + pristine_tree_hash: c10146467fddab6cba7e45799435b6b7c8f4a3ef features: python: additionalDependencies: 1.1.0 @@ -475,6 +475,10 @@ trackedFiles: id: a675d216a617 last_write_checksum: sha1:34fecd902db403b5eac2a7325cde9170d899595e pristine_git_object: 0b8a033a0d7c064747717f2e25906ab81fda6137 + docs/models/collectionrole.md: + id: efdb4a841e8c + last_write_checksum: sha1:d76929d2ac6321d82fe305445287ef27c08d2120 + pristine_git_object: 425dea2b9eb2346088b6a21a9cc64b5a3e7398c6 docs/models/completepazemobilesessionglobals.md: id: 344c55909f4d last_write_checksum: sha1:d08650aec48dbb8b41c823e4dd12734d5ee9903f @@ -1519,6 +1523,14 @@ trackedFiles: id: 8c84b32f7e4a last_write_checksum: sha1:28907a6e71e0d3f6bb8b1c0dc12c5fcc2bb0036a pristine_git_object: f5c8ceb6cda906b8f24dcd11986527c0e868a11a + docs/models/listrolesrequest.md: + id: 6a00a344163f + last_write_checksum: sha1:0613680bc7f6aafda17efbfbb731b1652cf0950c + pristine_git_object: c2481b4e983b4a0619f476597b548991dbc827ac + docs/models/listrolesresponse.md: + id: 4f11eb9526a4 + last_write_checksum: sha1:b50068d332fe8ddc8446f2bfb493c2a4b26a604b + pristine_git_object: e8c89ea5f71db55af5ab6ee94ae92eafc658b35e docs/models/listthreedsconfigurationsrequest.md: id: f39090671a4a last_write_checksum: sha1:e94a776b86836faac4debe98ab302dcb2957cac7 @@ -2025,8 +2037,8 @@ trackedFiles: pristine_git_object: 62ae5cde79d785815cb7ad75fb16833cc142d048 docs/models/permissionset.md: id: 818916f866f8 - last_write_checksum: sha1:bdfc0899457e415391a406c0fc97846eeb89392f - pristine_git_object: 7cbcb43d12a3591e995e7046657142a892c7cb71 + last_write_checksum: sha1:3c1ecf946b0faef3daf6b86716b7ce389222ca7c + pristine_git_object: 57cb30c636bef31a3e0629084b5e0c795e3199c5 docs/models/plaidpaymentmethodcreate.md: id: 0f87b45637cb last_write_checksum: sha1:282a2bca19c804e28b78dfa69984af3553f3286e @@ -2209,8 +2221,8 @@ trackedFiles: pristine_git_object: 62784bbbe8065c86489707a739e5d951ebb260c7 docs/models/role.md: id: b694540a5b1e - last_write_checksum: sha1:932df46b6a9ec306d8b83e79d6a32e896ff4e1c2 - pristine_git_object: dc1751513e16b7a58d99f6b69f66f029f3fc2150 + last_write_checksum: sha1:84c3cd4f436f3ae944c878a3f3a7ef8c0f17a9ab + pristine_git_object: 0e4948f057e7c56ece49bc987149d6c6733a5719 docs/models/roleassigneetype.md: id: cba29d79bf5a last_write_checksum: sha1:38554c41d5339b175367dcf008ec2b3ecec424ce @@ -2819,6 +2831,10 @@ trackedFiles: id: 7cc9081622e6 last_write_checksum: sha1:e600c27f9e1b33fab0b9ccb2a673593650da47c6 pristine_git_object: 0651fa727dee9a6ccde51039f59cb8db3347d5cd + docs/sdks/roles/README.md: + id: 69204c99730f + last_write_checksum: sha1:4bf33a78b8b9b2d8666b58c34c41787193e31e20 + pristine_git_object: d20402e3606caa16d3e4289c1b700348b5644bb5 docs/sdks/sessions/README.md: id: 57ad62844f79 last_write_checksum: sha1:065cc3cbecf4b33ae242ffc26fcd8f4f611e5797 @@ -2861,8 +2877,8 @@ trackedFiles: pristine_git_object: cb9c15d314c1930557401074329ed7a737147919 pyproject.toml: id: 5d07e7d72637 - last_write_checksum: sha1:96a597927556ebe45e3a3eff2cb29c4fc57a6fe2 - pristine_git_object: e6d96b055f5a9730d9097132b66a3ffc8693877d + last_write_checksum: sha1:2cae35e91f1c5b05654a15e84e247c0be0f7ad13 + pristine_git_object: 060f6ae1c3024270a23be58d82858b4abd22599d scripts/prepare_readme.py: id: e0c5957a6035 last_write_checksum: sha1:63a92abbd1de3323613f0d4c1ae4412076577710 @@ -2889,8 +2905,8 @@ trackedFiles: pristine_git_object: f8ef109bb6a9dbe37d0a7092606176a0738986fd src/gr4vy/_version.py: id: 1b5ad1da47aa - last_write_checksum: sha1:95577fb055c626a6cb2a6c3813ed5493f7784a7d - pristine_git_object: 2d95497c542bac8fe52f5a6d8ed2b8aa11fe85dd + last_write_checksum: sha1:f02aa9950a8688c0a66f0659297bcf81ca0e1d58 + pristine_git_object: 1ac5f10587dee7a3c4cdf1d9f79efd1c2fe47dcd src/gr4vy/account_updater.py: id: 6dc8f39e2fb9 last_write_checksum: sha1:fabc79a59dd06fb927ce2d834fbafbcad5836a4b @@ -3057,8 +3073,8 @@ trackedFiles: pristine_git_object: becb7dcefb2ea12bf3e199ed39ece68944623c13 src/gr4vy/models/__init__.py: id: 427cc7fddfe2 - last_write_checksum: sha1:cc8c1aa3dbdd4e498db99107cb4f63d703356112 - pristine_git_object: 812102c4e3978afe628cc79bffb07eb9fc335cdf + last_write_checksum: sha1:cc2f488d492f5a2e280e00a56ebd4d150fef6db1 + pristine_git_object: 4f406045061291a52b19b7171e6c78d2cd1faea9 src/gr4vy/models/accountsreceivablesreportspec.py: id: cc6a9368f988 last_write_checksum: sha1:1ce9432ce7ae682a7fff1e0fe1ddc7f82dc937e7 @@ -3379,6 +3395,10 @@ trackedFiles: id: 5a429482a3c6 last_write_checksum: sha1:3561ec711b599e0d373cb98b990d407d65b96a44 pristine_git_object: 7ae8e807c246e2973242c917956607b1e10f23a7 + src/gr4vy/models/collection_role_.py: + id: b0902f69eff5 + last_write_checksum: sha1:a7c2b8a9ad85042522ee9350f60feb19d94be3de + pristine_git_object: 8b67801432a8dd85d22cdf12409f1dc4191a885f src/gr4vy/models/complete_paze_mobile_sessionop.py: id: 77d97525df33 last_write_checksum: sha1:085fb62fd353fdbe536ebaa614def421bc316fb2 @@ -4027,6 +4047,10 @@ trackedFiles: id: e04af97d8861 last_write_checksum: sha1:b4ecfbd0d0fc20671656ef6fab9ad95382044f98 pristine_git_object: 03a431bf7eb754388985b3e0a8c708eae45a669d + src/gr4vy/models/list_rolesop.py: + id: f920b82150fb + last_write_checksum: sha1:4a222afa1cd7c6b2e21204f2d4a2a610bd3b66c5 + pristine_git_object: 7c9b626424667c9c5712b85919288d477cd3a8d7 src/gr4vy/models/list_three_ds_configurationsop.py: id: 8b8a0f0df5c1 last_write_checksum: sha1:e78d49e348b8e6f43a61f3ab7bf78af35daf3abc @@ -4449,8 +4473,8 @@ trackedFiles: pristine_git_object: 32fd236b47aa4029b809f09cfba72a3175b760c9 src/gr4vy/models/permissionset.py: id: 666b191a5512 - last_write_checksum: sha1:225a6a6568ce7ce086d9f63961c77f68a1578dd5 - pristine_git_object: 8ff1b3bb0bdd70b01e5b518b5f40deeaa192549d + last_write_checksum: sha1:58bd34a17667a9a57fc30621262354cd8cfc64d9 + pristine_git_object: 32bace52b5a660b90b70c549503f1bcfab23ccc0 src/gr4vy/models/plaidpaymentmethodcreate.py: id: 16d9f8af22ec last_write_checksum: sha1:747b39aefcec982bb9ef4c482d159fa71e15f0cd @@ -4585,8 +4609,8 @@ trackedFiles: pristine_git_object: 34374602cff8b4ee3781f6602f0cf58c7d3a5d1d src/gr4vy/models/role.py: id: c68dc475ca1a - last_write_checksum: sha1:2b1ab880d3212bced643fc161da611e313e1583e - pristine_git_object: 9d1b505884d35e10bdcd9861fee215420ce5e5fe + last_write_checksum: sha1:5cdcabd538005ffcf795d9e78db0f1cd62e7f535 + pristine_git_object: 4895735d816e5afa79b7cf5845f60ba35de30f1e src/gr4vy/models/roleassigneetype.py: id: 34cf5775310c last_write_checksum: sha1:33600ed88e334ce79895669c273d62bcab7aa622 @@ -4999,10 +5023,14 @@ trackedFiles: id: 3b9c7e91de97 last_write_checksum: sha1:d26934753b54cb00a302e86846a18c2001f3089a pristine_git_object: bc8980a7341eed86e62659760a541cf03b28b88d + src/gr4vy/roles.py: + id: 45196c128baa + last_write_checksum: sha1:135386b38717eacdbe099a01844209587026fcdf + pristine_git_object: b1cf593eedf1ab9ef7460d1dea9b085a4b011770 src/gr4vy/sdk.py: id: 924d48ddd6b1 - last_write_checksum: sha1:2b2e9cd3bdec5b466174abc5d98c397e7b7a9cf3 - pristine_git_object: 52088859f6677f500ce5af3d638db9b66f3962c2 + last_write_checksum: sha1:9af2d4f1616cd634132e0c6006dce7d08b815669 + pristine_git_object: 4e92414af7edcc5bc2798c46e34b0262b51513b2 src/gr4vy/sdkconfiguration.py: id: 12e6f5d93283 last_write_checksum: sha1:b3d04005a955111373b344e02ed4504bfbdd3106 @@ -9394,14 +9422,43 @@ examples: application/json: {"type": "error", "code": "bad_gateway", "status": 502, "message": "Request could not be processed"} "504": application/json: {"type": "error", "code": "gateway_timeout", "status": 504, "message": "Request could not be processed"} + list_roles: + speakeasy-default-list-roles: + parameters: + query: + limit: 20 + responses: + "200": + application/json: {"items": [], "limit": 20} + "400": + application/json: {"type": "error", "code": "bad_request", "status": 400, "message": "Request failed validation"} + "401": + application/json: {"type": "error", "code": "unauthorized", "status": 401, "message": "No valid API authentication found"} + "403": + application/json: {"type": "error", "code": "forbidden", "status": 403, "message": "Request failed validation"} + "404": + application/json: {"type": "error", "code": "not_found", "status": 404, "message": "The resource could not be found"} + "405": + application/json: {"type": "error", "code": "method_not_allowed", "status": 405, "message": "Method Not Allowed"} + "409": + application/json: {"type": "error", "code": "duplicate_record", "status": 409, "message": "Request failed validation"} + "422": + application/json: {} + "425": + application/json: {"type": "error", "code": "too_early", "status": 425, "message": "Request failed validation"} + "429": + application/json: {"type": "error", "code": "too_many_requests", "status": 429, "message": "Request failed validation"} + "500": + application/json: {"type": "error", "code": "server_error", "status": 500, "message": "Request could not be processed"} + "502": + application/json: {"type": "error", "code": "bad_gateway", "status": 502, "message": "Request could not be processed"} + "504": + application/json: {"type": "error", "code": "gateway_timeout", "status": 504, "message": "Request could not be processed"} examplesVersion: 1.0.2 generatedTests: {} releaseNotes: | ## Python SDK Changes: - * `gr4vy.api_key_pairs.list()`: `response.items[].roles[]` **Changed** - * `gr4vy.api_key_pairs.create()`: `response.roles[]` **Changed** - * `gr4vy.api_key_pairs.get()`: `response.roles[]` **Changed** - * `gr4vy.api_key_pairs.update()`: `response.roles[]` **Changed** + * `gr4vy.roles.list()`: **Added** generatedFiles: - .gitattributes - .vscode/settings.json diff --git a/.speakeasy/gen.yaml b/.speakeasy/gen.yaml index 8d94b456..e443b42b 100644 --- a/.speakeasy/gen.yaml +++ b/.speakeasy/gen.yaml @@ -29,7 +29,7 @@ generation: generateNewTests: false skipResponseBodyAssertions: false python: - version: 2.3.10 + version: 2.3.11 additionalDependencies: dev: freezegun: ^1.5.1 diff --git a/.speakeasy/workflow.lock b/.speakeasy/workflow.lock index ebcff380..473a6511 100644 --- a/.speakeasy/workflow.lock +++ b/.speakeasy/workflow.lock @@ -2,8 +2,8 @@ speakeasyVersion: 1.793.1 sources: openapi: sourceNamespace: openapi - sourceRevisionDigest: sha256:dda15e6c01c12458c9de938cb7e5b258c0f6869e0f3b80009c637cb972ae544d - sourceBlobDigest: sha256:c5f8e24e5316cc9662bed0a8029af680a052d296098297538c1e2a10d468748c + sourceRevisionDigest: sha256:e6f70a6e6124c87065ff3cc63a63df9fba484d2a877e1f8ff13679c0c6f49f87 + sourceBlobDigest: sha256:32b29920091979289cbbc9aed11d8858824f81b0eb91847cfb80e44f691d5ab3 tags: - latest - 1.0.0 @@ -11,10 +11,10 @@ targets: python: source: openapi sourceNamespace: openapi - sourceRevisionDigest: sha256:dda15e6c01c12458c9de938cb7e5b258c0f6869e0f3b80009c637cb972ae544d - sourceBlobDigest: sha256:c5f8e24e5316cc9662bed0a8029af680a052d296098297538c1e2a10d468748c + sourceRevisionDigest: sha256:e6f70a6e6124c87065ff3cc63a63df9fba484d2a877e1f8ff13679c0c6f49f87 + sourceBlobDigest: sha256:32b29920091979289cbbc9aed11d8858824f81b0eb91847cfb80e44f691d5ab3 codeSamplesNamespace: openapi-python-code-samples - codeSamplesRevisionDigest: sha256:666d355bb02ef4c2d6149162425854817df89e055e0a6874e3bd776ba579769e + codeSamplesRevisionDigest: sha256:4a12ab7e7563d9b3f9af2cd14af6292fa7e1609911885c6e9b8fd300ac5a9b23 workflow: workflowVersion: 1.0.0 speakeasyVersion: latest diff --git a/README.md b/README.md index 0085290c..030b8093 100644 --- a/README.md +++ b/README.md @@ -519,6 +519,10 @@ except ValueError as error: * [url](docs/sdks/executions/README.md#url) - Create URL for executed report * [get](docs/sdks/executions/README.md#get) - Get executed report +### [Roles](docs/sdks/roles/README.md) + +* [list](docs/sdks/roles/README.md#list) - List all roles + ### [ThreeDsScenarios](docs/sdks/threedsscenarios/README.md) * [create](docs/sdks/threedsscenarios/README.md#create) - Create a 3DS scenario diff --git a/RELEASES.md b/RELEASES.md index d71e47cf..0a2b1bf4 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2438,4 +2438,14 @@ Based on: ### Generated - [python v2.3.10] . ### Releases -- [PyPI v2.3.10] https://pypi.org/project/gr4vy/2.3.10 - . \ No newline at end of file +- [PyPI v2.3.10] https://pypi.org/project/gr4vy/2.3.10 - . + +## 2026-08-13 08:14:13 +### Changes +Based on: +- OpenAPI Doc +- Speakeasy CLI 1.793.1 (2.928.0) https://github.com/speakeasy-api/speakeasy +### Generated +- [python v2.3.11] . +### Releases +- [PyPI v2.3.11] https://pypi.org/project/gr4vy/2.3.11 - . \ No newline at end of file diff --git a/docs/models/collectionrole.md b/docs/models/collectionrole.md new file mode 100644 index 00000000..425dea2b --- /dev/null +++ b/docs/models/collectionrole.md @@ -0,0 +1,11 @@ +# CollectionRole + + +## Fields + +| Field | Type | Required | Description | Example | +| -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------- | +| `items` | List[[models.Role](../models/role.md)] | :heavy_check_mark: | A list of items returned for this request. | | +| `limit` | *Optional[int]* | :heavy_minus_sign: | The number of items for this page. | 20 | +| `next_cursor` | *OptionalNullable[str]* | :heavy_minus_sign: | The cursor pointing at the next page of items. | ZXhhbXBsZTE | +| `previous_cursor` | *OptionalNullable[str]* | :heavy_minus_sign: | The cursor pointing at the previous page of items. | Xkjss7asS | \ No newline at end of file diff --git a/docs/models/listrolesrequest.md b/docs/models/listrolesrequest.md new file mode 100644 index 00000000..c2481b4e --- /dev/null +++ b/docs/models/listrolesrequest.md @@ -0,0 +1,9 @@ +# ListRolesRequest + + +## Fields + +| Field | Type | Required | Description | Example | +| ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | ---------------------------------------------- | +| `cursor` | *OptionalNullable[str]* | :heavy_minus_sign: | A pointer to the page of results to return. | ZXhhbXBsZTE | +| `limit` | *Optional[int]* | :heavy_minus_sign: | The maximum number of items that are returned. | 20 | \ No newline at end of file diff --git a/docs/models/listrolesresponse.md b/docs/models/listrolesresponse.md new file mode 100644 index 00000000..e8c89ea5 --- /dev/null +++ b/docs/models/listrolesresponse.md @@ -0,0 +1,8 @@ +# ListRolesResponse + + +## Fields + +| Field | Type | Required | Description | +| ---------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------------- | +| `result` | [models.CollectionRole](../models/collectionrole.md) | :heavy_check_mark: | N/A | \ No newline at end of file diff --git a/docs/models/permissionset.md b/docs/models/permissionset.md index 7cbcb43d..57cb30c6 100644 --- a/docs/models/permissionset.md +++ b/docs/models/permissionset.md @@ -1,8 +1,10 @@ # PermissionSet +The permissions granted by a role. + ## Fields -| Field | Type | Required | Description | -| ------------------ | ------------------ | ------------------ | ------------------ | -| `allow` | List[*str*] | :heavy_check_mark: | N/A | \ No newline at end of file +| Field | Type | Required | Description | Example | +| --------------------------------------- | --------------------------------------- | --------------------------------------- | --------------------------------------- | --------------------------------------- | +| `allow` | List[*str*] | :heavy_check_mark: | The scopes granted by this role. | [
"transactions.read",
"reports.read"
] | \ No newline at end of file diff --git a/docs/models/role.md b/docs/models/role.md index dc175151..0e4948f0 100644 --- a/docs/models/role.md +++ b/docs/models/role.md @@ -5,12 +5,12 @@ | Field | Type | Required | Description | Example | | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | -| `type` | *Optional[Literal["role"]]* | :heavy_minus_sign: | N/A | | -| `id` | *str* | :heavy_check_mark: | N/A | | -| `name` | *str* | :heavy_check_mark: | N/A | | +| `type` | *Optional[Literal["role"]]* | :heavy_minus_sign: | Always `role`. | role | +| `id` | *str* | :heavy_check_mark: | The unique ID for the role. | fe26475d-ec3e-4884-9553-f7356683f7f9 | +| `name` | *str* | :heavy_check_mark: | The human-readable name of the role. | Administrator | | `slug` | *str* | :heavy_check_mark: | The unique, human-readable identifier for the role. | administrator | -| `description` | *str* | :heavy_check_mark: | N/A | | -| `permissions` | [models.PermissionSet](../models/permissionset.md) | :heavy_check_mark: | N/A | | +| `description` | *str* | :heavy_check_mark: | A description of the access this role grants. | Full read and write access. | +| `permissions` | [models.PermissionSet](../models/permissionset.md) | :heavy_check_mark: | The permissions granted by a role. | | | `assignable_to` | List[[models.RoleAssigneeType](../models/roleassigneetype.md)] | :heavy_check_mark: | The types of resource this role can be assigned to. | | | `applies_to` | List[*str*] | :heavy_check_mark: | The slugs of the roles this role is an add-on of. Empty when this role is not an add-on. | | | `is_standalone_assignable` | *bool* | :heavy_check_mark: | Whether this role can be assigned on its own, without being combined with another role. | | \ No newline at end of file diff --git a/docs/sdks/roles/README.md b/docs/sdks/roles/README.md new file mode 100644 index 00000000..d20402e3 --- /dev/null +++ b/docs/sdks/roles/README.md @@ -0,0 +1,62 @@ +# Roles + +## Overview + +### Available Operations + +* [list](#list) - List all roles + +## list + +List all roles available in the instance. + +### Example Usage + + +```python +from gr4vy import Gr4vy +import os + + +with Gr4vy( + bearer_auth=os.getenv("GR4VY_BEARER_AUTH", ""), +) as g_client: + + res = g_client.roles.list(limit=20) + + while res is not None: + # Handle items + + res = res.next() + +``` + +### Parameters + +| Parameter | Type | Required | Description | Example | +| ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------- | +| `cursor` | *OptionalNullable[str]* | :heavy_minus_sign: | A pointer to the page of results to return. | ZXhhbXBsZTE | +| `limit` | *Optional[int]* | :heavy_minus_sign: | The maximum number of items that are returned. | 20 | +| `retries` | [Optional[utils.RetryConfig]](../../models/utils/retryconfig.md) | :heavy_minus_sign: | Configuration to override the default retry behavior of the client. | | + +### Response + +**[models.ListRolesResponse](../../models/listrolesresponse.md)** + +### Errors + +| Error Type | Status Code | Content Type | +| -------------------------- | -------------------------- | -------------------------- | +| errors.Error400 | 400 | application/json | +| errors.Error401 | 401 | application/json | +| errors.Error403 | 403 | application/json | +| errors.Error404 | 404 | application/json | +| errors.Error405 | 405 | application/json | +| errors.Error409 | 409 | application/json | +| errors.HTTPValidationError | 422 | application/json | +| errors.Error425 | 425 | application/json | +| errors.Error429 | 429 | application/json | +| errors.Error500 | 500 | application/json | +| errors.Error502 | 502 | application/json | +| errors.Error504 | 504 | application/json | +| errors.APIError | 4XX, 5XX | \*/\* | \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index e6d96b05..060f6ae1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "gr4vy" -version = "2.3.10" +version = "2.3.11" description = "Python Client SDK Generated by Speakeasy." authors = [{ name = "Gr4vy" },] readme = "README-PYPI.md" diff --git a/src/gr4vy/_version.py b/src/gr4vy/_version.py index 2d95497c..1ac5f105 100644 --- a/src/gr4vy/_version.py +++ b/src/gr4vy/_version.py @@ -3,10 +3,10 @@ import importlib.metadata __title__: str = "gr4vy" -__version__: str = "2.3.10" +__version__: str = "2.3.11" __openapi_doc_version__: str = "1.0.0" __gen_version__: str = "2.928.0" -__user_agent__: str = "speakeasy-sdk/python 2.3.10 2.928.0 1.0.0 gr4vy" +__user_agent__: str = "speakeasy-sdk/python 2.3.11 2.928.0 1.0.0 gr4vy" try: if __package__ is not None: diff --git a/src/gr4vy/models/__init__.py b/src/gr4vy/models/__init__.py index 812102c4..4f406045 100644 --- a/src/gr4vy/models/__init__.py +++ b/src/gr4vy/models/__init__.py @@ -217,6 +217,7 @@ CollectionAPIKeyPair, CollectionAPIKeyPairTypedDict, ) + from .collection_role_ import CollectionRole, CollectionRoleTypedDict from .complete_paze_mobile_sessionop import ( CompletePazeMobileSessionGlobals, CompletePazeMobileSessionGlobalsTypedDict, @@ -906,6 +907,12 @@ ListReportsResponse, ListReportsResponseTypedDict, ) + from .list_rolesop import ( + ListRolesRequest, + ListRolesRequestTypedDict, + ListRolesResponse, + ListRolesResponseTypedDict, + ) from .list_three_ds_configurationsop import ( ListThreeDsConfigurationsRequest, ListThreeDsConfigurationsRequestTypedDict, @@ -1743,6 +1750,8 @@ "ClickToPaySessionTypedDict", "CollectionAPIKeyPair", "CollectionAPIKeyPairTypedDict", + "CollectionRole", + "CollectionRoleTypedDict", "CompletePazeMobileSessionGlobals", "CompletePazeMobileSessionGlobalsTypedDict", "CompletePazeMobileSessionRequest", @@ -2237,6 +2246,10 @@ "ListReportsRequestTypedDict", "ListReportsResponse", "ListReportsResponseTypedDict", + "ListRolesRequest", + "ListRolesRequestTypedDict", + "ListRolesResponse", + "ListRolesResponseTypedDict", "ListThreeDsConfigurationsRequest", "ListThreeDsConfigurationsRequestTypedDict", "ListTransactionActionsGlobals", @@ -2946,6 +2959,8 @@ "ClickToPaySessionRequestTypedDict": ".clicktopaysessionrequest", "CollectionAPIKeyPair": ".collection_apikeypair_", "CollectionAPIKeyPairTypedDict": ".collection_apikeypair_", + "CollectionRole": ".collection_role_", + "CollectionRoleTypedDict": ".collection_role_", "CompletePazeMobileSessionGlobals": ".complete_paze_mobile_sessionop", "CompletePazeMobileSessionGlobalsTypedDict": ".complete_paze_mobile_sessionop", "CompletePazeMobileSessionRequest": ".complete_paze_mobile_sessionop", @@ -3443,6 +3458,10 @@ "ListReportsRequestTypedDict": ".list_reportsop", "ListReportsResponse": ".list_reportsop", "ListReportsResponseTypedDict": ".list_reportsop", + "ListRolesRequest": ".list_rolesop", + "ListRolesRequestTypedDict": ".list_rolesop", + "ListRolesResponse": ".list_rolesop", + "ListRolesResponseTypedDict": ".list_rolesop", "ListThreeDsConfigurationsRequest": ".list_three_ds_configurationsop", "ListThreeDsConfigurationsRequestTypedDict": ".list_three_ds_configurationsop", "ListTransactionActionsGlobals": ".list_transaction_actionsop", diff --git a/src/gr4vy/models/collection_role_.py b/src/gr4vy/models/collection_role_.py new file mode 100644 index 00000000..8b678014 --- /dev/null +++ b/src/gr4vy/models/collection_role_.py @@ -0,0 +1,58 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from .role import Role, RoleTypedDict +from gr4vy.types import BaseModel, Nullable, OptionalNullable, UNSET, UNSET_SENTINEL +from pydantic import model_serializer +from typing import List, Optional +from typing_extensions import NotRequired, TypedDict + + +class CollectionRoleTypedDict(TypedDict): + items: List[RoleTypedDict] + r"""A list of items returned for this request.""" + limit: NotRequired[int] + r"""The number of items for this page.""" + next_cursor: NotRequired[Nullable[str]] + r"""The cursor pointing at the next page of items.""" + previous_cursor: NotRequired[Nullable[str]] + r"""The cursor pointing at the previous page of items.""" + + +class CollectionRole(BaseModel): + items: List[Role] + r"""A list of items returned for this request.""" + + limit: Optional[int] = 20 + r"""The number of items for this page.""" + + next_cursor: OptionalNullable[str] = UNSET + r"""The cursor pointing at the next page of items.""" + + previous_cursor: OptionalNullable[str] = UNSET + r"""The cursor pointing at the previous page of items.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["limit", "next_cursor", "previous_cursor"]) + nullable_fields = set(["next_cursor", "previous_cursor"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + is_nullable_and_explicitly_set = ( + k in nullable_fields + and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member + ) + + if val != UNSET_SENTINEL: + if ( + val is not None + or k not in optional_fields + or is_nullable_and_explicitly_set + ): + m[k] = val + + return m diff --git a/src/gr4vy/models/list_rolesop.py b/src/gr4vy/models/list_rolesop.py new file mode 100644 index 00000000..7c9b6264 --- /dev/null +++ b/src/gr4vy/models/list_rolesop.py @@ -0,0 +1,65 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from __future__ import annotations +from .collection_role_ import CollectionRole, CollectionRoleTypedDict +from gr4vy.types import BaseModel, Nullable, OptionalNullable, UNSET, UNSET_SENTINEL +from gr4vy.utils import FieldMetadata, QueryParamMetadata +from pydantic import model_serializer +from typing import Callable, Optional +from typing_extensions import Annotated, NotRequired, TypedDict + + +class ListRolesRequestTypedDict(TypedDict): + cursor: NotRequired[Nullable[str]] + r"""A pointer to the page of results to return.""" + limit: NotRequired[int] + r"""The maximum number of items that are returned.""" + + +class ListRolesRequest(BaseModel): + cursor: Annotated[ + OptionalNullable[str], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = UNSET + r"""A pointer to the page of results to return.""" + + limit: Annotated[ + Optional[int], + FieldMetadata(query=QueryParamMetadata(style="form", explode=True)), + ] = 20 + r"""The maximum number of items that are returned.""" + + @model_serializer(mode="wrap") + def serialize_model(self, handler): + optional_fields = set(["cursor", "limit"]) + nullable_fields = set(["cursor"]) + serialized = handler(self) + m = {} + + for n, f in type(self).model_fields.items(): + k = f.alias or n + val = serialized.get(k, serialized.get(n)) + is_nullable_and_explicitly_set = ( + k in nullable_fields + and (self.__pydantic_fields_set__.intersection({n})) # pylint: disable=no-member + ) + + if val != UNSET_SENTINEL: + if ( + val is not None + or k not in optional_fields + or is_nullable_and_explicitly_set + ): + m[k] = val + + return m + + +class ListRolesResponseTypedDict(TypedDict): + result: CollectionRoleTypedDict + + +class ListRolesResponse(BaseModel): + next: Callable[[], Optional[ListRolesResponse]] + + result: CollectionRole diff --git a/src/gr4vy/models/permissionset.py b/src/gr4vy/models/permissionset.py index 8ff1b3bb..32bace52 100644 --- a/src/gr4vy/models/permissionset.py +++ b/src/gr4vy/models/permissionset.py @@ -7,8 +7,14 @@ class PermissionSetTypedDict(TypedDict): + r"""The permissions granted by a role.""" + allow: List[str] + r"""The scopes granted by this role.""" class PermissionSet(BaseModel): + r"""The permissions granted by a role.""" + allow: List[str] + r"""The scopes granted by this role.""" diff --git a/src/gr4vy/models/role.py b/src/gr4vy/models/role.py index 9d1b5058..4895735d 100644 --- a/src/gr4vy/models/role.py +++ b/src/gr4vy/models/role.py @@ -14,11 +14,15 @@ class RoleTypedDict(TypedDict): id: str + r"""The unique ID for the role.""" name: str + r"""The human-readable name of the role.""" slug: str r"""The unique, human-readable identifier for the role.""" description: str + r"""A description of the access this role grants.""" permissions: PermissionSetTypedDict + r"""The permissions granted by a role.""" assignable_to: List[RoleAssigneeType] r"""The types of resource this role can be assigned to.""" applies_to: List[str] @@ -26,19 +30,24 @@ class RoleTypedDict(TypedDict): is_standalone_assignable: bool r"""Whether this role can be assigned on its own, without being combined with another role.""" type: Literal["role"] + r"""Always `role`.""" class Role(BaseModel): id: str + r"""The unique ID for the role.""" name: str + r"""The human-readable name of the role.""" slug: str r"""The unique, human-readable identifier for the role.""" description: str + r"""A description of the access this role grants.""" permissions: PermissionSet + r"""The permissions granted by a role.""" assignable_to: List[RoleAssigneeType] r"""The types of resource this role can be assigned to.""" @@ -53,6 +62,7 @@ class Role(BaseModel): Annotated[Optional[Literal["role"]], AfterValidator(validate_const("role"))], pydantic.Field(alias="type"), ] = "role" + r"""Always `role`.""" @model_serializer(mode="wrap") def serialize_model(self, handler): diff --git a/src/gr4vy/roles.py b/src/gr4vy/roles.py new file mode 100644 index 00000000..b1cf593e --- /dev/null +++ b/src/gr4vy/roles.py @@ -0,0 +1,322 @@ +"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.""" + +from .basesdk import BaseSDK +from gr4vy import errors, models, utils +from gr4vy._hooks import HookContext +from gr4vy.types import OptionalNullable, UNSET +from gr4vy.utils import get_security_from_env +from gr4vy.utils.unmarshal_json_response import unmarshal_json_response +from jsonpath import JSONPath +from typing import Any, Dict, List, Mapping, Optional, Union + + +class Roles(BaseSDK): + def list( + self, + *, + cursor: OptionalNullable[str] = UNSET, + limit: Optional[int] = 20, + retries: OptionalNullable[utils.RetryConfig] = UNSET, + server_url: Optional[str] = None, + timeout_ms: Optional[int] = None, + http_headers: Optional[Mapping[str, str]] = None, + ) -> Optional[models.ListRolesResponse]: + r"""List all roles + + List all roles available in the instance. + + :param cursor: A pointer to the page of results to return. + :param limit: The maximum number of items that are returned. + :param retries: Override the default retry configuration for this method + :param server_url: Override the default server URL for this method + :param timeout_ms: Override the default request timeout configuration for this method in milliseconds + :param http_headers: Additional headers to set or replace on requests. + """ + base_url = None + url_variables = None + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.ListRolesRequest( + cursor=cursor, + limit=limit, + ) + + req = self._build_request( + method="GET", + path="/roles", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=False, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(200, 200, 1, 1000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["5XX"]) + + http_res = self.do_request( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="list_roles", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, models.Security + ), + tags=["Roles"], + extensions=None, + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + retry_config=retry_config, + ) + + def next_func() -> Optional[models.ListRolesResponse]: + body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) + next_cursor = JSONPath("$.next_cursor").parse(body) + + if len(next_cursor) == 0: + return None + + next_cursor = next_cursor[0] + if next_cursor is None or str(next_cursor).strip() == "": + return None + + return self.list( + cursor=next_cursor, + limit=limit, + retries=retries, + server_url=server_url, + timeout_ms=timeout_ms, + http_headers=http_headers, + ) + + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + return models.ListRolesResponse( + result=unmarshal_json_response(models.CollectionRole, http_res), + next=next_func, + ) + if utils.match_response(http_res, "400", "application/json"): + response_data = unmarshal_json_response(errors.Error400Data, http_res) + raise errors.Error400(response_data, http_res) + if utils.match_response(http_res, "401", "application/json"): + response_data = unmarshal_json_response(errors.Error401Data, http_res) + raise errors.Error401(response_data, http_res) + if utils.match_response(http_res, "403", "application/json"): + response_data = unmarshal_json_response(errors.Error403Data, http_res) + raise errors.Error403(response_data, http_res) + if utils.match_response(http_res, "404", "application/json"): + response_data = unmarshal_json_response(errors.Error404Data, http_res) + raise errors.Error404(response_data, http_res) + if utils.match_response(http_res, "405", "application/json"): + response_data = unmarshal_json_response(errors.Error405Data, http_res) + raise errors.Error405(response_data, http_res) + if utils.match_response(http_res, "409", "application/json"): + response_data = unmarshal_json_response(errors.Error409Data, http_res) + raise errors.Error409(response_data, http_res) + if utils.match_response(http_res, "422", "application/json"): + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res + ) + raise errors.HTTPValidationError(response_data, http_res) + if utils.match_response(http_res, "425", "application/json"): + response_data = unmarshal_json_response(errors.Error425Data, http_res) + raise errors.Error425(response_data, http_res) + if utils.match_response(http_res, "429", "application/json"): + response_data = unmarshal_json_response(errors.Error429Data, http_res) + raise errors.Error429(response_data, http_res) + if utils.match_response(http_res, "500", "application/json"): + response_data = unmarshal_json_response(errors.Error500Data, http_res) + raise errors.Error500(response_data, http_res) + if utils.match_response(http_res, "502", "application/json"): + response_data = unmarshal_json_response(errors.Error502Data, http_res) + raise errors.Error502(response_data, http_res) + if utils.match_response(http_res, "504", "application/json"): + response_data = unmarshal_json_response(errors.Error504Data, http_res) + raise errors.Error504(response_data, http_res) + if utils.match_response(http_res, "4XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.APIError("API error occurred", http_res, http_res_text) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = utils.stream_to_text(http_res) + raise errors.APIError("API error occurred", http_res, http_res_text) + + raise errors.APIError("Unexpected response received", http_res) + + async def list_async( + self, + *, + cursor: OptionalNullable[str] = UNSET, + limit: Optional[int] = 20, + retries: OptionalNullable[utils.RetryConfig] = UNSET, + server_url: Optional[str] = None, + timeout_ms: Optional[int] = None, + http_headers: Optional[Mapping[str, str]] = None, + ) -> Optional[models.ListRolesResponse]: + r"""List all roles + + List all roles available in the instance. + + :param cursor: A pointer to the page of results to return. + :param limit: The maximum number of items that are returned. + :param retries: Override the default retry configuration for this method + :param server_url: Override the default server URL for this method + :param timeout_ms: Override the default request timeout configuration for this method in milliseconds + :param http_headers: Additional headers to set or replace on requests. + """ + base_url = None + url_variables = None + if timeout_ms is None: + timeout_ms = self.sdk_configuration.timeout_ms + + if server_url is not None: + base_url = server_url + else: + base_url = self._get_url(base_url, url_variables) + + request = models.ListRolesRequest( + cursor=cursor, + limit=limit, + ) + + req = self._build_request_async( + method="GET", + path="/roles", + base_url=base_url, + url_variables=url_variables, + request=request, + request_body_required=False, + request_has_path_params=False, + request_has_query_params=True, + user_agent_header="user-agent", + accept_header_value="application/json", + http_headers=http_headers, + security=self.sdk_configuration.security, + allow_empty_value=None, + timeout_ms=timeout_ms, + ) + + if retries == UNSET: + if self.sdk_configuration.retry_config is not UNSET: + retries = self.sdk_configuration.retry_config + else: + retries = utils.RetryConfig( + "backoff", utils.BackoffStrategy(200, 200, 1, 1000), True + ) + + retry_config = None + if isinstance(retries, utils.RetryConfig): + retry_config = (retries, ["5XX"]) + + http_res = await self.do_request_async( + hook_ctx=HookContext( + config=self.sdk_configuration, + base_url=base_url or "", + operation_id="list_roles", + oauth2_scopes=None, + security_source=get_security_from_env( + self.sdk_configuration.security, models.Security + ), + tags=["Roles"], + extensions=None, + ), + request=req, + is_error_status_code=lambda c: utils.match_status_codes(["4XX", "5XX"], c), + retry_config=retry_config, + ) + + def next_func() -> Optional[models.ListRolesResponse]: + body = utils.unmarshal_json(http_res.text, Union[Dict[Any, Any], List[Any]]) + next_cursor = JSONPath("$.next_cursor").parse(body) + + if len(next_cursor) == 0: + return None + + next_cursor = next_cursor[0] + if next_cursor is None or str(next_cursor).strip() == "": + return None + + return self.list( + cursor=next_cursor, + limit=limit, + retries=retries, + server_url=server_url, + timeout_ms=timeout_ms, + http_headers=http_headers, + ) + + response_data: Any = None + if utils.match_response(http_res, "200", "application/json"): + return models.ListRolesResponse( + result=unmarshal_json_response(models.CollectionRole, http_res), + next=next_func, + ) + if utils.match_response(http_res, "400", "application/json"): + response_data = unmarshal_json_response(errors.Error400Data, http_res) + raise errors.Error400(response_data, http_res) + if utils.match_response(http_res, "401", "application/json"): + response_data = unmarshal_json_response(errors.Error401Data, http_res) + raise errors.Error401(response_data, http_res) + if utils.match_response(http_res, "403", "application/json"): + response_data = unmarshal_json_response(errors.Error403Data, http_res) + raise errors.Error403(response_data, http_res) + if utils.match_response(http_res, "404", "application/json"): + response_data = unmarshal_json_response(errors.Error404Data, http_res) + raise errors.Error404(response_data, http_res) + if utils.match_response(http_res, "405", "application/json"): + response_data = unmarshal_json_response(errors.Error405Data, http_res) + raise errors.Error405(response_data, http_res) + if utils.match_response(http_res, "409", "application/json"): + response_data = unmarshal_json_response(errors.Error409Data, http_res) + raise errors.Error409(response_data, http_res) + if utils.match_response(http_res, "422", "application/json"): + response_data = unmarshal_json_response( + errors.HTTPValidationErrorData, http_res + ) + raise errors.HTTPValidationError(response_data, http_res) + if utils.match_response(http_res, "425", "application/json"): + response_data = unmarshal_json_response(errors.Error425Data, http_res) + raise errors.Error425(response_data, http_res) + if utils.match_response(http_res, "429", "application/json"): + response_data = unmarshal_json_response(errors.Error429Data, http_res) + raise errors.Error429(response_data, http_res) + if utils.match_response(http_res, "500", "application/json"): + response_data = unmarshal_json_response(errors.Error500Data, http_res) + raise errors.Error500(response_data, http_res) + if utils.match_response(http_res, "502", "application/json"): + response_data = unmarshal_json_response(errors.Error502Data, http_res) + raise errors.Error502(response_data, http_res) + if utils.match_response(http_res, "504", "application/json"): + response_data = unmarshal_json_response(errors.Error504Data, http_res) + raise errors.Error504(response_data, http_res) + if utils.match_response(http_res, "4XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.APIError("API error occurred", http_res, http_res_text) + if utils.match_response(http_res, "5XX", "*"): + http_res_text = await utils.stream_to_text_async(http_res) + raise errors.APIError("API error occurred", http_res, http_res_text) + + raise errors.APIError("Unexpected response received", http_res) diff --git a/src/gr4vy/sdk.py b/src/gr4vy/sdk.py index 52088859..4e92414a 100644 --- a/src/gr4vy/sdk.py +++ b/src/gr4vy/sdk.py @@ -34,6 +34,7 @@ from gr4vy.refunds_sdk import RefundsSDK from gr4vy.report_executions_sdk import ReportExecutionsSDK from gr4vy.reports_sdk import ReportsSDK + from gr4vy.roles import Roles from gr4vy.three_ds_scenarios import ThreeDsScenarios from gr4vy.transactions import Transactions @@ -57,6 +58,7 @@ class Gr4vy(BaseSDK): reports: "ReportsSDK" report_executions: "ReportExecutionsSDK" checkout_sessions: "CheckoutSessions" + roles: "Roles" merchant_accounts: "MerchantAccountsSDK" three_ds_scenarios: "ThreeDsScenarios" payouts: "Payouts" @@ -84,6 +86,7 @@ class Gr4vy(BaseSDK): "reports": ("gr4vy.reports_sdk", "ReportsSDK"), "report_executions": ("gr4vy.report_executions_sdk", "ReportExecutionsSDK"), "checkout_sessions": ("gr4vy.checkout_sessions", "CheckoutSessions"), + "roles": ("gr4vy.roles", "Roles"), "merchant_accounts": ("gr4vy.merchant_accounts_sdk", "MerchantAccountsSDK"), "three_ds_scenarios": ("gr4vy.three_ds_scenarios", "ThreeDsScenarios"), "payouts": ("gr4vy.payouts", "Payouts"), From a157bc11776e1fdcb2581b90198e813c7573e9d4 Mon Sep 17 00:00:00 2001 From: "speakeasy-github[bot]" <128539517+speakeasy-github[bot]@users.noreply.github.com> Date: Thu, 13 Aug 2026 08:16:27 +0000 Subject: [PATCH 2/2] empty commit to trigger [run-tests] workflow