diff --git a/.fern/replay.lock b/.fern/replay.lock new file mode 100644 index 0000000..98469bf --- /dev/null +++ b/.fern/replay.lock @@ -0,0 +1,10 @@ +# DO NOT EDIT MANUALLY - Managed by Fern Replay +version: "1.0" +generations: + - commit_sha: 1c0ed7f061d32b15b4d3fea2d5a307f57b3f4d31 + tree_hash: 4b6868bf849baafbdbec5aa6de743fafad709fcf + timestamp: 2026-06-16T13:15:23.133Z + cli_version: unknown + generator_versions: {} +current_generation: 1c0ed7f061d32b15b4d3fea2d5a307f57b3f4d31 +patches: [] diff --git a/.fernignore b/.fernignore index 3a381e0..9aae8ca 100644 --- a/.fernignore +++ b/.fernignore @@ -11,7 +11,6 @@ src/auth0/myorganization/token_provider.py src/auth0/myorganization/core/client_wrapper.py # Root auth0 __init__.pys -src/auth0/myorganization/__init__.py src/auth0/__init__.py # Tests @@ -33,3 +32,6 @@ CHANGELOG.md .github/ LICENSE NOTICE.txt +.fern/replay.lock +.fern/replay.yml +.gitattributes diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2843a09..6bb196e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -68,7 +68,7 @@ jobs: - name: Configure Python uses: actions/setup-python@v6 with: - python-version: "3.9" + python-version: "3.12" - name: Configure dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8645d7d..6a6379b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout code diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..af948ce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,125 @@ +# Contributing + +Thanks for your interest in contributing to this SDK! This document provides guidelines for contributing to the project. + +## Getting Started + +### Prerequisites + +- Python 3.9+ +- pip +- poetry + +### Installation + +Install the project dependencies: + +```bash +poetry install +``` + +### Building + +Build the project: + +```bash +poetry build +``` + +### Testing + +Run the test suite: + +```bash +poetry run pytest +``` + +### Linting and Formatting + +Check code style: + +```bash +poetry run ruff check . +poetry run ruff format . +``` + +### Type Checking + +Run the type checker: + +```bash +poetry run mypy . +``` + +## About Generated Code + +**Important**: Most files in this SDK are automatically generated by [Fern](https://buildwithfern.com) from the API definition. Direct modifications to generated files will be overwritten the next time the SDK is generated. + +### Generated Files + +The following directories contain generated code: +- `src/` - API client classes and types +- Most Python files in the project + +### How to Customize + +If you need to customize the SDK, you have two options: + +#### Option 1: Use `.fernignore` + +For custom code that should persist across SDK regenerations: + +1. Create a `.fernignore` file in the project root +2. Add file patterns for files you want to preserve (similar to `.gitignore` syntax) +3. Add your custom code to those files + +Files listed in `.fernignore` will not be overwritten when the SDK is regenerated. + +For more information, see the [Fern documentation on custom code](https://buildwithfern.com/learn/sdks/overview/custom-code). + +#### Option 2: Contribute to the Generator + +If you want to change how code is generated for all users of this SDK: + +1. The Python SDK generator lives in the [Fern repository](https://github.com/fern-api/fern) +2. Generator code is located at `generators/python-v2/` +3. Follow the [Fern contributing guidelines](https://github.com/fern-api/fern/blob/main/CONTRIBUTING.md) +4. Submit a pull request with your changes to the generator + +This approach is best for: +- Bug fixes in generated code +- New features that would benefit all users +- Improvements to code generation patterns + +## Making Changes + +### Workflow + +1. Create a new branch for your changes +2. Make your modifications +3. Run tests to ensure nothing breaks: `poetry run pytest` +4. Run linting and formatting: `poetry run ruff check .` and `poetry run ruff format .` +5. Run type checking: `poetry run mypy .` +6. Build the project: `poetry build` +7. Commit your changes with a clear commit message +8. Push your branch and create a pull request + +### Commit Messages + +Write clear, descriptive commit messages that explain what changed and why. + +### Code Style + +This project uses automated code formatting and linting. Run `poetry run ruff format .` and `poetry run ruff check .` before committing to ensure your code meets the project's style guidelines. + +## Questions or Issues? + +If you have questions or run into issues: + +1. Check the [Fern documentation](https://buildwithfern.com) +2. Search existing [GitHub issues](https://github.com/fern-api/fern/issues) +3. Open a new issue if your question hasn't been addressed + +## License + +By contributing to this project, you agree that your contributions will be licensed under the same license as the project. diff --git a/README.md b/README.md index 38d2eab..4fa5b95 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This library supports the following tooling versions: -- Python >= 3.9 +- Python >= 3.10 ### Installation diff --git a/poetry.lock b/poetry.lock index 3e2d014..1d60108 100644 --- a/poetry.lock +++ b/poetry.lock @@ -278,7 +278,7 @@ description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version < \"3.11\"" +markers = "python_version == \"3.10\"" files = [ {file = "exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598"}, {file = "exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219"}, @@ -391,64 +391,177 @@ files = [ {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] +[[package]] +name = "librt" +version = "0.11.0" +description = "Mypyc runtime library" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "librt-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e94ebfcfa2d5e9926d6c3b9aa4617ffc42a845b4321fb84021b872358c82a0f"}, + {file = "librt-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae627397a2f351560440d872d6f7c8dbb4072e57868e7b2fc5b8b430fe489d45"}, + {file = "librt-0.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc329359321b67d24efdf4bc69012b0597001649544db662c001db5a0184794c"}, + {file = "librt-0.11.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:7e82e642ab0f7608ce2fe53d76ca2280a9ee33a1b06556142c7c6fe80a86fc33"}, + {file = "librt-0.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88145c15c67731d54283d135b03244028c750cc9edc334a96a4f5950ebdb2884"}, + {file = "librt-0.11.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d36a51b3d93320b686588e27123f4995804dbf1bce81df78c02fc3c6eea9280"}, + {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3ac06a2a8b246327f11e186a53a100a4d5c7ed52346367e5ec751d51586c"}, + {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:461bbceede621f1ffb8839755f8663e886087ee7af16294cab7fb4d782c62eeb"}, + {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0cad8a4d6a8ff03c9b76f9414caccd78e7cfbc8a2e12fa334d8e1d9932753783"}, + {file = "librt-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f37aa505b3cf60701562eddb32df74b12a9e380c207fd8b06dd157a943ac7ea0"}, + {file = "librt-0.11.0-cp310-cp310-win32.whl", hash = "sha256:94663a21534637f0e787ec2a2a756022df6e5b7b2335a5cdd7d8e33d68a2af89"}, + {file = "librt-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:dec7db73758c2b54953fd8b7fe348c45188fe26b39ee18446196edd08453a5d4"}, + {file = "librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29"}, + {file = "librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b"}, + {file = "librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89"}, + {file = "librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d"}, + {file = "librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412"}, + {file = "librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d"}, + {file = "librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73"}, + {file = "librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c"}, + {file = "librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46"}, + {file = "librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a"}, + {file = "librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a"}, + {file = "librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d"}, + {file = "librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8"}, + {file = "librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a"}, + {file = "librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9"}, + {file = "librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c"}, + {file = "librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894"}, + {file = "librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230"}, + {file = "librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2"}, + {file = "librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be"}, + {file = "librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e"}, + {file = "librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e"}, + {file = "librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47"}, + {file = "librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44"}, + {file = "librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd"}, + {file = "librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b"}, + {file = "librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175"}, + {file = "librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96"}, + {file = "librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe"}, + {file = "librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f"}, + {file = "librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7"}, + {file = "librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1"}, + {file = "librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72"}, + {file = "librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f"}, + {file = "librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3"}, + {file = "librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd"}, + {file = "librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8"}, + {file = "librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c"}, + {file = "librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253"}, + {file = "librt-0.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6bd72d903911d995ab666dbd1871f8b1e80925a699af8063fbf50053329fb05f"}, + {file = "librt-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ef69ac715f3cd8e5cd252cb2aebfa72c015492aacc339d5d7bf8fef3c62c677"}, + {file = "librt-0.11.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:624a40c4a4ad7773315c287276cd024509b2c66ff5904f504bfc08d2c70293ab"}, + {file = "librt-0.11.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:41dc19fe150b69716c8ece4f76773a9e8813fe3e35e032a58b4d46423fb8d7c0"}, + {file = "librt-0.11.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e8bd98ea9c47ae90b319a087ab28dac493f1ffbc1ecd1f28fcdbf3b7e1108d1"}, + {file = "librt-0.11.0-cp39-cp39-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:84308fc49423ce6475d1c5d1985cd69a8ca9f0325fc7d5f81bb690a3f3625d4e"}, + {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ff0fbaf5f44a21beeb0110f2ab64f45135a9536a834b79c0d1ef018f2786bbfa"}, + {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9c028a9442a18e266955d364ce42259136e79a7ba14d773e0d778d5f70cd56f1"}, + {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9f1692105a02bcf853f355032a5fdc5494358ef83d8fd22d16de375c85cec3f5"}, + {file = "librt-0.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7a80a71e1fda83cc752a9141e87aae7fef279538597564d670e9ce513f286192"}, + {file = "librt-0.11.0-cp39-cp39-win32.whl", hash = "sha256:140695816ddf3c86eb972981a26f35efd871c44b0c3aed44c8cd01749386617f"}, + {file = "librt-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:92f7ff819c197fc30473190a12c2856f325ac90aabfccbeb2072d28cc2e234e3"}, + {file = "librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1"}, +] + [[package]] name = "mypy" -version = "1.14.1" +version = "1.20.2" description = "Optional static typing for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, + {file = "mypy-1.20.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cf5a4db6dca263010e2c7bff081c89383c72d187ba2cf4c44759aac970e2f0c4"}, + {file = "mypy-1.20.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b0e817b518bff7facd7f85ea05b643ad8bdcce684cf29784987b0a7c8e1f997"}, + {file = "mypy-1.20.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d7b9a485b40f8ca425460e89bf1da2814625b2da627c0dcc6aa46c92631d14"}, + {file = "mypy-1.20.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e1c12f6d2db3d78b909b5f77513c11eb7f2dd2782b96a3ab6dffc7d44575c99"}, + {file = "mypy-1.20.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:89dce27e142d25ffbc154c1819383b69f2e9234dc4ed4766f42e0e8cb264ab5c"}, + {file = "mypy-1.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:f376e37f9bf2a946872fc5fd1199c99310748e3c26c7a26683f13f8bdb756cbd"}, + {file = "mypy-1.20.2-cp310-cp310-win_arm64.whl", hash = "sha256:6e2b469efd811707bc530fd1effef0f5d6eebcb7fe376affae69025da4b979a2"}, + {file = "mypy-1.20.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4077797a273e56e8843d001e9dfe4ba10e33323d6ade647ff260e5cd97d9758c"}, + {file = "mypy-1.20.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cdecf62abcc4292500d7858aeae87a1f8f1150f4c4dd08fb0b336ee79b2a6df3"}, + {file = "mypy-1.20.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c566c3a88b6ece59b3d70f65bedef17304f48eb52ff040a6a18214e1917b3254"}, + {file = "mypy-1.20.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0deb80d062b2479f2c87ae568f89845afc71d11bc41b04179e58165fd9f31e98"}, + {file = "mypy-1.20.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bba9ad231e92a3e424b3e56b65aa17704993425bba97e302c832f9466bb85bac"}, + {file = "mypy-1.20.2-cp311-cp311-win_amd64.whl", hash = "sha256:baf593f2765fa3a6b1ef95807dbaa3d25b594f6a52adcc506a6b9cb115e1be67"}, + {file = "mypy-1.20.2-cp311-cp311-win_arm64.whl", hash = "sha256:20175a1c0f49863946ec20b7f63255768058ac4f07d2b9ded6a6b46cfb5a9100"}, + {file = "mypy-1.20.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4dbfcf869f6b0517f70cf0030ba6ea1d6645e132337a7d5204a18d8d5636c02b"}, + {file = "mypy-1.20.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4b6481b228d072315b053210b01ac320e1be243dc17f9e5887ef167f23f5fae4"}, + {file = "mypy-1.20.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:34397cdced6b90b836e38182076049fdb41424322e0b0728c946b0939ebdf9f6"}, + {file = "mypy-1.20.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a5da6976f20cae27059ea8d0c86e7cef3de720e04c4bb9ee18e3690fdb792066"}, + {file = "mypy-1.20.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:56908d7e08318d39f85b1f0c6cfd47b0cac1a130da677630dac0de3e0623e102"}, + {file = "mypy-1.20.2-cp312-cp312-win_amd64.whl", hash = "sha256:d52ad8d78522da1d308789df651ee5379088e77c76cb1994858d40a426b343b9"}, + {file = "mypy-1.20.2-cp312-cp312-win_arm64.whl", hash = "sha256:785b08db19c9f214dc37d65f7c165d19a30fcecb48abfa30f31b01b5acaabb58"}, + {file = "mypy-1.20.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:edfbfca868cdd6bd8d974a60f8a3682f5565d3f5c99b327640cedd24c4264026"}, + {file = "mypy-1.20.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e2877a02380adfcdbc69071a0f74d6e9dbbf593c0dc9d174e1f223ffd5281943"}, + {file = "mypy-1.20.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7488448de6007cd5177c6cea0517ac33b4c0f5ee9b5e9f2be51ce75511a85517"}, + {file = "mypy-1.20.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bb9c2fa06887e21d6a3a868762acb82aec34e2c6fd0174064f27c93ede68ad15"}, + {file = "mypy-1.20.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d56a78b646f2e3daa865bc70cd5ec5a46c50045801ca8ff17a0c43abc97e3ee"}, + {file = "mypy-1.20.2-cp313-cp313-win_amd64.whl", hash = "sha256:2a4102b03bb7481d9a91a6da8d174740c9c8c4401024684b9ca3b7cc5e49852f"}, + {file = "mypy-1.20.2-cp313-cp313-win_arm64.whl", hash = "sha256:a95a9248b0c6fd933a442c03c3b113c3b61320086b88e2c444676d3fd1ca3330"}, + {file = "mypy-1.20.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:419413398fe250aae057fd2fe50166b61077083c9b82754c341cf4fd73038f30"}, + {file = "mypy-1.20.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e73c07f23009962885c197ccb9b41356a30cc0e5a1d0c2ea8fd8fb1362d7f924"}, + {file = "mypy-1.20.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c64e5973df366b747646fc98da921f9d6eba9716d57d1db94a83c026a08e0fb"}, + {file = "mypy-1.20.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a65aa591af023864fd08a97da9974e919452cfe19cb146c8a5dc692626445dc"}, + {file = "mypy-1.20.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4fef51b01e638974a6e69885687e9bd40c8d1e09a6cd291cca0619625cf1f558"}, + {file = "mypy-1.20.2-cp314-cp314-win_amd64.whl", hash = "sha256:913485a03f1bcf5d279409a9d2b9ed565c151f61c09f29991e5faa14033da4c8"}, + {file = "mypy-1.20.2-cp314-cp314-win_arm64.whl", hash = "sha256:c3bae4f855d965b5453784300c12ffc63a548304ac7f99e55d4dc7c898673aa3"}, + {file = "mypy-1.20.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2de3dcea53babc1c3237a19002bc3d228ce1833278f093b8d619e06e7cc79609"}, + {file = "mypy-1.20.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:52b176444e2e5054dfcbcb8c75b0b719865c96247b37407184bbfca5c353f2c2"}, + {file = "mypy-1.20.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:688c3312e5dadb573a2c69c82af3a298d43ecf9e6d264e0f95df960b5f6ac19c"}, + {file = "mypy-1.20.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29752dbbf8cc53f89f6ac096d363314333045c257c9c75cbd189ca2de0455744"}, + {file = "mypy-1.20.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:803203d2b6ea644982c644895c2f78b28d0e208bba7b27d9b921e0ec5eb207c6"}, + {file = "mypy-1.20.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9bcb8aa397ff0093c824182fd76a935a9ba7ad097fcbef80ae89bf6c1731d8ec"}, + {file = "mypy-1.20.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e061b58443f1736f8a37c48978d7ab581636d6ab03e3d4f99e3fa90463bb9382"}, + {file = "mypy-1.20.2-py3-none-any.whl", hash = "sha256:a94c5a76ab46c5e6257c7972b6c8cff0574201ca7dc05647e33e795d78680563"}, + {file = "mypy-1.20.2.tar.gz", hash = "sha256:e8222c26daaafd9e8626dec58ae36029f82585890589576f769a650dd20fd665"}, ] [package.dependencies] +librt = {version = ">=0.8.0", markers = "platform_python_implementation != \"PyPy\""} mypy_extensions = ">=1.0.0" +pathspec = ">=1.0.0" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing_extensions = ">=4.6.0" +typing_extensions = [ + {version = ">=4.6.0", markers = "python_version < \"3.15\""}, + {version = ">=4.14.0", markers = "python_version >= \"3.15\""}, +] [package.extras] dmypy = ["psutil (>=4.0)"] faster-cache = ["orjson"] install-types = ["pip"] mypyc = ["setuptools (>=50)"] +native-parser = ["ast-serialize (>=0.1.1,<1.0.0)"] reports = ["lxml"] [[package]] @@ -475,6 +588,23 @@ files = [ {file = "packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4"}, ] +[[package]] +name = "pathspec" +version = "1.1.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189"}, + {file = "pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a"}, +] + +[package.extras] +hyperscan = ["hyperscan (>=0.7)"] +optional = ["typing-extensions (>=4)"] +re2 = ["google-re2 (>=1.1)"] + [[package]] name = "pluggy" version = "1.5.0" @@ -688,14 +818,14 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale [[package]] name = "pytest-xdist" -version = "3.6.1" +version = "3.8.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, + {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, + {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, ] [package.dependencies] @@ -724,25 +854,25 @@ six = ">=1.5" [[package]] name = "requests" -version = "2.32.4" +version = "2.34.2" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, - {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, + {file = "requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0"}, + {file = "requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed"}, ] [package.dependencies] -certifi = ">=2017.4.17" +certifi = ">=2023.5.7" charset_normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" +urllib3 = ">=1.26,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<8)"] [[package]] name = "ruff" @@ -856,26 +986,26 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20241206" +version = "2.9.0.20260518" description = "Typing stubs for python-dateutil" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, - {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, + {file = "types_python_dateutil-2.9.0.20260518-py3-none-any.whl", hash = "sha256:d6a9c5bd0de61460c8fdef8ab2b400f956a1a1075cce08d4e2b4434e478c50b8"}, + {file = "types_python_dateutil-2.9.0.20260518.tar.gz", hash = "sha256:51f02dc03b61c7f6a07df45797d4dfe8a1aa47f0b7db9ad89f6fd3a1a70e1b51"}, ] [[package]] name = "types-requests" -version = "2.32.0.20241016" +version = "2.33.0.20260518" description = "Typing stubs for requests" optional = false -python-versions = ">=3.8" +python-versions = ">=3.10" groups = ["dev"] files = [ - {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, - {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, + {file = "types_requests-2.33.0.20260518-py3-none-any.whl", hash = "sha256:626d697d1adaaff76e2044dc8c5c051d8f21abc157bdfe204a75558076fe0bf0"}, + {file = "types_requests-2.33.0.20260518.tar.gz", hash = "sha256:df7bd3bfe0ca8402dfb841e7d9be714bb5578203283d66d7dc4ef69343449a5e"}, ] [package.dependencies] @@ -888,11 +1018,25 @@ description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" groups = ["main", "dev"] +markers = "python_version < \"3.15\"" files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +markers = "python_version >= \"3.15\"" +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + [[package]] name = "urllib3" version = "2.2.3" @@ -913,5 +1057,5 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.1" -python-versions = ">=3.9,<4.0" -content-hash = "bd9ea8d5d169daa6e5d0025af88d2975e6b25ec91a4b8985134a8d1247f5b146" +python-versions = ">=3.10,<4.0" +content-hash = "b24a5e77fb15649f546a09f934dbc9e3a644fe2bc3ea160b5cad78f826976e24" diff --git a/pyproject.toml b/pyproject.toml index 0f20aed..e411f75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,6 @@ classifiers = [ "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -36,7 +35,7 @@ Repository = 'https://github.com/auth0/myorganization-python' Homepage = 'https://auth0.com' [tool.poetry.dependencies] -python = ">=3.9,<4.0" +python = ">=3.10,<4.0" httpx = ">=0.21.2" pydantic = ">= 1.9.2" pydantic-core = ">=2.18.2" diff --git a/reference.md b/reference.md index 2f84ce8..5b139a7 100644 --- a/reference.md +++ b/reference.md @@ -611,8 +611,14 @@ client = Auth0( client.organization.identity_providers.create( request=IdpOidcRequest( - name="oidcIdp", strategy="oidc", + options=IdpOidcOptionsRequest( + type="front_channel", + client_id="a8f3b2e7-5d1c-4f9a-8b0d-2e1c3a5b6f7d", + client_secret="KzQp2sVxR8nTgMjFhYcEWuLoIbDvUoC6A9B1zX7yWqFjHkGrP5sQdLmNp", + discovery_url="https://{yourDomain}/.well-known/openid-configuration", + ), + name="oidcIdp", domains=[ "mydomain.com" ], @@ -620,12 +626,6 @@ client.organization.identity_providers.create( show_as_button=True, assign_membership_on_login=False, is_enabled=True, - options=IdpOidcOptionsRequest( - type="front_channel", - client_id="a8f3b2e7-5d1c-4f9a-8b0d-2e1c3a5b6f7d", - client_secret="KzQp2sVxR8nTgMjFhYcEWuLoIbDvUoC6A9B1zX7yWqFjHkGrP5sQdLmNp", - discovery_url="https://{yourDomain}/.well-known/openid-configuration", - ), ), ) @@ -1059,8 +1059,8 @@ client.organization.identity_providers.detach( -## Organization Configuration IdentityProviders -
client.organization.configuration.identity_providers.get() -> GetIdpConfigurationResponseContent +## Organization Members +
client.organization.members.list(...) -> ListOrganizationMembersResponseContent
@@ -1072,7 +1072,7 @@ client.organization.identity_providers.detach(
-Retrieve the [Connection Profile](https://auth0.com/docs/authenticate/enterprise-connections/connection-profile) for this application. You should cache this information as it does not change frequently. +Retrieve a list of all members for this Organization. The `roles` field is only included for each member when the token also carries the `read:my_org:member_roles` scope; without that scope the `roles` field is omitted from the response.
@@ -1095,7 +1095,12 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.configuration.identity_providers.get() +client.organization.members.list( + fields="fields", + include_fields=True, + from_="from", + take=1, +) ``` @@ -1111,6 +1116,38 @@ client.organization.configuration.identity_providers.get()
+**fields:** `typing.Optional[str]` — Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + +
+
+ +
+
+ +**include_fields:** `typing.Optional[bool]` — Whether specified fields are to be included (true) or excluded (false). Defaults to true + +
+
+ +
+
+ +**from:** `typing.Optional[str]` — An optional cursor from which to start the selection (exclusive). + +
+
+ +
+
+ +**take:** `typing.Optional[int]` — Number of results per page. Defaults to 50. + +
+
+ +
+
+ **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -1123,8 +1160,7 @@ client.organization.configuration.identity_providers.get()
-## Organization Domains Verify -
client.organization.domains.verify.create(...) -> StartOrganizationDomainVerificationResponseContent +
client.organization.members.get(...) -> GetOrganizationMemberResponseContent
@@ -1136,7 +1172,7 @@ client.organization.configuration.identity_providers.get()
-Initiate the verification process for a domain specified by ID for this Organization. +Retrieve details of a member specified by user ID for this Organization.
@@ -1159,8 +1195,10 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.domains.verify.create( - domain_id="domain_id", +client.organization.members.get( + user_id="user_id", + fields="fields", + include_fields=True, ) ``` @@ -1177,7 +1215,23 @@ client.organization.domains.verify.create(
-**domain_id:** `OrgDomainId` +**user_id:** `OrgMemberId` + +
+
+ +
+
+ +**fields:** `typing.Optional[str]` — Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + +
+
+ +
+
+ +**include_fields:** `typing.Optional[bool]` — Whether specified fields are to be included (true) or excluded (false). Defaults to true
@@ -1197,8 +1251,8 @@ client.organization.domains.verify.create(
-## Organization Domains IdentityProviders -
client.organization.domains.identity_providers.get(...) -> ListDomainIdentityProvidersResponseContent +## Organization Memberships +
client.organization.memberships.delete_memberships(...)
@@ -1210,7 +1264,7 @@ client.organization.domains.verify.create(
-Retrieve the list of Identity Providers associated with a domain specified by ID for this Organization. +Remove one member from this Organization. The underlying user account is not deleted.
@@ -1233,8 +1287,10 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.domains.identity_providers.get( - domain_id="domain_id", +client.organization.memberships.delete_memberships( + members=[ + "auth0|1234567890" + ], ) ``` @@ -1251,7 +1307,7 @@ client.organization.domains.identity_providers.get(
-**domain_id:** `OrgDomainId` +**members:** `typing.List[OrgMemberId]`
@@ -1271,8 +1327,8 @@ client.organization.domains.identity_providers.get(
-## Organization IdentityProviders Domains -
client.organization.identity_providers.domains.create(...) -> CreateIdpDomainResponseContent +## Organization Invitations +
client.organization.invitations.list(...) -> ListMembersInvitationsResponseContent
@@ -1284,7 +1340,7 @@ client.organization.domains.identity_providers.get(
-Associate a domain with an Identity Provider specified by ID for this Organization. The domain must be claimed and verified. +Retrieve a list of all member invitations for this Organization.
@@ -1307,9 +1363,12 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.domains.create( - idp_id="idp_id", - domain="my-domain.com", +client.organization.invitations.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + sort="sort", ) ``` @@ -1326,7 +1385,7 @@ client.organization.identity_providers.domains.create(
-**idp_id:** `IdpId` +**fields:** `typing.Optional[str]` — Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered.
@@ -1334,7 +1393,31 @@ client.organization.identity_providers.domains.create(
-**domain:** `OrgDomainName` +**include_fields:** `typing.Optional[bool]` — Whether specified fields are to be included (true) or excluded (false). Defaults to true + +
+
+ +
+
+ +**from:** `typing.Optional[str]` — An optional cursor from which to start the selection (exclusive). + +
+
+ +
+
+ +**take:** `typing.Optional[int]` — Number of results per page. Defaults to 50. + +
+
+ +
+
+ +**sort:** `typing.Optional[str]` — Field to sort by. Use field:order where order is 1 for ascending and -1 for descending. Defaults to created_at:-1
@@ -1354,7 +1437,7 @@ client.organization.identity_providers.domains.create(
-
client.organization.identity_providers.domains.delete(...) +
client.organization.invitations.create(...) -> CreateMemberInvitationResponseContent
@@ -1366,7 +1449,7 @@ client.organization.identity_providers.domains.create(
-Remove a domain specified by name from an Identity Provider specified by ID for this Organization. +Create one or more member invitations for this Organization. If an active invitation already exists for a user, generating a new invitation will automatically revoke any outstanding invitations for that user. Roles specified in the payload will be granted to the user upon acceptance of the invitation.
@@ -1381,7 +1464,7 @@ Remove a domain specified by name from an Identity Provider specified by ID for
```python -from auth0.myorganization import Auth0 +from auth0.myorganization import Auth0, CreateMemberInvitationInvitee, MemberInvitationInviter from auth0.myorganization.environment import Auth0Environment client = Auth0( @@ -1389,9 +1472,20 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.domains.delete( - idp_id="idp_id", - domain="domain", +client.organization.invitations.create( + invitees=[ + CreateMemberInvitationInvitee( + email="user@example.com", + roles=[ + "rol_0000000000000001" + ], + ) + ], + inviter=MemberInvitationInviter( + name="Allison the Admin", + ), + identity_provider_id="con_2CZPv6IY0gWzDaQJ", + ttl_sec=3600, ) ``` @@ -1408,7 +1502,7 @@ client.organization.identity_providers.domains.delete(
-**idp_id:** `IdpId` +**invitees:** `typing.List[CreateMemberInvitationInvitee]`
@@ -1416,7 +1510,31 @@ client.organization.identity_providers.domains.delete(
-**domain:** `OrgDomainName` +**auth_0_custom_domain:** `typing.Optional[str]` + +
+
+ +
+
+ +**inviter:** `typing.Optional[MemberInvitationInviter]` + +
+
+ +
+
+ +**identity_provider_id:** `typing.Optional[str]` — Identity provider identifier. + +
+
+ +
+
+ +**ttl_sec:** `typing.Optional[int]` — Number of seconds for which the invitation is valid before expiration. If unspecified or set to 0, this value defaults to 604800 seconds (7 days). Max value: 2592000 seconds (30 days).
@@ -1436,8 +1554,7 @@ client.organization.identity_providers.domains.delete(
-## Organization IdentityProviders Provisioning -
client.organization.identity_providers.provisioning.get(...) -> GetIdPProvisioningConfigResponseContent +
client.organization.invitations.get(...) -> GetMemberInvitationResponseContent
@@ -1449,7 +1566,7 @@ client.organization.identity_providers.domains.delete(
-Retrieve the Provisioning Configuration for an Identity Provider specified by ID for this Organization. +Retrieve details of a member invitation specified by ID for this Organization.
@@ -1472,8 +1589,10 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.get( - idp_id="idp_id", +client.organization.invitations.get( + invitation_id="invitation_id", + fields="fields", + include_fields=True, ) ``` @@ -1490,7 +1609,23 @@ client.organization.identity_providers.provisioning.get(
-**idp_id:** `IdpId` +**invitation_id:** `InvitationId` + +
+
+ +
+
+ +**fields:** `typing.Optional[str]` — Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + +
+
+ +
+
+ +**include_fields:** `typing.Optional[bool]` — Whether specified fields are to be included (true) or excluded (false). Defaults to true
@@ -1510,7 +1645,7 @@ client.organization.identity_providers.provisioning.get(
-
client.organization.identity_providers.provisioning.create(...) -> CreateIdPProvisioningConfigResponseContent +
client.organization.invitations.delete(...)
@@ -1522,7 +1657,7 @@ client.organization.identity_providers.provisioning.get(
-Create a new Provisioning Configuration for an Identity Provider specified by ID for this Organization. +Revoke a member invitation specified by ID for this Organization.
@@ -1545,8 +1680,8 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.create( - idp_id="idp_id", +client.organization.invitations.delete( + invitation_id="invitation_id", ) ``` @@ -1563,7 +1698,7 @@ client.organization.identity_providers.provisioning.create(
-**idp_id:** `IdpId` +**invitation_id:** `InvitationId`
@@ -1583,7 +1718,8 @@ client.organization.identity_providers.provisioning.create(
-
client.organization.identity_providers.provisioning.delete(...) +## Organization Roles +
client.organization.roles.list(...) -> ListRolesResponseContent
@@ -1595,7 +1731,7 @@ client.organization.identity_providers.provisioning.create(
-Delete the Provisioning Configuration for an Identity Provider specified by ID for this Organization. +Retrieve the list of roles available for binding to members and invitations for this Organization. Only roles made visible to this Organization by the Tenant Admin are returned.
@@ -1618,8 +1754,10 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.delete( - idp_id="idp_id", +client.organization.roles.list( + from_="from", + take=1, + name="name", ) ``` @@ -1636,7 +1774,23 @@ client.organization.identity_providers.provisioning.delete(
-**idp_id:** `IdpId` +**from:** `typing.Optional[str]` — An optional cursor from which to start the selection (exclusive). + +
+
+ +
+
+ +**take:** `typing.Optional[int]` — Number of results per page. Defaults to 50. + +
+
+ +
+
+ +**name:** `typing.Optional[str]` — An optional filter on the name (case-insensitive).
@@ -1656,7 +1810,8 @@ client.organization.identity_providers.provisioning.delete(
-
client.organization.identity_providers.provisioning.update_attributes(...) -> GetIdPProvisioningConfigResponseContent +## Organization Configuration IdentityProviders +
client.organization.configuration.identity_providers.get() -> GetIdpConfigurationResponseContent
@@ -1668,7 +1823,7 @@ client.organization.identity_providers.provisioning.delete(
-Refresh the attribute mapping for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. Mappings are reset to the admin-defined defaults. +Retrieve the [Connection Profile](https://auth0.com/docs/authenticate/enterprise-connections/connection-profile) for this application. You should cache this information as it does not change frequently.
@@ -1691,12 +1846,7 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.update_attributes( - idp_id="idp_id", - request={ - "key": "value" - }, -) +client.organization.configuration.identity_providers.get() ``` @@ -1712,22 +1862,6 @@ client.organization.identity_providers.provisioning.update_attributes(
-**idp_id:** `IdpId` - -
-
- -
-
- -**request:** `typing.Dict[str, typing.Any]` - -
-
- -
-
- **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -1740,8 +1874,8 @@ client.organization.identity_providers.provisioning.update_attributes(
-## Organization IdentityProviders Provisioning ScimTokens -
client.organization.identity_providers.provisioning.scim_tokens.list(...) -> ListIdpProvisioningScimTokensResponseContent +## Organization Domains Verify +
client.organization.domains.verify.create(...) -> StartOrganizationDomainVerificationResponseContent
@@ -1753,7 +1887,7 @@ client.organization.identity_providers.provisioning.update_attributes(
-Retrieve a list of [SCIM tokens](https://auth0.com/docs/authenticate/protocols/scim/configure-inbound-scim#scim-endpoints-and-tokens) for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. +Initiate the verification process for a domain specified by ID for this Organization.
@@ -1776,8 +1910,8 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.scim_tokens.list( - idp_id="idp_id", +client.organization.domains.verify.create( + domain_id="domain_id", ) ``` @@ -1794,7 +1928,7 @@ client.organization.identity_providers.provisioning.scim_tokens.list(
-**idp_id:** `IdpId` +**domain_id:** `OrgDomainId`
@@ -1814,7 +1948,8 @@ client.organization.identity_providers.provisioning.scim_tokens.list(
-
client.organization.identity_providers.provisioning.scim_tokens.create(...) -> CreateIdpProvisioningScimTokenResponseContent +## Organization Domains IdentityProviders +
client.organization.domains.identity_providers.get(...) -> ListDomainIdentityProvidersResponseContent
@@ -1826,7 +1961,7 @@ client.organization.identity_providers.provisioning.scim_tokens.list(
-Create a new SCIM token for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. +Retrieve the list of Identity Providers associated with a domain specified by ID for this Organization.
@@ -1849,9 +1984,8 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.scim_tokens.create( - idp_id="idp_id", - token_lifetime=86400, +client.organization.domains.identity_providers.get( + domain_id="domain_id", ) ``` @@ -1868,15 +2002,7 @@ client.organization.identity_providers.provisioning.scim_tokens.create(
-**idp_id:** `IdpId` - -
-
- -
-
- -**token_lifetime:** `typing.Optional[int]` — Lifetime of the token in seconds. Do not set for non-expiring tokens. +**domain_id:** `OrgDomainId`
@@ -1896,7 +2022,8 @@ client.organization.identity_providers.provisioning.scim_tokens.create(
-
client.organization.identity_providers.provisioning.scim_tokens.delete(...) +## Organization IdentityProviders Domains +
client.organization.identity_providers.domains.create(...) -> CreateIdpDomainResponseContent
@@ -1908,7 +2035,7 @@ client.organization.identity_providers.provisioning.scim_tokens.create(
-Revoke a SCIM token specified by token ID for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. +Associate a domain with an Identity Provider specified by ID for this Organization. The domain must be claimed and verified.
@@ -1931,9 +2058,633 @@ client = Auth0( environment=Auth0Environment.DEFAULT, ) -client.organization.identity_providers.provisioning.scim_tokens.delete( +client.organization.identity_providers.domains.create( idp_id="idp_id", - idp_scim_token_id="idp_scim_token_id", + domain="my-domain.com", +) + +``` + +
+ + + +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**domain:** `OrgDomainName` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + + + +
+ +
client.organization.identity_providers.domains.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Remove a domain specified by name from an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.domains.delete( + idp_id="idp_id", + domain="domain", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**domain:** `OrgDomainName` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Organization IdentityProviders Provisioning +
client.organization.identity_providers.provisioning.get(...) -> GetIdPProvisioningConfigResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve the Provisioning Configuration for an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.get( + idp_id="idp_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.identity_providers.provisioning.create(...) -> CreateIdPProvisioningConfigResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a new Provisioning Configuration for an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.create( + idp_id="idp_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.identity_providers.provisioning.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete the Provisioning Configuration for an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.delete( + idp_id="idp_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.identity_providers.provisioning.update_attributes(...) -> GetIdPProvisioningConfigResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Refresh the attribute mapping for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. Mappings are reset to the admin-defined defaults. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.update_attributes( + idp_id="idp_id", + request={ + "key": "value" + }, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**request:** `typing.Dict[str, typing.Any]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +## Organization IdentityProviders Provisioning ScimTokens +
client.organization.identity_providers.provisioning.scim_tokens.list(...) -> ListIdpProvisioningScimTokensResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a list of [SCIM tokens](https://auth0.com/docs/authenticate/protocols/scim/configure-inbound-scim#scim-endpoints-and-tokens) for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.scim_tokens.list( + idp_id="idp_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.identity_providers.provisioning.scim_tokens.create(...) -> CreateIdpProvisioningScimTokenResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a new SCIM token for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.scim_tokens.create( + idp_id="idp_id", + token_lifetime=86400, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**idp_id:** `IdpId` + +
+
+ +
+
+ +**token_lifetime:** `typing.Optional[int]` — Lifetime of the token in seconds. Do not set for non-expiring tokens. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.identity_providers.provisioning.scim_tokens.delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Revoke a SCIM token specified by token ID for the Provisioning Configuration of an Identity Provider specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.identity_providers.provisioning.scim_tokens.delete( + idp_id="idp_id", + idp_scim_token_id="idp_scim_token_id", ) ``` @@ -1978,3 +2729,263 @@ client.organization.identity_providers.provisioning.scim_tokens.delete(
+## Organization Members Roles +
client.organization.members.roles.list(...) -> GetOrganizationMemberRolesResponseContent +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Retrieve a list of roles assigned to a member specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.members.roles.list( + user_id="user_id", + from_="from", + take=1, +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `OrgMemberId` + +
+
+ +
+
+ +**from:** `typing.Optional[str]` — An optional cursor from which to start the selection (exclusive). + +
+
+ +
+
+ +**take:** `typing.Optional[int]` — Number of results per page. Defaults to 50. + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.members.roles.assign(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Assign roles to a member specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.members.roles.assign( + user_id="user_id", + role_ids=[ + "rol_SO2j0sFo9NFa3F9w" + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `OrgMemberId` + +
+
+ +
+
+ +**request:** `OrganizationMemberRolesChangeRequestContent` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ +
client.organization.members.roles.unassign(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Remove roles from a member specified by ID for this Organization. +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from auth0.myorganization import Auth0 +from auth0.myorganization.environment import Auth0Environment + +client = Auth0( + token="", + environment=Auth0Environment.DEFAULT, +) + +client.organization.members.roles.unassign( + user_id="user_id", + role_ids=[ + "rol_SO2j0sFo9NFa3F9w" + ], +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**user_id:** `OrgMemberId` + +
+
+ +
+
+ +**request:** `OrganizationMemberRolesChangeRequestContent` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ + +
+
+
+ diff --git a/src/auth0/myorganization/__init__.py b/src/auth0/myorganization/__init__.py index 349b857..4522ba2 100644 --- a/src/auth0/myorganization/__init__.py +++ b/src/auth0/myorganization/__init__.py @@ -15,10 +15,10 @@ CreateIdentityProviderResponseContent, CreateIdpDomainResponseContent, CreateIdpProvisioningScimTokenResponseContent, + CreateMemberInvitationInvitee, CreateMemberInvitationResponseContent, CreateOrganizationDomainResponseContent, DomainIdp, - DomainVerificationEnum, ErrorResponseContent, FedMetadataXml, GetConfigurationResponseContent, @@ -29,6 +29,7 @@ GetOrganizationDetailsResponseContent, GetOrganizationDomainResponseContent, GetOrganizationMemberResponseContent, + GetOrganizationMemberRolesResponseContent, IdentityProviderConfigAdfs, IdentityProviderConfigGoogleApps, IdentityProviderConfigOidc, @@ -37,7 +38,6 @@ IdentityProviderConfigSamlp, IdentityProviderConfigWaad, IdentityProvidersConfig, - IdentityProvidersConfigDomainAlias, IdentityProvidersConfigEnabledFeaturesEnum, IdentityProvidersConfigOrganization, IdentityProvidersConfigProvisioningMethodsEnum, @@ -121,7 +121,10 @@ ListDomainIdentityProvidersResponseContent, ListIdentityProvidersResponseContent, ListIdpProvisioningScimTokensResponseContent, + ListMembersInvitationsResponseContent, ListOrganizationDomainsResponseContent, + ListOrganizationMembersResponseContent, + ListRolesResponseContent, Manual, MemberInvitation, MemberInvitationInvitee, @@ -130,6 +133,7 @@ OrgBranding, OrgBrandingColors, OrgDetails, + OrgDetailsRead, OrgDomain, OrgDomainId, OrgDomainName, @@ -137,9 +141,11 @@ OrgId, OrgMember, OrgMemberId, - OrgMemberRole, - OrgMemberRoleId, + OrgMemberIdReadOnly, OrganizationAccessLevelEnum, + OrganizationMemberRolesChangeRequestContent, + Role, + RoleId, StartOrganizationDomainVerificationResponseContent, UpdateIdentityProviderRequestContent, UpdateIdentityProviderResponseContent, @@ -158,6 +164,7 @@ UnauthorizedError, ) from . import organization, organization_details + from ._default_clients import DefaultAioHttpClient, DefaultAsyncHttpxClient from .client import AsyncAuth0, Auth0 from .environment import Auth0Environment from .version import __version__ @@ -177,10 +184,12 @@ "CreateIdentityProviderResponseContent": ".types", "CreateIdpDomainResponseContent": ".types", "CreateIdpProvisioningScimTokenResponseContent": ".types", + "CreateMemberInvitationInvitee": ".types", "CreateMemberInvitationResponseContent": ".types", "CreateOrganizationDomainResponseContent": ".types", + "DefaultAioHttpClient": "._default_clients", + "DefaultAsyncHttpxClient": "._default_clients", "DomainIdp": ".types", - "DomainVerificationEnum": ".types", "ErrorResponseContent": ".types", "FedMetadataXml": ".types", "ForbiddenError": ".errors", @@ -192,6 +201,7 @@ "GetOrganizationDetailsResponseContent": ".types", "GetOrganizationDomainResponseContent": ".types", "GetOrganizationMemberResponseContent": ".types", + "GetOrganizationMemberRolesResponseContent": ".types", "IdentityProviderConfigAdfs": ".types", "IdentityProviderConfigGoogleApps": ".types", "IdentityProviderConfigOidc": ".types", @@ -200,7 +210,6 @@ "IdentityProviderConfigSamlp": ".types", "IdentityProviderConfigWaad": ".types", "IdentityProvidersConfig": ".types", - "IdentityProvidersConfigDomainAlias": ".types", "IdentityProvidersConfigEnabledFeaturesEnum": ".types", "IdentityProvidersConfigOrganization": ".types", "IdentityProvidersConfigProvisioningMethodsEnum": ".types", @@ -284,7 +293,10 @@ "ListDomainIdentityProvidersResponseContent": ".types", "ListIdentityProvidersResponseContent": ".types", "ListIdpProvisioningScimTokensResponseContent": ".types", + "ListMembersInvitationsResponseContent": ".types", "ListOrganizationDomainsResponseContent": ".types", + "ListOrganizationMembersResponseContent": ".types", + "ListRolesResponseContent": ".types", "Manual": ".types", "MemberInvitation": ".types", "MemberInvitationInvitee": ".types", @@ -295,6 +307,7 @@ "OrgBranding": ".types", "OrgBrandingColors": ".types", "OrgDetails": ".types", + "OrgDetailsRead": ".types", "OrgDomain": ".types", "OrgDomainId": ".types", "OrgDomainName": ".types", @@ -302,9 +315,11 @@ "OrgId": ".types", "OrgMember": ".types", "OrgMemberId": ".types", - "OrgMemberRole": ".types", - "OrgMemberRoleId": ".types", + "OrgMemberIdReadOnly": ".types", "OrganizationAccessLevelEnum": ".types", + "OrganizationMemberRolesChangeRequestContent": ".types", + "Role": ".types", + "RoleId": ".types", "StartOrganizationDomainVerificationResponseContent": ".types", "TokenProvider": ".token_provider", "TooManyRequestsError": ".errors", @@ -359,10 +374,12 @@ def __dir__(): "CreateIdentityProviderResponseContent", "CreateIdpDomainResponseContent", "CreateIdpProvisioningScimTokenResponseContent", + "CreateMemberInvitationInvitee", "CreateMemberInvitationResponseContent", "CreateOrganizationDomainResponseContent", + "DefaultAioHttpClient", + "DefaultAsyncHttpxClient", "DomainIdp", - "DomainVerificationEnum", "ErrorResponseContent", "FedMetadataXml", "ForbiddenError", @@ -374,6 +391,7 @@ def __dir__(): "GetOrganizationDetailsResponseContent", "GetOrganizationDomainResponseContent", "GetOrganizationMemberResponseContent", + "GetOrganizationMemberRolesResponseContent", "IdentityProviderConfigAdfs", "IdentityProviderConfigGoogleApps", "IdentityProviderConfigOidc", @@ -382,7 +400,6 @@ def __dir__(): "IdentityProviderConfigSamlp", "IdentityProviderConfigWaad", "IdentityProvidersConfig", - "IdentityProvidersConfigDomainAlias", "IdentityProvidersConfigEnabledFeaturesEnum", "IdentityProvidersConfigOrganization", "IdentityProvidersConfigProvisioningMethodsEnum", @@ -466,7 +483,10 @@ def __dir__(): "ListDomainIdentityProvidersResponseContent", "ListIdentityProvidersResponseContent", "ListIdpProvisioningScimTokensResponseContent", + "ListMembersInvitationsResponseContent", "ListOrganizationDomainsResponseContent", + "ListOrganizationMembersResponseContent", + "ListRolesResponseContent", "Manual", "MemberInvitation", "MemberInvitationInvitee", @@ -477,6 +497,7 @@ def __dir__(): "OrgBranding", "OrgBrandingColors", "OrgDetails", + "OrgDetailsRead", "OrgDomain", "OrgDomainId", "OrgDomainName", @@ -484,9 +505,11 @@ def __dir__(): "OrgId", "OrgMember", "OrgMemberId", - "OrgMemberRole", - "OrgMemberRoleId", + "OrgMemberIdReadOnly", "OrganizationAccessLevelEnum", + "OrganizationMemberRolesChangeRequestContent", + "Role", + "RoleId", "StartOrganizationDomainVerificationResponseContent", "TokenProvider", "TooManyRequestsError", diff --git a/src/auth0/myorganization/client.py b/src/auth0/myorganization/client.py index 2d48176..3d124a9 100644 --- a/src/auth0/myorganization/client.py +++ b/src/auth0/myorganization/client.py @@ -42,6 +42,9 @@ class Auth0: timeout : typing.Optional[float] The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + max_retries : typing.Optional[int] + The default maximum number of retries for failed requests. Defaults to 2. Per-request `max_retries` in `request_options` takes precedence over this value. + follow_redirects : typing.Optional[bool] Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. @@ -69,6 +72,7 @@ def __init__( token: typing.Union[str, typing.Callable[[], str]], headers: typing.Optional[typing.Dict[str, str]] = None, timeout: typing.Optional[float] = None, + max_retries: typing.Optional[int] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.Client] = None, logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, @@ -76,9 +80,10 @@ def __init__( _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read ) + _defaulted_max_retries = max_retries if max_retries is not None else 2 if tenant_domain is not None: _tenant_domain = tenant_domain if tenant_domain is not None else "{TENANT}.auth0.com" - base_url = "https://{tenantDomain}/my-org".format(tenantDomain=_tenant_domain) + base_url = "https://{tenantDomain}/my-org/v1".format(tenantDomain=_tenant_domain) self._client_wrapper = SyncClientWrapper( base_url=_get_base_url(base_url=base_url, environment=environment), token=token, @@ -89,6 +94,7 @@ def __init__( if follow_redirects is not None else httpx.Client(timeout=_defaulted_timeout), timeout=_defaulted_timeout, + max_retries=_defaulted_max_retries, logging=logging, ) self._organization_details: typing.Optional[OrganizationDetailsClient] = None @@ -160,6 +166,9 @@ class AsyncAuth0: timeout : typing.Optional[float] The timeout to be used, in seconds, for requests. By default the timeout is 60 seconds, unless a custom httpx client is used, in which case this default is not enforced. + max_retries : typing.Optional[int] + The default maximum number of retries for failed requests. Defaults to 2. Per-request `max_retries` in `request_options` takes precedence over this value. + follow_redirects : typing.Optional[bool] Whether the default httpx client follows redirects or not, this is irrelevant if a custom httpx client is passed in. @@ -188,6 +197,7 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, timeout: typing.Optional[float] = None, + max_retries: typing.Optional[int] = None, follow_redirects: typing.Optional[bool] = True, httpx_client: typing.Optional[httpx.AsyncClient] = None, logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, @@ -195,9 +205,10 @@ def __init__( _defaulted_timeout = ( timeout if timeout is not None else 60 if httpx_client is None else httpx_client.timeout.read ) + _defaulted_max_retries = max_retries if max_retries is not None else 2 if tenant_domain is not None: _tenant_domain = tenant_domain if tenant_domain is not None else "{TENANT}.auth0.com" - base_url = "https://{tenantDomain}/my-org".format(tenantDomain=_tenant_domain) + base_url = "https://{tenantDomain}/my-org/v1".format(tenantDomain=_tenant_domain) self._client_wrapper = AsyncClientWrapper( base_url=_get_base_url(base_url=base_url, environment=environment), token=token, @@ -207,6 +218,7 @@ def __init__( if httpx_client is not None else _make_default_async_client(timeout=_defaulted_timeout, follow_redirects=follow_redirects), timeout=_defaulted_timeout, + max_retries=_defaulted_max_retries, logging=logging, ) self._organization_details: typing.Optional[AsyncOrganizationDetailsClient] = None diff --git a/src/auth0/myorganization/core/client_wrapper.py b/src/auth0/myorganization/core/client_wrapper.py index 4229ef6..79ab2e7 100644 --- a/src/auth0/myorganization/core/client_wrapper.py +++ b/src/auth0/myorganization/core/client_wrapper.py @@ -20,11 +20,13 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + max_retries: int = 2, ): self._token = token self._headers = headers self._base_url = base_url self._timeout = timeout + self._max_retries = max_retries def get_headers(self) -> typing.Dict[str, str]: # Dynamically get version from package metadata @@ -61,6 +63,9 @@ def get_base_url(self) -> str: def get_timeout(self) -> typing.Optional[float]: return self._timeout + def get_max_retries(self) -> int: + return self._max_retries + class SyncClientWrapper(BaseClientWrapper): def __init__( @@ -70,15 +75,17 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + max_retries: int = 2, httpx_client: httpx.Client, logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): - super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout) + super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout, max_retries=max_retries) self.httpx_client = HttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + base_max_retries=self.get_max_retries(), logging_config=logging, ) @@ -91,17 +98,19 @@ def __init__( headers: typing.Optional[typing.Dict[str, str]] = None, base_url: str, timeout: typing.Optional[float] = None, + max_retries: int = 2, async_token: typing.Optional[typing.Callable[[], typing.Awaitable[str]]] = None, httpx_client: httpx.AsyncClient, logging: typing.Optional[typing.Union[LogConfig, Logger]] = None, ): - super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout) + super().__init__(token=token, headers=headers, base_url=base_url, timeout=timeout, max_retries=max_retries) self._async_token = async_token self.httpx_client = AsyncHttpClient( httpx_client=httpx_client, base_headers=self.get_headers, base_timeout=self.get_timeout, base_url=self.get_base_url, + base_max_retries=self.get_max_retries(), async_base_headers=self.async_get_headers, logging_config=logging, ) diff --git a/src/auth0/myorganization/core/http_client.py b/src/auth0/myorganization/core/http_client.py index f0a39ca..f686c57 100644 --- a/src/auth0/myorganization/core/http_client.py +++ b/src/auth0/myorganization/core/http_client.py @@ -125,8 +125,7 @@ def _retry_timeout_from_retries(retries: int) -> float: def _should_retry(response: httpx.Response) -> bool: - retryable_400s = [429, 408, 409] - return response.status_code >= 500 or response.status_code in retryable_400s + return response.status_code >= 500 or response.status_code in [429, 408, 409] _SENSITIVE_HEADERS = frozenset( diff --git a/src/auth0/myorganization/core/http_sse/_api.py b/src/auth0/myorganization/core/http_sse/_api.py index f900b3b..fd13730 100644 --- a/src/auth0/myorganization/core/http_sse/_api.py +++ b/src/auth0/myorganization/core/http_sse/_api.py @@ -1,14 +1,17 @@ # This file was auto-generated by Fern from our API Definition. +import codecs import re from contextlib import asynccontextmanager, contextmanager -from typing import Any, AsyncGenerator, AsyncIterator, Iterator, cast +from typing import Any, AsyncGenerator, AsyncIterator, Iterator import httpx from ._decoders import SSEDecoder from ._exceptions import SSEError from ._models import ServerSentEvent +MAX_LINE_SIZE: int = 1_048_576 # 1 MiB + class EventSource: def __init__(self, response: httpx.Response) -> None: @@ -45,46 +48,101 @@ def _get_charset(self) -> str: def response(self) -> httpx.Response: return self._response + @staticmethod + def _normalize_sse_line_endings(buf: str) -> str: + """Normalize line endings per the SSE spec (\\r\\n → \\n, bare \\r → \\n). + + A trailing \\r is preserved because it may pair with a leading \\n in + the next chunk to form a single \\r\\n terminator. + """ + buf = buf.replace("\r\n", "\n") + if buf.endswith("\r"): + return buf[:-1].replace("\r", "\n") + "\r" + return buf.replace("\r", "\n") + def iter_sse(self) -> Iterator[ServerSentEvent]: self._check_content_type() decoder = SSEDecoder() charset = self._get_charset() + text_decoder = codecs.getincrementaldecoder(charset)(errors="replace") - buffer = "" + buf = "" for chunk in self._response.iter_bytes(): - # Decode chunk using detected charset - text_chunk = chunk.decode(charset, errors="replace") - buffer += text_chunk - - # Process complete lines - while "\n" in buffer: - line, buffer = buffer.split("\n", 1) - line = line.rstrip("\r") + buf += text_decoder.decode(chunk) + buf = self._normalize_sse_line_endings(buf) + + while "\n" in buf: + line, buf = buf.split("\n", 1) sse = decoder.decode(line) - # when we reach a "\n\n" => line = '' - # => decoder will attempt to return an SSE Event if sse is not None: yield sse - # Process any remaining data in buffer - if buffer.strip(): - line = buffer.rstrip("\r") + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + + # Flush any remaining bytes from the incremental decoder + buf += text_decoder.decode(b"", final=True) + buf = buf.replace("\r\n", "\n").replace("\r", "\n") + + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + + while "\n" in buf: + line, buf = buf.split("\n", 1) sse = decoder.decode(line) if sse is not None: yield sse + if buf.strip(): + sse = decoder.decode(buf) + if sse is not None: + yield sse + async def aiter_sse(self) -> AsyncGenerator[ServerSentEvent, None]: self._check_content_type() decoder = SSEDecoder() - lines = cast(AsyncGenerator[str, None], self._response.aiter_lines()) - try: - async for line in lines: - line = line.rstrip("\n") + charset = self._get_charset() + text_decoder = codecs.getincrementaldecoder(charset)(errors="replace") + + buf = "" + async for chunk in self._response.aiter_bytes(): + buf += text_decoder.decode(chunk) + buf = self._normalize_sse_line_endings(buf) + + while "\n" in buf: + line, buf = buf.split("\n", 1) sse = decoder.decode(line) if sse is not None: yield sse - finally: - await lines.aclose() + + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + + # Flush any remaining bytes from the incremental decoder + buf += text_decoder.decode(b"", final=True) + buf = buf.replace("\r\n", "\n").replace("\r", "\n") + + if len(buf) > MAX_LINE_SIZE: + raise SSEError( + f"SSE line exceeded maximum size of {MAX_LINE_SIZE} characters without encountering a newline" + ) + + while "\n" in buf: + line, buf = buf.split("\n", 1) + sse = decoder.decode(line) + if sse is not None: + yield sse + + if buf.strip(): + sse = decoder.decode(buf) + if sse is not None: + yield sse @contextmanager diff --git a/src/auth0/myorganization/core/pydantic_utilities.py b/src/auth0/myorganization/core/pydantic_utilities.py index fea3a08..6587f5e 100644 --- a/src/auth0/myorganization/core/pydantic_utilities.py +++ b/src/auth0/myorganization/core/pydantic_utilities.py @@ -135,111 +135,21 @@ def _decimal_encoder(dec_value: Any) -> Any: Model = TypeVar("Model", bound=pydantic.BaseModel) -def _get_discriminator_and_variants(type_: Type[Any]) -> Tuple[Optional[str], Optional[List[Type[Any]]]]: - """ - Extract the discriminator field name and union variants from a discriminated union type. - Supports Annotated[Union[...], Field(discriminator=...)] patterns. - Returns (discriminator, variants) or (None, None) if not a discriminated union. - """ - origin = typing_extensions.get_origin(type_) - - if origin is typing_extensions.Annotated: - args = typing_extensions.get_args(type_) - if len(args) >= 2: - inner_type = args[0] - # Check annotations for discriminator - discriminator = None - for annotation in args[1:]: - if hasattr(annotation, "discriminator"): - discriminator = getattr(annotation, "discriminator", None) - break - - if discriminator: - inner_origin = typing_extensions.get_origin(inner_type) - if inner_origin is Union: - variants = list(typing_extensions.get_args(inner_type)) - return discriminator, variants - return None, None - - -def _get_field_annotation(model: Type[Any], field_name: str) -> Optional[Type[Any]]: - """Get the type annotation of a field from a Pydantic model.""" - if IS_PYDANTIC_V2: - fields = getattr(model, "model_fields", {}) - field_info = fields.get(field_name) - if field_info: - return cast(Optional[Type[Any]], field_info.annotation) - else: - fields = getattr(model, "__fields__", {}) - field_info = fields.get(field_name) - if field_info: - return cast(Optional[Type[Any]], field_info.outer_type_) - return None - - -def _find_variant_by_discriminator( - variants: List[Type[Any]], - discriminator: str, - discriminator_value: Any, -) -> Optional[Type[Any]]: - """Find the union variant that matches the discriminator value.""" - for variant in variants: - if not (inspect.isclass(variant) and issubclass(variant, pydantic.BaseModel)): - continue - - disc_annotation = _get_field_annotation(variant, discriminator) - if disc_annotation and is_literal_type(disc_annotation): - literal_args = get_args(disc_annotation) - if literal_args and literal_args[0] == discriminator_value: - return variant - return None - - -def _is_string_type(type_: Type[Any]) -> bool: - """Check if a type is str or Optional[str].""" - if type_ is str: - return True - - origin = typing_extensions.get_origin(type_) - if origin is Union: - args = typing_extensions.get_args(type_) - # Optional[str] = Union[str, None] - non_none_args = [a for a in args if a is not type(None)] - if len(non_none_args) == 1 and non_none_args[0] is str: - return True - - return False - - def parse_sse_obj(sse: "ServerSentEvent", type_: Type[T]) -> T: """ Parse a ServerSentEvent into the appropriate type. - Handles two scenarios based on where the discriminator field is located: - - 1. Data-level discrimination: The discriminator (e.g., 'type') is inside the 'data' payload. - The union describes the data content, not the SSE envelope. - -> Returns: json.loads(data) parsed into the type + This function handles data-level discrimination where the discriminator + (e.g., 'type') is inside the 'data' payload. It parses the SSE data field + as JSON and deserializes it into the target type. - Example: ChatStreamResponse with discriminator='type' - Input: ServerSentEvent(event="message", data='{"type": "content-delta", ...}', id="") - Output: ContentDeltaEvent (parsed from data, SSE envelope stripped) - - 2. Event-level discrimination: The discriminator (e.g., 'event') is at the SSE event level. - The union describes the full SSE event structure. - -> Returns: SSE envelope with 'data' field JSON-parsed only if the variant expects non-string - - Example: JobStreamResponse with discriminator='event' - Input: ServerSentEvent(event="ERROR", data='{"code": "FAILED", ...}', id="123") - Output: JobStreamResponse_Error with data as ErrorData object - - But for variants where data is str (like STATUS_UPDATE): - Input: ServerSentEvent(event="STATUS_UPDATE", data='{"status": "processing"}', id="1") - Output: JobStreamResponse_StatusUpdate with data as string (not parsed) + Note: Protocol-level discrimination (where the discriminator comes from + the SSE event: field) is handled at code-generation time and does not + use this function. Args: sse: The ServerSentEvent object to parse - type_: The target discriminated union type + type_: The target type to deserialize into Returns: The parsed object of type T @@ -248,66 +158,30 @@ def parse_sse_obj(sse: "ServerSentEvent", type_: Type[T]) -> T: This function is only available in SDK contexts where http_sse module exists. """ sse_event = asdict(sse) - discriminator, variants = _get_discriminator_and_variants(type_) - - if discriminator is None or variants is None: - # Not a discriminated union - parse the data field as JSON - data_value = sse_event.get("data") - if isinstance(data_value, str) and data_value: - try: - parsed_data = json.loads(data_value) - return parse_obj_as(type_, parsed_data) - except json.JSONDecodeError as e: - _logger.warning( - "Failed to parse SSE data field as JSON: %s, data: %s", - e, - data_value[:100] if len(data_value) > 100 else data_value, - ) - return parse_obj_as(type_, sse_event) - data_value = sse_event.get("data") + if isinstance(data_value, str) and data_value: + try: + parsed_data = json.loads(data_value) + return parse_obj_as(type_, parsed_data) + except json.JSONDecodeError as e: + _logger.warning( + "Failed to parse SSE data field as JSON: %s, data: %s", + e, + data_value[:100] if len(data_value) > 100 else data_value, + ) + return parse_obj_as(type_, sse_event) - # Check if discriminator is at the top level (event-level discrimination) - if discriminator in sse_event: - # Case 2: Event-level discrimination - # Find the matching variant to check if 'data' field needs JSON parsing - disc_value = sse_event.get(discriminator) - matching_variant = _find_variant_by_discriminator(variants, discriminator, disc_value) - - if matching_variant is not None: - # Check what type the variant expects for 'data' - data_type = _get_field_annotation(matching_variant, "data") - if data_type is not None and not _is_string_type(data_type): - # Variant expects non-string data - parse JSON - if isinstance(data_value, str) and data_value: - try: - parsed_data = json.loads(data_value) - new_object = dict(sse_event) - new_object["data"] = parsed_data - return parse_obj_as(type_, new_object) - except json.JSONDecodeError as e: - _logger.warning( - "Failed to parse SSE data field as JSON for event-level discrimination: %s, data: %s", - e, - data_value[:100] if len(data_value) > 100 else data_value, - ) - # Either no matching variant, data is string type, or JSON parse failed - return parse_obj_as(type_, sse_event) - else: - # Case 1: Data-level discrimination - # The discriminator is inside the data payload - extract and parse data only - if isinstance(data_value, str) and data_value: - try: - parsed_data = json.loads(data_value) - return parse_obj_as(type_, parsed_data) - except json.JSONDecodeError as e: - _logger.warning( - "Failed to parse SSE data field as JSON for data-level discrimination: %s, data: %s", - e, - data_value[:100] if len(data_value) > 100 else data_value, - ) - return parse_obj_as(type_, sse_event) +_type_adapter_cache: Dict[int, Any] = {} + + +def _get_type_adapter(type_: Type[Any]) -> Any: + key = id(type_) + adapter = _type_adapter_cache.get(key) + if adapter is None: + adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] + _type_adapter_cache[key] = adapter + return adapter def parse_obj_as(type_: Type[T], object_: Any) -> T: @@ -342,8 +216,8 @@ def parse_obj_as(type_: Type[T], object_: Any) -> T: else: dealiased_object = convert_and_respect_annotation_metadata(object_=object_, annotation=type_, direction="read") if IS_PYDANTIC_V2: - adapter = pydantic.TypeAdapter(type_) # type: ignore[attr-defined] - return adapter.validate_python(dealiased_object) + adapter = _get_type_adapter(type_) + return adapter.validate_python(dealiased_object) # type: ignore[no-any-return] return pydantic.parse_obj_as(type_, dealiased_object) diff --git a/src/auth0/myorganization/core/serialization.py b/src/auth0/myorganization/core/serialization.py index c36e865..1d753e2 100644 --- a/src/auth0/myorganization/core/serialization.py +++ b/src/auth0/myorganization/core/serialization.py @@ -26,6 +26,75 @@ def __init__(self, *, alias: str) -> None: self.alias = alias +# Resolving type hints (typing.get_type_hints) is expensive because it eval/compiles +# forward-reference annotations. The result is constant for a given type, so we cache it. +# This is critical for hot paths like SSE event parsing, where the same (often large +# discriminated-union) type is converted on every single event. +_type_hints_cache: typing.Dict[typing.Any, typing.Dict[str, typing.Any]] = {} + + +def _get_cached_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]: + try: + cached = _type_hints_cache.get(expected_type) + except TypeError: + # Unhashable type; resolve without caching. + return _resolve_type_hints(expected_type) + if cached is None: + cached = _resolve_type_hints(expected_type) + _type_hints_cache[expected_type] = cached + return cached + + +def _resolve_type_hints(expected_type: typing.Any) -> typing.Dict[str, typing.Any]: + try: + return typing_extensions.get_type_hints(expected_type, include_extras=True) + except NameError: + # The type contains a circular reference, so we use the __annotations__ attribute directly. + return getattr(expected_type, "__annotations__", {}) + + +# Whether convert_and_respect_annotation_metadata can possibly rewrite anything for a given +# annotation, i.e. whether any reachable model/TypedDict field carries a FieldMetadata alias. +# This is constant per type, so we cache it and use it to short-circuit the recursive walk. +_requires_conversion_cache: typing.Dict[typing.Any, bool] = {} + + +def _requires_conversion(type_: typing.Any) -> bool: + try: + cached = _requires_conversion_cache.get(type_) + except TypeError: + # Unhashable annotation; compute without caching. + return _compute_requires_conversion(type_, set()) + if cached is None: + cached = _compute_requires_conversion(type_, set()) + _requires_conversion_cache[type_] = cached + return cached + + +def _compute_requires_conversion(type_: typing.Any, seen: typing.Set[typing.Any]) -> bool: + clean_type = _remove_annotations(type_) + + try: + if clean_type in seen: + return False + seen = seen | {clean_type} + except TypeError: + # Unhashable type; skip cycle tracking (the type graph is finite in practice). + pass + + # Models / TypedDicts: a field alias here means we must dealias; otherwise recurse into fields. + if (inspect.isclass(clean_type) and issubclass(clean_type, pydantic.BaseModel)) or typing_extensions.is_typeddict( + clean_type + ): + annotations = _get_cached_type_hints(clean_type) + if _get_alias_to_field_name(annotations): + return True + return any(_compute_requires_conversion(hint, seen) for hint in annotations.values()) + + # Containers / unions: recurse into the type arguments (List/Set/Sequence/Dict/Union/etc.). + return any(_compute_requires_conversion(arg, seen) for arg in typing_extensions.get_args(clean_type)) + + def convert_and_respect_annotation_metadata( *, object_: typing.Any, @@ -57,6 +126,13 @@ def convert_and_respect_annotation_metadata( return None if inner_type is None: inner_type = annotation + # The only thing this function ever rewrites is keys that carry a FieldMetadata + # alias. If nothing in the (cached) type graph has such an alias, the conversion is + # a content-identity transform, so we can skip the entire recursive walk. This is + # the hot path for SSE streaming, where a large discriminated union would otherwise + # be traversed on every single event. + if not _requires_conversion(annotation): + return object_ clean_type = _remove_annotations(inner_type) # Pydantic models @@ -160,12 +236,7 @@ def _convert_mapping( direction: typing.Literal["read", "write"], ) -> typing.Mapping[str, object]: converted_object: typing.Dict[str, object] = {} - try: - annotations = typing_extensions.get_type_hints(expected_type, include_extras=True) - except NameError: - # The TypedDict contains a circular reference, so - # we use the __annotations__ attribute directly. - annotations = getattr(expected_type, "__annotations__", {}) + annotations = _get_cached_type_hints(expected_type) aliases_to_field_names = _get_alias_to_field_name(annotations) for key, value in object_.items(): if direction == "read" and key in aliases_to_field_names: @@ -221,12 +292,12 @@ def _remove_annotations(type_: typing.Any) -> typing.Any: def get_alias_to_field_mapping(type_: typing.Any) -> typing.Dict[str, str]: - annotations = typing_extensions.get_type_hints(type_, include_extras=True) + annotations = _get_cached_type_hints(type_) return _get_alias_to_field_name(annotations) def get_field_to_alias_mapping(type_: typing.Any) -> typing.Dict[str, str]: - annotations = typing_extensions.get_type_hints(type_, include_extras=True) + annotations = _get_cached_type_hints(type_) return _get_field_to_alias_name(annotations) diff --git a/src/auth0/myorganization/environment.py b/src/auth0/myorganization/environment.py index e7c6f18..f249107 100644 --- a/src/auth0/myorganization/environment.py +++ b/src/auth0/myorganization/environment.py @@ -4,4 +4,4 @@ class Auth0Environment(enum.Enum): - DEFAULT = "https://%7BTENANT%7D.auth0.com/my-org" + DEFAULT = "https://%7BTENANT%7D.auth0.com/my-org/v1" diff --git a/src/auth0/myorganization/organization/__init__.py b/src/auth0/myorganization/organization/__init__.py index b543c1d..acd9d53 100644 --- a/src/auth0/myorganization/organization/__init__.py +++ b/src/auth0/myorganization/organization/__init__.py @@ -6,11 +6,15 @@ from importlib import import_module if typing.TYPE_CHECKING: - from . import configuration, domains, identity_providers + from . import configuration, domains, identity_providers, invitations, members, memberships, roles _dynamic_imports: typing.Dict[str, str] = { "configuration": ".configuration", "domains": ".domains", "identity_providers": ".identity_providers", + "invitations": ".invitations", + "members": ".members", + "memberships": ".memberships", + "roles": ".roles", } @@ -35,4 +39,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["configuration", "domains", "identity_providers"] +__all__ = ["configuration", "domains", "identity_providers", "invitations", "members", "memberships", "roles"] diff --git a/src/auth0/myorganization/organization/client.py b/src/auth0/myorganization/organization/client.py index 679155a..7711a7b 100644 --- a/src/auth0/myorganization/organization/client.py +++ b/src/auth0/myorganization/organization/client.py @@ -11,6 +11,10 @@ from .configuration.client import AsyncConfigurationClient, ConfigurationClient from .domains.client import AsyncDomainsClient, DomainsClient from .identity_providers.client import AsyncIdentityProvidersClient, IdentityProvidersClient + from .invitations.client import AsyncInvitationsClient, InvitationsClient + from .members.client import AsyncMembersClient, MembersClient + from .memberships.client import AsyncMembershipsClient, MembershipsClient + from .roles.client import AsyncRolesClient, RolesClient class OrganizationClient: @@ -20,6 +24,10 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._configuration: typing.Optional[ConfigurationClient] = None self._domains: typing.Optional[DomainsClient] = None self._identity_providers: typing.Optional[IdentityProvidersClient] = None + self._members: typing.Optional[MembersClient] = None + self._memberships: typing.Optional[MembershipsClient] = None + self._invitations: typing.Optional[InvitationsClient] = None + self._roles: typing.Optional[RolesClient] = None @property def with_raw_response(self) -> RawOrganizationClient: @@ -56,6 +64,38 @@ def identity_providers(self): self._identity_providers = IdentityProvidersClient(client_wrapper=self._client_wrapper) return self._identity_providers + @property + def members(self): + if self._members is None: + from .members.client import MembersClient # noqa: E402 + + self._members = MembersClient(client_wrapper=self._client_wrapper) + return self._members + + @property + def memberships(self): + if self._memberships is None: + from .memberships.client import MembershipsClient # noqa: E402 + + self._memberships = MembershipsClient(client_wrapper=self._client_wrapper) + return self._memberships + + @property + def invitations(self): + if self._invitations is None: + from .invitations.client import InvitationsClient # noqa: E402 + + self._invitations = InvitationsClient(client_wrapper=self._client_wrapper) + return self._invitations + + @property + def roles(self): + if self._roles is None: + from .roles.client import RolesClient # noqa: E402 + + self._roles = RolesClient(client_wrapper=self._client_wrapper) + return self._roles + class AsyncOrganizationClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -64,6 +104,10 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._configuration: typing.Optional[AsyncConfigurationClient] = None self._domains: typing.Optional[AsyncDomainsClient] = None self._identity_providers: typing.Optional[AsyncIdentityProvidersClient] = None + self._members: typing.Optional[AsyncMembersClient] = None + self._memberships: typing.Optional[AsyncMembershipsClient] = None + self._invitations: typing.Optional[AsyncInvitationsClient] = None + self._roles: typing.Optional[AsyncRolesClient] = None @property def with_raw_response(self) -> AsyncRawOrganizationClient: @@ -99,3 +143,35 @@ def identity_providers(self): self._identity_providers = AsyncIdentityProvidersClient(client_wrapper=self._client_wrapper) return self._identity_providers + + @property + def members(self): + if self._members is None: + from .members.client import AsyncMembersClient # noqa: E402 + + self._members = AsyncMembersClient(client_wrapper=self._client_wrapper) + return self._members + + @property + def memberships(self): + if self._memberships is None: + from .memberships.client import AsyncMembershipsClient # noqa: E402 + + self._memberships = AsyncMembershipsClient(client_wrapper=self._client_wrapper) + return self._memberships + + @property + def invitations(self): + if self._invitations is None: + from .invitations.client import AsyncInvitationsClient # noqa: E402 + + self._invitations = AsyncInvitationsClient(client_wrapper=self._client_wrapper) + return self._invitations + + @property + def roles(self): + if self._roles is None: + from .roles.client import AsyncRolesClient # noqa: E402 + + self._roles = AsyncRolesClient(client_wrapper=self._client_wrapper) + return self._roles diff --git a/src/auth0/myorganization/organization/identity_providers/provisioning/raw_client.py b/src/auth0/myorganization/organization/identity_providers/provisioning/raw_client.py index 1c183e1..3a2c500 100644 --- a/src/auth0/myorganization/organization/identity_providers/provisioning/raw_client.py +++ b/src/auth0/myorganization/organization/identity_providers/provisioning/raw_client.py @@ -338,9 +338,6 @@ def update_attributes( f"identity-providers/{encode_path_param(idp_id)}/provisioning/update-attributes", method="PUT", json=request, - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -734,9 +731,6 @@ async def update_attributes( f"identity-providers/{encode_path_param(idp_id)}/provisioning/update-attributes", method="PUT", json=request, - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) diff --git a/src/auth0/myorganization/organization/identity_providers/raw_client.py b/src/auth0/myorganization/organization/identity_providers/raw_client.py index f442220..e80a140 100644 --- a/src/auth0/myorganization/organization/identity_providers/raw_client.py +++ b/src/auth0/myorganization/organization/identity_providers/raw_client.py @@ -143,9 +143,6 @@ def create( json=convert_and_respect_annotation_metadata( object_=request, annotation=CreateIdentityProviderRequestContent, direction="write" ), - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -448,9 +445,6 @@ def update( json=convert_and_respect_annotation_metadata( object_=request, annotation=UpdateIdentityProviderRequestContent, direction="write" ), - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -556,9 +550,6 @@ def update_attributes( f"identity-providers/{encode_path_param(idp_id)}/update-attributes", method="PUT", json=request, - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -836,9 +827,6 @@ async def create( json=convert_and_respect_annotation_metadata( object_=request, annotation=CreateIdentityProviderRequestContent, direction="write" ), - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -1143,9 +1131,6 @@ async def update( json=convert_and_respect_annotation_metadata( object_=request, annotation=UpdateIdentityProviderRequestContent, direction="write" ), - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -1251,9 +1236,6 @@ async def update_attributes( f"identity-providers/{encode_path_param(idp_id)}/update-attributes", method="PUT", json=request, - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) diff --git a/src/auth0/myorganization/organization/invitations/__init__.py b/src/auth0/myorganization/organization/invitations/__init__.py new file mode 100644 index 0000000..5cde020 --- /dev/null +++ b/src/auth0/myorganization/organization/invitations/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/auth0/myorganization/organization/invitations/client.py b/src/auth0/myorganization/organization/invitations/client.py new file mode 100644 index 0000000..5580e0e --- /dev/null +++ b/src/auth0/myorganization/organization/invitations/client.py @@ -0,0 +1,507 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager +from ...core.request_options import RequestOptions +from ...types.create_member_invitation_invitee import CreateMemberInvitationInvitee +from ...types.create_member_invitation_response_content import CreateMemberInvitationResponseContent +from ...types.get_member_invitation_response_content import GetMemberInvitationResponseContent +from ...types.invitation_id import InvitationId +from ...types.list_members_invitations_response_content import ListMembersInvitationsResponseContent +from ...types.member_invitation import MemberInvitation +from ...types.member_invitation_inviter import MemberInvitationInviter +from .raw_client import AsyncRawInvitationsClient, RawInvitationsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class InvitationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawInvitationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawInvitationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawInvitationsClient + """ + return self._raw_client + + def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + sort: typing.Optional[str] = "created_at:-1", + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[MemberInvitation, ListMembersInvitationsResponseContent]: + """ + Retrieve a list of all member invitations for this Organization. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + sort : typing.Optional[str] + Field to sort by. Use field:order where order is 1 for ascending and -1 for descending. Defaults to created_at:-1 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[MemberInvitation, ListMembersInvitationsResponseContent] + List Members Invitations for an Organization. + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + response = client.organization.invitations.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + sort="sort", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list( + fields=fields, + include_fields=include_fields, + from_=from_, + take=take, + sort=sort, + request_options=request_options, + ) + + def create( + self, + *, + invitees: typing.Sequence[CreateMemberInvitationInvitee], + auth_0_custom_domain: typing.Optional[str] = None, + inviter: typing.Optional[MemberInvitationInviter] = OMIT, + identity_provider_id: typing.Optional[str] = OMIT, + ttl_sec: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateMemberInvitationResponseContent: + """ + Create one or more member invitations for this Organization. If an active invitation already exists for a user, generating a new invitation will automatically revoke any outstanding invitations for that user. Roles specified in the payload will be granted to the user upon acceptance of the invitation. + + Parameters + ---------- + invitees : typing.Sequence[CreateMemberInvitationInvitee] + + auth_0_custom_domain : typing.Optional[str] + + inviter : typing.Optional[MemberInvitationInviter] + + identity_provider_id : typing.Optional[str] + Identity provider identifier. + + ttl_sec : typing.Optional[int] + Number of seconds for which the invitation is valid before expiration. If unspecified or set to 0, this value defaults to 604800 seconds (7 days). Max value: 2592000 seconds (30 days). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateMemberInvitationResponseContent + Create Member Invitations for an Organization. + + Examples + -------- + from auth0 import Auth0, CreateMemberInvitationInvitee, MemberInvitationInviter + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.invitations.create( + invitees=[ + CreateMemberInvitationInvitee( + email="user@example.com", + roles=["rol_0000000000000001"], + ) + ], + inviter=MemberInvitationInviter( + name="Allison the Admin", + ), + identity_provider_id="con_2CZPv6IY0gWzDaQJ", + ttl_sec=3600, + ) + """ + _response = self._raw_client.create( + invitees=invitees, + auth_0_custom_domain=auth_0_custom_domain, + inviter=inviter, + identity_provider_id=identity_provider_id, + ttl_sec=ttl_sec, + request_options=request_options, + ) + return _response.data + + def get( + self, + invitation_id: InvitationId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetMemberInvitationResponseContent: + """ + Retrieve details of a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetMemberInvitationResponseContent + Get Member Invitation for an Organization by Id. + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.invitations.get( + invitation_id="invitation_id", + fields="fields", + include_fields=True, + ) + """ + _response = self._raw_client.get( + invitation_id, fields=fields, include_fields=include_fields, request_options=request_options + ) + return _response.data + + def delete(self, invitation_id: InvitationId, *, request_options: typing.Optional[RequestOptions] = None) -> None: + """ + Revoke a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.invitations.delete( + invitation_id="invitation_id", + ) + """ + _response = self._raw_client.delete(invitation_id, request_options=request_options) + return _response.data + + +class AsyncInvitationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawInvitationsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawInvitationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawInvitationsClient + """ + return self._raw_client + + async def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + sort: typing.Optional[str] = "created_at:-1", + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[MemberInvitation, ListMembersInvitationsResponseContent]: + """ + Retrieve a list of all member invitations for this Organization. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + sort : typing.Optional[str] + Field to sort by. Use field:order where order is 1 for ascending and -1 for descending. Defaults to created_at:-1 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[MemberInvitation, ListMembersInvitationsResponseContent] + List Members Invitations for an Organization. + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.organization.invitations.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + sort="sort", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list( + fields=fields, + include_fields=include_fields, + from_=from_, + take=take, + sort=sort, + request_options=request_options, + ) + + async def create( + self, + *, + invitees: typing.Sequence[CreateMemberInvitationInvitee], + auth_0_custom_domain: typing.Optional[str] = None, + inviter: typing.Optional[MemberInvitationInviter] = OMIT, + identity_provider_id: typing.Optional[str] = OMIT, + ttl_sec: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CreateMemberInvitationResponseContent: + """ + Create one or more member invitations for this Organization. If an active invitation already exists for a user, generating a new invitation will automatically revoke any outstanding invitations for that user. Roles specified in the payload will be granted to the user upon acceptance of the invitation. + + Parameters + ---------- + invitees : typing.Sequence[CreateMemberInvitationInvitee] + + auth_0_custom_domain : typing.Optional[str] + + inviter : typing.Optional[MemberInvitationInviter] + + identity_provider_id : typing.Optional[str] + Identity provider identifier. + + ttl_sec : typing.Optional[int] + Number of seconds for which the invitation is valid before expiration. If unspecified or set to 0, this value defaults to 604800 seconds (7 days). Max value: 2592000 seconds (30 days). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CreateMemberInvitationResponseContent + Create Member Invitations for an Organization. + + Examples + -------- + import asyncio + + from auth0 import ( + AsyncAuth0, + CreateMemberInvitationInvitee, + MemberInvitationInviter, + ) + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.invitations.create( + invitees=[ + CreateMemberInvitationInvitee( + email="user@example.com", + roles=["rol_0000000000000001"], + ) + ], + inviter=MemberInvitationInviter( + name="Allison the Admin", + ), + identity_provider_id="con_2CZPv6IY0gWzDaQJ", + ttl_sec=3600, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.create( + invitees=invitees, + auth_0_custom_domain=auth_0_custom_domain, + inviter=inviter, + identity_provider_id=identity_provider_id, + ttl_sec=ttl_sec, + request_options=request_options, + ) + return _response.data + + async def get( + self, + invitation_id: InvitationId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetMemberInvitationResponseContent: + """ + Retrieve details of a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetMemberInvitationResponseContent + Get Member Invitation for an Organization by Id. + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.invitations.get( + invitation_id="invitation_id", + fields="fields", + include_fields=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + invitation_id, fields=fields, include_fields=include_fields, request_options=request_options + ) + return _response.data + + async def delete( + self, invitation_id: InvitationId, *, request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Revoke a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.invitations.delete( + invitation_id="invitation_id", + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete(invitation_id, request_options=request_options) + return _response.data diff --git a/src/auth0/myorganization/organization/invitations/raw_client.py b/src/auth0/myorganization/organization/invitations/raw_client.py new file mode 100644 index 0000000..e8ec9d7 --- /dev/null +++ b/src/auth0/myorganization/organization/invitations/raw_client.py @@ -0,0 +1,974 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import encode_path_param +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata +from ...errors.bad_request_error import BadRequestError +from ...errors.forbidden_error import ForbiddenError +from ...errors.not_found_error import NotFoundError +from ...errors.too_many_requests_error import TooManyRequestsError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.create_member_invitation_invitee import CreateMemberInvitationInvitee +from ...types.create_member_invitation_response_content import CreateMemberInvitationResponseContent +from ...types.error_response_content import ErrorResponseContent +from ...types.get_member_invitation_response_content import GetMemberInvitationResponseContent +from ...types.invitation_id import InvitationId +from ...types.list_members_invitations_response_content import ListMembersInvitationsResponseContent +from ...types.member_invitation import MemberInvitation +from ...types.member_invitation_inviter import MemberInvitationInviter +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawInvitationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + sort: typing.Optional[str] = "created_at:-1", + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[MemberInvitation, ListMembersInvitationsResponseContent]: + """ + Retrieve a list of all member invitations for this Organization. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + sort : typing.Optional[str] + Field to sort by. Use field:order where order is 1 for ascending and -1 for descending. Defaults to created_at:-1 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[MemberInvitation, ListMembersInvitationsResponseContent] + List Members Invitations for an Organization. + """ + _response = self._client_wrapper.httpx_client.request( + "member-invitations", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + "from": from_, + "take": take, + "sort": sort, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListMembersInvitationsResponseContent, + parse_obj_as( + type_=ListMembersInvitationsResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.invitations + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + fields=fields, + include_fields=include_fields, + from_=_parsed_next, + take=take, + sort=sort, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def create( + self, + *, + invitees: typing.Sequence[CreateMemberInvitationInvitee], + auth_0_custom_domain: typing.Optional[str] = None, + inviter: typing.Optional[MemberInvitationInviter] = OMIT, + identity_provider_id: typing.Optional[str] = OMIT, + ttl_sec: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[CreateMemberInvitationResponseContent]: + """ + Create one or more member invitations for this Organization. If an active invitation already exists for a user, generating a new invitation will automatically revoke any outstanding invitations for that user. Roles specified in the payload will be granted to the user upon acceptance of the invitation. + + Parameters + ---------- + invitees : typing.Sequence[CreateMemberInvitationInvitee] + + auth_0_custom_domain : typing.Optional[str] + + inviter : typing.Optional[MemberInvitationInviter] + + identity_provider_id : typing.Optional[str] + Identity provider identifier. + + ttl_sec : typing.Optional[int] + Number of seconds for which the invitation is valid before expiration. If unspecified or set to 0, this value defaults to 604800 seconds (7 days). Max value: 2592000 seconds (30 days). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[CreateMemberInvitationResponseContent] + Create Member Invitations for an Organization. + """ + _response = self._client_wrapper.httpx_client.request( + "member-invitations", + method="POST", + json={ + "invitees": convert_and_respect_annotation_metadata( + object_=invitees, annotation=typing.Sequence[CreateMemberInvitationInvitee], direction="write" + ), + "inviter": convert_and_respect_annotation_metadata( + object_=inviter, annotation=MemberInvitationInviter, direction="write" + ), + "identity_provider_id": identity_provider_id, + "ttl_sec": ttl_sec, + }, + headers={ + "content-type": "application/json", + "auth0-custom-domain": str(auth_0_custom_domain) if auth_0_custom_domain is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateMemberInvitationResponseContent, + parse_obj_as( + type_=CreateMemberInvitationResponseContent, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def get( + self, + invitation_id: InvitationId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[GetMemberInvitationResponseContent]: + """ + Retrieve details of a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetMemberInvitationResponseContent] + Get Member Invitation for an Organization by Id. + """ + _response = self._client_wrapper.httpx_client.request( + f"member-invitations/{encode_path_param(invitation_id)}", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetMemberInvitationResponseContent, + parse_obj_as( + type_=GetMemberInvitationResponseContent, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def delete( + self, invitation_id: InvitationId, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Revoke a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"member-invitations/{encode_path_param(invitation_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawInvitationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + sort: typing.Optional[str] = "created_at:-1", + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[MemberInvitation, ListMembersInvitationsResponseContent]: + """ + Retrieve a list of all member invitations for this Organization. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + sort : typing.Optional[str] + Field to sort by. Use field:order where order is 1 for ascending and -1 for descending. Defaults to created_at:-1 + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[MemberInvitation, ListMembersInvitationsResponseContent] + List Members Invitations for an Organization. + """ + _response = await self._client_wrapper.httpx_client.request( + "member-invitations", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + "from": from_, + "take": take, + "sort": sort, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListMembersInvitationsResponseContent, + parse_obj_as( + type_=ListMembersInvitationsResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.invitations + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + fields=fields, + include_fields=include_fields, + from_=_parsed_next, + take=take, + sort=sort, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def create( + self, + *, + invitees: typing.Sequence[CreateMemberInvitationInvitee], + auth_0_custom_domain: typing.Optional[str] = None, + inviter: typing.Optional[MemberInvitationInviter] = OMIT, + identity_provider_id: typing.Optional[str] = OMIT, + ttl_sec: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[CreateMemberInvitationResponseContent]: + """ + Create one or more member invitations for this Organization. If an active invitation already exists for a user, generating a new invitation will automatically revoke any outstanding invitations for that user. Roles specified in the payload will be granted to the user upon acceptance of the invitation. + + Parameters + ---------- + invitees : typing.Sequence[CreateMemberInvitationInvitee] + + auth_0_custom_domain : typing.Optional[str] + + inviter : typing.Optional[MemberInvitationInviter] + + identity_provider_id : typing.Optional[str] + Identity provider identifier. + + ttl_sec : typing.Optional[int] + Number of seconds for which the invitation is valid before expiration. If unspecified or set to 0, this value defaults to 604800 seconds (7 days). Max value: 2592000 seconds (30 days). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[CreateMemberInvitationResponseContent] + Create Member Invitations for an Organization. + """ + _response = await self._client_wrapper.httpx_client.request( + "member-invitations", + method="POST", + json={ + "invitees": convert_and_respect_annotation_metadata( + object_=invitees, annotation=typing.Sequence[CreateMemberInvitationInvitee], direction="write" + ), + "inviter": convert_and_respect_annotation_metadata( + object_=inviter, annotation=MemberInvitationInviter, direction="write" + ), + "identity_provider_id": identity_provider_id, + "ttl_sec": ttl_sec, + }, + headers={ + "content-type": "application/json", + "auth0-custom-domain": str(auth_0_custom_domain) if auth_0_custom_domain is not None else None, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + CreateMemberInvitationResponseContent, + parse_obj_as( + type_=CreateMemberInvitationResponseContent, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def get( + self, + invitation_id: InvitationId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[GetMemberInvitationResponseContent]: + """ + Retrieve details of a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. Note: you cannot filter on ticket_id and this value will only be returned when fields are not filtered. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetMemberInvitationResponseContent] + Get Member Invitation for an Organization by Id. + """ + _response = await self._client_wrapper.httpx_client.request( + f"member-invitations/{encode_path_param(invitation_id)}", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetMemberInvitationResponseContent, + parse_obj_as( + type_=GetMemberInvitationResponseContent, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def delete( + self, invitation_id: InvitationId, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Revoke a member invitation specified by ID for this Organization. + + Parameters + ---------- + invitation_id : InvitationId + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"member-invitations/{encode_path_param(invitation_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/auth0/myorganization/organization/members/__init__.py b/src/auth0/myorganization/organization/members/__init__.py new file mode 100644 index 0000000..686aa75 --- /dev/null +++ b/src/auth0/myorganization/organization/members/__init__.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + +import typing +from importlib import import_module + +if typing.TYPE_CHECKING: + from . import roles +_dynamic_imports: typing.Dict[str, str] = {"roles": ".roles"} + + +def __getattr__(attr_name: str) -> typing.Any: + module_name = _dynamic_imports.get(attr_name) + if module_name is None: + raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") + try: + module = import_module(module_name, __package__) + if module_name == f".{attr_name}": + return module + else: + return getattr(module, attr_name) + except ImportError as e: + raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e + except AttributeError as e: + raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e + + +def __dir__(): + lazy_attrs = list(_dynamic_imports.keys()) + return sorted(lazy_attrs) + + +__all__ = ["roles"] diff --git a/src/auth0/myorganization/organization/members/client.py b/src/auth0/myorganization/organization/members/client.py new file mode 100644 index 0000000..99a588b --- /dev/null +++ b/src/auth0/myorganization/organization/members/client.py @@ -0,0 +1,294 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager +from ...core.request_options import RequestOptions +from ...types.get_organization_member_response_content import GetOrganizationMemberResponseContent +from ...types.list_organization_members_response_content import ListOrganizationMembersResponseContent +from ...types.org_member import OrgMember +from ...types.org_member_id import OrgMemberId +from .raw_client import AsyncRawMembersClient, RawMembersClient + +if typing.TYPE_CHECKING: + from .roles.client import AsyncRolesClient, RolesClient + + +class MembersClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMembersClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._roles: typing.Optional[RolesClient] = None + + @property + def with_raw_response(self) -> RawMembersClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMembersClient + """ + return self._raw_client + + def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[OrgMember, ListOrganizationMembersResponseContent]: + """ + Retrieve a list of all members for this Organization. The `roles` field is only included for each member when the token also carries the `read:my_org:member_roles` scope; without that scope the `roles` field is omitted from the response. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[OrgMember, ListOrganizationMembersResponseContent] + List members for an organization. + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + response = client.organization.members.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list( + fields=fields, include_fields=include_fields, from_=from_, take=take, request_options=request_options + ) + + def get( + self, + user_id: OrgMemberId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetOrganizationMemberResponseContent: + """ + Retrieve details of a member specified by user ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetOrganizationMemberResponseContent + Retrieve a member of the organization. + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.members.get( + user_id="user_id", + fields="fields", + include_fields=True, + ) + """ + _response = self._raw_client.get( + user_id, fields=fields, include_fields=include_fields, request_options=request_options + ) + return _response.data + + @property + def roles(self): + if self._roles is None: + from .roles.client import RolesClient # noqa: E402 + + self._roles = RolesClient(client_wrapper=self._client_wrapper) + return self._roles + + +class AsyncMembersClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMembersClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._roles: typing.Optional[AsyncRolesClient] = None + + @property + def with_raw_response(self) -> AsyncRawMembersClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMembersClient + """ + return self._raw_client + + async def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[OrgMember, ListOrganizationMembersResponseContent]: + """ + Retrieve a list of all members for this Organization. The `roles` field is only included for each member when the token also carries the `read:my_org:member_roles` scope; without that scope the `roles` field is omitted from the response. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[OrgMember, ListOrganizationMembersResponseContent] + List members for an organization. + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.organization.members.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list( + fields=fields, include_fields=include_fields, from_=from_, take=take, request_options=request_options + ) + + async def get( + self, + user_id: OrgMemberId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> GetOrganizationMemberResponseContent: + """ + Retrieve details of a member specified by user ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + GetOrganizationMemberResponseContent + Retrieve a member of the organization. + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.members.get( + user_id="user_id", + fields="fields", + include_fields=True, + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.get( + user_id, fields=fields, include_fields=include_fields, request_options=request_options + ) + return _response.data + + @property + def roles(self): + if self._roles is None: + from .roles.client import AsyncRolesClient # noqa: E402 + + self._roles = AsyncRolesClient(client_wrapper=self._client_wrapper) + return self._roles diff --git a/src/auth0/myorganization/organization/members/raw_client.py b/src/auth0/myorganization/organization/members/raw_client.py new file mode 100644 index 0000000..98b1e4d --- /dev/null +++ b/src/auth0/myorganization/organization/members/raw_client.py @@ -0,0 +1,519 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.jsonable_encoder import encode_path_param +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...errors.bad_request_error import BadRequestError +from ...errors.forbidden_error import ForbiddenError +from ...errors.not_found_error import NotFoundError +from ...errors.too_many_requests_error import TooManyRequestsError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.error_response_content import ErrorResponseContent +from ...types.get_organization_member_response_content import GetOrganizationMemberResponseContent +from ...types.list_organization_members_response_content import ListOrganizationMembersResponseContent +from ...types.org_member import OrgMember +from ...types.org_member_id import OrgMemberId +from pydantic import ValidationError + + +class RawMembersClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[OrgMember, ListOrganizationMembersResponseContent]: + """ + Retrieve a list of all members for this Organization. The `roles` field is only included for each member when the token also carries the `read:my_org:member_roles` scope; without that scope the `roles` field is omitted from the response. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[OrgMember, ListOrganizationMembersResponseContent] + List members for an organization. + """ + _response = self._client_wrapper.httpx_client.request( + "members", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + "from": from_, + "take": take, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListOrganizationMembersResponseContent, + parse_obj_as( + type_=ListOrganizationMembersResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.members + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + fields=fields, + include_fields=include_fields, + from_=_parsed_next, + take=take, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def get( + self, + user_id: OrgMemberId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[GetOrganizationMemberResponseContent]: + """ + Retrieve details of a member specified by user ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[GetOrganizationMemberResponseContent] + Retrieve a member of the organization. + """ + _response = self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetOrganizationMemberResponseContent, + parse_obj_as( + type_=GetOrganizationMemberResponseContent, # type: ignore + object_=_response.json(), + ), + ) + return HttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawMembersClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[OrgMember, ListOrganizationMembersResponseContent]: + """ + Retrieve a list of all members for this Organization. The `roles` field is only included for each member when the token also carries the `read:my_org:member_roles` scope; without that scope the `roles` field is omitted from the response. + + Parameters + ---------- + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[OrgMember, ListOrganizationMembersResponseContent] + List members for an organization. + """ + _response = await self._client_wrapper.httpx_client.request( + "members", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + "from": from_, + "take": take, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListOrganizationMembersResponseContent, + parse_obj_as( + type_=ListOrganizationMembersResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.members + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + fields=fields, + include_fields=include_fields, + from_=_parsed_next, + take=take, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def get( + self, + user_id: OrgMemberId, + *, + fields: typing.Optional[str] = None, + include_fields: typing.Optional[bool] = True, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[GetOrganizationMemberResponseContent]: + """ + Retrieve details of a member specified by user ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + fields : typing.Optional[str] + Comma-separated list of fields to include or exclude (based on value provided for include_fields) in the result. Leave empty to retrieve all fields. + + include_fields : typing.Optional[bool] + Whether specified fields are to be included (true) or excluded (false). Defaults to true + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[GetOrganizationMemberResponseContent] + Retrieve a member of the organization. + """ + _response = await self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}", + method="GET", + params={ + "fields": fields, + "include_fields": include_fields, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _data = typing.cast( + GetOrganizationMemberResponseContent, + parse_obj_as( + type_=GetOrganizationMemberResponseContent, # type: ignore + object_=_response.json(), + ), + ) + return AsyncHttpResponse(response=_response, data=_data) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/auth0/myorganization/organization/members/roles/__init__.py b/src/auth0/myorganization/organization/members/roles/__init__.py new file mode 100644 index 0000000..5cde020 --- /dev/null +++ b/src/auth0/myorganization/organization/members/roles/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/auth0/myorganization/organization/members/roles/client.py b/src/auth0/myorganization/organization/members/roles/client.py new file mode 100644 index 0000000..7915477 --- /dev/null +++ b/src/auth0/myorganization/organization/members/roles/client.py @@ -0,0 +1,322 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ....core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ....core.pagination import AsyncPager, SyncPager +from ....core.request_options import RequestOptions +from ....types.get_organization_member_roles_response_content import GetOrganizationMemberRolesResponseContent +from ....types.org_member_id import OrgMemberId +from ....types.role import Role +from ....types.role_id import RoleId +from .raw_client import AsyncRawRolesClient, RawRolesClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RolesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawRolesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawRolesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawRolesClient + """ + return self._raw_client + + def list( + self, + user_id: OrgMemberId, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Role, GetOrganizationMemberRolesResponseContent]: + """ + Retrieve a list of roles assigned to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Role, GetOrganizationMemberRolesResponseContent] + Retrieved memeber roles successfully. + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + response = client.organization.members.roles.list( + user_id="user_id", + from_="from", + take=1, + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(user_id, from_=from_, take=take, request_options=request_options) + + def assign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Assign roles to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.members.roles.assign( + user_id="user_id", + role_ids=["rol_SO2j0sFo9NFa3F9w"], + ) + """ + _response = self._raw_client.assign(user_id, role_ids=role_ids, request_options=request_options) + return _response.data + + def unassign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Remove roles from a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.members.roles.unassign( + user_id="user_id", + role_ids=["rol_SO2j0sFo9NFa3F9w"], + ) + """ + _response = self._raw_client.unassign(user_id, role_ids=role_ids, request_options=request_options) + return _response.data + + +class AsyncRolesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawRolesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawRolesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawRolesClient + """ + return self._raw_client + + async def list( + self, + user_id: OrgMemberId, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Role, GetOrganizationMemberRolesResponseContent]: + """ + Retrieve a list of roles assigned to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Role, GetOrganizationMemberRolesResponseContent] + Retrieved memeber roles successfully. + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.organization.members.roles.list( + user_id="user_id", + from_="from", + take=1, + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(user_id, from_=from_, take=take, request_options=request_options) + + async def assign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Assign roles to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.members.roles.assign( + user_id="user_id", + role_ids=["rol_SO2j0sFo9NFa3F9w"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.assign(user_id, role_ids=role_ids, request_options=request_options) + return _response.data + + async def unassign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> None: + """ + Remove roles from a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.members.roles.unassign( + user_id="user_id", + role_ids=["rol_SO2j0sFo9NFa3F9w"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.unassign(user_id, role_ids=role_ids, request_options=request_options) + return _response.data diff --git a/src/auth0/myorganization/organization/members/roles/raw_client.py b/src/auth0/myorganization/organization/members/roles/raw_client.py new file mode 100644 index 0000000..638af88 --- /dev/null +++ b/src/auth0/myorganization/organization/members/roles/raw_client.py @@ -0,0 +1,678 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ....core.api_error import ApiError +from ....core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ....core.http_response import AsyncHttpResponse, HttpResponse +from ....core.jsonable_encoder import encode_path_param +from ....core.pagination import AsyncPager, SyncPager +from ....core.parse_error import ParsingError +from ....core.pydantic_utilities import parse_obj_as +from ....core.request_options import RequestOptions +from ....errors.bad_request_error import BadRequestError +from ....errors.forbidden_error import ForbiddenError +from ....errors.not_found_error import NotFoundError +from ....errors.too_many_requests_error import TooManyRequestsError +from ....errors.unauthorized_error import UnauthorizedError +from ....types.error_response_content import ErrorResponseContent +from ....types.get_organization_member_roles_response_content import GetOrganizationMemberRolesResponseContent +from ....types.org_member_id import OrgMemberId +from ....types.role import Role +from ....types.role_id import RoleId +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawRolesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + user_id: OrgMemberId, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Role, GetOrganizationMemberRolesResponseContent]: + """ + Retrieve a list of roles assigned to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Role, GetOrganizationMemberRolesResponseContent] + Retrieved memeber roles successfully. + """ + _response = self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}/roles", + method="GET", + params={ + "from": from_, + "take": take, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + GetOrganizationMemberRolesResponseContent, + parse_obj_as( + type_=GetOrganizationMemberRolesResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.roles + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + user_id, + from_=_parsed_next, + take=take, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def assign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: + """ + Assign roles to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}/roles", + method="POST", + json={ + "role_ids": role_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + def unassign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> HttpResponse[None]: + """ + Remove roles from a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}/roles", + method="DELETE", + json={ + "role_ids": role_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawRolesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + user_id: OrgMemberId, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Role, GetOrganizationMemberRolesResponseContent]: + """ + Retrieve a list of roles assigned to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Role, GetOrganizationMemberRolesResponseContent] + Retrieved memeber roles successfully. + """ + _response = await self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}/roles", + method="GET", + params={ + "from": from_, + "take": take, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + GetOrganizationMemberRolesResponseContent, + parse_obj_as( + type_=GetOrganizationMemberRolesResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.roles + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + user_id, + from_=_parsed_next, + take=take, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def assign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: + """ + Assign roles to a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}/roles", + method="POST", + json={ + "role_ids": role_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + async def unassign( + self, + user_id: OrgMemberId, + *, + role_ids: typing.Sequence[RoleId], + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[None]: + """ + Remove roles from a member specified by ID for this Organization. + + Parameters + ---------- + user_id : OrgMemberId + + role_ids : typing.Sequence[RoleId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + f"members/{encode_path_param(user_id)}/roles", + method="DELETE", + json={ + "role_ids": role_ids, + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/auth0/myorganization/organization/memberships/__init__.py b/src/auth0/myorganization/organization/memberships/__init__.py new file mode 100644 index 0000000..5cde020 --- /dev/null +++ b/src/auth0/myorganization/organization/memberships/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/auth0/myorganization/organization/memberships/client.py b/src/auth0/myorganization/organization/memberships/client.py new file mode 100644 index 0000000..0cfed6e --- /dev/null +++ b/src/auth0/myorganization/organization/memberships/client.py @@ -0,0 +1,113 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.org_member_id import OrgMemberId +from .raw_client import AsyncRawMembershipsClient, RawMembershipsClient + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class MembershipsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawMembershipsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawMembershipsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawMembershipsClient + """ + return self._raw_client + + def delete_memberships( + self, *, members: typing.Sequence[OrgMemberId], request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Remove one member from this Organization. The underlying user account is not deleted. + + Parameters + ---------- + members : typing.Sequence[OrgMemberId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + client.organization.memberships.delete_memberships( + members=["auth0|1234567890"], + ) + """ + _response = self._raw_client.delete_memberships(members=members, request_options=request_options) + return _response.data + + +class AsyncMembershipsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawMembershipsClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawMembershipsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawMembershipsClient + """ + return self._raw_client + + async def delete_memberships( + self, *, members: typing.Sequence[OrgMemberId], request_options: typing.Optional[RequestOptions] = None + ) -> None: + """ + Remove one member from this Organization. The underlying user account is not deleted. + + Parameters + ---------- + members : typing.Sequence[OrgMemberId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + None + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + await client.organization.memberships.delete_memberships( + members=["auth0|1234567890"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.delete_memberships(members=members, request_options=request_options) + return _response.data diff --git a/src/auth0/myorganization/organization/memberships/raw_client.py b/src/auth0/myorganization/organization/memberships/raw_client.py new file mode 100644 index 0000000..098fe8a --- /dev/null +++ b/src/auth0/myorganization/organization/memberships/raw_client.py @@ -0,0 +1,224 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.parse_error import ParsingError +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...errors.bad_request_error import BadRequestError +from ...errors.forbidden_error import ForbiddenError +from ...errors.not_found_error import NotFoundError +from ...errors.too_many_requests_error import TooManyRequestsError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.error_response_content import ErrorResponseContent +from ...types.org_member_id import OrgMemberId +from pydantic import ValidationError + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class RawMembershipsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def delete_memberships( + self, *, members: typing.Sequence[OrgMemberId], request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[None]: + """ + Remove one member from this Organization. The underlying user account is not deleted. + + Parameters + ---------- + members : typing.Sequence[OrgMemberId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + HttpResponse[None] + """ + _response = self._client_wrapper.httpx_client.request( + "delete-memberships", + method="POST", + json={ + "members": members, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return HttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawMembershipsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def delete_memberships( + self, *, members: typing.Sequence[OrgMemberId], request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[None]: + """ + Remove one member from this Organization. The underlying user account is not deleted. + + Parameters + ---------- + members : typing.Sequence[OrgMemberId] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncHttpResponse[None] + """ + _response = await self._client_wrapper.httpx_client.request( + "delete-memberships", + method="POST", + json={ + "members": members, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return AsyncHttpResponse(response=_response, data=None) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 404: + raise NotFoundError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/auth0/myorganization/organization/roles/__init__.py b/src/auth0/myorganization/organization/roles/__init__.py new file mode 100644 index 0000000..5cde020 --- /dev/null +++ b/src/auth0/myorganization/organization/roles/__init__.py @@ -0,0 +1,4 @@ +# This file was auto-generated by Fern from our API Definition. + +# isort: skip_file + diff --git a/src/auth0/myorganization/organization/roles/client.py b/src/auth0/myorganization/organization/roles/client.py new file mode 100644 index 0000000..5c058ec --- /dev/null +++ b/src/auth0/myorganization/organization/roles/client.py @@ -0,0 +1,151 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager +from ...core.request_options import RequestOptions +from ...types.list_roles_response_content import ListRolesResponseContent +from ...types.role import Role +from .raw_client import AsyncRawRolesClient, RawRolesClient + + +class RolesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawRolesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawRolesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawRolesClient + """ + return self._raw_client + + def list( + self, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + name: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Role, ListRolesResponseContent]: + """ + Retrieve the list of roles available for binding to members and invitations for this Organization. Only roles made visible to this Organization by the Tenant Admin are returned. + + Parameters + ---------- + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + name : typing.Optional[str] + An optional filter on the name (case-insensitive). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Role, ListRolesResponseContent] + Retrieve roles for the organization. + + Examples + -------- + from auth0 import Auth0 + + client = Auth0( + token="YOUR_TOKEN", + ) + response = client.organization.roles.list( + from_="from", + take=1, + name="name", + ) + for item in response: + yield item + # alternatively, you can paginate page-by-page + for page in response.iter_pages(): + yield page + """ + return self._raw_client.list(from_=from_, take=take, name=name, request_options=request_options) + + +class AsyncRolesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawRolesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawRolesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawRolesClient + """ + return self._raw_client + + async def list( + self, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + name: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Role, ListRolesResponseContent]: + """ + Retrieve the list of roles available for binding to members and invitations for this Organization. Only roles made visible to this Organization by the Tenant Admin are returned. + + Parameters + ---------- + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + name : typing.Optional[str] + An optional filter on the name (case-insensitive). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Role, ListRolesResponseContent] + Retrieve roles for the organization. + + Examples + -------- + import asyncio + + from auth0 import AsyncAuth0 + + client = AsyncAuth0( + token="YOUR_TOKEN", + ) + + + async def main() -> None: + response = await client.organization.roles.list( + from_="from", + take=1, + name="name", + ) + async for item in response: + yield item + + # alternatively, you can paginate page-by-page + async for page in response.iter_pages(): + yield page + + + asyncio.run(main()) + """ + return await self._raw_client.list(from_=from_, take=take, name=name, request_options=request_options) diff --git a/src/auth0/myorganization/organization/roles/raw_client.py b/src/auth0/myorganization/organization/roles/raw_client.py new file mode 100644 index 0000000..30595cf --- /dev/null +++ b/src/auth0/myorganization/organization/roles/raw_client.py @@ -0,0 +1,256 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing +from json.decoder import JSONDecodeError + +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.pagination import AsyncPager, SyncPager +from ...core.parse_error import ParsingError +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...errors.bad_request_error import BadRequestError +from ...errors.forbidden_error import ForbiddenError +from ...errors.too_many_requests_error import TooManyRequestsError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.error_response_content import ErrorResponseContent +from ...types.list_roles_response_content import ListRolesResponseContent +from ...types.role import Role +from pydantic import ValidationError + + +class RawRolesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + def list( + self, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + name: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[Role, ListRolesResponseContent]: + """ + Retrieve the list of roles available for binding to members and invitations for this Organization. Only roles made visible to this Organization by the Tenant Admin are returned. + + Parameters + ---------- + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + name : typing.Optional[str] + An optional filter on the name (case-insensitive). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SyncPager[Role, ListRolesResponseContent] + Retrieve roles for the organization. + """ + _response = self._client_wrapper.httpx_client.request( + "roles", + method="GET", + params={ + "from": from_, + "take": take, + "name": name, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListRolesResponseContent, + parse_obj_as( + type_=ListRolesResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.roles + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list( + from_=_parsed_next, + take=take, + name=name, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) + + +class AsyncRawRolesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def list( + self, + *, + from_: typing.Optional[str] = None, + take: typing.Optional[int] = 50, + name: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncPager[Role, ListRolesResponseContent]: + """ + Retrieve the list of roles available for binding to members and invitations for this Organization. Only roles made visible to this Organization by the Tenant Admin are returned. + + Parameters + ---------- + from_ : typing.Optional[str] + An optional cursor from which to start the selection (exclusive). + + take : typing.Optional[int] + Number of results per page. Defaults to 50. + + name : typing.Optional[str] + An optional filter on the name (case-insensitive). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + AsyncPager[Role, ListRolesResponseContent] + Retrieve roles for the organization. + """ + _response = await self._client_wrapper.httpx_client.request( + "roles", + method="GET", + params={ + "from": from_, + "take": take, + "name": name, + }, + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + _parsed_response = typing.cast( + ListRolesResponseContent, + parse_obj_as( + type_=ListRolesResponseContent, # type: ignore + object_=_response.json(), + ), + ) + _items = _parsed_response.roles + _parsed_next = _parsed_response.next + _has_next = _parsed_next is not None and _parsed_next != "" + + async def _get_next(): + return await self.list( + from_=_parsed_next, + take=take, + name=name, + request_options=request_options, + ) + + return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + if _response.status_code == 400: + raise BadRequestError( + headers=dict(_response.headers), + body=typing.cast( + typing.Any, + parse_obj_as( + type_=typing.Any, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 401: + raise UnauthorizedError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 403: + raise ForbiddenError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + if _response.status_code == 429: + raise TooManyRequestsError( + headers=dict(_response.headers), + body=typing.cast( + ErrorResponseContent, + parse_obj_as( + type_=ErrorResponseContent, # type: ignore + object_=_response.json(), + ), + ), + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) + except ValidationError as e: + raise ParsingError( + status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e + ) + raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/auth0/myorganization/organization_details/raw_client.py b/src/auth0/myorganization/organization_details/raw_client.py index bd709b4..b44e4a8 100644 --- a/src/auth0/myorganization/organization_details/raw_client.py +++ b/src/auth0/myorganization/organization_details/raw_client.py @@ -152,9 +152,6 @@ def update( object_=branding, annotation=OrgBranding, direction="write" ), }, - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) @@ -360,9 +357,6 @@ async def update( object_=branding, annotation=OrgBranding, direction="write" ), }, - headers={ - "content-type": "application/json", - }, request_options=request_options, omit=OMIT, ) diff --git a/src/auth0/myorganization/types/__init__.py b/src/auth0/myorganization/types/__init__.py index d134221..a194782 100644 --- a/src/auth0/myorganization/types/__init__.py +++ b/src/auth0/myorganization/types/__init__.py @@ -14,6 +14,8 @@ from .create_identity_provider_response_content import CreateIdentityProviderResponseContent from .create_idp_domain_response_content import CreateIdpDomainResponseContent from .create_idp_provisioning_scim_token_response_content import CreateIdpProvisioningScimTokenResponseContent + from .create_member_invitation_invitee import CreateMemberInvitationInvitee + from .create_member_invitation_response_content import CreateMemberInvitationResponseContent from .create_organization_domain_response_content import CreateOrganizationDomainResponseContent from .domain_idp import DomainIdp from .error_response_content import ErrorResponseContent @@ -26,6 +28,7 @@ from .get_organization_details_response_content import GetOrganizationDetailsResponseContent from .get_organization_domain_response_content import GetOrganizationDomainResponseContent from .get_organization_member_response_content import GetOrganizationMemberResponseContent + from .get_organization_member_roles_response_content import GetOrganizationMemberRolesResponseContent from .identity_provider_config_adfs import IdentityProviderConfigAdfs from .identity_provider_config_google_apps import IdentityProviderConfigGoogleApps from .identity_provider_config_oidc import IdentityProviderConfigOidc @@ -117,7 +120,10 @@ from .list_domain_identity_providers_response_content import ListDomainIdentityProvidersResponseContent from .list_identity_providers_response_content import ListIdentityProvidersResponseContent from .list_idp_provisioning_scim_tokens_response_content import ListIdpProvisioningScimTokensResponseContent + from .list_members_invitations_response_content import ListMembersInvitationsResponseContent from .list_organization_domains_response_content import ListOrganizationDomainsResponseContent + from .list_organization_members_response_content import ListOrganizationMembersResponseContent + from .list_roles_response_content import ListRolesResponseContent from .manual import Manual from .member_invitation import MemberInvitation from .member_invitation_invitee import MemberInvitationInvitee @@ -133,10 +139,12 @@ from .org_domain_status_enum import OrgDomainStatusEnum from .org_id import OrgId from .org_member import OrgMember + from .org_member_id import OrgMemberId from .org_member_id_read_only import OrgMemberIdReadOnly - from .org_member_role import OrgMemberRole - from .org_member_role_id import OrgMemberRoleId from .organization_access_level_enum import OrganizationAccessLevelEnum + from .organization_member_roles_change_request_content import OrganizationMemberRolesChangeRequestContent + from .role import Role + from .role_id import RoleId from .start_organization_domain_verification_response_content import ( StartOrganizationDomainVerificationResponseContent, ) @@ -156,6 +164,8 @@ "CreateIdentityProviderResponseContent": ".create_identity_provider_response_content", "CreateIdpDomainResponseContent": ".create_idp_domain_response_content", "CreateIdpProvisioningScimTokenResponseContent": ".create_idp_provisioning_scim_token_response_content", + "CreateMemberInvitationInvitee": ".create_member_invitation_invitee", + "CreateMemberInvitationResponseContent": ".create_member_invitation_response_content", "CreateOrganizationDomainResponseContent": ".create_organization_domain_response_content", "DomainIdp": ".domain_idp", "ErrorResponseContent": ".error_response_content", @@ -168,6 +178,7 @@ "GetOrganizationDetailsResponseContent": ".get_organization_details_response_content", "GetOrganizationDomainResponseContent": ".get_organization_domain_response_content", "GetOrganizationMemberResponseContent": ".get_organization_member_response_content", + "GetOrganizationMemberRolesResponseContent": ".get_organization_member_roles_response_content", "IdentityProviderConfigAdfs": ".identity_provider_config_adfs", "IdentityProviderConfigGoogleApps": ".identity_provider_config_google_apps", "IdentityProviderConfigOidc": ".identity_provider_config_oidc", @@ -259,7 +270,10 @@ "ListDomainIdentityProvidersResponseContent": ".list_domain_identity_providers_response_content", "ListIdentityProvidersResponseContent": ".list_identity_providers_response_content", "ListIdpProvisioningScimTokensResponseContent": ".list_idp_provisioning_scim_tokens_response_content", + "ListMembersInvitationsResponseContent": ".list_members_invitations_response_content", "ListOrganizationDomainsResponseContent": ".list_organization_domains_response_content", + "ListOrganizationMembersResponseContent": ".list_organization_members_response_content", + "ListRolesResponseContent": ".list_roles_response_content", "Manual": ".manual", "MemberInvitation": ".member_invitation", "MemberInvitationInvitee": ".member_invitation_invitee", @@ -275,10 +289,12 @@ "OrgDomainStatusEnum": ".org_domain_status_enum", "OrgId": ".org_id", "OrgMember": ".org_member", + "OrgMemberId": ".org_member_id", "OrgMemberIdReadOnly": ".org_member_id_read_only", - "OrgMemberRole": ".org_member_role", - "OrgMemberRoleId": ".org_member_role_id", "OrganizationAccessLevelEnum": ".organization_access_level_enum", + "OrganizationMemberRolesChangeRequestContent": ".organization_member_roles_change_request_content", + "Role": ".role", + "RoleId": ".role_id", "StartOrganizationDomainVerificationResponseContent": ".start_organization_domain_verification_response_content", "UpdateIdentityProviderRequestContent": ".update_identity_provider_request_content", "UpdateIdentityProviderResponseContent": ".update_identity_provider_response_content", @@ -320,6 +336,8 @@ def __dir__(): "CreateIdentityProviderResponseContent", "CreateIdpDomainResponseContent", "CreateIdpProvisioningScimTokenResponseContent", + "CreateMemberInvitationInvitee", + "CreateMemberInvitationResponseContent", "CreateOrganizationDomainResponseContent", "DomainIdp", "ErrorResponseContent", @@ -332,6 +350,7 @@ def __dir__(): "GetOrganizationDetailsResponseContent", "GetOrganizationDomainResponseContent", "GetOrganizationMemberResponseContent", + "GetOrganizationMemberRolesResponseContent", "IdentityProviderConfigAdfs", "IdentityProviderConfigGoogleApps", "IdentityProviderConfigOidc", @@ -423,7 +442,10 @@ def __dir__(): "ListDomainIdentityProvidersResponseContent", "ListIdentityProvidersResponseContent", "ListIdpProvisioningScimTokensResponseContent", + "ListMembersInvitationsResponseContent", "ListOrganizationDomainsResponseContent", + "ListOrganizationMembersResponseContent", + "ListRolesResponseContent", "Manual", "MemberInvitation", "MemberInvitationInvitee", @@ -439,10 +461,12 @@ def __dir__(): "OrgDomainStatusEnum", "OrgId", "OrgMember", + "OrgMemberId", "OrgMemberIdReadOnly", - "OrgMemberRole", - "OrgMemberRoleId", "OrganizationAccessLevelEnum", + "OrganizationMemberRolesChangeRequestContent", + "Role", + "RoleId", "StartOrganizationDomainVerificationResponseContent", "UpdateIdentityProviderRequestContent", "UpdateIdentityProviderResponseContent", diff --git a/src/auth0/myorganization/types/create_member_invitation_invitee.py b/src/auth0/myorganization/types/create_member_invitation_invitee.py new file mode 100644 index 0000000..de64fce --- /dev/null +++ b/src/auth0/myorganization/types/create_member_invitation_invitee.py @@ -0,0 +1,28 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .role_id import RoleId + + +class CreateMemberInvitationInvitee(UniversalBaseModel): + email: str = pydantic.Field() + """ + The invitee's email. + """ + + roles: typing.Optional[typing.List[RoleId]] = pydantic.Field(default=None) + """ + List of role IDs to associate with the user. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/myorganization/types/create_member_invitation_response_content.py b/src/auth0/myorganization/types/create_member_invitation_response_content.py new file mode 100644 index 0000000..36e05a9 --- /dev/null +++ b/src/auth0/myorganization/types/create_member_invitation_response_content.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .member_invitation import MemberInvitation + +CreateMemberInvitationResponseContent = typing.List[MemberInvitation] diff --git a/src/auth0/myorganization/types/get_organization_member_roles_response_content.py b/src/auth0/myorganization/types/get_organization_member_roles_response_content.py new file mode 100644 index 0000000..bd412d6 --- /dev/null +++ b/src/auth0/myorganization/types/get_organization_member_roles_response_content.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .role import Role + + +class GetOrganizationMemberRolesResponseContent(UniversalBaseModel): + next: typing.Optional[str] = pydantic.Field(default=None) + """ + Pagination cursor for the next page of results + """ + + roles: typing.List[Role] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/myorganization/types/list_members_invitations_response_content.py b/src/auth0/myorganization/types/list_members_invitations_response_content.py new file mode 100644 index 0000000..33243b6 --- /dev/null +++ b/src/auth0/myorganization/types/list_members_invitations_response_content.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .member_invitation import MemberInvitation + + +class ListMembersInvitationsResponseContent(UniversalBaseModel): + next: typing.Optional[str] = pydantic.Field(default=None) + """ + Pagination cursor for the next page of results. + """ + + invitations: typing.Optional[typing.List[MemberInvitation]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/myorganization/types/list_organization_members_response_content.py b/src/auth0/myorganization/types/list_organization_members_response_content.py new file mode 100644 index 0000000..923e9aa --- /dev/null +++ b/src/auth0/myorganization/types/list_organization_members_response_content.py @@ -0,0 +1,25 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .org_member import OrgMember + + +class ListOrganizationMembersResponseContent(UniversalBaseModel): + next: typing.Optional[str] = pydantic.Field(default=None) + """ + Pagination cursor for the next page of results. + """ + + members: typing.Optional[typing.List[OrgMember]] = None + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/myorganization/types/list_roles_response_content.py b/src/auth0/myorganization/types/list_roles_response_content.py new file mode 100644 index 0000000..bff0af2 --- /dev/null +++ b/src/auth0/myorganization/types/list_roles_response_content.py @@ -0,0 +1,24 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .role import Role + + +class ListRolesResponseContent(UniversalBaseModel): + roles: typing.List[Role] + next: typing.Optional[str] = pydantic.Field(default=None) + """ + Pagination cursor for the next page of results + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/myorganization/types/member_invitation.py b/src/auth0/myorganization/types/member_invitation.py index ab3a13b..88058ae 100644 --- a/src/auth0/myorganization/types/member_invitation.py +++ b/src/auth0/myorganization/types/member_invitation.py @@ -10,7 +10,7 @@ from .member_invitation_invitee import MemberInvitationInvitee from .member_invitation_inviter import MemberInvitationInviter from .org_id import OrgId -from .org_member_role_id import OrgMemberRoleId +from .role_id import RoleId class MemberInvitation(UniversalBaseModel): @@ -29,7 +29,7 @@ class MemberInvitation(UniversalBaseModel): The ISO 8601 formatted timestamp representing the expiration time of the invitation. """ - roles: typing.Optional[typing.List[OrgMemberRoleId]] = None + roles: typing.Optional[typing.List[RoleId]] = None invitation_url: typing.Optional[str] = pydantic.Field(default=None) """ The invitation url to be sent to the invitee. diff --git a/src/auth0/myorganization/types/oauth_scope.py b/src/auth0/myorganization/types/oauth_scope.py index 36b523e..cf5f330 100644 --- a/src/auth0/myorganization/types/oauth_scope.py +++ b/src/auth0/myorganization/types/oauth_scope.py @@ -29,11 +29,9 @@ "create:my_org:member_invitations", "delete:my_org:member_invitations", "read:my_org:members", - "delete:my_org:members", "delete:my_org:memberships", "read:my_org:member_roles", "create:my_org:member_roles", - "update:my_org:member_roles", "delete:my_org:member_roles", "create:my_org:client_grants", "create:my_org:clients", diff --git a/src/auth0/myorganization/types/org_member.py b/src/auth0/myorganization/types/org_member.py index a36f2a7..77a4766 100644 --- a/src/auth0/myorganization/types/org_member.py +++ b/src/auth0/myorganization/types/org_member.py @@ -6,13 +6,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 from .org_member_id_read_only import OrgMemberIdReadOnly -from .org_member_role import OrgMemberRole +from .role import Role from .user_attributes import UserAttributes class OrgMember(UserAttributes): user_id: typing.Optional[OrgMemberIdReadOnly] = None - roles: typing.Optional[typing.List[OrgMemberRole]] = None + roles: typing.Optional[typing.List[Role]] = None created_at: typing.Optional[dt.datetime] = pydantic.Field(default=None) """ Date and time when this user was created (ISO_8601 format). @@ -28,6 +28,11 @@ class OrgMember(UserAttributes): Last date and time this user logged in (ISO_8601 format). """ + phone_number: typing.Optional[str] = pydantic.Field(default=None) + """ + Phone number associated with the user. + """ + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/auth0/myorganization/types/org_member_id.py b/src/auth0/myorganization/types/org_member_id.py new file mode 100644 index 0000000..0b56064 --- /dev/null +++ b/src/auth0/myorganization/types/org_member_id.py @@ -0,0 +1,6 @@ +# This file was auto-generated by Fern from our API Definition. + +OrgMemberId = str +""" +The user ID. +""" diff --git a/src/auth0/myorganization/types/organization_member_roles_change_request_content.py b/src/auth0/myorganization/types/organization_member_roles_change_request_content.py new file mode 100644 index 0000000..1c60589 --- /dev/null +++ b/src/auth0/myorganization/types/organization_member_roles_change_request_content.py @@ -0,0 +1,20 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .role_id import RoleId + + +class OrganizationMemberRolesChangeRequestContent(UniversalBaseModel): + role_ids: typing.List[RoleId] + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/auth0/myorganization/types/org_member_role.py b/src/auth0/myorganization/types/role.py similarity index 82% rename from src/auth0/myorganization/types/org_member_role.py rename to src/auth0/myorganization/types/role.py index 3b462c0..2860305 100644 --- a/src/auth0/myorganization/types/org_member_role.py +++ b/src/auth0/myorganization/types/role.py @@ -4,11 +4,11 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .org_member_role_id import OrgMemberRoleId +from .role_id import RoleId -class OrgMemberRole(UniversalBaseModel): - id: OrgMemberRoleId +class Role(UniversalBaseModel): + id: RoleId name: str description: typing.Optional[str] = None diff --git a/src/auth0/myorganization/types/org_member_role_id.py b/src/auth0/myorganization/types/role_id.py similarity index 86% rename from src/auth0/myorganization/types/org_member_role_id.py rename to src/auth0/myorganization/types/role_id.py index d3e804d..1c4f987 100644 --- a/src/auth0/myorganization/types/org_member_role_id.py +++ b/src/auth0/myorganization/types/role_id.py @@ -1,6 +1,6 @@ # This file was auto-generated by Fern from our API Definition. -OrgMemberRoleId = str +RoleId = str """ The ID of a role that can be assigned to a member of an organization. """ diff --git a/tests/utils/test_http_client.py b/tests/utils/test_http_client.py index 1523647..407e6f4 100644 --- a/tests/utils/test_http_client.py +++ b/tests/utils/test_http_client.py @@ -10,6 +10,7 @@ AsyncHttpClient, HttpClient, _build_url, + _should_retry, get_request_body, remove_none_from_dict, ) @@ -660,3 +661,34 @@ async def test_async_base_max_retries_used_as_default(mock_sleep: AsyncMock) -> assert response.status_code == 200 # 1 initial + 3 retries = 4 total attempts assert mock_client.request.call_count == 4 + + +# --------------------------------------------------------------------------- +# _should_retry unit tests +# --------------------------------------------------------------------------- + + +def _make_response(status_code: int) -> httpx.Response: + return httpx.Response(status_code=status_code, content=b"") + + +@pytest.mark.parametrize( + "status_code", + [408, 409, 429, 500, 501, 502, 503, 504, 599], +) +def test_should_retry_retryable_status_codes(status_code: int) -> None: + """Legacy mode: retries on 408, 409, 429, and all >= 500.""" + assert _should_retry(_make_response(status_code)) is True + + +@pytest.mark.parametrize( + "status_code", + [200, 201, 301, 400, 401, 403, 404], +) +def test_should_not_retry_non_retryable_status_codes(status_code: int) -> None: + assert _should_retry(_make_response(status_code)) is False + + +def test_should_retry_599_upper_boundary() -> None: + """Legacy mode retries on >= 500, which includes 599.""" + assert _should_retry(_make_response(599)) is True diff --git a/tests/wire/conftest.py b/tests/wire/conftest.py index 66716d1..c417af0 100644 --- a/tests/wire/conftest.py +++ b/tests/wire/conftest.py @@ -60,7 +60,7 @@ def verify_request_count( test_id: str, method: str, url_path: str, - query_params: Optional[Dict[str, str]], + query_params: Optional[Dict[str, Any]], expected: int, ) -> None: """Verifies the number of requests made to WireMock filtered by test ID for concurrency safety.""" @@ -71,7 +71,12 @@ def verify_request_count( "headers": {"X-Test-Id": {"equalTo": test_id}}, } if query_params: - query_parameters = {k: {"equalTo": v} for k, v in query_params.items()} + query_parameters = {} + for k, v in query_params.items(): + if isinstance(v, list): + query_parameters[k] = {"hasExactly": [{"equalTo": item} for item in v]} + else: + query_parameters[k] = {"equalTo": v} request_body["queryParameters"] = query_parameters response = httpx.post(f"{wiremock_admin_url}/requests/find", json=request_body) assert response.status_code == 200, "Failed to query WireMock requests" diff --git a/tests/wire/test_organization_identityProviders.py b/tests/wire/test_organization_identityProviders.py index 0445eb5..d905140 100644 --- a/tests/wire/test_organization_identityProviders.py +++ b/tests/wire/test_organization_identityProviders.py @@ -17,19 +17,19 @@ def test_organization_identityProviders_create() -> None: client = get_client(test_id) client.organization.identity_providers.create( request=IdpOidcRequest( - name="oidcIdp", strategy="oidc", - domains=["mydomain.com"], - display_name="OIDC IdP", - show_as_button=True, - assign_membership_on_login=False, - is_enabled=True, options=IdpOidcOptionsRequest( type="front_channel", client_id="a8f3b2e7-5d1c-4f9a-8b0d-2e1c3a5b6f7d", client_secret="KzQp2sVxR8nTgMjFhYcEWuLoIbDvUoC6A9B1zX7yWqFjHkGrP5sQdLmNp", discovery_url="https://{yourDomain}/.well-known/openid-configuration", ), + name="oidcIdp", + domains=["mydomain.com"], + display_name="OIDC IdP", + show_as_button=True, + assign_membership_on_login=False, + is_enabled=True, ), ) verify_request_count(test_id, "POST", "/identity-providers", None, 1) diff --git a/tests/wire/test_organization_invitations.py b/tests/wire/test_organization_invitations.py new file mode 100644 index 0000000..f4be29d --- /dev/null +++ b/tests/wire/test_organization_invitations.py @@ -0,0 +1,67 @@ +from .conftest import get_client, verify_request_count + +from auth0.myorganization import CreateMemberInvitationInvitee, MemberInvitationInviter + + +def test_organization_invitations_list_() -> None: + """Test list endpoint with WireMock""" + test_id = "organization.invitations.list_.0" + client = get_client(test_id) + client.organization.invitations.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + sort="sort", + ) + verify_request_count( + test_id, + "GET", + "/member-invitations", + {"fields": "fields", "include_fields": "true", "from": "from", "take": "1", "sort": "sort"}, + 1, + ) + + +def test_organization_invitations_create() -> None: + """Test create endpoint with WireMock""" + test_id = "organization.invitations.create.0" + client = get_client(test_id) + client.organization.invitations.create( + invitees=[ + CreateMemberInvitationInvitee( + email="user@example.com", + roles=["rol_0000000000000001"], + ) + ], + inviter=MemberInvitationInviter( + name="Allison the Admin", + ), + identity_provider_id="con_2CZPv6IY0gWzDaQJ", + ttl_sec=3600, + ) + verify_request_count(test_id, "POST", "/member-invitations", None, 1) + + +def test_organization_invitations_get() -> None: + """Test get endpoint with WireMock""" + test_id = "organization.invitations.get.0" + client = get_client(test_id) + client.organization.invitations.get( + invitation_id="invitation_id", + fields="fields", + include_fields=True, + ) + verify_request_count( + test_id, "GET", "/member-invitations/invitation_id", {"fields": "fields", "include_fields": "true"}, 1 + ) + + +def test_organization_invitations_delete() -> None: + """Test delete endpoint with WireMock""" + test_id = "organization.invitations.delete.0" + client = get_client(test_id) + client.organization.invitations.delete( + invitation_id="invitation_id", + ) + verify_request_count(test_id, "DELETE", "/member-invitations/invitation_id", None, 1) diff --git a/tests/wire/test_organization_members.py b/tests/wire/test_organization_members.py new file mode 100644 index 0000000..7ed97dc --- /dev/null +++ b/tests/wire/test_organization_members.py @@ -0,0 +1,28 @@ +from .conftest import get_client, verify_request_count + + +def test_organization_members_list_() -> None: + """Test list endpoint with WireMock""" + test_id = "organization.members.list_.0" + client = get_client(test_id) + client.organization.members.list( + fields="fields", + include_fields=True, + from_="from", + take=1, + ) + verify_request_count( + test_id, "GET", "/members", {"fields": "fields", "include_fields": "true", "from": "from", "take": "1"}, 1 + ) + + +def test_organization_members_get() -> None: + """Test get endpoint with WireMock""" + test_id = "organization.members.get.0" + client = get_client(test_id) + client.organization.members.get( + user_id="user_id", + fields="fields", + include_fields=True, + ) + verify_request_count(test_id, "GET", "/members/user_id", {"fields": "fields", "include_fields": "true"}, 1) diff --git a/tests/wire/test_organization_members_roles.py b/tests/wire/test_organization_members_roles.py new file mode 100644 index 0000000..fe58e88 --- /dev/null +++ b/tests/wire/test_organization_members_roles.py @@ -0,0 +1,35 @@ +from .conftest import get_client, verify_request_count + + +def test_organization_members_roles_list_() -> None: + """Test list endpoint with WireMock""" + test_id = "organization.members.roles.list_.0" + client = get_client(test_id) + client.organization.members.roles.list( + user_id="user_id", + from_="from", + take=1, + ) + verify_request_count(test_id, "GET", "/members/user_id/roles", {"from": "from", "take": "1"}, 1) + + +def test_organization_members_roles_assign() -> None: + """Test assign endpoint with WireMock""" + test_id = "organization.members.roles.assign.0" + client = get_client(test_id) + client.organization.members.roles.assign( + user_id="user_id", + role_ids=["rol_SO2j0sFo9NFa3F9w"], + ) + verify_request_count(test_id, "POST", "/members/user_id/roles", None, 1) + + +def test_organization_members_roles_unassign() -> None: + """Test unassign endpoint with WireMock""" + test_id = "organization.members.roles.unassign.0" + client = get_client(test_id) + client.organization.members.roles.unassign( + user_id="user_id", + role_ids=["rol_SO2j0sFo9NFa3F9w"], + ) + verify_request_count(test_id, "DELETE", "/members/user_id/roles", None, 1) diff --git a/tests/wire/test_organization_memberships.py b/tests/wire/test_organization_memberships.py new file mode 100644 index 0000000..e070c76 --- /dev/null +++ b/tests/wire/test_organization_memberships.py @@ -0,0 +1,11 @@ +from .conftest import get_client, verify_request_count + + +def test_organization_memberships_delete_memberships() -> None: + """Test deleteMemberships endpoint with WireMock""" + test_id = "organization.memberships.delete_memberships.0" + client = get_client(test_id) + client.organization.memberships.delete_memberships( + members=["auth0|1234567890"], + ) + verify_request_count(test_id, "POST", "/delete-memberships", None, 1) diff --git a/tests/wire/test_organization_roles.py b/tests/wire/test_organization_roles.py new file mode 100644 index 0000000..555526b --- /dev/null +++ b/tests/wire/test_organization_roles.py @@ -0,0 +1,13 @@ +from .conftest import get_client, verify_request_count + + +def test_organization_roles_list_() -> None: + """Test list endpoint with WireMock""" + test_id = "organization.roles.list_.0" + client = get_client(test_id) + client.organization.roles.list( + from_="from", + take=1, + name="name", + ) + verify_request_count(test_id, "GET", "/roles", {"from": "from", "take": "1", "name": "name"}, 1) diff --git a/wiremock/docker-compose.test.yml b/wiremock/docker-compose.test.yml index 58747d5..95f0ae9 100644 --- a/wiremock/docker-compose.test.yml +++ b/wiremock/docker-compose.test.yml @@ -5,7 +5,7 @@ services: - "0:8080" # Use dynamic port to avoid conflicts with concurrent tests volumes: - ./wiremock-mappings.json:/home/wiremock/mappings/wiremock-mappings.json - command: ["--global-response-templating", "--verbose"] + command: ["--verbose"] healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/__admin/health"] interval: 2s diff --git a/wiremock/wiremock-mappings.json b/wiremock/wiremock-mappings.json index 3ecaf66..e48106c 100644 --- a/wiremock/wiremock-mappings.json +++ b/wiremock/wiremock-mappings.json @@ -1,8 +1,8 @@ { "mappings": [ { - "id": "29a326c1-09e0-4f0b-8b3a-55872064dbed", - "name": "Get Organization details - default", + "id": "7e1a7c64-7b52-4634-91d9-6d6f54b94bff", + "name": "Get Organization details - getOrgDetailsResponseExample", "request": { "urlPathTemplate": "/details", "method": "GET", @@ -19,7 +19,7 @@ "Content-Type": "application/json" } }, - "uuid": "29a326c1-09e0-4f0b-8b3a-55872064dbed", + "uuid": "7e1a7c64-7b52-4634-91d9-6d6f54b94bff", "persistent": true, "priority": 3, "metadata": { @@ -33,8 +33,8 @@ "postServeActions": [] }, { - "id": "175cc45b-1346-42bb-baab-e94c1318da4c", - "name": "Update Organization details - default", + "id": "1ef770f7-cd42-42cc-973f-f17501df9506", + "name": "Update Organization details - updateOrgDetailsResponseExample", "request": { "urlPathTemplate": "/details", "method": "PATCH", @@ -51,7 +51,7 @@ "Content-Type": "application/json" } }, - "uuid": "175cc45b-1346-42bb-baab-e94c1318da4c", + "uuid": "1ef770f7-cd42-42cc-973f-f17501df9506", "persistent": true, "priority": 3, "metadata": { @@ -64,8 +64,8 @@ } }, { - "id": "ddb32f2f-bb17-4551-8c51-a0f78dbb849f", - "name": "Get My Organization API configuration - default", + "id": "6408a1a2-98b8-4c29-b15f-17b2c9b9db75", + "name": "Get My Organization API configuration - getOrgDetailsResponseExample", "request": { "urlPathTemplate": "/config", "method": "GET", @@ -82,7 +82,7 @@ "Content-Type": "application/json" } }, - "uuid": "ddb32f2f-bb17-4551-8c51-a0f78dbb849f", + "uuid": "6408a1a2-98b8-4c29-b15f-17b2c9b9db75", "persistent": true, "priority": 3, "metadata": { @@ -96,8 +96,8 @@ "postServeActions": [] }, { - "id": "bd99fc7f-05db-4c0a-8386-d3665f0719ca", - "name": "List domains - default", + "id": "db02a313-8a43-4071-87d7-0d15bd2d26e0", + "name": "List domains - listOrgDomainsResponseExample", "request": { "urlPathTemplate": "/domains", "method": "GET", @@ -122,7 +122,7 @@ "Content-Type": "application/json" } }, - "uuid": "bd99fc7f-05db-4c0a-8386-d3665f0719ca", + "uuid": "db02a313-8a43-4071-87d7-0d15bd2d26e0", "persistent": true, "priority": 3, "metadata": { @@ -136,8 +136,8 @@ "postServeActions": [] }, { - "id": "0d1dcca2-642a-4199-b3fe-00d5ec1c8bc2", - "name": "Create a domain - default", + "id": "11952e27-c837-49f7-be35-80be3f545a8c", + "name": "Create a domain - createOrgDomainResponseExample", "request": { "urlPathTemplate": "/domains", "method": "POST", @@ -154,7 +154,7 @@ "Content-Type": "application/json" } }, - "uuid": "0d1dcca2-642a-4199-b3fe-00d5ec1c8bc2", + "uuid": "11952e27-c837-49f7-be35-80be3f545a8c", "persistent": true, "priority": 3, "metadata": { @@ -167,8 +167,8 @@ } }, { - "id": "a3c27ba8-01e3-4c19-8725-d3281492d7a6", - "name": "Get a domain - default", + "id": "4444919f-7bcd-4034-aa50-a59f14c9b216", + "name": "Get a domain - getOrgDomainResponseExample", "request": { "urlPathTemplate": "/domains/{domain_id}", "method": "GET", @@ -190,7 +190,7 @@ "Content-Type": "application/json" } }, - "uuid": "a3c27ba8-01e3-4c19-8725-d3281492d7a6", + "uuid": "4444919f-7bcd-4034-aa50-a59f14c9b216", "persistent": true, "priority": 3, "metadata": { @@ -239,8 +239,8 @@ } }, { - "id": "8b89e2ae-9635-46f3-b4e8-fc575b9b6157", - "name": "List Identity Providers - default", + "id": "11d47f7f-d5a5-43f5-aea7-569c6ce3bd63", + "name": "List Identity Providers - listIdPsResponseExample", "request": { "urlPathTemplate": "/identity-providers", "method": "GET", @@ -257,7 +257,7 @@ "Content-Type": "application/json" } }, - "uuid": "8b89e2ae-9635-46f3-b4e8-fc575b9b6157", + "uuid": "11d47f7f-d5a5-43f5-aea7-569c6ce3bd63", "persistent": true, "priority": 3, "metadata": { @@ -271,8 +271,8 @@ "postServeActions": [] }, { - "id": "3e11b717-d0f8-4c19-9f25-9aaba50cba21", - "name": "Create an Identity Provider - default", + "id": "a6f57fc1-f7ac-4b02-8f4d-bd3fe46eecd4", + "name": "Create an Identity Provider - createIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers", "method": "POST", @@ -289,7 +289,7 @@ "Content-Type": "application/json" } }, - "uuid": "3e11b717-d0f8-4c19-9f25-9aaba50cba21", + "uuid": "a6f57fc1-f7ac-4b02-8f4d-bd3fe46eecd4", "persistent": true, "priority": 3, "metadata": { @@ -302,8 +302,8 @@ } }, { - "id": "58ee7dab-8682-4472-892e-bd11d76b0671", - "name": "Get an Identity Provider - default", + "id": "ebff1d9b-9b46-4505-ad12-9dbda71462b0", + "name": "Get an Identity Provider - getIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}", "method": "GET", @@ -325,7 +325,7 @@ "Content-Type": "application/json" } }, - "uuid": "58ee7dab-8682-4472-892e-bd11d76b0671", + "uuid": "ebff1d9b-9b46-4505-ad12-9dbda71462b0", "persistent": true, "priority": 3, "metadata": { @@ -374,8 +374,8 @@ } }, { - "id": "97299d05-1531-4f32-8e96-5a64fc5751f8", - "name": "Update an Identity Provider - default", + "id": "44ef7b8f-877a-4ec9-8be1-7204c4c0c994", + "name": "Update an Identity Provider - updateIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}", "method": "PATCH", @@ -397,7 +397,7 @@ "Content-Type": "application/json" } }, - "uuid": "97299d05-1531-4f32-8e96-5a64fc5751f8", + "uuid": "44ef7b8f-877a-4ec9-8be1-7204c4c0c994", "persistent": true, "priority": 3, "metadata": { @@ -410,8 +410,8 @@ } }, { - "id": "9925321b-b5a5-4ccc-abb9-04139e218c65", - "name": "Refresh Identity Provider attribute mapping - default", + "id": "1e4d66ac-950b-4cd7-a16d-65e8adb4a94b", + "name": "Refresh Identity Provider attribute mapping - getIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/update-attributes", "method": "PUT", @@ -433,7 +433,7 @@ "Content-Type": "application/json" } }, - "uuid": "9925321b-b5a5-4ccc-abb9-04139e218c65", + "uuid": "1e4d66ac-950b-4cd7-a16d-65e8adb4a94b", "persistent": true, "priority": 3, "metadata": { @@ -482,8 +482,332 @@ } }, { - "id": "a032561e-549d-47e8-8ebb-dad06d2a6ce4", - "name": "Get Identity Provider configuration - default", + "id": "7d845899-c08f-4508-a7cb-134b5d39faca", + "name": "List members - listOrgDomainsResponseExample", + "request": { + "urlPathTemplate": "/members", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "queryParameters": { + "fields": { + "equalTo": "fields" + }, + "include_fields": { + "equalTo": "true" + }, + "from": { + "equalTo": "from" + }, + "take": { + "equalTo": "1" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"next\": \"next\",\n \"members\": [\n {\n \"email\": \"roadrunner@acme.com\",\n \"name\": \"name\",\n \"nickname\": \"nickname\",\n \"given_name\": \"given_name\",\n \"family_name\": \"family_name\",\n \"user_id\": \"auth0|123234235\",\n \"roles\": [\n {\n \"id\": \"rol_BKI0BKI0BKI0BKI0\",\n \"name\": \"role1\"\n },\n {\n \"id\": \"rol_BKW1BKIfBKd0BaI0\",\n \"name\": \"role2\"\n }\n ],\n \"created_at\": \"2024-01-15T09:30:00Z\",\n \"updated_at\": \"2024-01-15T09:30:00Z\",\n \"last_login\": \"2024-01-15T09:30:00Z\",\n \"phone_number\": \"phone_number\"\n }\n ]\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "7d845899-c08f-4508-a7cb-134b5d39faca", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + }, + "postServeActions": [] + }, + { + "id": "9a6aefd1-96f4-48e6-943b-0e62bf9cfd0c", + "name": "Get a member - listOrgDomainsResponseExample", + "request": { + "urlPathTemplate": "/members/{user_id}", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "user_id": { + "equalTo": "user_id" + } + }, + "queryParameters": { + "fields": { + "equalTo": "fields" + }, + "include_fields": { + "equalTo": "true" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"email\": \"roadrunner@acme.com\",\n \"name\": \"roadrunner\",\n \"nickname\": \"beepbeep\",\n \"given_name\": \"Road\",\n \"family_name\": \"Runner\",\n \"user_id\": \"auth0|123234235\",\n \"roles\": [\n {\n \"id\": \"rol_BKI0BKI0BKI0BKI0\",\n \"name\": \"role1\",\n \"description\": \"description\"\n },\n {\n \"id\": \"rol_BKW1BKIfBKd0BaI0\",\n \"name\": \"role2\",\n \"description\": \"description\"\n }\n ],\n \"created_at\": \"2025-05-01T12:00:00Z\",\n \"updated_at\": \"2025-05-02T12:00:00Z\",\n \"last_login\": \"2025-05-03T12:00:00Z\",\n \"phone_number\": \"phone_number\"\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "9a6aefd1-96f4-48e6-943b-0e62bf9cfd0c", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "c6126f0a-ce12-4ccd-a21c-8313aebc991f", + "name": "Delete memberships - deleteOrganizationMembershipsRequestExample", + "request": { + "urlPathTemplate": "/delete-memberships", + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } + }, + "response": { + "status": 200, + "body": "\"\"", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "c6126f0a-ce12-4ccd-a21c-8313aebc991f", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "4e72920b-af60-44e5-a6ba-aede11137221", + "name": "List member invitations - listMembersInvitationsResponseExample", + "request": { + "urlPathTemplate": "/member-invitations", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "queryParameters": { + "fields": { + "equalTo": "fields" + }, + "include_fields": { + "equalTo": "true" + }, + "from": { + "equalTo": "from" + }, + "take": { + "equalTo": "1" + }, + "sort": { + "equalTo": "sort" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"next\": \"next\",\n \"invitations\": [\n {\n \"id\": \"uinv_12345678abcdefgh\",\n \"organization_id\": \"org_12345678abcdefgh\",\n \"inviter\": {\n \"name\": \"Allison the Admin\"\n },\n \"invitee\": {\n \"email\": \"user@example.com\"\n },\n \"identity_provider_id\": \"con_2CZPv6IY0gWzDaQJ\",\n \"created_at\": \"2025-04-11T20:11:45Z\",\n \"expires_at\": \"2025-04-11T20:11:45Z\",\n \"roles\": [\n \"rol_BKW1BKIfBKd0BaI0\"\n ],\n \"invitation_url\": \"https://example.auth0.com/login?invitation=uinv_12345678abcdefgh&organization=org_12345678abcdefgh\",\n \"ticket_id\": \"1asdfasd23usjdef\"\n }\n ]\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "4e72920b-af60-44e5-a6ba-aede11137221", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + }, + "postServeActions": [] + }, + { + "id": "81839c8d-109e-443d-835e-bd23c6354a48", + "name": "Create member invitations - listMembersInvitationsResponseExample", + "request": { + "urlPathTemplate": "/member-invitations", + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + } + }, + "response": { + "status": 201, + "body": "[\n {\n \"id\": \"uinv_12345678abcdefgh\",\n \"organization_id\": \"org_12345678abcdefgh\",\n \"inviter\": {\n \"name\": \"Allison the Admin\"\n },\n \"invitee\": {\n \"email\": \"user@example.com\"\n },\n \"identity_provider_id\": \"con_2CZPv6IY0gWzDaQJ\",\n \"created_at\": \"2025-04-11T20:11:45Z\",\n \"expires_at\": \"2025-04-11T20:11:45Z\",\n \"roles\": [\n \"rol_BKW1BKIfBKd0BaI0\"\n ],\n \"invitation_url\": \"https://example.auth0.com/login?invitation=uinv_12345678abcdefgh&organization=org_12345678abcdefgh\",\n \"ticket_id\": \"1asdfasd23usjdef\"\n }\n]", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "81839c8d-109e-443d-835e-bd23c6354a48", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "bf390045-ef2a-4a70-8a73-16e2fc5363e0", + "name": "Get a member invitation - GetMemberInvitationResponseExample", + "request": { + "urlPathTemplate": "/member-invitations/{invitation_id}", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "invitation_id": { + "equalTo": "invitation_id" + } + }, + "queryParameters": { + "fields": { + "equalTo": "fields" + }, + "include_fields": { + "equalTo": "true" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"id\": \"uinv_12345678abcdefgh\",\n \"organization_id\": \"org_12345678abcdefgh\",\n \"inviter\": {\n \"name\": \"Allison the Admin\"\n },\n \"invitee\": {\n \"email\": \"user@example.com\"\n },\n \"identity_provider_id\": \"con_2CZPv6IY0gWzDaQJ\",\n \"created_at\": \"2025-04-11T20:11:45Z\",\n \"expires_at\": \"2025-04-11T20:11:45Z\",\n \"roles\": [\n \"rol_BKW1BKIfBKd0BaI0\"\n ],\n \"invitation_url\": \"https://example.auth0.com/login?invitation=uinv_12345678abcdefgh&organization=org_12345678abcdefgh\",\n \"ticket_id\": \"1asdfasd23usjdef\"\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "bf390045-ef2a-4a70-8a73-16e2fc5363e0", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "44e77096-1347-4434-83ae-3fad0ed2847b", + "name": "Revoke a member invitation - default", + "request": { + "urlPathTemplate": "/member-invitations/{invitation_id}", + "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "invitation_id": { + "equalTo": "invitation_id" + } + } + }, + "response": { + "status": 200, + "body": "\"\"", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "44e77096-1347-4434-83ae-3fad0ed2847b", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "7e78a97c-a095-412c-863f-ef31d9d6c7c0", + "name": "List roles - listRolesResponseExample", + "request": { + "urlPathTemplate": "/roles", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "queryParameters": { + "from": { + "equalTo": "from" + }, + "take": { + "equalTo": "1" + }, + "name": { + "equalTo": "name" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"roles\": [\n {\n \"id\": \"rol_BKI0BKI0BKI0BKI0\",\n \"name\": \"Admin\",\n \"description\": \"Administrator role with full access\"\n },\n {\n \"id\": \"rol_BKI0BKI0BKI0BKI1\",\n \"name\": \"User\",\n \"description\": \"Standard user role with limited access\"\n }\n ],\n \"next\": \"abc123\"\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "7e78a97c-a095-412c-863f-ef31d9d6c7c0", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + }, + "postServeActions": [] + }, + { + "id": "8b9f07ca-37e5-4810-82fa-fe76436925f8", + "name": "Get Identity Provider configuration - getIdpConfigurationResponseExample", "request": { "urlPathTemplate": "/config/identity-providers", "method": "GET", @@ -500,7 +824,7 @@ "Content-Type": "application/json" } }, - "uuid": "a032561e-549d-47e8-8ebb-dad06d2a6ce4", + "uuid": "8b9f07ca-37e5-4810-82fa-fe76436925f8", "persistent": true, "priority": 3, "metadata": { @@ -514,8 +838,8 @@ "postServeActions": [] }, { - "id": "1a6cb620-9021-415a-98b0-c777a13f658e", - "name": "Start domain verification - default", + "id": "071dc861-6513-45ca-bbde-07ee67629ad7", + "name": "Start domain verification - updateOrgDomainResponseExample", "request": { "urlPathTemplate": "/domains/{domain_id}/verify", "method": "POST", @@ -537,7 +861,7 @@ "Content-Type": "application/json" } }, - "uuid": "1a6cb620-9021-415a-98b0-c777a13f658e", + "uuid": "071dc861-6513-45ca-bbde-07ee67629ad7", "persistent": true, "priority": 3, "metadata": { @@ -550,8 +874,8 @@ } }, { - "id": "736aebae-5a32-4a51-b735-1331ee4bd9ab", - "name": "List Identity Providers for a domain - default", + "id": "883745b4-a5d3-4e55-a102-3f6de84187f1", + "name": "List Identity Providers for a domain - getOrgDomainIdentityProvidersResponseExample", "request": { "urlPathTemplate": "/domains/{domain_id}/identity-providers", "method": "GET", @@ -573,7 +897,7 @@ "Content-Type": "application/json" } }, - "uuid": "736aebae-5a32-4a51-b735-1331ee4bd9ab", + "uuid": "883745b4-a5d3-4e55-a102-3f6de84187f1", "persistent": true, "priority": 3, "metadata": { @@ -586,8 +910,8 @@ } }, { - "id": "79faaaa8-f657-4412-a338-31db2a0c22fc", - "name": "Associate a domain with an Identity Provider - default", + "id": "8d1092ea-dd63-4d47-be98-5400f6d5590b", + "name": "Associate a domain with an Identity Provider - createIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/domains", "method": "POST", @@ -609,7 +933,7 @@ "Content-Type": "application/json" } }, - "uuid": "79faaaa8-f657-4412-a338-31db2a0c22fc", + "uuid": "8d1092ea-dd63-4d47-be98-5400f6d5590b", "persistent": true, "priority": 3, "metadata": { @@ -661,8 +985,8 @@ } }, { - "id": "3ef1a37b-4761-4156-9d5f-1330d174d47b", - "name": "Get a Provisioning Configuration - default", + "id": "c24355de-8f84-4694-bd72-97dd531392b5", + "name": "Get a Provisioning Configuration - getIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/provisioning", "method": "GET", @@ -684,7 +1008,7 @@ "Content-Type": "application/json" } }, - "uuid": "3ef1a37b-4761-4156-9d5f-1330d174d47b", + "uuid": "c24355de-8f84-4694-bd72-97dd531392b5", "persistent": true, "priority": 3, "metadata": { @@ -697,8 +1021,8 @@ } }, { - "id": "a58b006c-012a-4283-b025-5b0fc5d9c644", - "name": "Create a Provisioning Configuration - default", + "id": "028859ea-0007-45a9-9463-044b0d9067e3", + "name": "Create a Provisioning Configuration - createIdPProvisioningResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/provisioning", "method": "POST", @@ -720,7 +1044,7 @@ "Content-Type": "application/json" } }, - "uuid": "a58b006c-012a-4283-b025-5b0fc5d9c644", + "uuid": "028859ea-0007-45a9-9463-044b0d9067e3", "persistent": true, "priority": 3, "metadata": { @@ -769,8 +1093,8 @@ } }, { - "id": "b52f90fe-1fb8-43af-b6c6-92036c78eafd", - "name": "Refresh Provisioning Configuration attribute mapping - default", + "id": "98cc5ac5-93ef-4cdf-8ef0-9eac5d023886", + "name": "Refresh Provisioning Configuration attribute mapping - getIdPResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/provisioning/update-attributes", "method": "PUT", @@ -792,7 +1116,7 @@ "Content-Type": "application/json" } }, - "uuid": "b52f90fe-1fb8-43af-b6c6-92036c78eafd", + "uuid": "98cc5ac5-93ef-4cdf-8ef0-9eac5d023886", "persistent": true, "priority": 3, "metadata": { @@ -805,8 +1129,8 @@ } }, { - "id": "e0c33ec4-40b3-42d5-8a5c-0dd959cdb9ce", - "name": "List Provisioning SCIM tokens - default", + "id": "bcdcd71b-d2a7-4d9a-b4ab-22d5e5c95c46", + "name": "List Provisioning SCIM tokens - listOrgDomainsResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/provisioning/scim-tokens", "method": "GET", @@ -828,7 +1152,7 @@ "Content-Type": "application/json" } }, - "uuid": "e0c33ec4-40b3-42d5-8a5c-0dd959cdb9ce", + "uuid": "bcdcd71b-d2a7-4d9a-b4ab-22d5e5c95c46", "persistent": true, "priority": 3, "metadata": { @@ -841,8 +1165,8 @@ } }, { - "id": "97c46081-373a-430e-a4bb-91e718763cd8", - "name": "Create a Provisioning SCIM token - default", + "id": "1fc598f0-4985-4b9b-89cb-6ab5dc54325a", + "name": "Create a Provisioning SCIM token - createIdPProvisioningScimTokenResponseExample", "request": { "urlPathTemplate": "/identity-providers/{idp_id}/provisioning/scim-tokens", "method": "POST", @@ -864,7 +1188,7 @@ "Content-Type": "application/json" } }, - "uuid": "97c46081-373a-430e-a4bb-91e718763cd8", + "uuid": "1fc598f0-4985-4b9b-89cb-6ab5dc54325a", "persistent": true, "priority": 3, "metadata": { @@ -914,9 +1238,125 @@ } } } + }, + { + "id": "c8ae9a84-d822-47b5-8ec8-a500489495dc", + "name": "List member roles - listOrganizationMemberRolesResponseExample", + "request": { + "urlPathTemplate": "/members/{user_id}/roles", + "method": "GET", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "user_id": { + "equalTo": "user_id" + } + }, + "queryParameters": { + "from": { + "equalTo": "from" + }, + "take": { + "equalTo": "1" + } + } + }, + "response": { + "status": 200, + "body": "{\n \"next\": \"next\",\n \"roles\": [\n {\n \"id\": \"rol_SO2j0sFo9NFa3F9w\",\n \"name\": \"role1\",\n \"description\": \"role1 description\"\n },\n {\n \"id\": \"rol_BO2j1sFo9NFa3F9w\",\n \"name\": \"role2\",\n \"description\": \"role2 description\"\n }\n ]\n}", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "c8ae9a84-d822-47b5-8ec8-a500489495dc", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "c3e57548-ed49-4b95-8fe8-a14e5e0c9492", + "name": "Assign roles to a member - assignOrganizationMemberRolesRequestExample", + "request": { + "urlPathTemplate": "/members/{user_id}/roles", + "method": "POST", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "user_id": { + "equalTo": "user_id" + } + } + }, + "response": { + "status": 200, + "body": "\"\"", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "c3e57548-ed49-4b95-8fe8-a14e5e0c9492", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } + }, + { + "id": "c1b9e307-010e-4b6d-8d36-d5053dd3e6fd", + "name": "Remove roles from a member - unassignOrganizationMemberRolesRequestExample", + "request": { + "urlPathTemplate": "/members/{user_id}/roles", + "method": "DELETE", + "headers": { + "Authorization": { + "matches": "Bearer .+" + } + }, + "pathParameters": { + "user_id": { + "equalTo": "user_id" + } + } + }, + "response": { + "status": 200, + "body": "\"\"", + "headers": { + "Content-Type": "application/json" + } + }, + "uuid": "c1b9e307-010e-4b6d-8d36-d5053dd3e6fd", + "persistent": true, + "priority": 3, + "metadata": { + "mocklab": { + "created": { + "at": "2020-01-01T00:00:00.000Z", + "via": "SYSTEM" + } + } + } } ], "meta": { - "total": 26 + "total": 37 } } \ No newline at end of file