diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml new file mode 100644 index 0000000..5992ce6 --- /dev/null +++ b/.github/workflows/backport.yaml @@ -0,0 +1,46 @@ +name: Pull Request Backporting using Git Backporting + +on: + pull_request_target: + types: + - closed + - labeled + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NO_SQUASH_OPTION: true + +jobs: + backporting: + name: "Backporting" + concurrency: + group: backporting-${{ github.head_ref }} + cancel-in-progress: true + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + && (contains(github.event.pull_request.labels.*.name, 'backport') + || contains(github.event.pull_request.labels.*.name, 'backport-squash')) + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport') + ) + ) + runs-on: ubuntu-latest + steps: + - name: Override no-squash option + if: > + (github.event.action == 'closed' && contains(github.event.pull_request.labels.*.name, 'backport-squash')) + || (github.event.action == 'labeled' && contains(github.event.label.name, 'backport-squash')) + shell: bash + run: | + echo "NO_SQUASH_OPTION=false" >> $GITHUB_ENV + - name: Backporting + uses: kiegroup/git-backporting@v4.5.3 + with: + target-branch: 0.12.x + pull-request: ${{ github.event.pull_request.url }} + no-squash: ${{ env.NO_SQUASH_OPTION }} diff --git a/.github/workflows/check-openapi-change.yaml b/.github/workflows/check-openapi-change.yaml new file mode 100644 index 0000000..227e515 --- /dev/null +++ b/.github/workflows/check-openapi-change.yaml @@ -0,0 +1,70 @@ +# 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 check the build/tests are still working. +# It could be tested running `gh act workflow_dispatch -e ./test/gha_workflows/workflow_dispatch_event_example.json`. +name: Update Horreum auto-generated client + +on: + workflow_dispatch: + inputs: + branch: + description: Branch or tag of https://github.com/Hyperfoil/Horreum + required: true + # this event should be triggered by Horreum repo using peter-evans/repository-dispatch@v2 + repository_dispatch: + types: [ detected-horreum-openapi-change ] + +jobs: + check-openapi-change: + name: check-openapi-change ${{ github.event.client_payload.branch }} + runs-on: ubuntu-latest + env: + FORCE_COLOR: "1" + PRE_COMMIT_COLOR: "always" + strategy: + fail-fast: false + matrix: + python: [ "3.9", "3.10", "3.11" ] + steps: + - name: Fetch Horreum branch + id: fetch-horreum-branch + run: | + if [ "${{ github.event_name }}" = "repository_dispatch" ]; then + 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 + - name: Fetch client branch + id: fetch-client-branch + run: | + if [ "${{ steps.fetch-horreum-branch.outputs.horreum_branch }}" = "master" ]; then + echo "horreum_client_branch=main" >> $GITHUB_OUTPUT + else + echo "horreum_client_branch=${{ steps.fetch-horreum-branch.outputs.horreum_branch }}" >> $GITHUB_OUTPUT + fi + - uses: actions/checkout@v4 + with: + ref: ${{ steps.fetch-client-branch.outputs.horreum_client_branch }} + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Upgrade pip + run: | + pip install --constraint=./dev-constraints.txt pip + pip --version + - name: Install poetry + run: | + pip install --constraint=./dev-constraints.txt poetry + poetry --version + - name: Install Nox + run: | + pip install --constraint=./dev-constraints.txt nox nox-poetry + nox --version + - name: Generate horreum client + run: make HORREUM_BRANCH=${{ steps.fetch-horreum-branch.outputs.horreum_branch }} generate + - name: Test horreum + run: nox --python=${{ matrix.python }} -s tests diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4caa2fd..dec6b6e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,17 +1,17 @@ # 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 +# This is going to be triggered on every pull request as well as on all stable branches (e.g., main and 0.12.x). name: Python client ci on: push: branches: - main + - 0.12.x pull_request: jobs: test: - name: ${{ matrix.session }} ${{ matrix.python }} + name: test/${{ matrix.python }} runs-on: ubuntu-latest strategy: fail-fast: false @@ -27,7 +27,19 @@ jobs: 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 + - name: Upgrade pip + run: | + pip install --constraint=./dev-constraints.txt pip + pip --version + - name: Install poetry + run: | + pip install --constraint=./dev-constraints.txt poetry + poetry --version + - name: Install Nox + run: | + pip install --constraint=./dev-constraints.txt nox nox-poetry + nox --version + - name: Generate horreum client + run: make generate + - name: Test horreum + run: nox --python=${{ matrix.python }} -s tests diff --git a/.github/workflows/generate-client.yaml b/.github/workflows/generate-client.yaml deleted file mode 100644 index 8b25a5e..0000000 --- a/.github/workflows/generate-client.yaml +++ /dev/null @@ -1,54 +0,0 @@ -# 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 index c8a971c..8e5fa8a 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -1,28 +1,25 @@ -# 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 +# This workflow will build the python distribution, and it will publish to Pypi +# This is going to be triggered on every tag `v*`, e.g., `v0.13`. name: Publish Horreum library on: push: tags: - v* + workflow_dispatch: jobs: - test: - name: ${{ matrix.session }} ${{ matrix.python }} + publish: + name: Publish horreum package runs-on: ubuntu-latest + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing env: FORCE_COLOR: "1" + PY_VERSION: "3.10" 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) @@ -31,8 +28,29 @@ jobs: echo "::error title='$GIT_TAG tag does not match project version'::" exit 1 fi + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ env.PY_VERSION }} + - name: Upgrade pip + run: | + pip install --constraint=./dev-constraints.txt pip + pip --version + - name: Install poetry + run: | + pip install --constraint=./dev-constraints.txt poetry + poetry --version + - name: Install Nox + run: | + pip install --constraint=./dev-constraints.txt nox nox-poetry + nox --version + - name: Generate horreum client + # HORREUM_BRANCH must be properly set to the Horreum branch you want to fetch the openapi + run: make generate + - name: Test horreum + run: nox --python=${{ env.PY_VERSION }} -s tests - name: Build python library - run: make generate && poetry build --ansi + run: poetry build --ansi - name: Publish package on PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: diff --git a/.gitignore b/.gitignore index bdd715c..e82eca0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,9 @@ bin/ # Kiota **/.kiota.log + +# Openapi +openapi/openapi.yaml + +# Generated code +src/horreum/raw_client/** diff --git a/GET_STARTED.md b/GET_STARTED.md deleted file mode 100644 index a01a281..0000000 --- a/GET_STARTED.md +++ /dev/null @@ -1,52 +0,0 @@ -
- -# 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 index 8a3a6b6..1172bde 100644 --- a/Makefile +++ b/Makefile @@ -29,6 +29,8 @@ 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" +OPENAPI_PATH = "${PROJECT_PATH}/openapi" +OPENAPI_SPEC = "${OPENAPI_PATH}/openapi.yaml" .PHONY: help help: ## Display this help. @@ -36,9 +38,13 @@ help: ## Display this help. ##@ Development +.PHONY: clean-bin +clean-bin: ## Clean external tools + @rm -rf ${PROJECT_BIN} + .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 +clean: ## Clean output directories + @rm -rf ${PROJECT_DIST} ${GENERATED_CLIENT_PATH} ${OPENAPI_PATH} .PHONY: kiota kiota: ${PROJECT_BIN}/kiota ## Install kiota tool under ${PROJECT_PATH}/bin @@ -58,10 +64,17 @@ ${PROJECT_BIN}/kiota: .PHONY: tools tools: kiota ## Install external tools. +${OPENAPI_SPEC}: + @{\ + set -e ;\ + mkdir -p ${OPENAPI_PATH} ;\ + echo "fetching openapi spec from ${HORREUM_OPENAPI_PATH}" ;\ + curl -sSfL -o ${OPENAPI_SPEC} ${HORREUM_OPENAPI_PATH} ;\ + } + .PHONY: generate -generate: tools ## Generate the Horreum client +generate: tools ${OPENAPI_SPEC} ## 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} ;\ + ${PROJECT_BIN}/kiota generate -l python -c HorreumRawClient -n raw_client -d ${OPENAPI_PATH}/openapi.yaml -o ${GENERATED_CLIENT_PATH} ;\ } diff --git a/README.md b/README.md index 68e5daf..d1490fd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Horreum python client is a high-level python library to interact with [Horreum]( 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. +Refer to the [get started guide](docs/GET_STARTED.md) for comprehensive instructions on installing and utilizing this library. ## What is Horreum? @@ -42,9 +42,10 @@ Contributions to `horreum-client-python` Please check our [CONTRIBUTING.md](./CO ### Development -Install all dev dependencies (consider using Python virtual environments): +Install poetry dependency (consider using Python virtual environments): ```bash -pip install -r dev-requirements.txt +pip install --constraint=./dev-constraints.txt poetry +poetry --version ``` Generate source files @@ -58,15 +59,32 @@ poetry build ``` #### Tests +Tests can be executed using [nox](https://nox.thea.codes/en/stable/) sessions. -Right now tests are not automated, therefore you need to start up the Horreum server manually, +To install it in your local environment, please run: +```bash +pip install --constraint=./dev-constraints.txt nox nox-poetry +nox --version +``` + +To check available sessions, run: +```bash +nox -l +``` + +And execute them by running: +``` +nox -s +``` + +Right now integrations tests are not fully 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/ +nox -s its ``` ### If you have any idea or doubt 👇 diff --git a/dev-constraints.txt b/dev-constraints.txt new file mode 100644 index 0000000..e1ea1a2 --- /dev/null +++ b/dev-constraints.txt @@ -0,0 +1,7 @@ +pip==23.3.1 +nox==2024.3.2 +nox-poetry==1.0.3 +poetry==1.8.2 +pytest==8.1.1 +pytest-asyncio==0.23.6 +virtualenv==20.24.6 \ No newline at end of file diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index 60a3f21..0000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -poetry==1.8.2 -pytest==8.1.1 -pytest-asyncio==0.23.6 \ No newline at end of file diff --git a/docs/GET_STARTED.md b/docs/GET_STARTED.md new file mode 100644 index 0000000..3cd1d65 --- /dev/null +++ b/docs/GET_STARTED.md @@ -0,0 +1,92 @@ +
+ +# Getting Started + +
+ +In this document you can find all information to get started using the Horreum python library from scratch. + +> **NOTE**: At the moment of writing, the `horreum` package is not yet accessible on PyPI. Consequently, the sole method +> for installation involves constructing it from source and subsequently installing the generated `wheel`. + +--- +## Prerequisites + +* Python environment, e.g., `pyenv` or `miniconda` with `poetry` dependency installed: +```bash +pip install --constraint=./dev-constraints.txt poetry +poetry --version +``` + +## Installation + +Once all [prerequisites](#prerequisites) are satisfied, run the following commands. + +First of all, generate the Horreum client: + +```bash +make generate +``` + +Then, simply build the _wheel_ 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 + +Horreum Python library leverages the auto-generated client using [Kiota](https://github.com/microsoft/kiota) tool to translate all REST requests +into python methods calls. + +Right now Horreum library does not expose any specific high-level api, but it simply offers the Kiota generated client +which is made available through the `HorreumClient.raw_client` property. + +Check [Kiota python examples](https://github.com/microsoft/kiota-samples/tree/main/get-started/quickstart/python) to see +how to interact with a Kiota auto-generated Python client. + +Here a very simple example: + +```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 '.' +``` + +Other examples can be found in the [test folder](../test), for instance: + +```bash +# Import Horreum Test model +>>> from horreum.raw_client.models.test import Test +>>> from horreum.raw_client.models.protected_type_access import ProtectedType_access + +# Initialize the client +>>> client = await new_horreum_client(base_url="http://localhost:8080", username="..", password="..") + +# Create new Horreum test +>>> t = Test(name="TestName", description="Simple test", owner="dev-team", access=ProtectedType_access.PUBLIC) +>>> created = await client.raw_client.api.test.post(t) +Test(additional_data={}, access=, owner='dev-team', compare_url=None, datastore_id=1, description='Simple test', fingerprint_filter=None, fingerprint_labels=None, folder=None, id=12, name='TestName', notifications_enabled=True, timeline_function=None, timeline_labels=None, tokens=None, transformers=None) + +# Delete the Horreum test +>>> await client.raw_client.api.test.by_id(12).delete() +``` diff --git a/docs/RELEASE.md b/docs/RELEASE.md new file mode 100644 index 0000000..2022a19 --- /dev/null +++ b/docs/RELEASE.md @@ -0,0 +1,77 @@ +# Release + +This document aims to describe how to perform a release of the Horreum python library. + +The versioning pattern should follow the [Horreum](https://github.com/Hyperfoil/Horreum) +versioning scheme to keep coherence among all Horreum-related projects versions. + +## Procedure + +### Tag a new version + +Checkout to your branch, either a "stable" (e.g., `0.12.x`) or the `main` one. + +```bash +git checkout origin/main +``` + +Update the project version: + +```bash +poetry version patch +``` + +This will bump your version, from `0.12-dev` to `0.12`. + +To double-check the version, run: +```bash +poetry version +# horreum 0.12 +``` + +Commit the changes and tag a new version: +```bash +git add . +git commit -m "Tag version 0.12" +git tag v0.12 +``` + +Ensure the tag is in the form of `v$(poetry version)`. + +Push changes and tag: +```bash +git push origin main +git push origin v0.12 +``` + +If you are releasing a new patch from a _stable_ branch all previous operations must be performed +from _stable_ rather than from `main`. + +### Create stable branch + +> **NOTE**: If the _stable_ branch already exists, simply skip this step, as this means the following steps have already been done. + +To create a _stable_ branch from the `main` one, e.g., `0.12.x`, run the following commands. + +```bash +git checkout origin/main -b 0.12.x +git checkout origin/main +``` + +Update version to the next development one: +```bash +poetry version 0.13-dev +``` + +Commit the changes: +```bash +git add . +git commit -m "Next is 0.13" +``` + +Push changes: +```bash +git push origin 0.12.x +git push origin main +``` + diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..7052062 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,43 @@ +import nox +from nox_poetry import Session, session + +py_versions = ["3.9", "3.10", "3.11"] + +nox.needs_version = ">= 2021.6.6" +nox.options.sessions = ( + "tests", +) + + +@session(python=py_versions) +def tests(s: Session): + """ Run unit tests """ + s.install(".") + s.install( + "asyncio", + "pytest", + "pytest-asyncio" + ) + # run tests + s.run( + "pytest", + "test/horreum_client_test.py", + *s.posargs + ) + + +@session(python=py_versions) +def its(s: Session): + """ Run integration tests """ + s.install(".") + s.install( + "asyncio", + "pytest", + "pytest-asyncio" + ) + # run tests + s.run( + "pytest", + "test/horreum_client_it.py", + *s.posargs + ) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml deleted file mode 100644 index 92cd711..0000000 --- a/openapi/openapi.yaml +++ /dev/null @@ -1,4610 +0,0 @@ ---- -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 index 0bb9249..6515303 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,6 +22,20 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin 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 = "argcomplete" +version = "3.2.3" +description = "Bash tab completion for argparse" +optional = false +python-versions = ">=3.8" +files = [ + {file = "argcomplete-3.2.3-py3-none-any.whl", hash = "sha256:c12355e0494c76a2a7b73e3a59b09024ca0ba1e279fb9ed6c1b82d5b74b6a70c"}, + {file = "argcomplete-3.2.3.tar.gz", hash = "sha256:bf7900329262e481be5a15f56f19736b376df6f82ed27576fa893652c5de6c23"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + [[package]] name = "certifi" version = "2024.2.2" @@ -207,6 +221,23 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "colorlog" +version = "6.8.2" +description = "Add colours to the output of Python's logging module." +optional = false +python-versions = ">=3.6" +files = [ + {file = "colorlog-6.8.2-py3-none-any.whl", hash = "sha256:4dcbb62368e2800cb3c5abd348da7e53f6c362dda502ec27c560b2e58a66bd33"}, + {file = "colorlog-6.8.2.tar.gz", hash = "sha256:3e3e079a41feb5a1b64f978b5ea4f46040a94f11f0e8bbb8261e3dbbeca64d44"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + [[package]] name = "cryptography" version = "42.0.5" @@ -292,6 +323,17 @@ files = [ [package.dependencies] packaging = "*" +[[package]] +name = "distlib" +version = "0.3.8" +description = "Distribution utilities" +optional = false +python-versions = "*" +files = [ + {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, + {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, +] + [[package]] name = "exceptiongroup" version = "1.2.0" @@ -306,6 +348,22 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "filelock" +version = "3.13.1" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.8" +files = [ + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] + [[package]] name = "h11" version = "0.14.0" @@ -548,6 +606,43 @@ files = [ microsoft-kiota_abstractions = ">=1.0.0,<2.0.0" python-dateutil = ">=2.8.2" +[[package]] +name = "nox" +version = "2024.3.2" +description = "Flexible test automation." +optional = false +python-versions = ">=3.7" +files = [ + {file = "nox-2024.3.2-py3-none-any.whl", hash = "sha256:e53514173ac0b98dd47585096a55572fe504fecede58ced708979184d05440be"}, + {file = "nox-2024.3.2.tar.gz", hash = "sha256:f521ae08a15adbf5e11f16cb34e8d0e6ea521e0b92868f684e91677deb974553"}, +] + +[package.dependencies] +argcomplete = ">=1.9.4,<4.0" +colorlog = ">=2.6.1,<7.0.0" +packaging = ">=20.9" +virtualenv = ">=20.14.1" + +[package.extras] +tox-to-nox = ["jinja2", "tox"] +uv = ["uv"] + +[[package]] +name = "nox-poetry" +version = "1.0.3" +description = "nox-poetry" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "nox_poetry-1.0.3-py3-none-any.whl", hash = "sha256:a2fffeb70ae81840479e68287afe1c772bf376f70f1e92f99832a20b3c64d064"}, + {file = "nox_poetry-1.0.3.tar.gz", hash = "sha256:dc7ecbbd812a333a0c0b558f57e5b37f7c12926cddbcecaf2264957fd373824e"}, +] + +[package.dependencies] +nox = ">=2020.8.22" +packaging = ">=20.9" +tomlkit = ">=0.7" + [[package]] name = "opentelemetry-api" version = "1.23.0" @@ -700,6 +795,21 @@ tzdata = ">=2020.1" [package.extras] test = ["time-machine (>=2.6.0)"] +[[package]] +name = "platformdirs" +version = "4.2.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.8" +files = [ + {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, + {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, +] + +[package.extras] +docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] + [[package]] name = "pluggy" version = "1.4.0" @@ -879,6 +989,17 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +[[package]] +name = "tomlkit" +version = "0.12.4" +description = "Style preserving TOML library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomlkit-0.12.4-py3-none-any.whl", hash = "sha256:5cd82d48a3dd89dee1f9d64420aa20ae65cfbd00668d6f094d7578a78efbb77b"}, + {file = "tomlkit-0.12.4.tar.gz", hash = "sha256:7ca1cfc12232806517a8515047ba66a19369e71edf2439d0f5824f91032b6cc3"}, +] + [[package]] name = "typing-extensions" version = "4.10.0" @@ -918,6 +1039,26 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "virtualenv" +version = "20.25.1" +description = "Virtual Python Environment builder" +optional = false +python-versions = ">=3.7" +files = [ + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, +] + +[package.dependencies] +distlib = ">=0.3.7,<1" +filelock = ">=3.12.2,<4" +platformdirs = ">=3.9.1,<5" + +[package.extras] +docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] +test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] + [[package]] name = "wrapt" version = "1.16.0" @@ -1014,5 +1155,5 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" -python-versions = "^3.10" -content-hash = "0f259faa8559a5a87db0e93a4368724eef579cd8a321eee8ce8c90afd962d2f3" +python-versions = "^3.9" +content-hash = "61822287999dc23a35ed9e47ff476c46cff4d4ce68c4569c738d753e23d7c2df" diff --git a/pyproject.toml b/pyproject.toml index 69ecdcb..bc875b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,15 +2,25 @@ name = "horreum" version = "0.13-dev" description = "Horreum python library" +keywords = ["horreum", "performance", "change-detection"] authors = ["Andrea Lamparelli "] +maintainers = [ + "Andrea Lamparelli ", + "John O'Hara ", + "Stale Pedersen " +] license = "Apache 2.0" readme = "README.md" +homepage = "https://github.com/Hyperfoil/horreum-client-python" include = [ - "src/horreum/raw_client/**/*", # all files in "src/horreum/raw_client" and any subfolders + "src/horreum/raw_client/**/*", # this folder includes auto-generated client ] +[tool.poetry.urls] +"Issues" = "https://github.com/Hyperfoil/horreum-client-python/issues" + [tool.poetry.dependencies] -python = "^3.10" +python = "^3.9" microsoft-kiota-abstractions = "^1.3.1" microsoft-kiota-http = "^1.3.1" microsoft-kiota-serialization-json = "^1.1.0" @@ -23,6 +33,8 @@ microsoft-kiota-serialization-multipart = "^0.1.0" [tool.poetry.group.dev.dependencies] pytest = "^8.1.1" pytest-asyncio = "^0.23.6" +nox = "^2024.3.2" +nox-poetry = "^1.0.3" [build-system] requires = ["poetry-core"] diff --git a/src/horreum/horreum_client.py b/src/horreum/horreum_client.py index cd673d6..41d1a3d 100644 --- a/src/horreum/horreum_client.py +++ b/src/horreum/horreum_client.py @@ -1,3 +1,5 @@ +from importlib.metadata import version + from kiota_abstractions.authentication import AuthenticationProvider from kiota_abstractions.authentication.access_token_provider import AccessTokenProvider from kiota_abstractions.authentication.anonymous_authentication_provider import AnonymousAuthenticationProvider @@ -6,9 +8,7 @@ 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 +from .raw_client.horreum_raw_client import HorreumRawClient async def setup_auth_provider(base_url: str, username: str, password: str) -> AccessTokenProvider: @@ -61,8 +61,9 @@ async def setup(self): # High-level API # ################## - def version(self) -> str: - return info.get_client_version() + @staticmethod + def version() -> str: + return version("horreum") async def new_horreum_client(base_url: str, username: str = None, password: str = None) -> HorreumClient: diff --git a/src/horreum/raw_client/.gitignore b/src/horreum/raw_client/.gitignore deleted file mode 100644 index 266de2d..0000000 --- a/src/horreum/raw_client/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# 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 deleted file mode 100644 index 4f4fe7c..0000000 --- a/src/horreum/raw_client/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .horreum_raw_client import HorreumRawClient - -__all__ = [ - HorreumRawClient -] diff --git a/src/horreum/service/__init__.py b/src/horreum/service/__init__.py deleted file mode 100644 index 4d79f23..0000000 --- a/src/horreum/service/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# 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 deleted file mode 100644 index ea97e66..0000000 --- a/src/horreum/service/info_service.py +++ /dev/null @@ -1,5 +0,0 @@ -from importlib.metadata import version - - -def get_client_version(): - return version("horreum") diff --git a/test/workflow_dispatch_event_example.json b/test/gha_workflows/workflow_dispatch_event_example.json similarity index 100% rename from test/workflow_dispatch_event_example.json rename to test/gha_workflows/workflow_dispatch_event_example.json diff --git a/test/horreum_client_it.py b/test/horreum_client_it.py new file mode 100644 index 0000000..2423b6b --- /dev/null +++ b/test/horreum_client_it.py @@ -0,0 +1,93 @@ +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 + + +@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(): + with pytest.raises(RuntimeError) as ex: + await new_horreum_client(base_url="http://localhost:8080", password=password) + assert str(ex.value) == "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/horreum_client_test.py b/test/horreum_client_test.py index 450a0f7..76199af 100644 --- a/test/horreum_client_test.py +++ b/test/horreum_client_test.py @@ -1,101 +1,17 @@ -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() +@pytest.mark.asyncio +async def test_check_client_version(anonymous_client: HorreumClient): + version = HorreumClient.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