Skip to content

Commit

Permalink
merge: Merge pull request #29 from DSD-DBS/feat-upgrade-client
Browse files Browse the repository at this point in the history
feat: Update specification and client builder
  • Loading branch information
micha91 authored May 14, 2024
2 parents c2a685a + b861fe0 commit bb27a16
Show file tree
Hide file tree
Showing 967 changed files with 34,187 additions and 12,576 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ pre-commit install
```

## Updating the auto generated part
To update the auto generated part of the code, execute the `open_api_client_build/build_client_source.sh` script with `path` or `url` as first
arg and the path to the OpenAPI-Specification as second arg from the project root directory. For Copyright reasons, we don't publish the Specification here,
but we used the Specification of Polarion version 2023.04 to generate the code published here.
To update the auto generated part of the code, execute the `open_api_client_build/build_client.py` script with `path` or `url` as first
arg and the path to the OpenAPI-Specification as second arg from the project root directory. The publicly available [specification](https://developer.siemens.com/polarion/polarion-rest-apispec.json)
from the Polarion developer Portal was used.

# Contributing

Expand Down
151 changes: 151 additions & 0 deletions open_api_client_build/build_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Copyright DB InfraGO AG and contributors
# SPDX-License-Identifier: Apache-2.0
"""Script to fix the specification and build code from it.
Usage: needs 2 args for execution. First one is either 'url' or 'path',
second one is the path to the Open API Spec. E.g.
./build_client_source.sh path /download/spec.json will take the spec
from the given path
"""
import json
import os
import pathlib
import re
import shutil
import subprocess
import sys
import tempfile

import httpx

error_code_pattern = re.compile("[4,5][0-9]{2}")
script_path = pathlib.Path(os.path.dirname(os.path.realpath(__file__)))
rest_api_path = script_path.parent / "polarion_rest_api_client"
template_path = script_path / "custom_templates"
config_path = script_path / "config.yaml"
autoflake_commands = [
"autoflake",
"-i",
"-r",
"--remove-all-unused-imports",
"--remove-unused-variables",
"--ignore-init-module-imports",
"./open_api_client",
]
black_commands = [
"black",
"./open_api_client",
]
isort_commands = [
"isort",
"./open_api_client",
]


def fix_spec(src: str, path: str | os.PathLike):
"""Fix errors in the specification."""
if src == "path":
with open(path, "r") as f:
spec = json.load(f)
elif src == "url":
response = httpx.get(path)
spec = response.json()
else:
raise Exception(
"you have to provide a file or url keyword as 1st arg."
)
spec_paths = spec["paths"]
if (
octet_schema := spec_paths.get(
"/projects/{projectId}/testruns/{testRunId}/actions/importXUnitTestResults",
{},
)
.get("post", {})
.get("requestBody", {})
.get("content", {})
.get("application/octet-stream", {})
.get("schema")
):
if octet_schema.get("type") == "object":
octet_schema["type"] = "string"
octet_schema["format"] = "binary"

for spec_path in spec_paths.values():
for operation_description in spec_path.values():
if responses := operation_description.get("responses"):
if "4XX-5XX" in responses:
for code, resp in responses.items():
if error_code_pattern.fullmatch(code):
resp["content"] = responses["4XX-5XX"]["content"]
del responses["4XX-5XX"]

schemas = spec["components"]["schemas"]
if (
downloads := schemas.get("jobsSingleGetResponse", {})
.get("properties", {})
.get("data", {})
.get("properties", {})
.get("links", {})
.get("properties", {})
.get("downloads")
):
if "items" not in downloads and downloads.get("type") == "array":
downloads["items"] = {"type": "string"}

if (
downloads := schemas.get("jobsSinglePostResponse", {})
.get("properties", {})
.get("data", {})
.get("properties", {})
.get("links", {})
.get("properties", {})
.get("downloads")
):
if "items" not in downloads and downloads.get("type") == "array":
downloads["items"] = {"type": "string"}

if (
error_source := schemas.get("errors", {})
.get("properties", {})
.get("errors", {})
.get("items", {})
.get("properties", {})
.get("source")
):
error_source["nullable"] = True
if resource := error_source.get("properties", {}).get("resource"):
resource["nullable"] = True

with tempfile.NamedTemporaryFile("w", delete=False) as f:
json.dump(spec, f)
f.close()

generator_path = shutil.which("openapi-python-client")
assert generator_path is not None, (
"Did not find openapi-python-client generator - "
"please install dev requirements"
)

subprocess.run(
[
generator_path,
"update",
"--meta",
"none",
"--path",
f.name,
f"--custom-template-path={template_path}",
"--config",
config_path,
],
cwd=rest_api_path,
check=True,
)

subprocess.run(autoflake_commands, cwd=rest_api_path, check=True)
subprocess.run(black_commands, cwd=rest_api_path, check=True)
subprocess.run(isort_commands, cwd=rest_api_path, check=True)


if __name__ == "__main__":
fix_spec(sys.argv[1], sys.argv[2])
18 changes: 0 additions & 18 deletions open_api_client_build/build_client_source.sh

This file was deleted.

2 changes: 2 additions & 0 deletions open_api_client_build/custom_templates/model.py.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,15 @@ field_dict.update(self.additional_properties)
{% endfor %}

{{ _prepare_field_dict() }}
{% if model.required_properties | length > 0 or model.optional_properties | length > 0 %}
field_dict.update({
{% for property in model.required_properties + model.optional_properties %}
{% if property.required %}
"{{ property.name }}": {{ property.python_name }},
{% endif %}
{% endfor %}
})
{% endif %}
{% for property in model.optional_properties %}
{% if not property.required %}
if {{ property.python_name }} is not UNSET:
Expand Down
Loading

0 comments on commit bb27a16

Please sign in to comment.