diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..7949eec
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,50 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+
+A clear and concise description of what the bug is.
+
+Feel free to attach screenshots as well.
+
+**To Reproduce**
+
+Steps to reproduce the behavior.
+
+Any sample data would help greatly.
+
+**Version**
+
+What is the version of Horreum python client ?
+
+If you are using a development branch; what is the commit id ?
+
+```
+git rev-parse HEAD
+```
+
+What is the version of Horreum ?
+
+If you are using a development branch; what is the commit id ?
+
+```
+git rev-parse HEAD
+```
+
+**Java**
+
+What is the version of Python ?
+
+```
+python --version
+```
+
+**Tip**
+
+Use \`\`\` before and after the text to keep the output as is.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..1c23e60
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: feature
+assignees: ''
+
+---
+
+**Feature idea.**
+
+A clear and concise description of what the feature is.
+
+**Describe the solution you'd like**
+
+A clear and concise description of what you want to happen.
+
+**Additional information**
+
+Any additional information you can provide, like an overall design description
\ No newline at end of file
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..585e880
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,25 @@
+
+
+## Fixes Issue
+
+
+
+
+
+## Changes proposed
+
+
+
+
+
+
+## Check List (Check all the applicable boxes)
+
+- [ ] My code follows the code style of this project.
+- [ ] My change requires changes to the documentation.
+- [ ] I have updated the documentation accordingly.
+- [ ] All new and existing tests passed.
\ No newline at end of file
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
new file mode 100644
index 0000000..4caa2fd
--- /dev/null
+++ b/.github/workflows/ci.yaml
@@ -0,0 +1,33 @@
+# This workflow will run the full CI for the Horreum python library including the build and the tests execution
+# This is going to be triggered on every pull request as well as on main branch.
+# TODO: trigger tests once implemented
+name: Python client ci
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ test:
+ name: ${{ matrix.session }} ${{ matrix.python }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ python: [ "3.9", "3.10", "3.11" ]
+ env:
+ FORCE_COLOR: "1"
+ PRE_COMMIT_COLOR: "always"
+ steps:
+ - name: Check out the repository
+ uses: actions/checkout@v4
+ - name: Set up Python ${{ matrix.python }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python }}
+ - name: Install development dependencies
+ run: pip install -r dev-requirements.txt
+ - name: Build python library
+ run: poetry build
diff --git a/.github/workflows/generate-client.yaml b/.github/workflows/generate-client.yaml
new file mode 100644
index 0000000..8b25a5e
--- /dev/null
+++ b/.github/workflows/generate-client.yaml
@@ -0,0 +1,54 @@
+# This workflow will fetch the new openapi from the provided branch of https://github.com/Hyperfoil/Horreum and based on
+# that it will re-generate the Horreum raw client and creates a new pull request against the corresponding branch here.
+# It could be tested running `gh act workflow_dispatch -e ./test/workflow_dispatch_event_example.json`. Remember to
+# comment out the pull request creation :)
+name: Autogenerate Horreum client
+
+on:
+ workflow_dispatch:
+ inputs:
+ branch:
+ description: Branch or tag of https://github.com/Hyperfoil/Horreum
+ required: true
+ repository_dispatch:
+ types: [ generate-horreum-client ]
+
+jobs:
+ generate:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Fetch branch
+ id: fetch-branch
+ run: |
+ if [ "${{ github.event_name }}" = "repository_dispatch" ]; then
+ # Generated by peter-evans/repository-dispatch@v3
+ echo "HORREUM_BRANCH=${{ github.event.client_payload.branch }}" >> $GITHUB_OUTPUT
+ elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ echo "HORREUM_BRANCH=${{ github.event.inputs.branch }}" >> $GITHUB_OUTPUT
+ else
+ echo "Unknown event: ${{ github.event_name }}"
+ exit 1
+ fi
+ - uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+ - uses: actions/setup-python@v5
+ with:
+ python-version: '3.10'
+ cache: 'pip'
+ - name: Install development dependencies
+ run: pip install -r dev-requirements.txt
+ - name: Generate horreum client
+ run: make HORREUM_BRANCH=${{ steps.fetch-branch.outputs.HORREUM_BRANCH }} generate
+ - run: git --no-pager diff
+ - name: Create Pull Request
+ uses: gr2m/create-or-update-pull-request-action@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ title: Generate Horreum client from github.com/Hyperfoil/Horreum:${{ steps.fetch-branch.outputs.HORREUM_BRANCH }}
+ body: |
+ There is a new change in the openapi spec of Horreum in branch ${{ steps.fetch-branch.outputs.HORREUM_BRANCH }}.
+ Verify that there are no breaking changes.
+ branch: horreum-openapi-${{ steps.fetch-branch.outputs.HORREUM_BRANCH }}
+ commit-message: Horreum openapi client updated
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
new file mode 100644
index 0000000..c8a971c
--- /dev/null
+++ b/.github/workflows/publish.yaml
@@ -0,0 +1,41 @@
+# This workflow will build the python distribution and it will publish to Pypi
+# This is going to be triggered on on every tag `v*`, e.g., `v0.13`.
+# TODO: trigger tests once implemented, to ensure everything is working before publishing
+name: Publish Horreum library
+
+on:
+ push:
+ tags:
+ - v*
+
+jobs:
+ test:
+ name: ${{ matrix.session }} ${{ matrix.python }}
+ runs-on: ubuntu-latest
+ env:
+ FORCE_COLOR: "1"
+ steps:
+ - name: Check out the repository
+ uses: actions/checkout@v4
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.10"
+ - name: Install development dependencies
+ run: pip install -r dev-requirements.txt
+ - name: Check version coherence
+ run: |
+ PROJECT_VERSION=$(poetry version | cut -d' ' -f2)
+ GIT_TAG=$(git describe --tags --match="v*")
+ if [[ "${GIT_TAG:1}" =~ $PROJECT_VERSION ]]; then
+ echo "::error title='$GIT_TAG tag does not match project version'::"
+ exit 1
+ fi
+ - name: Build python library
+ run: make generate && poetry build --ansi
+ - name: Publish package on PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ verbose: true
+ print-hash: true
+ packages-dir: ./dist/
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bdd715c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+# IDE configs
+.idea
+
+# Dist
+dist/
+bin/
+
+# Py cache
+**/__pycache__
+
+# Kiota
+**/.kiota.log
diff --git a/GET_STARTED.md b/GET_STARTED.md
new file mode 100644
index 0000000..a01a281
--- /dev/null
+++ b/GET_STARTED.md
@@ -0,0 +1,52 @@
+
+
+# Get Started Guide
+
+
+
+In this document you can find all information to get started using the Horreum python library from scratch.
+
+Right now the library is not published anywhere, therefore the only way to install it is from source.
+
+---
+## Prerequisites
+
+* Python environment, e.g., `pyenv` or `miniconda` with all development dependencies installed:
+```bash
+pip install -r dev-requirements.txt
+```
+
+## Installation
+
+Once all dependencies are installed simply build the `whl` by running:
+
+```bash
+poetry build
+```
+
+Now you can install the local build of `horreum` python client:
+
+```bash
+pip install dist/horreum-*.dev0-py3-none-any.whl --force-reinstall
+```
+
+## Usage
+
+```bash
+>>> import asyncio
+
+# Import the constructor function
+>>> from horreum.horreum_client import new_horreum_client
+
+# Initialize the client
+>>> client = await new_horreum_client(base_url="http://localhost:8080", username="..", password="..")
+
+# Call the api using the underlying raw client, in this case retrieve the Horreum server version
+>>> await client.raw_client.api.config.version.get()
+VersionInfo(additional_data={}, privacy_statement=None, start_timestamp=1710864862253, version='0.13.0')
+```
+
+The previous api call is equivalent to the following `cURL`:
+```bash
+curl --silent -X 'GET' 'http://localhost:8080/api/config/version' -H 'accept: application/json' | jq '.'
+```
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8a3a6b6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,67 @@
+# useful paths
+MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+PROJECT_PATH := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
+PROJECT_BIN := $(PROJECT_PATH)/bin
+PROJECT_DIST := $(PROJECT_PATH)/dist
+
+OS_NAME := $(shell uname -s | tr A-Z a-z)
+OS_ARCH := $(shell uname -m | tr A-Z a-z)
+
+ifeq ($(OS_NAME),linux)
+ OS_NAME = "linux"
+endif
+ifeq ($(OS_NAME),darwin)
+ OS_NAME = "osx"
+endif
+
+ifeq ($(OS_ARCH),x86_64)
+ OS_ARCH = x64
+endif
+ifneq ($(filter %86,$(OS_ARCH)),)
+ OS_ARCH = x86
+endif
+ifneq ($(filter arm%,$(OS_ARCH)),)
+ OS_ARCH = arm64
+endif
+
+# env variables
+KIOTA_VERSION ?= "v1.12.0"
+HORREUM_BRANCH ?= "master"
+HORREUM_OPENAPI_PATH ?= "https://raw.githubusercontent.com/Hyperfoil/Horreum/${HORREUM_BRANCH}/docs/site/content/en/openapi/openapi.yaml"
+GENERATED_CLIENT_PATH = "${PROJECT_PATH}/src/horreum/raw_client"
+
+.PHONY: help
+help: ## Display this help.
+ @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+##@ Development
+
+.PHONY: clean
+clean: ## Clean external tools and output dirs
+ @rm -rf ${PROJECT_BIN} ${PROJECT_DIST} ${GENERATED_CLIENT_PATH}/api ${GENERATED_CLIENT_PATH}/models ${GENERATED_CLIENT_PATH}/horreum_raw_client.py ${GENERATED_CLIENT_PATH}/kiota-lock.json
+
+.PHONY: kiota
+kiota: ${PROJECT_BIN}/kiota ## Install kiota tool under ${PROJECT_PATH}/bin
+
+${PROJECT_BIN}/kiota:
+ @{\
+ set -e ;\
+ echo "installing kiota version ${KIOTA_VERSION}" ;\
+ mkdir -p ${PROJECT_BIN}/kiota-installation ;\
+ cd ${PROJECT_BIN}/kiota-installation ;\
+ curl -sLO https://github.com/microsoft/kiota/releases/download/${KIOTA_VERSION}/${OS_NAME}-${OS_ARCH}.zip ;\
+ unzip -o ${OS_NAME}-${OS_ARCH}.zip ;\
+ mv kiota ${PROJECT_BIN}/ ;\
+ rm -rf ${PROJECT_BIN}/kiota-installation ;\
+ }
+
+.PHONY: tools
+tools: kiota ## Install external tools.
+
+.PHONY: generate
+generate: tools ## Generate the Horreum client
+ @{\
+ set -e ;\
+ curl -sSfL -o ${PROJECT_PATH}/openapi/openapi.yaml ${HORREUM_OPENAPI_PATH} ;\
+ ${PROJECT_BIN}/kiota generate -l python -c HorreumRawClient -n raw_client -d ${PROJECT_PATH}/openapi/openapi.yaml -o ${GENERATED_CLIENT_PATH} ;\
+ }
diff --git a/README.md b/README.md
index 7dc87e3..68e5daf 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,16 @@
---
-## What is Horreum ?
+## What is Horreum Python Client?
+
+Horreum python client is a high-level python library to interact with [Horreum](#what-is-horreum) web server.
+
+The raw client is generated using [kiota](https://github.com/microsoft/kiota) openapi generator tool starting from
+the [Horreum OpenAPI spec](https://github.com/Hyperfoil/Horreum/blob/master/docs/site/content/en/openapi/openapi.yaml).
+
+Refer to the [get started guide](./GET_STARTED.md) for comprehensive instructions on installing and utilizing this library.
+
+## What is Horreum?
[Horreum](https://github.com/Hyperfoil/Horreum) is a service for storing performance data and regression analysis.
@@ -22,7 +31,7 @@ Please, visit our project website:
for more information.
[Horreum](https://github.com/Hyperfoil/Horreum) is a [Quarkus](https://quarkus.io/) based application which uses
-[Quinoa](https://quarkiverse.github.io/quarkiverse-docs/quarkus-quinoa/dev/) as its [nodejs](https://nodejs.org/en) engine.
+[Quinoa](https://quarkiverse.github.io/quarkiverse-docs/quarkus-quinoa/dev/) as its [Node.js](https://nodejs.org/en) engine.
This project is about providing a simplified setup and examples to use
[Horreum](https://github.com/Hyperfoil/Horreum) using the [Python](https://www.python.org/) programming language.
@@ -31,6 +40,35 @@ This project is about providing a simplified setup and examples to use
Contributions to `horreum-client-python` Please check our [CONTRIBUTING.md](./CONTRIBUTING.md)
+### Development
+
+Install all dev dependencies (consider using Python virtual environments):
+```bash
+pip install -r dev-requirements.txt
+```
+
+Generate source files
+```bash
+make generate
+```
+
+Build the library using `poetry`:
+```bash
+poetry build
+```
+
+#### Tests
+
+Right now tests are not automated, therefore you need to start up the Horreum server manually,
+you can check more details in [Horreum README](https://github.com/Hyperfoil/Horreum/blob/master/README.md#getting-started-with-development-server).
+
+> **_NOTE_**: The database should be empty to get all tests working
+
+Once the Horreum server is up and running on `localhost:8080`, you can trigger integration tests by running:
+```bash
+pytest test/
+```
+
### If you have any idea or doubt 👇
* [Ask a question](https://github.com/Hyperfoil/horreum-client-python/discussions)
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..60a3f21
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,3 @@
+poetry==1.8.2
+pytest==8.1.1
+pytest-asyncio==0.23.6
\ No newline at end of file
diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml
new file mode 100644
index 0000000..92cd711
--- /dev/null
+++ b/openapi/openapi.yaml
@@ -0,0 +1,4610 @@
+---
+openapi: 3.0.3
+info:
+ title: Horreum REST API
+ description: "Horreum automated change anomaly detection. For more information,\
+ \ please see [https://horreum.hyperfoil.io/](https://horreum.hyperfoil.io/)"
+ version: "0.13"
+tags:
+- name: Config
+ description: Endpoint providing configuration for the Horreum System
+ x-smallrye-profile-external: ""
+- name: Dataset
+ description: Datasets are used as the basis for all change detection and reporting
+ x-smallrye-profile-external: ""
+- name: Experiment
+ description: Experiments allow users to apply change detection rules to two different
+ datasets. This allows for pass/fail of KPIS based on A/B testing
+ x-smallrye-profile-external: ""
+- name: Run
+ description: Manage test runs. Runs are instances of results of a benchmark execution
+ x-smallrye-profile-external: ""
+- name: Schema
+ description: Manage schemas
+ x-smallrye-profile-external: ""
+- name: Test
+ description: Endpoint giving access to tests defined in Horreum.
+ x-smallrye-profile-external: ""
+paths:
+ /api/config/datastore:
+ put:
+ tags:
+ - Config
+ description: Update an existing Datastore definition
+ operationId: updateDatastore
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Datastore'
+ responses:
+ "200":
+ description: The ID of the updated Datastore
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ post:
+ tags:
+ - Config
+ description: Create a new Datastore
+ operationId: newDatastore
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Datastore'
+ responses:
+ "200":
+ description: The ID for the new Datastore
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ /api/config/datastore/{id}:
+ delete:
+ tags:
+ - Config
+ description: Test a Datastore
+ operationId: deleteDatastore
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "204":
+ description: No Content
+ /api/config/datastore/{id}/test:
+ get:
+ tags:
+ - Config
+ description: Test a Datastore connection
+ operationId: testDatastore
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DatastoreTestResponse'
+ /api/config/datastore/{team}:
+ get:
+ tags:
+ - Config
+ description: Obtain list of configured datastores for particular team
+ operationId: datastores
+ parameters:
+ - name: team
+ in: path
+ description: name of the team to search for defined datastores
+ required: true
+ schema:
+ type: string
+ example: perf-team
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Datastore'
+ /api/config/keycloak:
+ get:
+ tags:
+ - Config
+ description: Obtain configuration information about keycloak server securing
+ Horreum instance
+ operationId: keycloak
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/KeycloakConfig'
+ /api/config/version:
+ get:
+ tags:
+ - Config
+ description: Obtain version of the running Horreum instance
+ operationId: version
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/VersionInfo'
+ /api/dataset/bySchema:
+ get:
+ tags:
+ - Dataset
+ description: "Retrieve a paginated list of Datasets, with total count, by Schema"
+ operationId: listBySchema
+ parameters:
+ - name: uri
+ in: query
+ description: Schema URI
+ required: true
+ schema:
+ type: string
+ example: uri:techempower:0.1
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Schemas
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ default: start
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DatasetList'
+ /api/dataset/list/{testId}:
+ get:
+ tags:
+ - Dataset
+ description: "Retrieve a paginated list of Datasets, with total count, by Test"
+ operationId: listByTest
+ parameters:
+ - name: testId
+ in: path
+ description: Test ID of test to retrieve list of Datasets
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: filter
+ in: query
+ description: JOSN Filter expression to apply to query
+ schema:
+ type: string
+ example:
+ buildID: 111111
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Schemas
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ - name: viewId
+ in: query
+ description: Optional View ID to filter datasets by view
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DatasetList'
+ /api/dataset/{datasetId}/labelValues:
+ get:
+ tags:
+ - Dataset
+ operationId: labelValues
+ parameters:
+ - name: datasetId
+ in: path
+ required: true
+ schema:
+ format: int32
+ type: integer
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/LabelValue'
+ /api/dataset/{datasetId}/previewLabel:
+ post:
+ tags:
+ - Dataset
+ operationId: previewLabel
+ parameters:
+ - name: datasetId
+ in: path
+ required: true
+ schema:
+ format: int32
+ type: integer
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Label'
+ required: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/LabelPreview'
+ /api/dataset/{datasetId}/summary:
+ get:
+ tags:
+ - Dataset
+ operationId: getSummary
+ parameters:
+ - name: datasetId
+ in: path
+ required: true
+ schema:
+ format: int32
+ type: integer
+ - name: viewId
+ in: query
+ schema:
+ format: int32
+ type: integer
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/DatasetSummary'
+ /api/dataset/{id}:
+ get:
+ tags:
+ - Dataset
+ description: Retrieve Dataset by ID
+ operationId: getDataset
+ parameters:
+ - name: id
+ in: path
+ description: Dataset ID to retrieve
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "404":
+ description: No Dataset with the given id was found
+ content:
+ application/json: {}
+ "200":
+ description: JVM system properties of a particular host.
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Dataset'
+ /api/experiment/models:
+ get:
+ tags:
+ - Experiment
+ description: Retrieve a list of Condition Config models
+ operationId: models
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/ConditionConfig'
+ /api/experiment/run:
+ get:
+ tags:
+ - Experiment
+ description: Run an experiment for a given dataset and experiment profile
+ operationId: runExperiments
+ parameters:
+ - name: datasetId
+ in: query
+ description: The dataset to run the experiment on
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: Array of experiment results
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/ExperimentResult'
+ /api/experiment/{testId}/profiles:
+ get:
+ tags:
+ - Experiment
+ description: Retrieve Experiment Profiles by Test ID
+ operationId: profiles
+ parameters:
+ - name: testId
+ in: path
+ description: Test ID to retrieve Experiment Profiles for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/ExperimentProfile'
+ post:
+ tags:
+ - Experiment
+ description: 'Save new or update existing Experiment Profiles for a Test '
+ operationId: addOrUpdateProfile
+ parameters:
+ - name: testId
+ in: path
+ description: Test ID to retrieve Experiment Profiles for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ExperimentProfile'
+ required: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ /api/experiment/{testId}/profiles/{profileId}:
+ delete:
+ tags:
+ - Experiment
+ description: Delete an Experiment Profiles for a Test
+ operationId: deleteProfile
+ parameters:
+ - name: testId
+ in: path
+ description: Test ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: profileId
+ in: path
+ description: Experiment Profile ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ responses:
+ "204":
+ description: No Content
+ /api/run/autocomplete:
+ get:
+ tags:
+ - Run
+ operationId: autocomplete
+ parameters:
+ - name: query
+ in: query
+ required: true
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: string
+ /api/run/bySchema:
+ get:
+ tags:
+ - Run
+ description: Retrieve a paginated list of Runs with available count for a given
+ Schema URI
+ operationId: listBySchema
+ parameters:
+ - name: uri
+ in: query
+ description: Schema URI
+ required: true
+ schema:
+ type: string
+ example: uri:my-schema:0.1
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Tests
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RunsSummary'
+ /api/run/count:
+ get:
+ tags:
+ - Run
+ description: Run count summary for given Test ID
+ operationId: runCount
+ parameters:
+ - name: testId
+ in: query
+ description: Test ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RunCount'
+ /api/run/data:
+ post:
+ tags:
+ - Run
+ description: Upload a new Run
+ operationId: addRunFromData
+ parameters:
+ - name: start
+ in: query
+ description: "start timestamp of run, or json path expression"
+ required: true
+ schema:
+ type: string
+ examples:
+ scalar value:
+ value: 2023-10-23T00:13:35Z
+ json path:
+ value: $.buildTimeStamp
+ - name: stop
+ in: query
+ description: "stop timestamp of run, or json path expression"
+ required: true
+ schema:
+ type: string
+ examples:
+ scalar value:
+ value: 2023-10-23T00:13:35Z
+ json path:
+ value: $.buildTimeStamp
+ - name: test
+ in: query
+ description: test name of ID
+ required: true
+ schema:
+ type: string
+ example: my-benchmark
+ - name: owner
+ in: query
+ description: Name of the new owner
+ schema:
+ type: string
+ example: perf-team
+ - name: access
+ in: query
+ description: New Access level
+ schema:
+ $ref: '#/components/schemas/Access'
+ example: 0
+ - name: token
+ in: query
+ description: Horreum internal token. Incompatible with Keycloak
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ - name: schema
+ in: query
+ description: Schema URI
+ schema:
+ type: string
+ example: uri:my-benchmark:0.2
+ - name: description
+ in: query
+ description: Run description
+ schema:
+ type: string
+ example: AWS runs
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: string
+ example:
+ - tag: main
+ score: 2031.7424089224041
+ params:
+ size: "1000"
+ useTreeSet: "true"
+ $schema: urn:jmh:0.2
+ testName: org.drools.benchmarks.datastructures.QueueBenchmark.benchmark
+ - $schema: urn:horreum:jenkins-plugin:0.1
+ jobName: upstream-perf-bre-datastructures
+ buildUrl: https://qe.com/job/TESTING/job/upstream-perfx-datastructures/125/
+ startTime: 1698020160763
+ uploadTime: 1698020592674
+ buildNumber: 125
+ jobFullName: TESTING/RHBA/_upstream/decisions/8.x/performance/nightly/upstream-perf-bre-datastructures
+ scheduleTime: 1698020160756
+ jobDisplayName: upstream-perf-bre-datastructures
+ buildDisplayName: '#125'
+ responses:
+ "200":
+ description: id of the newly generated run
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ "400":
+ description: Some fields are missing or invalid
+ content:
+ application/json: {}
+ /api/run/list:
+ get:
+ tags:
+ - Run
+ description: Retrieve a paginated list of Runs with available count
+ operationId: listAllRuns
+ parameters:
+ - name: query
+ in: query
+ description: query string to filter runs
+ schema:
+ type: string
+ - name: matchAll
+ in: query
+ description: match all Runs?
+ schema:
+ type: boolean
+ example: false
+ - name: roles
+ in: query
+ description: "__my, __all or a comma delimited list of roles"
+ schema:
+ type: string
+ example: __my
+ - name: trashed
+ in: query
+ description: show trashed runs
+ schema:
+ type: boolean
+ example: false
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Tests
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RunsSummary'
+ /api/run/list/{testId}:
+ get:
+ tags:
+ - Run
+ description: Retrieve a paginated list of Runs with available count for a given
+ Test ID
+ operationId: listTestRuns
+ parameters:
+ - name: testId
+ in: path
+ description: Test ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: trashed
+ in: query
+ description: include trashed runs
+ schema:
+ type: boolean
+ example: false
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Tests
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RunsSummary'
+ /api/run/recalculateAll:
+ post:
+ tags:
+ - Run
+ description: Recalculate Datasets for Runs between two dates
+ operationId: recalculateAll
+ parameters:
+ - name: from
+ in: query
+ description: start timestamp
+ schema:
+ type: string
+ example: 1698013206000
+ - name: to
+ in: query
+ description: end timestamp
+ schema:
+ type: string
+ example: 1698013206000
+ responses:
+ "201":
+ description: Created
+ /api/run/test:
+ post:
+ tags:
+ - Run
+ description: Upload a new Run
+ operationId: add
+ parameters:
+ - name: test
+ in: query
+ description: test name of ID
+ schema:
+ type: string
+ example: my-benchmark
+ - name: owner
+ in: query
+ description: Name of the new owner
+ schema:
+ type: string
+ example: perf-team
+ - name: access
+ in: query
+ description: New Access level
+ schema:
+ $ref: '#/components/schemas/Access'
+ example: 0
+ - name: token
+ in: query
+ description: API token
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Run'
+ required: true
+ responses:
+ "200":
+ description: OK
+ /api/run/{id}:
+ get:
+ tags:
+ - Run
+ description: Get extended Run information by Run ID
+ operationId: getRun
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ - name: token
+ in: query
+ description: Run API token
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ responses:
+ "404":
+ description: If no Run have been found with the given id
+ content:
+ application/json: {}
+ "200":
+ description: Run data with the referenced schemas and generated datasets
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RunExtended'
+ /api/run/{id}/data:
+ get:
+ tags:
+ - Run
+ description: Get Run data by Run ID
+ operationId: getData
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ - name: token
+ in: query
+ description: Run API token
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ - name: schemaUri
+ in: query
+ description: FIlter by Schmea URI
+ schema:
+ type: string
+ example: uri:my-benchmark:0.1
+ responses:
+ "200":
+ description: Run payload
+ content:
+ application/json:
+ schema:
+ type: object
+ example: "{ \"buildID\": 1709, ...}"
+ /api/run/{id}/description:
+ post:
+ tags:
+ - Run
+ description: Update Run description
+ operationId: updateDescription
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ requestBody:
+ content:
+ text/plain:
+ schema:
+ type: string
+ required: true
+ responses:
+ "201":
+ description: Created
+ /api/run/{id}/dropToken:
+ post:
+ tags:
+ - Run
+ description: Remove access token for Run
+ operationId: dropToken
+ parameters:
+ - name: id
+ in: path
+ description: Token ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 102
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: string
+ /api/run/{id}/labelValues:
+ get:
+ tags:
+ - Run
+ description: Get all the label values for the run
+ operationId: labelValues
+ parameters:
+ - name: id
+ in: path
+ description: Run Id
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: filter
+ in: query
+ description: either a required json sub-document or path expression
+ schema:
+ default: "{}"
+ type: string
+ examples:
+ object:
+ description: json object that must exist in the values object
+ value: "{labelName:necessaryValue,...}"
+ string:
+ description: valid filtering jsonpath that returns null if not found (not
+ predicates)
+ value: $.count ? (@ < 20 && @ > 10)
+ - name: sort
+ in: query
+ description: label name for sorting
+ schema:
+ default: ""
+ type: string
+ - name: direction
+ in: query
+ description: either Ascending or Descending
+ schema:
+ default: Ascending
+ type: string
+ example: count
+ - name: limit
+ in: query
+ description: the maximum number of results to include
+ schema:
+ format: int32
+ default: 2147483647
+ type: integer
+ example: 10
+ - name: page
+ in: query
+ description: which page to skip to when using a limit
+ schema:
+ format: int32
+ default: 0
+ type: integer
+ example: 2
+ responses:
+ "200":
+ description: label Values
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/ExportedLabelValues'
+ example: "[ { \"datasetId\" : 101, \"runId\": 201, \"values\" : { [labelName]\
+ \ : labelValue } },...]"
+ /api/run/{id}/metadata:
+ get:
+ tags:
+ - Run
+ description: Get Run meta data by Run ID
+ operationId: getMetadata
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ - name: token
+ in: query
+ description: Run API token
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ - name: schemaUri
+ in: query
+ description: Filter by Schmea URI
+ schema:
+ type: string
+ example: uri:my-benchmark:0.1
+ responses:
+ "200":
+ description: Run payload
+ content:
+ application/json:
+ schema:
+ type: object
+ example: "{ \"metaDataID\": 1709, ...}"
+ /api/run/{id}/recalculate:
+ post:
+ tags:
+ - Run
+ description: Recalculate Datasets for Run
+ operationId: recalculateDatasets
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: Array of generated Datasets
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ format: int32
+ type: integer
+ example:
+ - 101
+ - 102
+ - 103
+ /api/run/{id}/resetToken:
+ post:
+ tags:
+ - Run
+ description: Regenerate access token for Run
+ operationId: resetToken
+ parameters:
+ - name: id
+ in: path
+ description: Token ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 102
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ /api/run/{id}/schema:
+ post:
+ tags:
+ - Run
+ description: Update Run schema for part of JSON data
+ operationId: updateSchema
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: path
+ in: query
+ description: JSON path expression to update schema
+ schema:
+ type: string
+ example: $.schemaURI
+ requestBody:
+ content:
+ text/plain:
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: object
+ additionalProperties:
+ type: string
+ /api/run/{id}/summary:
+ get:
+ tags:
+ - Run
+ description: Get Run Summary information by Run ID
+ operationId: getRunSummary
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ - name: token
+ in: query
+ description: Run API token
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ responses:
+ "404":
+ description: If no Run have been found with the given id
+ content:
+ application/json: {}
+ "200":
+ description: Run summary with the referenced schemas and generated datasets
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RunSummary'
+ /api/run/{id}/trash:
+ post:
+ tags:
+ - Run
+ description: Trash a Run with a given ID
+ operationId: trash
+ parameters:
+ - name: id
+ in: path
+ description: Run ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: isTrashed
+ in: query
+ description: should run be trashed?
+ schema:
+ type: boolean
+ example: true
+ responses:
+ "201":
+ description: Created
+ /api/run/{id}/updateAccess:
+ post:
+ tags:
+ - Run
+ description: Update the Access configuration for a Run
+ operationId: updateAccess
+ parameters:
+ - name: id
+ in: path
+ description: Run ID to update Access
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: owner
+ in: query
+ description: Name of the new owner
+ required: true
+ schema:
+ type: string
+ example: perf-team
+ - name: access
+ in: query
+ description: New Access level
+ required: true
+ schema:
+ $ref: '#/components/schemas/Access'
+ example: 0
+ responses:
+ "201":
+ description: Created
+ /api/schema:
+ get:
+ tags:
+ - Schema
+ description: Retrieve a paginated list of Schemas with available count
+ operationId: list
+ parameters:
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Schemas
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SchemaQueryResult'
+ post:
+ tags:
+ - Schema
+ description: Save a new Schema
+ operationId: add
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Schema'
+ responses:
+ "200":
+ description: Import a new Schema
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ example: 103
+ /api/schema/allLabels:
+ get:
+ tags:
+ - Schema
+ description: Retrieve list of Labels for ny name. Allows users to retrieve all
+ Label Definitions that have the same name
+ operationId: allLabels
+ parameters:
+ - name: name
+ in: query
+ description: Label name
+ schema:
+ type: string
+ example: buildID
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/LabelInfo'
+ /api/schema/allTransformers:
+ get:
+ tags:
+ - Schema
+ description: Retrieve all transformers
+ operationId: allTransformers
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/TransformerInfo'
+ /api/schema/descriptors:
+ get:
+ tags:
+ - Schema
+ description: Retrieve a list of Schema Descriptors
+ operationId: descriptors
+ parameters:
+ - name: id
+ in: query
+ description: Limit to a single Schema by ID
+ schema:
+ type: array
+ items:
+ format: int32
+ type: integer
+ example: 102
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/SchemaDescriptor'
+ /api/schema/findUsages:
+ get:
+ tags:
+ - Schema
+ description: Find all usages of a Schema by label name
+ operationId: findUsages
+ parameters:
+ - name: label
+ in: query
+ description: Name of label to search for
+ required: true
+ schema:
+ type: string
+ example: Throughput
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/LabelLocation'
+ /api/schema/idByUri/{uri}:
+ get:
+ tags:
+ - Schema
+ description: Retrieve Schema ID by uri
+ operationId: idByUri
+ parameters:
+ - name: uri
+ in: path
+ description: Schema uri
+ required: true
+ schema:
+ type: string
+ example: uri:my-schema:0.1
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ /api/schema/import:
+ post:
+ tags:
+ - Schema
+ description: Import an previously exported Schema either as a new Schema or
+ to update an existing Schema
+ operationId: importSchema
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SchemaExport'
+ required: true
+ responses:
+ "201":
+ description: Import a new Schema or update an existing Schema
+ /api/schema/{id}:
+ get:
+ tags:
+ - Schema
+ description: Retrieve Schema by ID
+ operationId: getSchema
+ parameters:
+ - name: id
+ in: path
+ description: Schema ID to retrieve
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: token
+ in: query
+ description: API token for authorization
+ schema:
+ type: string
+ example: 101
+ responses:
+ "404":
+ description: No Schema with the given id was found
+ content:
+ application/json: {}
+ "200":
+ description: Returns Schema if a matching id is found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Schema'
+ delete:
+ tags:
+ - Schema
+ description: Delete a Schema by id
+ operationId: delete
+ parameters:
+ - name: id
+ in: path
+ description: Schema ID to delete
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "204":
+ description: No Content
+ /api/schema/{id}/dropToken:
+ delete:
+ tags:
+ - Schema
+ description: Remove access token for schema
+ operationId: dropToken
+ parameters:
+ - name: id
+ in: path
+ description: Token ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 102
+ responses:
+ "204":
+ description: No Content
+ /api/schema/{id}/export:
+ get:
+ tags:
+ - Schema
+ description: Export a Schema
+ operationId: exportSchema
+ parameters:
+ - name: id
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: A JSON representation of the SchemaExport object
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/SchemaExport'
+ /api/schema/{id}/resetToken:
+ post:
+ tags:
+ - Schema
+ description: Regenerate access token for schema
+ operationId: resetToken
+ parameters:
+ - name: id
+ in: path
+ description: Token ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 102
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ /api/schema/{id}/updateAccess:
+ post:
+ tags:
+ - Schema
+ description: Update the Access configuration for a Schema
+ operationId: updateAccess
+ parameters:
+ - name: id
+ in: path
+ description: Schema ID to update Access
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: owner
+ in: query
+ description: Name of the new owner
+ required: true
+ schema:
+ type: string
+ example: perf-team
+ - name: access
+ in: query
+ description: New Access level
+ required: true
+ schema:
+ $ref: '#/components/schemas/Access'
+ example: 0
+ responses:
+ "201":
+ description: Created
+ /api/schema/{schemaId}/labels:
+ get:
+ tags:
+ - Schema
+ description: Retrieve list of Labels for a Schema by Schema ID
+ operationId: labels
+ parameters:
+ - name: schemaId
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Label'
+ post:
+ tags:
+ - Schema
+ description: Save new or update existing Label for a Schema
+ operationId: addOrUpdateLabel
+ parameters:
+ - name: schemaId
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Label'
+ required: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ /api/schema/{schemaId}/labels/{labelId}:
+ delete:
+ tags:
+ - Schema
+ description: Delete existing Label from a Schema
+ operationId: deleteLabel
+ parameters:
+ - name: schemaId
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: labelId
+ in: path
+ description: Label ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ responses:
+ "204":
+ description: No Content
+ /api/schema/{schemaId}/transformers:
+ get:
+ tags:
+ - Schema
+ description: List all Transformers defined for a Schema
+ operationId: listTransformers
+ parameters:
+ - name: schemaId
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Transformer'
+ post:
+ tags:
+ - Schema
+ description: Save new or update existing Transformer defintion
+ operationId: addOrUpdateTransformer
+ parameters:
+ - name: schemaId
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Transformer'
+ required: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ /api/schema/{schemaId}/transformers/{transformerId}:
+ delete:
+ tags:
+ - Schema
+ description: Delete a Transformer defined for a Schema
+ operationId: deleteTransformer
+ parameters:
+ - name: schemaId
+ in: path
+ description: Schema ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: transformerId
+ in: path
+ description: Transformer ID
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ responses:
+ "204":
+ description: No Content
+ /api/test:
+ get:
+ tags:
+ - Test
+ description: Retrieve a paginated list of Tests with available count
+ operationId: list
+ parameters:
+ - name: roles
+ in: query
+ description: "__my, __all or a comma delimited list of roles"
+ schema:
+ type: string
+ example: __my
+ - name: limit
+ in: query
+ description: limit the number of results
+ schema:
+ format: int32
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: filter by page number of a paginated list of Tests
+ schema:
+ format: int32
+ type: integer
+ example: 2
+ - name: sort
+ in: query
+ description: Field name to sort results
+ schema:
+ default: name
+ type: string
+ example: name
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ $ref: '#/components/schemas/SortDirection'
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TestQueryResult'
+ post:
+ tags:
+ - Test
+ description: Create a new test
+ operationId: add
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Test'
+ required: true
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Test'
+ /api/test/byName/{name}:
+ get:
+ tags:
+ - Test
+ description: Retrieve a test by name
+ operationId: getByNameOrId
+ parameters:
+ - name: name
+ in: path
+ description: Name of test to retrieve
+ required: true
+ schema:
+ type: string
+ example: my-comprehensive-benchmark
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Test'
+ /api/test/folders:
+ get:
+ tags:
+ - Test
+ description: Retrieve a list of all folders
+ operationId: folders
+ parameters:
+ - name: roles
+ in: query
+ description: "\"__my\", \"__all\" or a comma delimited list of roles"
+ schema:
+ type: string
+ example: __my
+ responses:
+ "200":
+ description: List of all folders
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: string
+ example:
+ - quarkus
+ - ocp-perf-team
+ /api/test/import:
+ post:
+ tags:
+ - Test
+ description: Import a previously exported Test either as a new Test or to update
+ an existing Test
+ operationId: importTest
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TestExport'
+ required: true
+ responses:
+ "201":
+ description: Import a new Test or update an existing Test
+ /api/test/summary:
+ get:
+ tags:
+ - Test
+ description: Retrieve a summary of Tests in a folder
+ operationId: summary
+ parameters:
+ - name: roles
+ in: query
+ description: "\"__my\", \"__all\" or a comma delimited list of roles"
+ schema:
+ type: string
+ example: __my
+ - name: folder
+ in: query
+ description: name of the Folder containing the Tests
+ schema:
+ type: string
+ example: My Team Folder
+ - name: limit
+ in: query
+ description: limit the result count
+ schema:
+ default: 20
+ type: integer
+ example: 20
+ - name: page
+ in: query
+ description: 'filter by page number of a paginated list of '
+ schema:
+ default: 1
+ type: integer
+ example: 1
+ - name: direction
+ in: query
+ description: Sort direction
+ schema:
+ allOf:
+ - $ref: '#/components/schemas/SortDirection'
+ - default: Ascending
+ example: Ascending
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TestListing'
+ /api/test/{id}:
+ get:
+ tags:
+ - Test
+ description: Retrieve a test by id
+ operationId: get
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int32
+ type: integer
+ - name: token
+ in: query
+ schema:
+ type: string
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Test'
+ delete:
+ tags:
+ - Test
+ description: Delete a Test by id
+ operationId: delete
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int32
+ type: integer
+ responses:
+ "204":
+ description: No Content
+ /api/test/{id}/addToken:
+ post:
+ tags:
+ - Test
+ description: "Add a Test API Token for access to provide access to a test data\
+ \ for integrated tooling, e.g. reporting services"
+ operationId: addToken
+ parameters:
+ - name: id
+ in: path
+ description: ID of test to add token to
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TestToken'
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ format: int32
+ type: integer
+ /api/test/{id}/export:
+ get:
+ tags:
+ - Test
+ operationId: export
+ parameters:
+ - name: id
+ in: path
+ required: true
+ schema:
+ format: int32
+ type: integer
+ responses:
+ "200":
+ description: A Test definition formatted as json
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TestExport'
+ /api/test/{id}/fingerprint:
+ get:
+ tags:
+ - Test
+ description: List all Fingerprints for a Test
+ operationId: listFingerprints
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to retrieve Fingerprints for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/Fingerprints'
+ /api/test/{id}/labelValues:
+ get:
+ tags:
+ - Test
+ description: List all Label Values for a Test
+ operationId: listLabelValues
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to retrieve Label Values for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: filtering
+ in: query
+ description: Retrieve values for Filtering Labels
+ schema:
+ default: true
+ type: boolean
+ example: true
+ - name: metrics
+ in: query
+ description: Retrieve values for Metric Labels
+ schema:
+ default: true
+ type: boolean
+ example: false
+ - name: filter
+ in: query
+ description: either a required json sub-document or path expression
+ schema:
+ default: "{}"
+ type: string
+ examples:
+ object:
+ description: json object that must exist in the values object
+ value: "{labelName:necessaryValue,...}"
+ string:
+ description: valid filtering jsonpath that returns null if not found (not
+ predicates)
+ value: $.count ? (@ < 20 && @ > 10)
+ - name: before
+ in: query
+ description: ISO-like date time string or epoch millis
+ schema:
+ default: ""
+ type: string
+ example: 1970-01-01T00:00:00+00:00 or an integer
+ - name: after
+ in: query
+ description: ISO-like date time string or epoch millis
+ schema:
+ default: ""
+ type: string
+ example: 1970-01-01T00:00:00+00:00 or an integer
+ - name: sort
+ in: query
+ description: json path to sortable value or start or stop for sorting by time
+ schema:
+ default: ""
+ type: string
+ example: $.label or start or stop
+ - name: direction
+ in: query
+ description: either Ascending or Descending
+ schema:
+ default: Ascending
+ type: string
+ example: count
+ - name: limit
+ in: query
+ description: the maximum number of results to include
+ schema:
+ format: int32
+ default: 2147483647
+ type: integer
+ example: 10
+ - name: page
+ in: query
+ description: which page to skip to when using a limit
+ schema:
+ format: int32
+ default: 0
+ type: integer
+ example: 2
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/ExportedLabelValues'
+ /api/test/{id}/move:
+ post:
+ tags:
+ - Test
+ description: Update the folder for a Test. Tests can be moved to different folders
+ operationId: updateFolder
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to update
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: folder
+ in: query
+ description: New folder to store the tests
+ schema:
+ type: string
+ example: My Benchmark Folder
+ responses:
+ "201":
+ description: Created
+ /api/test/{id}/notifications:
+ post:
+ tags:
+ - Test
+ description: "Update notifications for a Test. It is possible to disable notifications\
+ \ for a Test, so that no notifications are sent to subscribers"
+ operationId: updateNotifications
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to update
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: enabled
+ in: query
+ description: Whether notifications are enabled
+ required: true
+ schema:
+ type: boolean
+ example: false
+ responses:
+ "201":
+ description: Created
+ /api/test/{id}/recalculate:
+ get:
+ tags:
+ - Test
+ description: Get recalculation status for Test
+ operationId: getRecalculationStatus
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to retrieve recalculation status for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/RecalculationStatus'
+ post:
+ tags:
+ - Test
+ description: Recalculate Datasets for Test
+ operationId: recalculateDatasets
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to recalculate datasets for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "201":
+ description: Created
+ /api/test/{id}/revokeToken/{tokenId}:
+ delete:
+ tags:
+ - Test
+ description: Revoke a Token defined for a Test
+ operationId: dropToken
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to revoke token
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: tokenId
+ in: path
+ description: ID of token to revoke
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 202
+ responses:
+ "204":
+ description: No Content
+ /api/test/{id}/tokens:
+ get:
+ tags:
+ - Test
+ description: A collection of Test Tokens for a given Test
+ operationId: tokens
+ parameters:
+ - name: id
+ in: path
+ description: ID of test to retrieve list of tokens
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ responses:
+ "200":
+ description: OK
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ $ref: '#/components/schemas/TestToken'
+ /api/test/{id}/transformers:
+ post:
+ tags:
+ - Test
+ description: Update transformers for Test
+ operationId: updateTransformers
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to retrieve Label Values for
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ requestBody:
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ format: int32
+ type: integer
+ required: true
+ responses:
+ "201":
+ description: Created
+ /api/test/{id}/updateAccess:
+ post:
+ tags:
+ - Test
+ description: Update the Access configuration for a Test
+ operationId: updateAccess
+ parameters:
+ - name: id
+ in: path
+ description: Test ID to revoke token
+ required: true
+ schema:
+ format: int32
+ type: integer
+ example: 101
+ - name: owner
+ in: query
+ description: Name of the new owner
+ required: true
+ schema:
+ type: string
+ example: perf-team
+ - name: access
+ in: query
+ description: New Access level for the Test
+ required: true
+ schema:
+ $ref: '#/components/schemas/Access'
+ example: 0
+ responses:
+ "201":
+ description: Created
+components:
+ schemas:
+ Access:
+ description: "Resources have different visibility within the UI. 'PUBLIC', 'PROTECTED'\
+ \ and 'PRIVATE'. Restricted resources are not visible to users who do not\
+ \ have the correct permissions"
+ enum:
+ - PUBLIC
+ - PROTECTED
+ - PRIVATE
+ type: string
+ Action:
+ required:
+ - id
+ - event
+ - type
+ - config
+ - secrets
+ - testId
+ - active
+ - runAlways
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ event:
+ type: string
+ type:
+ type: string
+ config:
+ type: object
+ oneOf:
+ - $ref: '#/components/schemas/HttpAction'
+ - $ref: '#/components/schemas/GithubIssueCommentAction'
+ - $ref: '#/components/schemas/GithubIssueCreateAction'
+ secrets:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/Secret'
+ testId:
+ format: int32
+ type: integer
+ active:
+ type: boolean
+ runAlways:
+ type: boolean
+ ActionLog:
+ description: Action Log
+ required:
+ - testId
+ - event
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/PersistentLog'
+ properties:
+ testId:
+ format: int32
+ type: integer
+ event:
+ type: string
+ type:
+ type: string
+ BetterOrWorse:
+ description: Result of running an Experiment
+ enum:
+ - BETTER
+ - SAME
+ - WORSE
+ type: string
+ ChangeDetection:
+ required:
+ - id
+ - model
+ - config
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ model:
+ type: string
+ config:
+ type: object
+ oneOf:
+ - $ref: '#/components/schemas/RelativeDifferenceDetectionConfig'
+ - $ref: '#/components/schemas/FixedThresholdDetectionConfig'
+ discriminator:
+ propertyName: model
+ mapping:
+ relativeDifference: '#/components/schemas/RelativeDifferenceDetectionConfig'
+ fixedThreshold: '#/components/schemas/FixedThresholdDetectionConfig'
+ ChangeDetectionModelType:
+ description: Type of Change Detection Model
+ enum:
+ - FIXED_THRESHOLD
+ - RELATIVE_DIFFERENCE
+ type: string
+ ComparisonResult:
+ description: Result of performing a Comparison
+ type: object
+ properties:
+ overall:
+ description: Was the Experiment dataset better or worse than the baseline
+ dataset
+ enum:
+ - BETTER
+ - SAME
+ - WORSE
+ type: string
+ allOf:
+ - $ref: '#/components/schemas/BetterOrWorse'
+ experimentValue:
+ format: double
+ description: Experiment value
+ type: number
+ baselineValue:
+ format: double
+ description: Baseline value
+ type: number
+ result:
+ description: The relative difference between the Experiment and Baseline
+ Datasets
+ type: string
+ ConditionComponent:
+ required:
+ - name
+ - title
+ - description
+ - type
+ - properties
+ type: object
+ properties:
+ name:
+ description: Change detection model component name
+ type: string
+ example: min
+ title:
+ description: Change detection model component title
+ type: string
+ example: Minimum
+ description:
+ description: Change detection model component description
+ type: string
+ example: Lower bound for acceptable datapoint values.
+ type:
+ description: UI Component type
+ enum:
+ - LOG_SLIDER
+ - ENUM
+ - NUMBER_BOUND
+ - SWITCH
+ type: object
+ example: '"LOG_SLIDER"'
+ properties:
+ description: Map of properties for component
+ type: object
+ additionalProperties: {}
+ example: ""
+ ConditionConfig:
+ description: A configuration object for Change detection models
+ required:
+ - name
+ - title
+ - description
+ - ui
+ type: object
+ properties:
+ name:
+ description: Name of Change detection model
+ type: string
+ example: fixedThreshold
+ title:
+ description: UI name for change detection model
+ type: string
+ example: Fixed Threshold
+ description:
+ description: Change detection model description
+ type: string
+ example: This model checks that the datapoint value is within fixed bounds.
+ ui:
+ description: A list of UI components for dynamically building the UI components
+ type: array
+ items:
+ $ref: '#/components/schemas/ConditionComponent'
+ defaults:
+ description: A dictionary of UI default configuration items for dynamically
+ building the UI components
+ type: object
+ additionalProperties: {}
+ Dataset:
+ description: A dataset is the JSON document used as the basis for all comparisons
+ and reporting
+ required:
+ - testid
+ - data
+ - ordinal
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedType'
+ - $ref: '#/components/schemas/ProtectedTimeType'
+ properties:
+ id:
+ format: int32
+ description: Dataset Unique ID
+ type: integer
+ example: 101
+ description:
+ description: Run description
+ type: string
+ example: Run on AWS with m7g.large
+ testid:
+ format: int32
+ description: Test ID that Dataset relates to
+ type: integer
+ example: 101
+ data:
+ description: Data payload
+ type: string
+ ordinal:
+ format: int32
+ description: Dataset ordinal for ordered list of Datasets derived from a
+ Run
+ type: integer
+ example: 1
+ validationErrors:
+ description: List of Validation Errors
+ type: array
+ items:
+ $ref: '#/components/schemas/ValidationError'
+ runId:
+ format: int32
+ description: Run ID that Dataset relates to
+ type: integer
+ example: 101
+ DatasetInfo:
+ required:
+ - id
+ - runId
+ - ordinal
+ - testId
+ type: object
+ properties:
+ id:
+ format: int32
+ description: Dataset ID for Dataset
+ type: integer
+ example: 101
+ runId:
+ format: int32
+ description: Run ID that Dataset relates to
+ type: integer
+ example: 101
+ ordinal:
+ format: int32
+ description: Ordinal position in ordered list
+ type: integer
+ example: 2
+ testId:
+ format: int32
+ description: Test ID that Dataset relates to
+ type: integer
+ example: 103
+ DatasetList:
+ description: Result containing a subset of Dataset Summaries and the total count
+ of available. Used in paginated tables
+ required:
+ - total
+ - datasets
+ type: object
+ properties:
+ total:
+ format: int64
+ description: Total number of Dataset Summaries available
+ type: integer
+ example: 64
+ datasets:
+ description: List of Dataset Summaries. This is often a subset of total
+ available.
+ type: array
+ items:
+ $ref: '#/components/schemas/DatasetSummary'
+ DatasetLog:
+ description: Dataset Log
+ required:
+ - source
+ - testId
+ - runId
+ - datasetId
+ - datasetOrdinal
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/PersistentLog'
+ properties:
+ source:
+ type: string
+ testId:
+ format: int32
+ type: integer
+ runId:
+ format: int32
+ type: integer
+ datasetId:
+ format: int32
+ type: integer
+ datasetOrdinal:
+ format: int32
+ type: integer
+ DatasetSummary:
+ required:
+ - id
+ - runId
+ - ordinal
+ - testId
+ - testname
+ - schemas
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedTimeType'
+ properties:
+ id:
+ format: int32
+ description: Unique Dataset ID
+ type: integer
+ example: 101
+ runId:
+ format: int32
+ description: Run ID that Dataset relates to
+ type: integer
+ example: 202
+ ordinal:
+ format: int32
+ description: Ordinal position of Dataset Summary on returned List
+ type: integer
+ example: 3
+ testId:
+ format: int32
+ description: Test ID that Dataset relates to
+ type: integer
+ example: 202
+ testname:
+ description: Test name that the Dataset relates to
+ type: string
+ example: my-comprehensive-benchmark
+ description:
+ description: Dataset description
+ type: string
+ example: Run on AWS with m7g.large
+ view:
+ description: map of view component ids to the LabelValueMap to render the
+ component for this dataset
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/IndexedLabelValueMap'
+ example: "{ \"[view_component_id]\": { \"[labelName]\": labelValue} }"
+ schemas:
+ description: List of Schema usages
+ type: array
+ items:
+ $ref: '#/components/schemas/SchemaUsage'
+ validationErrors:
+ description: List of Validation Errors
+ type: array
+ items:
+ $ref: '#/components/schemas/ValidationError'
+ Datastore:
+ description: Type of backend datastore
+ required:
+ - access
+ - owner
+ - id
+ - name
+ - builtIn
+ - config
+ - type
+ type: object
+ properties:
+ access:
+ description: Access rights for the test. This defines the visibility of
+ the Test in the UI
+ enum:
+ - PUBLIC
+ - PROTECTED
+ - PRIVATE
+ type: string
+ allOf:
+ - $ref: '#/components/schemas/Access'
+ example: PUBLIC
+ owner:
+ description: Name of the team that owns the test. Users must belong to the
+ team that owns a test to make modifications
+ type: string
+ example: performance-team
+ id:
+ format: int32
+ description: Unique Datastore id
+ type: integer
+ example: 101
+ name:
+ description: "Name of the datastore, used to identify the datastore in the\
+ \ Test definition"
+ type: string
+ example: Perf Elasticsearch
+ builtIn:
+ description: Is this a built-in datastore? Built-in datastores cannot be
+ deleted or modified
+ type: boolean
+ example: false
+ config:
+ type: object
+ oneOf:
+ - $ref: '#/components/schemas/ElasticsearchDatastoreConfig'
+ - $ref: '#/components/schemas/PostgresDatastoreConfig'
+ type:
+ description: Type of backend datastore
+ enum:
+ - POSTGRES
+ - ELASTICSEARCH
+ type: string
+ example: ELASTICSEARCH
+ DatastoreTestResponse:
+ type: object
+ properties:
+ msg:
+ type: string
+ success:
+ type: boolean
+ DatastoreType:
+ description: Type of backend datastore
+ enum:
+ - POSTGRES
+ - ELASTICSEARCH
+ type: string
+ example: ELASTICSEARCH
+ ElasticsearchDatastoreConfig:
+ description: Type of backend datastore
+ required:
+ - builtIn
+ - url
+ type: object
+ properties:
+ builtIn:
+ description: Built In
+ type: boolean
+ apiKey:
+ description: Elasticsearch API KEY
+ type: string
+ url:
+ description: Elasticsearch url
+ type: string
+ username:
+ description: Elasticsearch username
+ type: string
+ password:
+ description: Elasticsearch password
+ type: string
+ ErrorDetails:
+ required:
+ - type
+ - message
+ type: object
+ properties:
+ type:
+ description: Validation Error type
+ type: string
+ code:
+ type: string
+ path:
+ type: string
+ evaluationPath:
+ type: string
+ schemaPath:
+ type: string
+ deprecated: true
+ schemaLocation:
+ type: string
+ instanceLocation:
+ type: string
+ property:
+ type: string
+ arguments:
+ type: array
+ items:
+ type: string
+ details:
+ type: string
+ messageKey:
+ type: string
+ valid:
+ type: boolean
+ message:
+ type: string
+ ExperimentComparison:
+ required:
+ - model
+ - config
+ - variableId
+ type: object
+ properties:
+ model:
+ description: Name of comparison model
+ type: string
+ example: relativeDifference
+ config:
+ description: Model JSON configuration
+ type: string
+ variableId:
+ format: int32
+ description: Variable ID to run experiment against
+ type: integer
+ example: 101
+ variableName:
+ description: Variable Name to run experiment against
+ type: string
+ example: Throughput
+ ExperimentProfile:
+ description: An Experiment Profile defines the labels and filters for the dataset
+ and baseline
+ required:
+ - id
+ - name
+ - selectorLabels
+ - baselineLabels
+ - comparisons
+ type: object
+ properties:
+ id:
+ format: int32
+ description: Experiment Profile unique ID
+ type: integer
+ example: 101
+ name:
+ description: Name of Experiment Profile
+ type: string
+ example: Techempower comparison
+ testId:
+ format: int32
+ description: Test ID that Experiment Profile relates to
+ type: integer
+ example: 101
+ selectorLabels:
+ description: Array of selector labels
+ type: array
+ items:
+ type: string
+ example:
+ - Framework
+ selectorFilter:
+ description: Selector filter to apply to Selector label values
+ type: string
+ example: value => value === 'quarkus-resteasy-reactive-hibernate-reactive'
+ baselineLabels:
+ description: Array of selector labels for comparison Baseline
+ type: array
+ items:
+ type: string
+ example:
+ - timestamp
+ baselineFilter:
+ description: Selector filter to apply to Baseline label values
+ type: string
+ example: value => value === 1666955225547
+ comparisons:
+ description: Collection of Experiment Comparisons to run during an Experiment
+ evaluation
+ type: array
+ items:
+ $ref: '#/components/schemas/ExperimentComparison'
+ extraLabels:
+ description: These labels are not used by Horreum but are added to the result
+ event and therefore can be used e.g. when firing an Action.
+ type: array
+ items:
+ type: string
+ ExperimentResult:
+ description: Result of running an Experiment
+ type: object
+ properties:
+ profile:
+ description: Experiment profile that results relates to
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ExperimentProfile'
+ logs:
+ description: A list of log statements recorded while Experiment was evaluated
+ type: array
+ items:
+ $ref: '#/components/schemas/DatasetLog'
+ datasetInfo:
+ description: Dataset Info about dataset used for experiment
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/DatasetInfo'
+ baseline:
+ description: A list of Dataset Info for experiment baseline(s)
+ type: array
+ items:
+ $ref: '#/components/schemas/DatasetInfo'
+ results:
+ description: A Map of all comparisons and results evaluated during an Experiment
+ type: object
+ additionalProperties:
+ $ref: '#/components/schemas/ComparisonResult'
+ extraLabels:
+ type: string
+ notify:
+ type: boolean
+ ExportedLabelValues:
+ description: A map of label names to label values with the associated datasetId
+ and runId
+ required:
+ - start
+ - stop
+ type: object
+ properties:
+ values:
+ $ref: '#/components/schemas/LabelValueMap'
+ runId:
+ format: int32
+ description: the run id that created the dataset
+ type: integer
+ example: 101
+ datasetId:
+ format: int32
+ description: the unique dataset id
+ type: integer
+ example: 101
+ start:
+ format: date-time
+ description: Start timestamp
+ type: string
+ example: 2019-09-26T07:58:30.996+0200
+ stop:
+ format: date-time
+ description: Stop timestamp
+ type: string
+ example: 2019-09-26T07:58:30.996+0200
+ Extractor:
+ description: "An Extractor defines how values are extracted from a JSON document,\
+ \ for use in Labels etc."
+ required:
+ - name
+ - jsonpath
+ - isarray
+ type: object
+ properties:
+ name:
+ description: Name of extractor. This name is used in Combination Functions
+ to refer to values by name
+ type: string
+ example: buildID
+ jsonpath:
+ description: JSON path expression defining the location of the extractor
+ value in the JSON document. This is a pSQL json path expression
+ type: string
+ example: $.buildInfo.buildID
+ isarray:
+ description: Does the JSON path expression reference an Array?
+ type: boolean
+ example: false
+ FingerprintValue:
+ description: Representation of Fingerprint. If the Fingerprint has children
+ the value will be null.
+ type: object
+ properties:
+ name:
+ description: Fingerprint name
+ type: string
+ example: Mode
+ value:
+ description: Fingerprint name
+ example: Library
+ children:
+ description: List of Fingerprint children
+ type: array
+ items:
+ description: Cyclic reference to io.hyperfoil.tools.horreum.api.data.FingerprintValue
+ Fingerprints:
+ description: A list of Fingerprints representing one dataset
+ type: object
+ properties:
+ values:
+ type: array
+ items:
+ $ref: '#/components/schemas/FingerprintValue'
+ FixThresholdConfig:
+ required:
+ - value
+ - enabled
+ - inclusive
+ type: object
+ properties:
+ value:
+ description: Threshold Value
+ type: integer
+ example: 95
+ enabled:
+ description: Threshold enabled/disabled
+ type: boolean
+ example: true
+ inclusive:
+ description: Is threshold inclusive of defined value?
+ type: boolean
+ example: false
+ FixedThresholdDetectionConfig:
+ required:
+ - builtIn
+ - min
+ - max
+ type: object
+ properties:
+ builtIn:
+ description: Built In
+ type: boolean
+ min:
+ description: Lower bound for acceptable datapoint values
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/FixThresholdConfig'
+ max:
+ description: Upper bound for acceptable datapoint values
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/FixThresholdConfig'
+ GithubIssueCommentAction:
+ type: object
+ properties:
+ issueUrl:
+ type: string
+ owner:
+ type: string
+ repo:
+ type: string
+ issue:
+ type: string
+ formatter:
+ type: string
+ GithubIssueCreateAction:
+ type: object
+ properties:
+ owner:
+ type: string
+ repo:
+ type: string
+ title:
+ type: string
+ formatter:
+ type: string
+ HttpAction:
+ type: object
+ properties:
+ url:
+ type: string
+ IndexedLabelValueMap:
+ type: object
+ additionalProperties:
+ $ref: '#/components/schemas/LabelValueMap'
+ KeycloakConfig:
+ type: object
+ properties:
+ realm:
+ description: Keycloak realm securing Horreum instance
+ type: string
+ example: horreum
+ url:
+ description: URL of Keycloak instance securing Horreum
+ type: string
+ example: https://horreum-keycloak.example.com
+ clientId:
+ description: Keycloak client ID in Horreum realm for User Interface
+ type: string
+ example: horreum-ui
+ Label:
+ description: "A Label is a core component of Horreum, defining which components\
+ \ of the JSON document are part of a KPI and how the metric values are calculated"
+ required:
+ - id
+ - name
+ - extractors
+ - filtering
+ - metrics
+ - schemaId
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedType'
+ properties:
+ id:
+ format: int32
+ description: Unique ID for Label
+ type: integer
+ example: 101
+ name:
+ description: "Name for label. NOTE: all Labels are considered to have the\
+ \ same semantic meaning throughout the entire system"
+ type: string
+ example: Throughput
+ extractors:
+ description: "A collection of Extractors, that will be combined in the Combination\
+ \ Function"
+ type: array
+ items:
+ $ref: '#/components/schemas/Extractor'
+ function:
+ description: A Combination Function that defines how values from Extractors
+ are combined to produce a Label Value
+ type: string
+ example: "value => { return ((value.reduce((a,b) => a+b))/value.length*1000).toFixed(3);\
+ \ }"
+ filtering:
+ description: Is Label a filtering label? Filtering labels contains values
+ that are used to filter datasets for comparison
+ type: boolean
+ example: true
+ metrics:
+ description: Is Label a metrics label? Metrics labels are contain Metrics
+ that are used for comparison
+ type: boolean
+ example: true
+ schemaId:
+ format: int32
+ description: Schema ID that the Label relates to
+ type: integer
+ example: 101
+ LabelInFingerprint:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/LabelLocation'
+ LabelInReport:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/LabelLocation'
+ properties:
+ configId:
+ format: int32
+ type: integer
+ title:
+ type: string
+ where:
+ type: string
+ name:
+ type: string
+ LabelInRule:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/LabelLocation'
+ properties:
+ ruleId:
+ format: int32
+ type: integer
+ ruleName:
+ type: string
+ LabelInVariable:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/LabelLocation'
+ properties:
+ variableId:
+ format: int32
+ type: integer
+ variableName:
+ type: string
+ LabelInView:
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/LabelLocation'
+ properties:
+ viewId:
+ format: int32
+ type: integer
+ viewName:
+ type: string
+ componentId:
+ format: int32
+ type: integer
+ header:
+ type: string
+ LabelInfo:
+ required:
+ - name
+ - metrics
+ - filtering
+ - schemas
+ type: object
+ properties:
+ name:
+ description: Label name
+ type: string
+ example: buildID
+ metrics:
+ description: Is label a metrics label?
+ type: boolean
+ example: true
+ filtering:
+ description: Is label a filtering label?
+ type: boolean
+ example: false
+ schemas:
+ description: List of schemas where label is referenced
+ type: array
+ items:
+ $ref: '#/components/schemas/SchemaDescriptor'
+ LabelLocation:
+ type: object
+ properties:
+ type:
+ description: Location of Label usage
+ type: string
+ example: VIEW
+ testId:
+ format: int32
+ description: Unique ID for location that references Schema
+ type: integer
+ example: 101
+ testName:
+ description: Test name that references Schema
+ type: string
+ example: My Benchmark
+ LabelPreview:
+ description: Preview a Label Value derived from a Dataset Data. A preview allows
+ users to apply a Label to a dataset and preview the Label Value result and
+ processing errors in the UI
+ type: object
+ properties:
+ value:
+ description: "Value value extracted from Dataset. This can be a scalar,\
+ \ array or JSON object"
+ type: string
+ output:
+ description: Description of errors occurred attempting to generate Label
+ Value Preview
+ type: string
+ LabelValue:
+ description: Label Value derived from Label definition and Dataset Data
+ required:
+ - id
+ - name
+ - schema
+ type: object
+ properties:
+ id:
+ format: int32
+ description: Unique ID for Label Value
+ type: integer
+ example: 101
+ name:
+ description: Label name
+ type: string
+ example: buildID
+ schema:
+ description: Summary description of Schema
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/SchemaDescriptor'
+ value:
+ description: "Value value extracted from Dataset. This can be a scalar,\
+ \ array or JSON object"
+ type: string
+ example: "1724"
+ LabelValueMap:
+ description: a map of label name to value
+ type: object
+ example: "{ \"[labelName]\": labelValue}"
+ MissingDataRule:
+ required:
+ - id
+ - maxStaleness
+ - testId
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ name:
+ type: string
+ labels:
+ type: array
+ items:
+ type: string
+ condition:
+ type: string
+ maxStaleness:
+ format: int64
+ type: integer
+ lastNotification:
+ format: date-time
+ type: string
+ example: 2022-03-10T16:15:50Z
+ testId:
+ format: int32
+ type: integer
+ PersistentLog:
+ description: Persistent Log
+ required:
+ - id
+ - level
+ - timestamp
+ - message
+ type: object
+ properties:
+ id:
+ format: int64
+ type: integer
+ level:
+ format: int32
+ type: integer
+ timestamp:
+ format: date-time
+ type: string
+ example: 2022-03-10T16:15:50Z
+ message:
+ type: string
+ PostgresDatastoreConfig:
+ description: Built in backend datastore
+ required:
+ - builtIn
+ type: object
+ properties:
+ builtIn:
+ description: Built In
+ type: boolean
+ ProtectedTimeType:
+ required:
+ - access
+ - owner
+ - start
+ - stop
+ type: object
+ properties:
+ access:
+ description: Access rights for the test. This defines the visibility of
+ the Test in the UI
+ enum:
+ - PUBLIC
+ - PROTECTED
+ - PRIVATE
+ type: string
+ allOf:
+ - $ref: '#/components/schemas/Access'
+ example: PUBLIC
+ owner:
+ description: Name of the team that owns the test. Users must belong to the
+ team that owns a test to make modifications
+ type: string
+ example: performance-team
+ start:
+ format: int64
+ description: Run Start timestamp
+ type: integer
+ example: 1704965908267
+ stop:
+ format: int64
+ description: Run Stop timestamp
+ type: integer
+ example: 1704965908267
+ ProtectedType:
+ required:
+ - access
+ - owner
+ type: object
+ properties:
+ access:
+ description: Access rights for the test. This defines the visibility of
+ the Test in the UI
+ enum:
+ - PUBLIC
+ - PROTECTED
+ - PRIVATE
+ type: string
+ allOf:
+ - $ref: '#/components/schemas/Access'
+ example: PUBLIC
+ owner:
+ description: Name of the team that owns the test. Users must belong to the
+ team that owns a test to make modifications
+ type: string
+ example: performance-team
+ RecalculationStatus:
+ required:
+ - timestamp
+ - totalRuns
+ - finished
+ - datasets
+ type: object
+ properties:
+ timestamp:
+ format: int64
+ description: Recalculation timestamp
+ type: integer
+ example: 1698013206000
+ totalRuns:
+ format: int64
+ description: Total number of Runs being recalculated
+ type: integer
+ example: 152
+ finished:
+ format: int64
+ description: Total number of completed recalculations
+ type: integer
+ example: 93
+ datasets:
+ format: int64
+ description: Total number of generated datasets
+ type: integer
+ example: 186
+ RelativeDifferenceDetectionConfig:
+ required:
+ - builtIn
+ - filter
+ - window
+ - threshold
+ - minPrevious
+ type: object
+ properties:
+ builtIn:
+ description: Built In
+ type: boolean
+ filter:
+ description: Relative Difference Detection filter
+ type: string
+ example: mean
+ window:
+ format: int32
+ description: Number of most recent datapoints used for aggregating the value
+ for comparison.
+ type: integer
+ example: 5
+ threshold:
+ format: double
+ description: Maximum difference between the aggregated value of last
+ datapoints and the mean of preceding values.
+ type: number
+ example: 0.2
+ minPrevious:
+ format: int32
+ description: Minimal number of preceding datapoints
+ type: integer
+ example: 5
+ ReportComponent:
+ description: Report Component
+ required:
+ - name
+ - order
+ - labels
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ name:
+ type: string
+ order:
+ format: int32
+ type: integer
+ labels:
+ description: Array of labels
+ type: array
+ items:
+ type: string
+ example:
+ - Framework
+ function:
+ type: string
+ unit:
+ type: string
+ reportId:
+ format: int32
+ type: integer
+ ReportLog:
+ description: Report Log
+ required:
+ - reportId
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/PersistentLog'
+ properties:
+ reportId:
+ format: int32
+ type: integer
+ Run:
+ required:
+ - access
+ - owner
+ - start
+ - stop
+ - id
+ - testid
+ - data
+ - trashed
+ type: object
+ properties:
+ access:
+ description: Access rights for the test. This defines the visibility of
+ the Test in the UI
+ enum:
+ - PUBLIC
+ - PROTECTED
+ - PRIVATE
+ type: string
+ allOf:
+ - $ref: '#/components/schemas/Access'
+ example: PUBLIC
+ owner:
+ description: Name of the team that owns the test. Users must belong to the
+ team that owns a test to make modifications
+ type: string
+ example: performance-team
+ start:
+ format: int64
+ description: Run Start timestamp
+ type: integer
+ example: 1704965908267
+ stop:
+ format: int64
+ description: Run Stop timestamp
+ type: integer
+ example: 1704965908267
+ id:
+ format: int32
+ description: Unique Run ID
+ type: integer
+ example: 101
+ description:
+ description: Run description
+ type: string
+ example: Run on AWS with m7g.large
+ testid:
+ format: int32
+ description: Test ID run relates to
+ type: integer
+ example: 101
+ data:
+ description: Run result payload
+ type: string
+ metadata:
+ description: "JSON metadata related to run, can be tool configuration etc"
+ type: string
+ trashed:
+ description: Has Run been deleted from UI
+ type: boolean
+ example: false
+ datasets:
+ description: Collection of Datasets derived from Run payload
+ type: array
+ items:
+ $ref: '#/components/schemas/Dataset'
+ validationErrors:
+ description: Collection of Validation Errors in Run payload
+ type: array
+ items:
+ $ref: '#/components/schemas/ValidationError'
+ RunCount:
+ required:
+ - total
+ - active
+ - trashed
+ type: object
+ properties:
+ total:
+ format: int64
+ description: Total count of Runs visible
+ type: integer
+ example: 100
+ active:
+ format: int64
+ description: Total count of active Runs visible
+ type: integer
+ example: 95
+ trashed:
+ format: int64
+ description: Total count of trashed Runs
+ type: integer
+ example: 5
+ RunExtended:
+ required:
+ - access
+ - owner
+ - start
+ - stop
+ - id
+ - testid
+ - data
+ - trashed
+ - schemas
+ - testname
+ - datasets
+ type: object
+ properties:
+ access:
+ description: Access rights for the test. This defines the visibility of
+ the Test in the UI
+ enum:
+ - PUBLIC
+ - PROTECTED
+ - PRIVATE
+ type: string
+ allOf:
+ - $ref: '#/components/schemas/Access'
+ example: PUBLIC
+ owner:
+ description: Name of the team that owns the test. Users must belong to the
+ team that owns a test to make modifications
+ type: string
+ example: performance-team
+ start:
+ format: int64
+ description: Run Start timestamp
+ type: integer
+ example: 1704965908267
+ stop:
+ format: int64
+ description: Run Stop timestamp
+ type: integer
+ example: 1704965908267
+ id:
+ format: int32
+ description: Unique Run ID
+ type: integer
+ example: 101
+ description:
+ description: Run description
+ type: string
+ example: Run on AWS with m7g.large
+ testid:
+ format: int32
+ description: Test ID run relates to
+ type: integer
+ example: 101
+ data:
+ description: Run result payload
+ type: string
+ metadata:
+ description: "JSON metadata related to run, can be tool configuration etc"
+ type: string
+ trashed:
+ description: Has Run been deleted from UI
+ type: boolean
+ example: false
+ validationErrors:
+ description: Collection of Validation Errors in Run payload
+ type: array
+ items:
+ $ref: '#/components/schemas/ValidationError'
+ schemas:
+ description: List of Schema Usages
+ type: array
+ items:
+ $ref: '#/components/schemas/SchemaUsage'
+ testname:
+ description: Test name run references
+ type: string
+ example: My benchmark
+ datasets:
+ description: List of DatasetIDs
+ type: array
+ items:
+ format: int32
+ type: integer
+ example:
+ - 101
+ - 102
+ - 104
+ - 106
+ RunSummary:
+ required:
+ - id
+ - testid
+ - testname
+ - trashed
+ - hasMetadata
+ - datasets
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedTimeType'
+ properties:
+ id:
+ format: int32
+ description: Run unique ID
+ type: integer
+ example: 202
+ testid:
+ format: int32
+ description: test ID run relates to
+ type: integer
+ example: 101
+ token:
+ type: string
+ testname:
+ description: test ID run relates to
+ type: string
+ example: My benchmark
+ trashed:
+ description: has Run been trashed in the UI
+ type: boolean
+ example: false
+ hasMetadata:
+ description: does Run have metadata uploaded alongside Run data
+ type: boolean
+ example: false
+ description:
+ description: Run description
+ type: string
+ example: Run on AWS with m7g.large
+ schemas:
+ description: List of all Schema Usages for Run
+ type: array
+ items:
+ $ref: '#/components/schemas/SchemaUsage'
+ datasets:
+ description: Array of datasets ids
+ type: array
+ items:
+ format: int32
+ type: integer
+ example:
+ - 101
+ - 102
+ - 103
+ validationErrors:
+ description: Array of validation errors
+ type: array
+ items:
+ $ref: '#/components/schemas/ValidationError'
+ RunsSummary:
+ required:
+ - total
+ - runs
+ type: object
+ properties:
+ total:
+ format: int64
+ description: Total count of Runs visible
+ type: integer
+ example: 1
+ runs:
+ description: List of Run Summaries
+ type: array
+ items:
+ $ref: '#/components/schemas/RunSummary'
+ Schema:
+ description: Data object that describes the schema definition for a test
+ required:
+ - id
+ - uri
+ - name
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedType'
+ properties:
+ id:
+ format: int32
+ description: Unique Schema ID
+ type: integer
+ example: 101
+ uri:
+ description: "Unique, versioned schema URI"
+ type: string
+ example: uri:my-schema:0.1
+ name:
+ description: Schema name
+ type: string
+ example: My benchmark schema
+ description:
+ description: Schema Description
+ type: string
+ example: Schema for processing my benchmark
+ schema:
+ description: JSON validation schema. Used to validate uploaded JSON documents
+ type: string
+ example: "{ \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\
+ , \"$id\": \"https://example.com/product.schema.json\", \"title\": \"\
+ Product\", \"description\": \"A product in the catalog\", \"type\":\
+ \ \"object\"}"
+ token:
+ description: Array of API tokens associated with test
+ type: string
+ example: ""
+ SchemaDescriptor:
+ required:
+ - id
+ - name
+ - uri
+ type: object
+ properties:
+ id:
+ format: int32
+ description: Schema unique ID
+ type: integer
+ example: 1
+ name:
+ description: Schema name
+ type: string
+ example: my-benchmark-schema
+ uri:
+ description: Schema name
+ type: string
+ example: uri:my-schmea:0.1
+ SchemaExport:
+ description: Represents a Schema with all associated data used for export/import
+ operations.
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/Schema'
+ properties:
+ labels:
+ description: Array of Labels associated with schema
+ type: array
+ items:
+ $ref: '#/components/schemas/Label'
+ transformers:
+ description: Array of Transformers associated with schema
+ type: array
+ items:
+ $ref: '#/components/schemas/Transformer'
+ SchemaQueryResult:
+ required:
+ - schemas
+ - count
+ type: object
+ properties:
+ schemas:
+ description: Array of Schemas
+ type: array
+ items:
+ $ref: '#/components/schemas/Schema'
+ count:
+ format: int64
+ description: Count of available Schemas. This is a count of Schemas that
+ the current user has access to
+ type: integer
+ example: 64
+ SchemaUsage:
+ required:
+ - id
+ - name
+ - uri
+ - source
+ - type
+ - hasJsonSchema
+ type: object
+ properties:
+ id:
+ format: int32
+ description: Schema unique ID
+ type: integer
+ example: 1
+ name:
+ description: Schema name
+ type: string
+ example: my-benchmark-schema
+ uri:
+ description: Schema name
+ type: string
+ example: uri:my-schmea:0.1
+ source:
+ format: int32
+ description: "Source of schema usage, 0 is data, 1 is metadata. DataSets\
+ \ always use 0"
+ type: integer
+ example: 1
+ type:
+ format: int32
+ description: "Location of Schema Usage, 0 for Run, 1 for Dataset"
+ type: integer
+ example: 1
+ key:
+ description: Ordinal position of schema usage in Run/Dataset
+ type: string
+ example: "1"
+ hasJsonSchema:
+ description: Does schema have a JSON validation schema defined?
+ type: boolean
+ example: false
+ Secret:
+ type: object
+ properties:
+ token:
+ type: string
+ modified:
+ type: boolean
+ SortDirection:
+ enum:
+ - Ascending
+ - Descending
+ type: string
+ TableReport:
+ description: Table Report
+ required:
+ - id
+ - config
+ - created
+ - comments
+ - data
+ - logs
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ config:
+ description: Table Report Config
+ required:
+ - id
+ - title
+ - seriesLabels
+ - components
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/TableReportConfig'
+ properties:
+ id:
+ format: int32
+ type: integer
+ title:
+ type: string
+ test:
+ $ref: '#/components/schemas/Test'
+ filterLabels:
+ description: ArrayNode of filter labels
+ type: array
+ items:
+ type: string
+ filterFunction:
+ type: string
+ categoryLabels:
+ description: ArrayNode of category labels
+ type: array
+ items:
+ type: string
+ categoryFunction:
+ type: string
+ categoryFormatter:
+ type: string
+ seriesLabels:
+ description: ArrayNode of series labels
+ type: array
+ items:
+ type: string
+ seriesFunction:
+ type: string
+ seriesFormatter:
+ type: string
+ scaleLabels:
+ description: ArrayNode of filter labels
+ type: array
+ items:
+ type: string
+ scaleFunction:
+ type: string
+ scaleFormatter:
+ type: string
+ scaleDescription:
+ type: string
+ components:
+ description: List of ReportComponents
+ type: array
+ items:
+ description: Report Component
+ required:
+ - name
+ - order
+ - labels
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ name:
+ type: string
+ order:
+ format: int32
+ type: integer
+ labels:
+ description: Array of labels
+ type: array
+ items:
+ type: string
+ example:
+ - Framework
+ function:
+ type: string
+ unit:
+ type: string
+ reportId:
+ format: int32
+ type: integer
+ created:
+ format: date-time
+ description: Created timestamp
+ type: string
+ example: 2019-09-26T07:58:30.996+0200
+ comments:
+ description: List of ReportComments
+ type: array
+ items:
+ required:
+ - level
+ - comment
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ level:
+ format: int32
+ type: integer
+ category:
+ type: string
+ componentId:
+ format: int32
+ type: integer
+ comment:
+ type: string
+ data:
+ description: List of TableReportData
+ type: array
+ items:
+ description: Table Report Data
+ required:
+ - datasetId
+ - runId
+ - ordinal
+ - category
+ - series
+ - scale
+ - values
+ type: object
+ properties:
+ datasetId:
+ format: int32
+ type: integer
+ runId:
+ format: int32
+ type: integer
+ ordinal:
+ format: int32
+ type: integer
+ category:
+ type: string
+ series:
+ type: string
+ scale:
+ type: string
+ values:
+ description: Array of values
+ type: array
+ items:
+ type: number
+ logs:
+ description: List of ReportLogs
+ type: array
+ items:
+ description: Report Log
+ required:
+ - reportId
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/PersistentLog'
+ properties:
+ reportId:
+ format: int32
+ type: integer
+ TableReportConfig:
+ description: Table Report Config
+ required:
+ - id
+ - title
+ - seriesLabels
+ - components
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ title:
+ type: string
+ test:
+ $ref: '#/components/schemas/Test'
+ filterLabels:
+ description: ArrayNode of filter labels
+ type: array
+ items:
+ type: string
+ filterFunction:
+ type: string
+ categoryLabels:
+ description: ArrayNode of category labels
+ type: array
+ items:
+ type: string
+ categoryFunction:
+ type: string
+ categoryFormatter:
+ type: string
+ seriesLabels:
+ description: ArrayNode of series labels
+ type: array
+ items:
+ type: string
+ seriesFunction:
+ type: string
+ seriesFormatter:
+ type: string
+ scaleLabels:
+ description: ArrayNode of filter labels
+ type: array
+ items:
+ type: string
+ scaleFunction:
+ type: string
+ scaleFormatter:
+ type: string
+ scaleDescription:
+ type: string
+ components:
+ description: List of ReportComponents
+ type: array
+ items:
+ description: Report Component
+ required:
+ - name
+ - order
+ - labels
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ name:
+ type: string
+ order:
+ format: int32
+ type: integer
+ labels:
+ description: Array of labels
+ type: array
+ items:
+ type: string
+ example:
+ - Framework
+ function:
+ type: string
+ unit:
+ type: string
+ reportId:
+ format: int32
+ type: integer
+ TableReportData:
+ description: Table Report Data
+ required:
+ - datasetId
+ - runId
+ - ordinal
+ - category
+ - series
+ - scale
+ - values
+ type: object
+ properties:
+ datasetId:
+ format: int32
+ type: integer
+ runId:
+ format: int32
+ type: integer
+ ordinal:
+ format: int32
+ type: integer
+ category:
+ type: string
+ series:
+ type: string
+ scale:
+ type: string
+ values:
+ description: Array of values
+ type: array
+ items:
+ type: number
+ Test:
+ description: Represents a Test. Tests are typically equivalent to a particular
+ benchmark
+ required:
+ - id
+ - name
+ - datastoreId
+ - notificationsEnabled
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedType'
+ properties:
+ id:
+ format: int32
+ description: Unique Test id
+ type: integer
+ example: 101
+ name:
+ description: Test name
+ type: string
+ example: my-comprehensive-benchmark
+ folder:
+ description: Name of folder that the test is stored in. Folders allow tests
+ to be organised in the UI
+ type: string
+ example: My Team Folder
+ description:
+ description: Description of the test
+ type: string
+ example: Comprehensive benchmark to tests the limits of any system it is
+ run against
+ datastoreId:
+ format: int32
+ description: backend ID for backing datastore
+ type: integer
+ tokens:
+ description: Array of API tokens associated with test
+ type: array
+ items:
+ $ref: '#/components/schemas/TestToken'
+ timelineLabels:
+ description: List of label names that are used for determining metric to
+ use as the time series
+ type: array
+ items:
+ type: string
+ example:
+ - timestamp
+ timelineFunction:
+ description: Label function to modify timeline labels to a produce a value
+ used for ordering datapoints
+ type: string
+ example: timestamp => timestamp
+ fingerprintLabels:
+ description: 'Array of Label names that are used to create a fingerprint '
+ type: array
+ items:
+ type: string
+ example:
+ - build_tag
+ fingerprintFilter:
+ description: Filter function to filter out datasets that are comparable
+ for the purpose of change detection
+ type: string
+ example: value => value === "true"
+ compareUrl:
+ description: URL to external service that can be called to compare runs. This
+ is typically an external reporting/visulization service
+ type: string
+ example: "(ids, token) => 'http://repoting.example.com/report/specj?q='\
+ \ + ids.join('&q=') + \"&token=\"+token"
+ transformers:
+ description: Array for transformers defined for the Test
+ type: array
+ items:
+ $ref: '#/components/schemas/Transformer'
+ notificationsEnabled:
+ description: Are notifications enabled for the test
+ type: boolean
+ example: true
+ TestExport:
+ description: Represents a Test with all associated data used for export/import
+ operations.
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/Test'
+ properties:
+ variables:
+ description: Array of Variables associated with test
+ type: array
+ items:
+ $ref: '#/components/schemas/Variable'
+ missingDataRules:
+ description: Array of MissingDataRules associated with test
+ type: array
+ items:
+ $ref: '#/components/schemas/MissingDataRule'
+ experiments:
+ description: Array of ExperimentProfiles associated with test
+ type: array
+ items:
+ $ref: '#/components/schemas/ExperimentProfile'
+ actions:
+ description: Array of Actions associated with test
+ type: array
+ items:
+ $ref: '#/components/schemas/Action'
+ subscriptions:
+ description: Watcher object associated with test
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/Watch'
+ datastore:
+ description: Datastore associated with test
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/Datastore'
+ TestListing:
+ required:
+ - tests
+ - count
+ type: object
+ properties:
+ tests:
+ description: Array of Test Summaries
+ type: array
+ items:
+ $ref: '#/components/schemas/TestSummary'
+ count:
+ format: int64
+ description: Number of tests when pagination is ignored
+ type: integer
+ TestQueryResult:
+ required:
+ - tests
+ - count
+ type: object
+ properties:
+ tests:
+ description: Array of Tests
+ type: array
+ items:
+ $ref: '#/components/schemas/Test'
+ count:
+ format: int64
+ description: Count of available tests. This is a count of tests that the
+ current user has access to
+ type: integer
+ example: 64
+ TestSummary:
+ required:
+ - id
+ - name
+ - datastoreId
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedType'
+ properties:
+ id:
+ format: int32
+ description: ID of tests
+ type: integer
+ example: 101
+ name:
+ description: Test name
+ type: string
+ example: my-comprehensive-benchmark
+ folder:
+ description: Name of folder that the test is stored in. Folders allow tests
+ to be organised in the UI
+ type: string
+ example: My Team Folder
+ description:
+ description: Description of the test
+ type: string
+ example: Comprehensive benchmark to tests the limits of any system it is
+ run against
+ datasets:
+ description: Total number of Datasets for the Test
+ type: number
+ example: 202
+ runs:
+ description: Total number of Runs for the Test
+ type: number
+ example: 101
+ watching:
+ description: Subscriptions for each test for authenticated user
+ uniqueItems: true
+ type: array
+ items:
+ type: string
+ example: []
+ datastoreId:
+ format: int32
+ description: Datastore id
+ type: integer
+ example: 1
+ TestToken:
+ required:
+ - id
+ - value
+ - permissions
+ - description
+ type: object
+ properties:
+ id:
+ format: int32
+ description: Unique Token id
+ type: integer
+ example: 101
+ testId:
+ format: int32
+ description: Test ID to apply Token
+ type: integer
+ example: 201
+ value:
+ description: Test value
+ type: string
+ example: 094678029a2aaf9a2847502273099bb3a1b2338c2b9c618ed09aef0181666e38
+ permissions:
+ format: int32
+ type: integer
+ description:
+ description: Token description
+ type: string
+ example: my reporting service token
+ TransformationLog:
+ description: Transformation Log
+ required:
+ - id
+ - level
+ - timestamp
+ - message
+ type: object
+ properties:
+ id:
+ format: int64
+ type: integer
+ level:
+ format: int32
+ type: integer
+ timestamp:
+ format: date-time
+ type: string
+ example: 2022-03-10T16:15:50Z
+ message:
+ type: string
+ testId:
+ format: int32
+ type: integer
+ runId:
+ format: int32
+ type: integer
+ Transformer:
+ description: A transformer extracts labals and applies a Function to convert
+ a Run into one or more Datasets
+ required:
+ - id
+ - name
+ - extractors
+ - schemaId
+ - schemaUri
+ - schemaName
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ProtectedType'
+ properties:
+ id:
+ format: int32
+ description: Unique Transformer id
+ type: integer
+ example: 101
+ name:
+ description: Transformer name
+ type: string
+ example: normalize-techempower-result
+ description:
+ description: Transformer description
+ type: string
+ example: Normalizers a techempower output file to separate each framework
+ into a dataset and normalize the JSON structure
+ targetSchemaUri:
+ description: "The schema associated with the calculated Datasets. Where\
+ \ a transformer creates a new JSON object with a new structure, this Schema\
+ \ is used to extafct values from the new Dataset JSON document"
+ type: string
+ example: uri:normalized-techempower:0.1
+ extractors:
+ description: A collection of extractors to extract JSON values to create
+ new Dataset JSON document
+ type: array
+ items:
+ $ref: '#/components/schemas/Extractor'
+ function:
+ type: string
+ schemaId:
+ format: int32
+ description: Schema ID that the transform is registered against
+ type: integer
+ example: 101
+ schemaUri:
+ description: Schema Uri that the transform is registered against
+ type: string
+ example: urn:techempower:0.1
+ schemaName:
+ description: Schema name that the transform is registered against
+ type: string
+ example: techempower
+ TransformerInfo:
+ required:
+ - schemaId
+ - schemaUri
+ - schemaName
+ - transformerId
+ - transformerName
+ type: object
+ properties:
+ schemaId:
+ format: int32
+ description: Schema ID
+ type: integer
+ example: 101
+ schemaUri:
+ description: Schema uri
+ type: string
+ example: uri:my-schema:0.1
+ schemaName:
+ description: Schema name
+ type: string
+ example: my-benchmark-schema
+ transformerId:
+ format: int32
+ description: Transformer ID
+ type: integer
+ example: 201
+ transformerName:
+ description: Transformer name
+ type: string
+ example: my-dataset-transformer
+ ValidationError:
+ required:
+ - schemaId
+ - error
+ type: object
+ properties:
+ schemaId:
+ format: int32
+ description: Schema ID that Validation Error relates to
+ type: integer
+ example: 101
+ error:
+ description: Validation Error Details
+ type: object
+ allOf:
+ - $ref: '#/components/schemas/ErrorDetails'
+ Variable:
+ required:
+ - id
+ - testId
+ - name
+ - order
+ - labels
+ - changeDetection
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ testId:
+ format: int32
+ type: integer
+ name:
+ type: string
+ group:
+ type: string
+ order:
+ format: int32
+ type: integer
+ labels:
+ type: array
+ items:
+ type: string
+ calculation:
+ type: string
+ changeDetection:
+ type: array
+ items:
+ $ref: '#/components/schemas/ChangeDetection'
+ VersionInfo:
+ required:
+ - version
+ - startTimestamp
+ type: object
+ properties:
+ version:
+ description: Version of Horreum
+ type: string
+ example: 0.9.4
+ startTimestamp:
+ format: int64
+ description: Timestamp of server startup
+ type: integer
+ example: 2023-10-18 18:00:57
+ privacyStatement:
+ description: Privacy statement
+ type: string
+ example: link/to/external/privacy/statement
+ Watch:
+ required:
+ - users
+ - optout
+ - teams
+ - testId
+ type: object
+ properties:
+ id:
+ format: int32
+ type: integer
+ users:
+ type: array
+ items:
+ type: string
+ optout:
+ type: array
+ items:
+ type: string
+ teams:
+ type: array
+ items:
+ type: string
+ testId:
+ format: int32
+ type: integer
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..0bb9249
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,1018 @@
+# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+
+[[package]]
+name = "anyio"
+version = "4.3.0"
+description = "High level compatibility layer for multiple asynchronous event loop implementations"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"},
+ {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"},
+]
+
+[package.dependencies]
+exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
+idna = ">=2.8"
+sniffio = ">=1.1"
+typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
+test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
+trio = ["trio (>=0.23)"]
+
+[[package]]
+name = "certifi"
+version = "2024.2.2"
+description = "Python package for providing Mozilla's CA Bundle."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
+ {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
+]
+
+[[package]]
+name = "cffi"
+version = "1.16.0"
+description = "Foreign Function Interface for Python calling C code."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
+ {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
+ {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
+ {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
+ {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
+ {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
+ {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
+ {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
+ {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
+ {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
+ {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
+ {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
+ {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
+ {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
+ {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
+ {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
+ {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
+ {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
+ {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
+ {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
+ {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
+ {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
+ {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
+ {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
+ {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
+ {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
+]
+
+[package.dependencies]
+pycparser = "*"
+
+[[package]]
+name = "charset-normalizer"
+version = "3.3.2"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+optional = false
+python-versions = ">=3.7.0"
+files = [
+ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
+ {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
+ {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
+ {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
+ {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
+ {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
+ {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
+ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
+]
+
+[[package]]
+name = "colorama"
+version = "0.4.6"
+description = "Cross-platform colored terminal text."
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
+files = [
+ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
+ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
+]
+
+[[package]]
+name = "cryptography"
+version = "42.0.5"
+description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16"},
+ {file = "cryptography-42.0.5-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278"},
+ {file = "cryptography-42.0.5-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d"},
+ {file = "cryptography-42.0.5-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da"},
+ {file = "cryptography-42.0.5-cp37-abi3-win32.whl", hash = "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74"},
+ {file = "cryptography-42.0.5-cp37-abi3-win_amd64.whl", hash = "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940"},
+ {file = "cryptography-42.0.5-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc"},
+ {file = "cryptography-42.0.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc"},
+ {file = "cryptography-42.0.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30"},
+ {file = "cryptography-42.0.5-cp39-abi3-win32.whl", hash = "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413"},
+ {file = "cryptography-42.0.5-cp39-abi3-win_amd64.whl", hash = "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c"},
+ {file = "cryptography-42.0.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac"},
+ {file = "cryptography-42.0.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd"},
+ {file = "cryptography-42.0.5.tar.gz", hash = "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1"},
+]
+
+[package.dependencies]
+cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
+
+[package.extras]
+docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
+docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
+nox = ["nox"]
+pep8test = ["check-sdist", "click", "mypy", "ruff"]
+sdist = ["build"]
+ssh = ["bcrypt (>=3.1.5)"]
+test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
+test-randomorder = ["pytest-randomly"]
+
+[[package]]
+name = "deprecated"
+version = "1.2.14"
+description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
+ {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
+]
+
+[package.dependencies]
+wrapt = ">=1.10,<2"
+
+[package.extras]
+dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
+
+[[package]]
+name = "deprecation"
+version = "2.1.0"
+description = "A library to handle automated deprecations"
+optional = false
+python-versions = "*"
+files = [
+ {file = "deprecation-2.1.0-py2.py3-none-any.whl", hash = "sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a"},
+ {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"},
+]
+
+[package.dependencies]
+packaging = "*"
+
+[[package]]
+name = "exceptiongroup"
+version = "1.2.0"
+description = "Backport of PEP 654 (exception groups)"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
+ {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
+]
+
+[package.extras]
+test = ["pytest (>=6)"]
+
+[[package]]
+name = "h11"
+version = "0.14.0"
+description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
+ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
+]
+
+[[package]]
+name = "h2"
+version = "4.1.0"
+description = "HTTP/2 State-Machine based protocol implementation"
+optional = false
+python-versions = ">=3.6.1"
+files = [
+ {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"},
+ {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"},
+]
+
+[package.dependencies]
+hpack = ">=4.0,<5"
+hyperframe = ">=6.0,<7"
+
+[[package]]
+name = "hpack"
+version = "4.0.0"
+description = "Pure-Python HPACK header compression"
+optional = false
+python-versions = ">=3.6.1"
+files = [
+ {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"},
+ {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"},
+]
+
+[[package]]
+name = "httpcore"
+version = "1.0.4"
+description = "A minimal low-level HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"},
+ {file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"},
+]
+
+[package.dependencies]
+certifi = "*"
+h11 = ">=0.13,<0.15"
+
+[package.extras]
+asyncio = ["anyio (>=4.0,<5.0)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+trio = ["trio (>=0.22.0,<0.25.0)"]
+
+[[package]]
+name = "httpx"
+version = "0.27.0"
+description = "The next generation HTTP client."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"},
+ {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"},
+]
+
+[package.dependencies]
+anyio = "*"
+certifi = "*"
+h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""}
+httpcore = "==1.*"
+idna = "*"
+sniffio = "*"
+
+[package.extras]
+brotli = ["brotli", "brotlicffi"]
+cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
+http2 = ["h2 (>=3,<5)"]
+socks = ["socksio (==1.*)"]
+
+[[package]]
+name = "hyperframe"
+version = "6.0.1"
+description = "HTTP/2 framing layer for Python"
+optional = false
+python-versions = ">=3.6.1"
+files = [
+ {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"},
+ {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"},
+]
+
+[[package]]
+name = "idna"
+version = "3.6"
+description = "Internationalized Domain Names in Applications (IDNA)"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"},
+ {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"},
+]
+
+[[package]]
+name = "importlib-metadata"
+version = "6.11.0"
+description = "Read metadata from Python packages"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "importlib_metadata-6.11.0-py3-none-any.whl", hash = "sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b"},
+ {file = "importlib_metadata-6.11.0.tar.gz", hash = "sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443"},
+]
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
+perf = ["ipython"]
+testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
+
+[[package]]
+name = "iniconfig"
+version = "2.0.0"
+description = "brain-dead simple config-ini parsing"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
+ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
+]
+
+[[package]]
+name = "jwcrypto"
+version = "1.5.6"
+description = "Implementation of JOSE Web standards"
+optional = false
+python-versions = ">= 3.8"
+files = [
+ {file = "jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789"},
+ {file = "jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039"},
+]
+
+[package.dependencies]
+cryptography = ">=3.4"
+typing-extensions = ">=4.5.0"
+
+[[package]]
+name = "microsoft-kiota-abstractions"
+version = "1.3.1"
+description = "Core abstractions for kiota generated libraries in Python"
+optional = false
+python-versions = "*"
+files = [
+ {file = "microsoft_kiota_abstractions-1.3.1-py2.py3-none-any.whl", hash = "sha256:76930f9fe44e90afb849ecfae8a08acb2effe61a2975441022e0f5229383d73c"},
+ {file = "microsoft_kiota_abstractions-1.3.1.tar.gz", hash = "sha256:9e082212edf9e5a533eff4139cf1f7a3afc4a74938b95fa486c10ee9f7654d58"},
+]
+
+[package.dependencies]
+opentelemetry-api = ">=1.19.0"
+opentelemetry-sdk = ">=1.19.0"
+std-uritemplate = ">=0.0.38"
+
+[[package]]
+name = "microsoft-kiota-http"
+version = "1.3.1"
+description = "Kiota http request adapter implementation for httpx library"
+optional = false
+python-versions = "*"
+files = [
+ {file = "microsoft_kiota_http-1.3.1-py2.py3-none-any.whl", hash = "sha256:d62972c6ed4c785f9808a15479a7421abb38a9519b39e6933e5d05555b9fb427"},
+ {file = "microsoft_kiota_http-1.3.1.tar.gz", hash = "sha256:09d85310379f88af0a0967925d1fcbe82f2520a9fe6fa1fd50e79af813bc451d"},
+]
+
+[package.dependencies]
+httpx = {version = ">=0.23.0", extras = ["http2"]}
+microsoft-kiota_abstractions = ">=1.0.0,<2.0.0"
+opentelemetry-api = ">=1.20.0"
+opentelemetry-sdk = ">=1.20.0"
+
+[[package]]
+name = "microsoft-kiota-serialization-form"
+version = "0.1.0"
+description = "Implementation of Kiota Serialization Interfaces for URI-Form encoded serialization"
+optional = false
+python-versions = "*"
+files = [
+ {file = "microsoft_kiota_serialization_form-0.1.0-py2.py3-none-any.whl", hash = "sha256:5bc76fb2fc67d7c1f878f876d252ea814e4fc38df505099b9b86de52d974380a"},
+ {file = "microsoft_kiota_serialization_form-0.1.0.tar.gz", hash = "sha256:663ece0cb1a41fe9ddfc9195aa3f15f219e14d2a1ee51e98c53ad8d795b2785d"},
+]
+
+[package.dependencies]
+microsoft-kiota_abstractions = ">=1.0.0,<2.0.0"
+pendulum = ">=3.0.0"
+
+[[package]]
+name = "microsoft-kiota-serialization-json"
+version = "1.1.0"
+description = "Implementation of Kiota Serialization interfaces for JSON"
+optional = false
+python-versions = "*"
+files = [
+ {file = "microsoft_kiota_serialization_json-1.1.0-py2.py3-none-any.whl", hash = "sha256:a32c73dd92ef2677dbf1deca2fb44b5111c0ef572b15c47205bff521800cd750"},
+ {file = "microsoft_kiota_serialization_json-1.1.0.tar.gz", hash = "sha256:72bf65d81c0356ad87c229694733f4eb558628a13c5ee8dd274980c4b2d9b64b"},
+]
+
+[package.dependencies]
+microsoft-kiota_abstractions = ">=1.0.0,<2.0.0"
+pendulum = ">=3.0.0b1"
+
+[[package]]
+name = "microsoft-kiota-serialization-multipart"
+version = "0.1.0"
+description = "Implementation of Kiota Serialization Interfaces for Multipart serialization"
+optional = false
+python-versions = "*"
+files = [
+ {file = "microsoft_kiota_serialization_multipart-0.1.0-py2.py3-none-any.whl", hash = "sha256:ef183902e77807806b8a181cdde53ba5bc04c6c9bdb2f7d80f8bad5d720e0015"},
+ {file = "microsoft_kiota_serialization_multipart-0.1.0.tar.gz", hash = "sha256:14e89e92582e6630ddbc70ac67b70bf189dacbfc41a96d3e1d10339e86c8dde5"},
+]
+
+[package.dependencies]
+microsoft-kiota_abstractions = ">=1.0.0,<2.0.0"
+
+[[package]]
+name = "microsoft-kiota-serialization-text"
+version = "1.0.0"
+description = "Implementation of Kiota Serialization interfaces for text/plain"
+optional = false
+python-versions = "*"
+files = [
+ {file = "microsoft_kiota_serialization_text-1.0.0-py2.py3-none-any.whl", hash = "sha256:1d3789e012b603e059a36cc675d1fd08cb81e0dde423d970c0af2eabce9c0d43"},
+ {file = "microsoft_kiota_serialization_text-1.0.0.tar.gz", hash = "sha256:c3dd3f409b1c4f4963bd1e41d51b65f7e53e852130bb441d79b77dad88ee76ed"},
+]
+
+[package.dependencies]
+microsoft-kiota_abstractions = ">=1.0.0,<2.0.0"
+python-dateutil = ">=2.8.2"
+
+[[package]]
+name = "opentelemetry-api"
+version = "1.23.0"
+description = "OpenTelemetry Python API"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "opentelemetry_api-1.23.0-py3-none-any.whl", hash = "sha256:cc03ea4025353048aadb9c64919099663664672ea1c6be6ddd8fee8e4cd5e774"},
+ {file = "opentelemetry_api-1.23.0.tar.gz", hash = "sha256:14a766548c8dd2eb4dfc349739eb4c3893712a0daa996e5dbf945f9da665da9d"},
+]
+
+[package.dependencies]
+deprecated = ">=1.2.6"
+importlib-metadata = ">=6.0,<7.0"
+
+[[package]]
+name = "opentelemetry-sdk"
+version = "1.23.0"
+description = "OpenTelemetry Python SDK"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "opentelemetry_sdk-1.23.0-py3-none-any.whl", hash = "sha256:a93c96990ac0f07c6d679e2f1015864ff7a4f5587122dd5af968034436efb1fd"},
+ {file = "opentelemetry_sdk-1.23.0.tar.gz", hash = "sha256:9ddf60195837b59e72fd2033d6a47e2b59a0f74f0ec37d89387d89e3da8cab7f"},
+]
+
+[package.dependencies]
+opentelemetry-api = "1.23.0"
+opentelemetry-semantic-conventions = "0.44b0"
+typing-extensions = ">=3.7.4"
+
+[[package]]
+name = "opentelemetry-semantic-conventions"
+version = "0.44b0"
+description = "OpenTelemetry Semantic Conventions"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "opentelemetry_semantic_conventions-0.44b0-py3-none-any.whl", hash = "sha256:7c434546c9cbd797ab980cc88bf9ff3f4a5a28f941117cad21694e43d5d92019"},
+ {file = "opentelemetry_semantic_conventions-0.44b0.tar.gz", hash = "sha256:2e997cb28cd4ca81a25a9a43365f593d0c2b76be0685015349a89abdf1aa4ffa"},
+]
+
+[[package]]
+name = "packaging"
+version = "24.0"
+description = "Core utilities for Python packages"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
+ {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+]
+
+[[package]]
+name = "pendulum"
+version = "3.0.0"
+description = "Python datetimes made easy"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pendulum-3.0.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2cf9e53ef11668e07f73190c805dbdf07a1939c3298b78d5a9203a86775d1bfd"},
+ {file = "pendulum-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fb551b9b5e6059377889d2d878d940fd0bbb80ae4810543db18e6f77b02c5ef6"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c58227ac260d5b01fc1025176d7b31858c9f62595737f350d22124a9a3ad82d"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60fb6f415fea93a11c52578eaa10594568a6716602be8430b167eb0d730f3332"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b69f6b4dbcb86f2c2fe696ba991e67347bcf87fe601362a1aba6431454b46bde"},
+ {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:138afa9c373ee450ede206db5a5e9004fd3011b3c6bbe1e57015395cd076a09f"},
+ {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:83d9031f39c6da9677164241fd0d37fbfc9dc8ade7043b5d6d62f56e81af8ad2"},
+ {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c2308af4033fa534f089595bcd40a95a39988ce4059ccd3dc6acb9ef14ca44a"},
+ {file = "pendulum-3.0.0-cp310-none-win_amd64.whl", hash = "sha256:9a59637cdb8462bdf2dbcb9d389518c0263799189d773ad5c11db6b13064fa79"},
+ {file = "pendulum-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3725245c0352c95d6ca297193192020d1b0c0f83d5ee6bb09964edc2b5a2d508"},
+ {file = "pendulum-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6c035f03a3e565ed132927e2c1b691de0dbf4eb53b02a5a3c5a97e1a64e17bec"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597e66e63cbd68dd6d58ac46cb7a92363d2088d37ccde2dae4332ef23e95cd00"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99a0f8172e19f3f0c0e4ace0ad1595134d5243cf75985dc2233e8f9e8de263ca"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77d8839e20f54706aed425bec82a83b4aec74db07f26acd039905d1237a5e1d4"},
+ {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afde30e8146292b059020fbc8b6f8fd4a60ae7c5e6f0afef937bbb24880bdf01"},
+ {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:660434a6fcf6303c4efd36713ca9212c753140107ee169a3fc6c49c4711c2a05"},
+ {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dee9e5a48c6999dc1106eb7eea3e3a50e98a50651b72c08a87ee2154e544b33e"},
+ {file = "pendulum-3.0.0-cp311-none-win_amd64.whl", hash = "sha256:d4cdecde90aec2d67cebe4042fd2a87a4441cc02152ed7ed8fb3ebb110b94ec4"},
+ {file = "pendulum-3.0.0-cp311-none-win_arm64.whl", hash = "sha256:773c3bc4ddda2dda9f1b9d51fe06762f9200f3293d75c4660c19b2614b991d83"},
+ {file = "pendulum-3.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:409e64e41418c49f973d43a28afe5df1df4f1dd87c41c7c90f1a63f61ae0f1f7"},
+ {file = "pendulum-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a38ad2121c5ec7c4c190c7334e789c3b4624798859156b138fcc4d92295835dc"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fde4d0b2024b9785f66b7f30ed59281bd60d63d9213cda0eb0910ead777f6d37"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2c5675769fb6d4c11238132962939b960fcb365436b6d623c5864287faa319"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8af95e03e066826f0f4c65811cbee1b3123d4a45a1c3a2b4fc23c4b0dff893b5"},
+ {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2165a8f33cb15e06c67070b8afc87a62b85c5a273e3aaa6bc9d15c93a4920d6f"},
+ {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ad5e65b874b5e56bd942546ea7ba9dd1d6a25121db1c517700f1c9de91b28518"},
+ {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17fe4b2c844bbf5f0ece69cfd959fa02957c61317b2161763950d88fed8e13b9"},
+ {file = "pendulum-3.0.0-cp312-none-win_amd64.whl", hash = "sha256:78f8f4e7efe5066aca24a7a57511b9c2119f5c2b5eb81c46ff9222ce11e0a7a5"},
+ {file = "pendulum-3.0.0-cp312-none-win_arm64.whl", hash = "sha256:28f49d8d1e32aae9c284a90b6bb3873eee15ec6e1d9042edd611b22a94ac462f"},
+ {file = "pendulum-3.0.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d4e2512f4e1a4670284a153b214db9719eb5d14ac55ada5b76cbdb8c5c00399d"},
+ {file = "pendulum-3.0.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:3d897eb50883cc58d9b92f6405245f84b9286cd2de6e8694cb9ea5cb15195a32"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e169cc2ca419517f397811bbe4589cf3cd13fca6dc38bb352ba15ea90739ebb"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17c3084a4524ebefd9255513692f7e7360e23c8853dc6f10c64cc184e1217ab"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:826d6e258052715f64d05ae0fc9040c0151e6a87aae7c109ba9a0ed930ce4000"},
+ {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2aae97087872ef152a0c40e06100b3665d8cb86b59bc8471ca7c26132fccd0f"},
+ {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ac65eeec2250d03106b5e81284ad47f0d417ca299a45e89ccc69e36130ca8bc7"},
+ {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5346d08f3f4a6e9e672187faa179c7bf9227897081d7121866358af369f44f9"},
+ {file = "pendulum-3.0.0-cp37-none-win_amd64.whl", hash = "sha256:235d64e87946d8f95c796af34818c76e0f88c94d624c268693c85b723b698aa9"},
+ {file = "pendulum-3.0.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a881d9c2a7f85bc9adafcfe671df5207f51f5715ae61f5d838b77a1356e8b7b"},
+ {file = "pendulum-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7762d2076b9b1cb718a6631ad6c16c23fc3fac76cbb8c454e81e80be98daa34"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8e36a8130819d97a479a0e7bf379b66b3b1b520e5dc46bd7eb14634338df8c"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dc843253ac373358ffc0711960e2dd5b94ab67530a3e204d85c6e8cb2c5fa10"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a78ad3635d609ceb1e97d6aedef6a6a6f93433ddb2312888e668365908c7120"},
+ {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a137e9e0d1f751e60e67d11fc67781a572db76b2296f7b4d44554761049d6"},
+ {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c95984037987f4a457bb760455d9ca80467be792236b69d0084f228a8ada0162"},
+ {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d29c6e578fe0f893766c0d286adbf0b3c726a4e2341eba0917ec79c50274ec16"},
+ {file = "pendulum-3.0.0-cp38-none-win_amd64.whl", hash = "sha256:deaba8e16dbfcb3d7a6b5fabdd5a38b7c982809567479987b9c89572df62e027"},
+ {file = "pendulum-3.0.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b11aceea5b20b4b5382962b321dbc354af0defe35daa84e9ff3aae3c230df694"},
+ {file = "pendulum-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a90d4d504e82ad236afac9adca4d6a19e4865f717034fc69bafb112c320dcc8f"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:825799c6b66e3734227756fa746cc34b3549c48693325b8b9f823cb7d21b19ac"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad769e98dc07972e24afe0cff8d365cb6f0ebc7e65620aa1976fcfbcadc4c6f3"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6fc26907eb5fb8cc6188cc620bc2075a6c534d981a2f045daa5f79dfe50d512"},
+ {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c717eab1b6d898c00a3e0fa7781d615b5c5136bbd40abe82be100bb06df7a56"},
+ {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3ddd1d66d1a714ce43acfe337190be055cdc221d911fc886d5a3aae28e14b76d"},
+ {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:822172853d7a9cf6da95d7b66a16c7160cb99ae6df55d44373888181d7a06edc"},
+ {file = "pendulum-3.0.0-cp39-none-win_amd64.whl", hash = "sha256:840de1b49cf1ec54c225a2a6f4f0784d50bd47f68e41dc005b7f67c7d5b5f3ae"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b1f74d1e6ffe5d01d6023870e2ce5c2191486928823196f8575dcc786e107b1"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:729e9f93756a2cdfa77d0fc82068346e9731c7e884097160603872686e570f07"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e586acc0b450cd21cbf0db6bae386237011b75260a3adceddc4be15334689a9a"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22e7944ffc1f0099a79ff468ee9630c73f8c7835cd76fdb57ef7320e6a409df4"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa30af36bd8e50686846bdace37cf6707bdd044e5cb6e1109acbad3277232e04"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:440215347b11914ae707981b9a57ab9c7b6983ab0babde07063c6ee75c0dc6e7"},
+ {file = "pendulum-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:314c4038dc5e6a52991570f50edb2f08c339debdf8cea68ac355b32c4174e820"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5acb1d386337415f74f4d1955c4ce8d0201978c162927d07df8eb0692b2d8533"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a789e12fbdefaffb7b8ac67f9d8f22ba17a3050ceaaa635cd1cc4645773a4b1e"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:860aa9b8a888e5913bd70d819306749e5eb488e6b99cd6c47beb701b22bdecf5"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5ebc65ea033ef0281368217fbf59f5cb05b338ac4dd23d60959c7afcd79a60a0"},
+ {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9fef18ab0386ef6a9ac7bad7e43ded42c83ff7ad412f950633854f90d59afa8"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c134ba2f0571d0b68b83f6972e2307a55a5a849e7dac8505c715c531d2a8795"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:385680812e7e18af200bb9b4a49777418c32422d05ad5a8eb85144c4a285907b"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eec91cd87c59fb32ec49eb722f375bd58f4be790cae11c1b70fac3ee4f00da0"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4386bffeca23c4b69ad50a36211f75b35a4deb6210bdca112ac3043deb7e494a"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dfbcf1661d7146d7698da4b86e7f04814221081e9fe154183e34f4c5f5fa3bf8"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:04a1094a5aa1daa34a6b57c865b25f691848c61583fb22722a4df5699f6bf74c"},
+ {file = "pendulum-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5b0ec85b9045bd49dd3a3493a5e7ddfd31c36a2a60da387c419fa04abcaecb23"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0a15b90129765b705eb2039062a6daf4d22c4e28d1a54fa260892e8c3ae6e157"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:bb8f6d7acd67a67d6fedd361ad2958ff0539445ef51cbe8cd288db4306503cd0"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd69b15374bef7e4b4440612915315cc42e8575fcda2a3d7586a0d88192d0c88"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc00f8110db6898360c53c812872662e077eaf9c75515d53ecc65d886eec209a"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83a44e8b40655d0ba565a5c3d1365d27e3e6778ae2a05b69124db9e471255c4a"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1a3604e9fbc06b788041b2a8b78f75c243021e0f512447806a6d37ee5214905d"},
+ {file = "pendulum-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:92c307ae7accebd06cbae4729f0ba9fa724df5f7d91a0964b1b972a22baa482b"},
+ {file = "pendulum-3.0.0.tar.gz", hash = "sha256:5d034998dea404ec31fae27af6b22cff1708f830a1ed7353be4d1019bb9f584e"},
+]
+
+[package.dependencies]
+python-dateutil = ">=2.6"
+tzdata = ">=2020.1"
+
+[package.extras]
+test = ["time-machine (>=2.6.0)"]
+
+[[package]]
+name = "pluggy"
+version = "1.4.0"
+description = "plugin and hook calling mechanisms for python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
+ {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
+]
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+testing = ["pytest", "pytest-benchmark"]
+
+[[package]]
+name = "pycparser"
+version = "2.21"
+description = "C parser in Python"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
+ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
+]
+
+[[package]]
+name = "pytest"
+version = "8.1.1"
+description = "pytest: simple powerful testing with Python"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"},
+ {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "sys_platform == \"win32\""}
+exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
+iniconfig = "*"
+packaging = "*"
+pluggy = ">=1.4,<2.0"
+tomli = {version = ">=1", markers = "python_version < \"3.11\""}
+
+[package.extras]
+testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
+
+[[package]]
+name = "pytest-asyncio"
+version = "0.23.6"
+description = "Pytest support for asyncio"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "pytest-asyncio-0.23.6.tar.gz", hash = "sha256:ffe523a89c1c222598c76856e76852b787504ddb72dd5d9b6617ffa8aa2cde5f"},
+ {file = "pytest_asyncio-0.23.6-py3-none-any.whl", hash = "sha256:68516fdd1018ac57b846c9846b954f0393b26f094764a28c955eabb0536a4e8a"},
+]
+
+[package.dependencies]
+pytest = ">=7.0.0,<9"
+
+[package.extras]
+docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
+testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
+
+[[package]]
+name = "python-dateutil"
+version = "2.9.0.post0"
+description = "Extensions to the standard Python datetime module"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
+files = [
+ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
+ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
+]
+
+[package.dependencies]
+six = ">=1.5"
+
+[[package]]
+name = "python-keycloak"
+version = "3.9.1"
+description = "python-keycloak is a Python package providing access to the Keycloak API."
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+ {file = "python_keycloak-3.9.1-py3-none-any.whl", hash = "sha256:898d1fc73560171d3870251f981e069f854cc67bc0a51b96703355512d8d3cf3"},
+ {file = "python_keycloak-3.9.1.tar.gz", hash = "sha256:50c8073172ca0630f3569c6b631134216b60f4e347cc5bb669a57e6ffba50f7e"},
+]
+
+[package.dependencies]
+deprecation = ">=2.1.0"
+jwcrypto = ">=1.5.4,<2.0.0"
+requests = ">=2.20.0"
+requests-toolbelt = ">=0.6.0"
+
+[package.extras]
+docs = ["Sphinx (>=6.1.0,<7.0.0)", "alabaster (>=0.7.12,<0.8.0)", "commonmark (>=0.9.1,<0.10.0)", "m2r2 (>=0.3.2,<0.4.0)", "mock (>=4.0.3,<5.0.0)", "readthedocs-sphinx-ext (>=2.1.9,<3.0.0)", "recommonmark (>=0.7.1,<0.8.0)", "sphinx-autoapi (>=3.0.0,<4.0.0)", "sphinx-rtd-theme (>=1.0.0,<2.0.0)"]
+
+[[package]]
+name = "requests"
+version = "2.31.0"
+description = "Python HTTP for Humans."
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
+ {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
+]
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+charset-normalizer = ">=2,<4"
+idna = ">=2.5,<4"
+urllib3 = ">=1.21.1,<3"
+
+[package.extras]
+socks = ["PySocks (>=1.5.6,!=1.5.7)"]
+use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
+
+[[package]]
+name = "requests-toolbelt"
+version = "1.0.0"
+description = "A utility belt for advanced users of python-requests"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+files = [
+ {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"},
+ {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"},
+]
+
+[package.dependencies]
+requests = ">=2.0.1,<3.0.0"
+
+[[package]]
+name = "six"
+version = "1.16.0"
+description = "Python 2 and 3 compatibility utilities"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+files = [
+ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
+]
+
+[[package]]
+name = "sniffio"
+version = "1.3.1"
+description = "Sniff out which async library your code is running under"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
+ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
+]
+
+[[package]]
+name = "std-uritemplate"
+version = "0.0.55"
+description = "std-uritemplate implementation for Python"
+optional = false
+python-versions = ">=3.8,<4.0"
+files = [
+ {file = "std_uritemplate-0.0.55-py3-none-any.whl", hash = "sha256:4c5e3c068db007697c11e6047d16c9b64f07e8259ffa4dd4d9248ed8491ad430"},
+ {file = "std_uritemplate-0.0.55.tar.gz", hash = "sha256:9073f56a77e44d0583fb6645c37e4a640a34f22a255d00e3793cd3f30da58a68"},
+]
+
+[[package]]
+name = "tomli"
+version = "2.0.1"
+description = "A lil' TOML parser"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
+ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
+]
+
+[[package]]
+name = "typing-extensions"
+version = "4.10.0"
+description = "Backported and Experimental Type Hints for Python 3.8+"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
+ {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
+]
+
+[[package]]
+name = "tzdata"
+version = "2024.1"
+description = "Provider of IANA time zone data"
+optional = false
+python-versions = ">=2"
+files = [
+ {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
+ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
+]
+
+[[package]]
+name = "urllib3"
+version = "2.2.1"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
+ {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
+]
+
+[package.extras]
+brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
+h2 = ["h2 (>=4,<5)"]
+socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
+zstd = ["zstandard (>=0.18.0)"]
+
+[[package]]
+name = "wrapt"
+version = "1.16.0"
+description = "Module for decorators, wrappers and monkey patching."
+optional = false
+python-versions = ">=3.6"
+files = [
+ {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
+ {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
+ {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
+ {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
+ {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
+ {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
+ {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
+ {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
+ {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
+ {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
+ {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
+ {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
+ {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
+ {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
+ {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
+ {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
+ {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
+ {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
+ {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
+ {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
+ {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
+ {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
+ {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
+ {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
+ {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
+ {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
+ {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
+ {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
+ {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
+ {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
+ {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
+ {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
+ {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
+ {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
+ {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
+ {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
+ {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
+ {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
+ {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
+ {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
+ {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
+ {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
+ {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
+ {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
+ {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
+ {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
+ {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
+ {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
+ {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
+ {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
+ {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
+ {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
+ {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
+ {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
+ {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
+ {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
+ {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
+ {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
+ {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
+ {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
+ {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
+ {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
+ {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
+ {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
+ {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
+ {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
+ {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
+ {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
+ {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
+ {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
+]
+
+[[package]]
+name = "zipp"
+version = "3.18.1"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "zipp-3.18.1-py3-none-any.whl", hash = "sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b"},
+ {file = "zipp-3.18.1.tar.gz", hash = "sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715"},
+]
+
+[package.extras]
+docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
+testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
+
+[metadata]
+lock-version = "2.0"
+python-versions = "^3.10"
+content-hash = "0f259faa8559a5a87db0e93a4368724eef579cd8a321eee8ce8c90afd962d2f3"
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..69ecdcb
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,32 @@
+[tool.poetry]
+name = "horreum"
+version = "0.13-dev"
+description = "Horreum python library"
+authors = ["Andrea Lamparelli "]
+license = "Apache 2.0"
+readme = "README.md"
+include = [
+ "src/horreum/raw_client/**/*", # all files in "src/horreum/raw_client" and any subfolders
+]
+
+[tool.poetry.dependencies]
+python = "^3.10"
+microsoft-kiota-abstractions = "^1.3.1"
+microsoft-kiota-http = "^1.3.1"
+microsoft-kiota-serialization-json = "^1.1.0"
+microsoft-kiota-serialization-text = "^1.0.0"
+python-keycloak = "^3.9.1"
+microsoft-kiota-serialization-form = "^0.1.0"
+microsoft-kiota-serialization-multipart = "^0.1.0"
+
+
+[tool.poetry.group.dev.dependencies]
+pytest = "^8.1.1"
+pytest-asyncio = "^0.23.6"
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
+
+[tool.pytest.ini_options]
+asyncio_mode = "auto"
diff --git a/src/horreum/__init__.py b/src/horreum/__init__.py
new file mode 100644
index 0000000..f296a0e
--- /dev/null
+++ b/src/horreum/__init__.py
@@ -0,0 +1,5 @@
+from horreum.horreum_client import new_horreum_client
+
+__all__ = [
+ new_horreum_client
+]
diff --git a/src/horreum/horreum_client.py b/src/horreum/horreum_client.py
new file mode 100644
index 0000000..cd673d6
--- /dev/null
+++ b/src/horreum/horreum_client.py
@@ -0,0 +1,79 @@
+from kiota_abstractions.authentication import AuthenticationProvider
+from kiota_abstractions.authentication.access_token_provider import AccessTokenProvider
+from kiota_abstractions.authentication.anonymous_authentication_provider import AnonymousAuthenticationProvider
+from kiota_abstractions.authentication.base_bearer_token_authentication_provider import (
+ BaseBearerTokenAuthenticationProvider)
+from kiota_http.httpx_request_adapter import HttpxRequestAdapter
+
+from .keycloak_access_provider import KeycloakAccessProvider
+from .raw_client import HorreumRawClient
+
+from .service import info_service as info
+
+
+async def setup_auth_provider(base_url: str, username: str, password: str) -> AccessTokenProvider:
+ # Use not authenticated client to fetch the auth mechanism
+ auth_provider = AnonymousAuthenticationProvider()
+ req_adapter = HttpxRequestAdapter(auth_provider)
+ req_adapter.base_url = base_url
+ auth_client = HorreumRawClient(req_adapter)
+
+ auth_config = await auth_client.api.config.keycloak.get()
+ # TODO: we could generalize using a generic OIDC client
+ return KeycloakAccessProvider(auth_config, username, password)
+
+
+class HorreumClient:
+ __base_url: str
+ __username: str
+ __password: str
+
+ # Raw client, this could be used to interact with the low-level api
+ raw_client: HorreumRawClient
+ auth_provider: AuthenticationProvider
+
+ def __init__(self, base_url: str, username: str = None, password: str = None):
+ self.__base_url = base_url
+ self.__username = username
+ self.__password = password
+
+ async def setup(self):
+ """
+ Set up the authentication provider, based on the Horreum configuration, and the low-level horreum api client
+ """
+
+ if self.__username is not None:
+ # Bearer token authentication
+ access_provider = await setup_auth_provider(self.__base_url, self.__username, self.__password)
+ self.auth_provider = BaseBearerTokenAuthenticationProvider(access_provider)
+ elif self.__password is not None:
+ raise RuntimeError("providing password without username, have you missed something?")
+ else:
+ # Anonymous authentication
+ self.auth_provider = AnonymousAuthenticationProvider()
+
+ req_adapter = HttpxRequestAdapter(self.auth_provider)
+ req_adapter.base_url = self.__base_url
+
+ self.raw_client = HorreumRawClient(req_adapter)
+
+ ##################
+ # High-level API #
+ ##################
+
+ def version(self) -> str:
+ return info.get_client_version()
+
+
+async def new_horreum_client(base_url: str, username: str = None, password: str = None) -> HorreumClient:
+ """
+ Initialize the horreum client
+ :param base_url: horreum api base url
+ :param username: auth username
+ :param password: auth password
+ :return: HorreumClient instance
+ """
+ client = HorreumClient(base_url, username, password)
+ await client.setup()
+
+ return client
diff --git a/src/horreum/keycloak_access_provider.py b/src/horreum/keycloak_access_provider.py
new file mode 100644
index 0000000..9cbef72
--- /dev/null
+++ b/src/horreum/keycloak_access_provider.py
@@ -0,0 +1,30 @@
+from typing import Dict, Any
+
+from keycloak import KeycloakOpenID
+from kiota_abstractions.authentication import AllowedHostsValidator
+from kiota_abstractions.authentication.access_token_provider import AccessTokenProvider
+
+from .raw_client.models.keycloak_config import KeycloakConfig
+
+
+class KeycloakAccessProvider(AccessTokenProvider):
+ config: KeycloakConfig
+ username: str
+ password: str
+
+ def __init__(self, config: KeycloakConfig, username: str, password: str):
+ super()
+ self.config = config
+ self.username = username
+ self.password = password
+ self.keycloak_openid = KeycloakOpenID(
+ server_url=config.url,
+ client_id=config.client_id,
+ realm_name=config.realm
+ )
+
+ async def get_authorization_token(self, uri: str, additional_authentication_context: Dict[str, Any] = {}) -> str:
+ return self.keycloak_openid.token(username=self.username, password=self.password)["access_token"]
+
+ def get_allowed_hosts_validator(self) -> AllowedHostsValidator:
+ return AllowedHostsValidator(allowed_hosts=[])
diff --git a/src/horreum/raw_client/.gitignore b/src/horreum/raw_client/.gitignore
new file mode 100644
index 0000000..266de2d
--- /dev/null
+++ b/src/horreum/raw_client/.gitignore
@@ -0,0 +1,5 @@
+# Ignore everything, this dir will contain generated code
+*
+
+# Keep this file versioned
+!__init__.py
\ No newline at end of file
diff --git a/src/horreum/raw_client/__init__.py b/src/horreum/raw_client/__init__.py
new file mode 100644
index 0000000..4f4fe7c
--- /dev/null
+++ b/src/horreum/raw_client/__init__.py
@@ -0,0 +1,5 @@
+from .horreum_raw_client import HorreumRawClient
+
+__all__ = [
+ HorreumRawClient
+]
diff --git a/src/horreum/service/__init__.py b/src/horreum/service/__init__.py
new file mode 100644
index 0000000..4d79f23
--- /dev/null
+++ b/src/horreum/service/__init__.py
@@ -0,0 +1,5 @@
+# from .info_service import get_client_version
+
+__all__ = [
+ "info_service"
+]
diff --git a/src/horreum/service/info_service.py b/src/horreum/service/info_service.py
new file mode 100644
index 0000000..ea97e66
--- /dev/null
+++ b/src/horreum/service/info_service.py
@@ -0,0 +1,5 @@
+from importlib.metadata import version
+
+
+def get_client_version():
+ return version("horreum")
diff --git a/test/__init__.py b/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 0000000..21c6085
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,13 @@
+import asyncio
+
+import pytest
+
+
+@pytest.fixture(scope="session")
+def event_loop():
+ try:
+ loop = asyncio.get_running_loop()
+ except RuntimeError:
+ loop = asyncio.new_event_loop()
+ yield loop
+ loop.close()
diff --git a/test/horreum_client_test.py b/test/horreum_client_test.py
new file mode 100644
index 0000000..450a0f7
--- /dev/null
+++ b/test/horreum_client_test.py
@@ -0,0 +1,101 @@
+import httpx
+import pytest
+from kiota_abstractions.authentication import BaseBearerTokenAuthenticationProvider
+from kiota_abstractions.base_request_configuration import RequestConfiguration
+from kiota_abstractions.headers_collection import HeadersCollection
+from kiota_abstractions.method import Method
+from kiota_abstractions.request_information import RequestInformation
+
+from horreum.horreum_client import new_horreum_client, HorreumClient
+from horreum.raw_client.api.test.test_request_builder import TestRequestBuilder
+from horreum.raw_client.models.protected_type_access import ProtectedType_access
+from horreum.raw_client.models.test import Test
+
+username = "user"
+password = "secret"
+
+
+@pytest.fixture()
+async def anonymous_client_without_check() -> HorreumClient:
+ print("Setting up anonymous client")
+ return await new_horreum_client(base_url="http://localhost:8080")
+
+
+@pytest.fixture()
+async def anonymous_client() -> HorreumClient:
+ print("Setting up anonymous client")
+ client = await new_horreum_client(base_url="http://localhost:8080")
+ try:
+ await client.raw_client.api.config.version.get()
+ except httpx.ConnectError:
+ pytest.fail("Unable to fetch Horreum version, is Horreum running in the background?")
+ return client
+
+
+@pytest.fixture()
+async def authenticated_client() -> HorreumClient:
+ print("Setting up authenticated client")
+ client = await new_horreum_client(base_url="http://localhost:8080", username=username, password=password)
+ try:
+ await client.raw_client.api.config.version.get()
+ except httpx.ConnectError:
+ pytest.fail("Unable to fetch Horreum version, is Horreum running in the background?")
+ return client
+
+
+def test_check_client_version(anonymous_client_without_check: HorreumClient):
+ version = anonymous_client_without_check.version()
+ # TODO: we could load the toml and check the versions match
+ assert version != ""
+
+
+@pytest.mark.asyncio
+async def test_check_server_version(anonymous_client: HorreumClient):
+ version = await anonymous_client.raw_client.api.config.version.get()
+ assert version.version != ""
+ assert version.start_timestamp != ""
+
+
+@pytest.mark.asyncio
+async def test_check_missing_token(anonymous_client: HorreumClient):
+ req = RequestInformation(Method("GET"), "/api/")
+ await anonymous_client.auth_provider.authenticate_request(req)
+ assert len(req.headers.get(BaseBearerTokenAuthenticationProvider.AUTHORIZATION_HEADER)) == 0
+
+
+@pytest.mark.asyncio
+async def test_check_auth_token(authenticated_client: HorreumClient):
+ req = RequestInformation(Method("GET"), "/api/")
+ await authenticated_client.auth_provider.authenticate_request(req)
+ assert len(req.headers.get(BaseBearerTokenAuthenticationProvider.AUTHORIZATION_HEADER)) == 1
+ assert req.headers.get(BaseBearerTokenAuthenticationProvider.AUTHORIZATION_HEADER).pop().startswith("Bearer")
+
+
+@pytest.mark.asyncio
+async def test_missing_username_with_password():
+ try:
+ await new_horreum_client(base_url="http://localhost:8080", password=password)
+ pytest.fail("expect RuntimeError here")
+ except RuntimeError as e:
+ assert str(e) == "providing password without username, have you missed something?"
+
+
+@pytest.mark.asyncio
+async def test_check_no_tests(authenticated_client: HorreumClient):
+ query_params = TestRequestBuilder.TestRequestBuilderGetQueryParameters(limit=1, page=0)
+ config = RequestConfiguration(query_parameters=query_params, headers=HeadersCollection())
+ assert (await authenticated_client.raw_client.api.test.get(config)).count == 0
+
+
+@pytest.mark.asyncio
+async def test_check_create_test(authenticated_client: HorreumClient):
+ # Create new test
+ t = Test(name="TestName", description="Simple test", owner="dev-team", access=ProtectedType_access.PUBLIC)
+ created = await authenticated_client.raw_client.api.test.post(t)
+ assert created is not None
+ assert (await authenticated_client.raw_client.api.test.get()).count == 1
+
+ # TODO: we could automate setup/teardown process
+ # Delete test
+ await authenticated_client.raw_client.api.test.by_id(created.id).delete()
+ assert (await authenticated_client.raw_client.api.test.get()).count == 0
diff --git a/test/workflow_dispatch_event_example.json b/test/workflow_dispatch_event_example.json
new file mode 100644
index 0000000..9e0a715
--- /dev/null
+++ b/test/workflow_dispatch_event_example.json
@@ -0,0 +1,6 @@
+{
+ "action": "workflow_dispatch",
+ "inputs": {
+ "branch": "master"
+ }
+}