diff --git a/.generator/conftest.py b/.generator/conftest.py deleted file mode 100644 index 98a89ec1223..00000000000 --- a/.generator/conftest.py +++ /dev/null @@ -1,509 +0,0 @@ -# coding=utf-8 -"""Define basic fixtures.""" - -import json -import os -import pathlib -import re -import warnings -import zlib -from collections import defaultdict - -import pytest -from dateutil.relativedelta import relativedelta -from jinja2 import Environment, FileSystemLoader, Template -from pytest_bdd import given, parsers, then, when -import hashlib - -from generator import openapi -from generator.formatter import ( - camel_case, - snake_case, - untitle_case, - format_parameters, - format_data_with_schema, - get_response_type, - upperfirst, - escape_method_reserved_name, -) - - -MODIFIED_FEATURES = {pathlib.Path(p).resolve() for p in os.getenv("BDD_MODIFIED_FEATURES", "").split(" ") if p} - -ROOT_PATH = pathlib.Path(__file__).parent.parent - - -PATTERN_ALPHANUM = re.compile(r"[^A-Za-z0-9]+") - - -if os.getenv("CI") is not None: - - def formatwarning(message, category, filename, lineno, line=None): - p = pathlib.Path(filename) - if p.is_absolute() and filename.startswith(str(ROOT_PATH)): - p = p.relative_to(ROOT_PATH) - return f"\n::warning file={p},line={lineno}::{message}\n" - - warnings.formatwarning = formatwarning - - -def pytest_bdd_before_scenario(request, feature, scenario): - if MODIFIED_FEATURES: - current = pathlib.Path(scenario.feature.filename).resolve() - if current not in MODIFIED_FEATURES: - pytest.skip(f"Feature file {scenario.feature.filename} has not been modified") - - -def lookup(value, path): - result = value - for dot_path in path.split("."): - for part in dot_path.split("["): - if "]" in part: - index = int(part[: part.index("]")]) - result = result[index] - else: - result = result[snake_case(part)] - return result - - -JINJA_ENV = Environment(loader=FileSystemLoader(pathlib.Path(__file__).parent / "src" / "generator" / "templates")) -JINJA_ENV.filters["tojson"] = json.dumps -JINJA_ENV.filters["snake_case"] = snake_case -JINJA_ENV.filters["camel_case"] = camel_case -JINJA_ENV.filters["untitle_case"] = untitle_case -JINJA_ENV.filters["upperfirst"] = upperfirst -JINJA_ENV.globals["format_data_with_schema"] = format_data_with_schema -JINJA_ENV.globals["format_parameters"] = format_parameters -JINJA_ENV.globals["get_response_type"] = get_response_type -JINJA_ENV.globals["get_type_at_path"] = openapi.get_type_at_path -JINJA_ENV.filters["escape_method_reserved_name"] = escape_method_reserved_name - -JAVA_EXAMPLE_J2 = JINJA_ENV.get_template("example.j2") - - -def pytest_bdd_after_scenario(request, feature, scenario): - try: - operation_specs = request.getfixturevalue("operation_specs") - version = request.getfixturevalue("api_version") - context = request.getfixturevalue("context") - except Exception: - return - operation_id = context["api_request"]["operation_id"] - - status_code = context["status_code"] - if status_code >= 300: - return - - operation_spec = operation_specs[version][operation_id] - response_spec = operation_spec.spec["responses"][str(status_code)] - group_name = "-".join(operation_spec.spec["tags"][0].split(" ")).lower() - context["api_response"] = response_spec - - unique_suffix = "" - scenario_name = f"{operation_spec.spec['summary']} returns \"{response_spec['description']}\" response" - if scenario_name != scenario.name: - unique_suffix = "_" + str(zlib.adler32(scenario.name.encode("utf-8"))) - - data = JAVA_EXAMPLE_J2.render( - context=context, - version=version, - scenario=scenario, - operation_spec=operation_spec.spec, - ) - - output = ROOT_PATH / "examples" / version / group_name / f"{operation_id}{unique_suffix}.java" - output.parent.mkdir(parents=True, exist_ok=True) - - with output.open("w") as f: - f.write(data) - - -def pytest_bdd_apply_tag(tag, function): - """Register tags as custom markers and skip test for '@skip' ones.""" - skip_tags = {} - if tag in skip_tags: - marker = pytest.mark.skip(reason=f"skipped because '{tag}' in {skip_tags}") - marker(function) - return True - return False - - -@pytest.fixture -def api_version(request): - path = pathlib.Path(request.node.__scenario_report__.scenario.feature.filename) - return path.parent.parent.name - - -@pytest.fixture -def unique(request): - main = PATTERN_ALPHANUM.sub("-", request.node.__scenario_report__.scenario.feature.name) - if main.endswith("s"): - # Let's strip the plural present in most names - main = main[:-1] - return f"Example-{main}" - - -TIME_FORMATTER = { - "now": "OffsetDateTime.now()", - "timestamp": "{sret}.toInstant().getEpochSecond()", - "isoformat": "{sret}", - "units": { - "s": "{sret}.plusSeconds({num})", - "m": "{sret}.plusMinutes({num})", - "h": "{sret}.plusHours({num})", - "d": "{sret}.plusDays({num})", - "M": "{sret}.plusMonths({num})", - "y": "{sret}.plusYears({num})", - }, -} - -def relative_time(imports, calls, freezed_time, iso): - time_re = re.compile(r"now( *([+-]) *(\d+)([smhdMy]))?") - - def func(arg): - imports["java.time"].add("OffsetDateTime") - sret = TIME_FORMATTER["now"] - ret = freezed_time - m = time_re.match(arg) - if m: - if m.group(1): - sign = m.group(2) - num = int(sign + m.group(3)) - unit = m.group(4) - if unit == "s": - ret += relativedelta(seconds=num) - elif unit == "m": - ret += relativedelta(minutes=num) - elif unit == "h": - ret += relativedelta(hours=num) - elif unit == "d": - ret += relativedelta(days=num) - elif unit == "M": - ret += relativedelta(months=num) - elif unit == "y": - ret += relativedelta(years=num) - else: - raise ValueError(f"Unknown unit {unit}") - sret = TIME_FORMATTER["units"][unit].format(sret=sret, num=num) - - if iso: - return ( - ret.isoformat(timespec="seconds"), - TIME_FORMATTER["isoformat"].format(sret=sret), - ) - return int(ret.timestamp()), TIME_FORMATTER["timestamp"].format(sret=sret) - return "", "" - - def store_calls(arg): - result, value = func(arg) - calls[result] = value - return result - - return store_calls - - -@pytest.fixture -def context(request, unique, freezed_time): - """ - Return a mapping with all defined fixtures, all objects created by `given` steps, - and the undo operations to perform after a test scenario. - """ - - class MarkUsed(dict): - def __init__(self, *args, **kwargs): - dict.__init__(self, *args, **kwargs) - self.__used_keys__ = set() - - def __getitem__(self, key): - value = super().__getitem__(key) - self.__used_keys__.add(value) - return value - - def is_used(self, key): - return key in self.__used_keys__ - - replace_values = MarkUsed() - imports = defaultdict(set) - given = defaultdict(dict) - - unique_hash = hashlib.sha256(unique.encode("utf-8")).hexdigest()[:16] - ctx = { - "unique": unique, - "unique_lower": unique.lower(), - "unique_upper": unique.upper(), - "unique_alnum": PATTERN_ALPHANUM.sub("", unique), - "unique_lower_alnum": PATTERN_ALPHANUM.sub("", unique).lower(), - "unique_upper_alnum": PATTERN_ALPHANUM.sub("", unique).upper(), - "unique_hash": unique_hash, - "timestamp": relative_time(imports, replace_values, freezed_time, False), - "timeISO": relative_time(imports, replace_values, freezed_time, True), - "uuid": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", - "_replace_values": replace_values, - "_imports": imports, - "_given": given, - "_key_to_json_path": defaultdict(dict), - "_enable_operations": set(), - } - - yield ctx - - -@pytest.fixture -def freezed_time(): - from dateutil import parser - - return parser.isoparse("2021-11-11T11:11:11.111111+00:00") - - -@given('a valid "apiKeyAuth" key in the system') -def a_valid_api_key(context): - """a valid API key.""" - - -@given('a valid "appKeyAuth" key in the system') -def a_valid_application_key(context): - """a valid Application key.""" - - -@pytest.fixture(scope="module") -def specs(): - result = {} - for f in (ROOT_PATH / ".generator" / "schemas").rglob("openapi.yaml"): - version = f.parent.name - result[version] = openapi.load(f) - - return result - - -@pytest.fixture(scope="module") -def operation_specs(specs): - by_operation = {} - - for version, spec in specs.items(): - by_operation[version] = {} - for path in spec["paths"]: - if path.startswith("x-"): - continue - for method, operation in spec["paths"][path].items(): - by_operation[version][operation["operationId"]] = openapi.Operation( - operation["operationId"], operation, method, path - ) - - return by_operation - - -@given(parsers.parse('an instance of "{name}" API')) -def api(context, api_version, specs, name): - """Return an API instance.""" - assert name in {tag["name"].replace(" ", "") for tag in specs[api_version]["tags"]} - api_name = name.replace("-", "") - context["api_instance"] = {"name": api_name} - - -@given(parsers.parse('operation "{name}" enabled')) -def operation_enabled(context, name): - """Enable the unstable operation specific in the clause.""" - context["_enable_operations"].add(name) - - -@given(parsers.parse('new "{name}" request'), target_fixture="operation_id") -def api_request(context, operation_specs, api_version, name): - """Call an endpoint.""" - context["api_request"] = {"operation_id": name, "kwargs": {}} - operation_spec = operation_specs[api_version][name] - try: - context["api_request"]["schema"] = operation_spec.request() - except KeyError: - pass - return name - - -@given(parsers.parse("body with value {data}")) -def request_body(request, context, data): - """Set request body.""" - tpl = Template(data).render(**context) - context["body"] = { - "tpl": data, - "value": json.loads(tpl), - } - - -@given(parsers.parse('body from file "{path}"')) -def request_body_from_file(request, context, path, api_version): - """Set request body.""" - body_file = ( - ROOT_PATH / "src" / "test" / "resources" / "com" / "datadog" / "api" / "client" / api_version / "api" / path - ) - with body_file.open() as f: - data = f.read() - tpl = Template(data).render(**context) - context["body"] = { - "tpl": data, - "value": json.loads(tpl), - } - - -@given(parsers.parse('request contains "{name}" parameter from "{path}"')) -def request_parameter(context, operation_id, api_version, operation_specs, name, path): - """Set request parameter.""" - try: - value = lookup(context, path) - value = value.value(value) # trigger replacement recording - except KeyError: - if path != "REPLACE.ME": - raise - - parameters = operation_specs[api_version][operation_id].spec["parameters"] - for parameter in parameters: - if parameter["name"] == name: - schema = parameter.get("schema", {}) - value = schema.get("example", schema.get("default", parameter.get("example"))) - if value is None: - primitive_lookup = { - "string": { - "date-time": "2021-11-11T11:11:11.111+00:00", - "uuid": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", - None: name, - }, - "integer": { - "int32": 1, - "int64": 9223372036854775807, - None: 1, - }, - "boolean": { - None: True, - }, - } - - if schema.get("type") == "array": - type_ = schema.get("items", {}).get("type") - items_format_ = schema.get("items", {}).get("format") - value = [primitive_lookup[type_][items_format_]] - else: - type_ = schema.get("type") - format_ = schema.get("format") - value = primitive_lookup[type_][format_] - - break - - context["api_request"]["kwargs"][name] = { - "path": path, - "value": value, - } - - -@given(parsers.parse('request contains "{name}" parameter with value {value}')) -def request_parameter_with_value(context, name, value): - """Set request parameter.""" - tpl = Template(value).render(**context) - context["api_request"]["kwargs"][name] = { - "tpl": value, - "value": json.loads(tpl), - } - - -def build_given(version, operation): - def wrapper(request, context, specs, operation_specs): - def make_path(keys): - result = operation["key"] + "_" + "_".join(str(k) for k in keys) - return result.upper() - - # store response in fixtures - def record_value(schema): - if "default" in schema.spec and "enum" in schema.spec: - return schema.spec["default"] - - value = openapi.generate_value(schema) - key = make_path(schema.keys) - context["_given"][operation["step"]][key] = schema.spec - if context["_replace_values"].get(value, key) != key: - value = openapi.generate_value(schema, use_random=True, prefix=key) - - context["_replace_values"][value] = key - keys = [operation["source"]] + list(schema.keys) if "source" in operation else schema.keys - json_path = "".join(f"[{k}]" if isinstance(k, int) else f".{k}" for k in keys).strip(".") - assert context["_key_to_json_path"][operation["key"]].get(key, json_path) == json_path - context["_key_to_json_path"][operation["key"]][key] = json_path - return value - - operation_spec = operation_specs[version][operation["operationId"]] - response_spec = operation_spec.response() - if "source" in operation: - response_spec = lookup(response_spec, operation["source"]) - - response_spec.keys = () - response_spec.value = record_value - response_spec.__source__ = operation.get("source") - - context[operation["key"]] = response_spec - - return wrapper - - -for f in (ROOT_PATH / "src" / "test" / "resources" / "com" / "datadog" / "api").rglob("given.json"): - version = f.parent.parent.name - with f.open() as fp: - for settings in json.load(fp): - given(settings["step"])(build_given(version, settings)) - - -@when("the request is sent") -def execute_request(context, api_version): - """Execute the prepared request.""" - - -@when("the request with pagination is sent") -def execute_request_with_pagination(context): - """Execute the prepared request paginated.""" - context["pagination"] = True - - -@then(parsers.parse("the response status is {status:d} {description}")) -def the_status_is(context, status, description): - """Check the status.""" - context["status_code"] = status - - -@then(parsers.parse('the response "{response_path}" is equal to {value}')) -def expect_equal(context, response_path, value): - """Compare a response attribute to a value.""" - - -@then(parsers.parse('the response "{response_path}" has the same value as "{fixture_path}"')) -def expect_equal_value(context, response_path, fixture_path): - """Compare a response attribute to another attribute.""" - - -@then(parsers.parse('the response "{response_path}" has length {fixture_length:d}')) -def expect_equal_length(context, response_path, fixture_length): - """Check the length of a response attribute.""" - - -@then(parsers.parse("the response has {fixture_length:d} items")) -def expect_response_items(context, fixture_length): - """Check the length of a response.""" - - -@then(parsers.parse('the response "{response_path}" is false')) -def expect_false(context, response_path): - """Check that a response attribute is false.""" - - -@then(parsers.parse('the response "{response_path}" has field "{field}"')) -def expect_response_has_field(context, response_path, field): - """Check that a response has field.""" - - -@then(parsers.parse('the response "{response_path}" does not have field "{field}"')) -def expect_response_does_not_have_field(context, response_path, field): - """Check that a response path does not have field.""" - - -@then(parsers.parse('the response "{response_path}" has item with field "{key_path}" with value {value}')) -def expect_array_contains_object(context, response_path, key_path, value): - """Check that a response attribute contains an object with the specified key and value.""" - - -@then(parsers.parse('the response "{response_path}" array contains value {value}')) -def expect_array_contains_object(context, response_path, value): - """Check that a response array contains the specified value.""" diff --git a/.generator/poetry.lock b/.generator/poetry.lock deleted file mode 100644 index 75f78f8fb4b..00000000000 --- a/.generator/poetry.lock +++ /dev/null @@ -1,449 +0,0 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. - -[[package]] -name = "click" -version = "8.2.1" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.10" -groups = ["main"] -files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main"] -markers = "python_version < \"3.11\"" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "iniconfig" -version = "2.1.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, - {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, -] - -[[package]] -name = "jinja2" -version = "3.1.6" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, - {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonref" -version = "1.1.0" -description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, - {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, -] - -[[package]] -name = "mako" -version = "1.3.10" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, - {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, -] - -[package.dependencies] -MarkupSafe = ">=0.9.2" - -[package.extras] -babel = ["Babel"] -lingua = ["lingua"] -testing = ["pytest"] - -[[package]] -name = "markdown" -version = "3.3.7" -description = "Python implementation of Markdown." -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, - {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, -] - -[package.extras] -testing = ["coverage", "pyyaml"] - -[[package]] -name = "markupsafe" -version = "3.0.2" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, -] - -[[package]] -name = "packaging" -version = "25.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, - {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, -] - -[[package]] -name = "parse" -version = "1.20.2" -description = "parse() is the opposite of format()" -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558"}, - {file = "parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce"}, -] - -[[package]] -name = "parse-type" -version = "0.6.6" -description = "Simplifies to build parse types based on the parse module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,>=2.7" -groups = ["main"] -files = [ - {file = "parse_type-0.6.6-py2.py3-none-any.whl", hash = "sha256:3ca79bbe71e170dfccc8ec6c341edfd1c2a0fc1e5cfd18330f93af938de2348c"}, - {file = "parse_type-0.6.6.tar.gz", hash = "sha256:513a3784104839770d690e04339a8b4d33439fcd5dd99f2e4580f9fc1097bfb2"}, -] - -[package.dependencies] -parse = {version = ">=1.18.0", markers = "python_version >= \"3.0\""} -six = ">=1.15" - -[package.extras] -develop = ["build (>=0.5.1)", "coverage (>=4.4)", "pylint", "pytest (<5.0) ; python_version < \"3.0\"", "pytest (>=5.0) ; python_version >= \"3.0\"", "pytest-cov", "pytest-html (>=1.19.0)", "ruff ; python_version >= \"3.7\"", "setuptools", "setuptools-scm", "tox (>=2.8,<4.0)", "twine (>=1.13.0)", "virtualenv (<20.22.0) ; python_version <= \"3.6\"", "virtualenv (>=20.0.0) ; python_version > \"3.6\"", "wheel"] -docs = ["Sphinx (>=1.6)", "sphinx_bootstrap_theme (>=0.6.0)"] -testing = ["pytest (<5.0) ; python_version < \"3.0\"", "pytest (>=5.0) ; python_version >= \"3.0\"", "pytest-html (>=1.19.0)"] - -[[package]] -name = "pluggy" -version = "1.6.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, - {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["coverage", "pytest", "pytest-benchmark"] - -[[package]] -name = "pytest" -version = "7.4.4" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, - {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-bdd" -version = "6.1.1" -description = "BDD for pytest" -optional = false -python-versions = ">=3.7,<4.0" -groups = ["main"] -files = [ - {file = "pytest_bdd-6.1.1-py3-none-any.whl", hash = "sha256:57eba5878d77036f356a85fb1d108cb061d8af4fb4d032b1a424fa9abe9e498b"}, - {file = "pytest_bdd-6.1.1.tar.gz", hash = "sha256:138af3592bcce5d4684b0d690777cf199b39ce45d423ca28086047ffe6111010"}, -] - -[package.dependencies] -Mako = "*" -parse = "*" -parse-type = "*" -pytest = ">=6.2.0" -typing-extensions = "*" - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pyyaml" -version = "6.0.2" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, -] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["main"] -markers = "python_version < \"3.11\"" -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -description = "Backported and Experimental Type Hints for Python 3.9+" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, - {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, -] - -[metadata] -lock-version = "2.1" -python-versions = "^3.10" -content-hash = "53ffe9e825714cc73508f8128a2a0e79309d806ec6ff077a742d6c295d033c7a" diff --git a/.generator/pyproject.toml b/.generator/pyproject.toml deleted file mode 100644 index a53e4b79310..00000000000 --- a/.generator/pyproject.toml +++ /dev/null @@ -1,23 +0,0 @@ -[tool.poetry] -name = "generator" -version = "0.1.0" -description = "" -authors = ["Datadog "] -license = "Apache-2.0" - -[tool.poetry.dependencies] -python = "^3.10" -click = "^8.1.4" -PyYAML = "^6.0" -jsonref = "^1.1.0" -jinja2 = "^3.1.6" -markdown = "3.3.7" -pytest = "^7.4.0" -pytest-bdd = "^6.1.1" -python-dateutil = "^2.8.2" - -[tool.poetry.dev-dependencies] - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" diff --git a/.generator/src/generator/__init__.py b/.generator/src/generator/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/.generator/src/generator/__main__.py b/.generator/src/generator/__main__.py deleted file mode 100644 index 4cafccbafc7..00000000000 --- a/.generator/src/generator/__main__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .cli import cli - -cli() diff --git a/.generator/src/generator/cli.py b/.generator/src/generator/cli.py deleted file mode 100644 index f774166ffcf..00000000000 --- a/.generator/src/generator/cli.py +++ /dev/null @@ -1,150 +0,0 @@ -import pathlib - -import click -from jinja2 import Environment, FileSystemLoader - -from . import openapi -from . import formatter - -PACKAGE_NAME = "com.datadog.api.client.{}" -COMMON_PACKAGE_NAME = "com.datadog.api.client" -GENERATED_ANNOTATION = ( - '@jakarta.annotation.Generated(value = "https://github.com/DataDog/datadog-api-client-java/blob/master/.generator")' -) - - -@click.command() -@click.argument( - "specs", - nargs=-1, - type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=pathlib.Path), -) -@click.option( - "-o", - "--output", - type=click.Path(path_type=pathlib.Path), -) -def cli(specs, output): - """ - Generate a Ruby code snippet from OpenAPI specification. - """ - env = Environment(loader=FileSystemLoader(str(pathlib.Path(__file__).parent / "templates"))) - - env.filters["accept_headers"] = openapi.accept_headers - env.filters["attribute_name"] = formatter.attribute_name - env.filters["camel_case"] = formatter.camel_case - env.filters["collection_format"] = openapi.collection_format - env.filters["format_server"] = openapi.format_server - env.filters["format_value"] = formatter.format_value - env.filters["parameter_schema"] = openapi.parameter_schema - env.filters["parameters"] = openapi.parameters - env.filters["return_type"] = openapi.return_type - env.filters["simple_type"] = formatter.simple_type - env.filters["snake_case"] = formatter.snake_case - env.filters["untitle_case"] = formatter.untitle_case - env.filters["upperfirst"] = formatter.upperfirst - env.filters["variable_name"] = formatter.variable_name - env.filters["is_primitive"] = openapi.is_primitive - env.filters["is_java_base_type"] = openapi.is_java_base_type - env.filters["is_model"] = openapi.is_model - env.filters["get_required_attributes"] = openapi.get_required_attributes - env.filters["escape_html"] = formatter.escape_html - env.filters["docstring"] = formatter.docstring - env.filters["inline_docstring"] = formatter.inline_docstring - env.filters["un_parameterize_type"] = formatter.un_parameterize_type - env.filters["is_parameterized_type"] = formatter.is_parameterized_type - env.filters["escape_method_reserved_name"] = formatter.escape_method_reserved_name - - env.globals["enumerate"] = enumerate - env.globals["get_name"] = openapi.get_name - env.globals["get_type_for_attribute"] = openapi.get_type_for_attribute - env.globals["get_type_for_parameter"] = openapi.get_type_for_parameter - env.globals["get_parameter_schema_from_name"] = openapi.get_parameter_schema_from_name - env.globals["get_type_at_path"] = openapi.get_type_at_path - - env.globals["get_type"] = openapi.type_to_java - env.globals["get_api_models"] = openapi.get_api_models - env.globals["common_package_name"] = COMMON_PACKAGE_NAME - env.globals["generated_annotation"] = GENERATED_ANNOTATION - env.globals["get_accessors"] = openapi.get_accessors - env.globals["get_default"] = openapi.get_default - env.globals["get_container_type"] = openapi.get_container_type - env.globals["get_security_names"] = openapi.get_security_names - env.globals["prepare_oneof_methods"] = formatter.prepare_oneof_methods - - api_j2 = env.get_template("Api.j2") - model_j2 = env.get_template("model.j2") - - common_files = { - "AbstractOpenApiSchema.java": env.get_template("AbstractOpenApiSchema.j2"), - "ApiClient.java": env.get_template("ApiClient.j2"), - "ApiException.java": env.get_template("ApiException.j2"), - "ApiResponse.java": env.get_template("ApiResponse.j2"), - "JSON.java": env.get_template("JSON.j2"), - "ModelEnum.java": env.get_template("modelEnumBase.j2"), - "JsonTimeSerializer.java": env.get_template("JsonTimeSerializer.j2"), - "Pair.java": env.get_template("Pair.j2"), - "RFC3339DateFormat.java": env.get_template("RFC3339DateFormat.j2"), - "ServerConfiguration.java": env.get_template("ServerConfiguration.j2"), - "ServerVariable.java": env.get_template("ServerVariable.j2"), - "StringUtil.java": env.get_template("StringUtil.j2"), - "PaginationIterable.java": env.get_template("PaginationIterable.j2"), - "PaginationIterator.java": env.get_template("PaginationIterator.j2"), - "UnparsedObject.java": env.get_template("UnparsedObject.j2"), - "ZstdEncoder.java": env.get_template("ZstdEncoder.j2"), - "RetryConfig.java": env.get_template("RetryConfig.j2"), - } - - auth_files = { - "ApiKeyAuth.java": env.get_template("auth/ApiKeyAuth.j2"), - "Authentication.java": env.get_template("auth/Authentication.j2"), - "HttpBasicAuth.java": env.get_template("auth/HttpBasicAuth.j2"), - "HttpBearerAuth.java": env.get_template("auth/HttpBearerAuth.j2"), - "OAuth.java": env.get_template("auth/OAuth.j2"), - "OAuthFlow.java": env.get_template("auth/OAuthFlow.j2"), - } - - output.mkdir(parents=True, exist_ok=True) - - auth_path = output / "auth" - auth_path.mkdir(parents=True, exist_ok=True) - for name, template in auth_files.items(): - filename = auth_path / name - with filename.open("w") as fp: - fp.write(template.render()) - - all_specs = {} - all_apis = {} - - for spec_path in specs: - spec = openapi.load(spec_path) - version = spec_path.parent.name - all_specs[version] = spec - - apis = openapi.apis(spec) - all_apis[version] = apis - models = openapi.models(spec) - - env.globals["openapi"] = spec - env.globals["package_name"] = PACKAGE_NAME.format(version) - env.globals["version"] = version - - model_dir = output / version / "model" - model_dir.mkdir(parents=True, exist_ok=True) - for name, model in models.items(): - model_path = model_dir / f"{name}.java" - with model_path.open("w") as fp: - fp.write(model_j2.render(name=name, model=model)) - - api_dir = output / version / "api" - api_dir.mkdir(parents=True, exist_ok=True) - for name, operations in apis.items(): - api_name = formatter.upperfirst(formatter.camel_case(name)) + "Api" - filename = api_dir / f"{api_name}.java" - with filename.open("w") as fp: - fp.write(api_j2.render(name=api_name, operations=operations)) - - for name, template in common_files.items(): - filename = output / name - with filename.open("w") as fp: - fp.write(template.render(specs=all_specs, apis=all_apis)) diff --git a/.generator/src/generator/formatter.py b/.generator/src/generator/formatter.py deleted file mode 100644 index 9916fe62243..00000000000 --- a/.generator/src/generator/formatter.py +++ /dev/null @@ -1,789 +0,0 @@ -"""Data formatter.""" -from functools import singledispatch -import re - -import markdown - -from . import openapi - -KEYWORDS = { - "abstract", - "assert", - "boolean", - "break", - "byte", - "case", - "catch", - "char", - "class", - "const", - "continue", - "default", - "double", - "do", - "else", - "enum", - "equals", - "extends", - "false", - "final", - "finally", - "float", - "for", - "goto", - "if", - "implements", - "import", - "instanceof", - "int", - "interface", - "long", - "native", - "new", - "null", - "package", - "private", - "protected", - "public", - "return", - "short", - "static", - "strictfp", - "super", - "switch", - "synchronized", - "this", - "throw", - "throws", - "transient", - "true", - "try", - "void", - "volatile", - "while", -} - - -HTML_ESCAPE_CHARACTERS = { - "&": "&", - "'": "'", - ">": ">", - "<": "<", - '"': """, - "`": "`", - "=": "=", - "\\": "\\\\", -} - -METHOD_KEYWORDS = { - "Class", -} - - -PATTERN_DOUBLE_UNDERSCORE = re.compile(r"__+") -PATTERN_LEADING_ALPHA = re.compile(r"(.)([A-Z][a-z0-9]+)") -PATTERN_FOLLOWING_ALPHA = re.compile(r"([a-z0-9])([A-Z])") -PATTERN_WHITESPACE = re.compile(r"\W") - -UN_PARAMETERIZE = re.compile(r"<[^>]+>") - - -# TODO: revisit this and find permanent solution -# Edge cases to maintain backward compatibility with Openapi generator -SNAKE_CASE_EDGE_CASES = { - "CN": "C_N", - "OU": "O_U", - "ST": "S_T", - "C": "C", - "O": "O", - "L": "L", - "logs_indexed_3day_sum": "logs_indexed3day_sum", - "logs_indexed_7day_sum": "logs_indexed7day_sum", - "logs_indexed_15day_sum": "logs_indexed15day_sum", - "logs_indexed_30day_sum": "logs_indexed30day_sum", - "logs_indexed_45day_sum": "logs_indexed45day_sum", - "logs_indexed_60day_sum": "logs_indexed60day_sum", - "logs_indexed_90day_sum": "logs_indexed90day_sum", - "logs_indexed_180day_sum": "logs_indexed180day_sum", -} - - -def snake_case(value): - if value in SNAKE_CASE_EDGE_CASES: - return SNAKE_CASE_EDGE_CASES[value] - - s1 = PATTERN_LEADING_ALPHA.sub(r"\1_\2", value) - s1 = PATTERN_FOLLOWING_ALPHA.sub(r"\1_\2", s1).lower() - s1 = PATTERN_WHITESPACE.sub("_", s1) - s1 = s1.rstrip("_") - return PATTERN_DOUBLE_UNDERSCORE.sub("_", s1) - - -def camel_case(value): - return "".join(upperfirst(x) if idx != 0 else x for idx, x in enumerate(snake_case(value).replace("-", "_").split("_"))) - - -def untitle_case(value): - return value[0].lower() + value[1:] - - -def upperfirst(value): - return value[0].upper() + value[1:] - -def escape_method_reserved_name(method_name): - """ - Escape reserved language keywords for method names like getClass, setClass, isClass, etc. - """ - if method_name in METHOD_KEYWORDS: - return f"{method_name}Attribute" - return method_name - - -def schema_name(schema): - if not schema: - return None - - if hasattr(schema, "__reference__"): - return schema.__reference__["$ref"].split("/")[-1] - - -def escape_reserved_keyword(word): - """ - Escape reserved language keywords like openapi generator does it - :param word: Word to escape - :return: The escaped word if it was a reserved keyword, the word unchanged otherwise - """ - if word in KEYWORDS: - return f"_{word}" - return word - - -def attribute_name(attribute): - return escape_reserved_keyword(upperfirst(camel_case(attribute))) - - -def variable_name(attribute): - return escape_reserved_keyword((camel_case(attribute))) - - -def un_parameterize_type(type): - return UN_PARAMETERIZE.sub("", type) - - -def is_parameterized_type(type): - """Check if a type has generic parameters (e.g., List).""" - return '<' in type and '>' in type - - -def format_value(value, quotes='"', schema=None, default_value=False, type_=None): - if schema: - if "enum" in schema and default_value: - index = schema["enum"].index(value) - enum_varnames = schema["x-enum-varnames"][index] - name = schema_name(schema) - return f"{name}.{enum_varnames}" - if "oneOf" in schema and default_value: - if isinstance(value, bool): - value = "true" if value else "false" - if type_: - return f"new {type_}({value})" - if schema.get("type") == "integer": - if schema.get("format") == "int64": - return f"{value}l" - if schema.get("type") == "string": - return f'"{value}"' - if isinstance(value, str): - return f"{quotes}{value}{quotes}" - elif isinstance(value, bool): - return "true" if value else "false" - elif value is None: - return "null" - return value - - -def simple_type(schema): - """Return the simple type of a schema. - - :param schema: The schema to extract the type from - :return: The simple type name - """ - type_name = schema.get("type") - type_format = schema.get("format") - - if type_name == "integer": - return { - "int32": "Integer", - "int64": "Long", - None: "Integer", - }[type_format] - - if type_name == "number": - return { - "double": "Double", - None: "Integer", - }[type_format] - - if type_name == "string": - return { - "date": "OffsetDateTime", - "date-time": "OffsetDateTime", - "binary": "File", - "uuid": "UUID", - }.get(type_format, "String") - if type_name == "boolean": - return "Boolean" - - return None - - -def escape_html(text): - if not text: - return "" - text = " ".join(text.splitlines()) - return "".join(HTML_ESCAPE_CHARACTERS.get(c, c) for c in text) - - -def docstring(text, indent=3): - if not text: - return "" - blank = " " * indent - return "\n".join("{}* {}".format(blank, line) for line in markdown.markdown(text).replace("h4>", "h3>").replace("h5>", "h4>").splitlines()) - - -def inline_docstring(text): - if not text: - return "" - return markdown.markdown(text).replace("

", "").replace("

", "").replace("\n", " ") - - -def format_parameters(kwargs, spec, replace_values=None, has_body=False): - parameters_spec = {p["name"]: p for p in spec.get("parameters", [])} - required_parameters = "" - optional_parameters = "" - has_optional = False - imports = set() - - parameters_spec = {p["name"]: p for p in spec.get("parameters", [])} - if "requestBody" in spec and "multipart/form-data" in spec["requestBody"]["content"]: - parent = spec["requestBody"]["content"]["multipart/form-data"]["schema"] - for name, schema in parent["properties"].items(): - parameters_spec[name] = { - "in": "form", - "schema": schema, - "name": name, - "description": schema.get("description"), - "required": name in parent.get("required", []), - } - - for p in parameters_spec.values(): - required = p.get("required", False) - if required: - k = p["name"] - v = kwargs.pop(k) # otherwise there is a missing required parameters - _, parameters, extra_imports = format_data_with_schema( - v["value"], - p["schema"], - replace_values=replace_values, - ) - imports |= extra_imports - required_parameters += f", {parameters}" if required_parameters else parameters - else: - has_optional = True - - body_is_required = spec.get("requestBody", {"required": None}).get("required", False) - if has_body and body_is_required: - required_parameters += ", body" if required_parameters else "body" - elif has_body and not body_is_required: - if not optional_parameters: - optional_parameters = f"new {spec.get('operationId')}OptionalParameters()" - optional_parameters += ".body(body)" - - if has_optional: - for k, v in kwargs.items(): - _, parameters, extra_imports = format_data_with_schema( - v["value"], - parameters_spec[k]["schema"], - replace_values=replace_values, - ) - if not optional_parameters: - optional_parameters = f"new {spec.get('operationId')}OptionalParameters()" - imports |= extra_imports - optional_parameters += f".{untitle_case(camel_case(k))}({parameters})" - return required_parameters, optional_parameters, imports - - -def get_name_and_imports(schema): - imports = set() - - name = None - if hasattr(schema, "__reference__"): - name = schema.__reference__["$ref"].split("/")[-1] - schema_type = schema.get("type") - if schema_type == "array": - if hasattr(schema["items"], "__reference__"): - name = schema["items"].__reference__["$ref"].split("/")[-1] - imports.add(name) - name = f"List<{name}>" - return name, imports - if schema_type == "string": - if schema.get("enum"): - imports.add(name) - return name, imports - - if "additionalProperties" not in schema or not schema["additionalProperties"]: - imports.add(name) - - return name, imports - - -def _format_oneof(schema, data, name, default_name, replace_values, imports): - matched = 0 - matched_sub_schema = None - extra_imports = one_of_imports = set() - for sub_schema in schema["oneOf"]: - try: - if "items" in sub_schema and not isinstance(data, list): - continue - if sub_schema.get("nullable") and data is None: - # only one schema can be nullable - value = "null" - else: - sub_schema["nullable"] = False - named, value, one_of_imports = format_data_with_schema( - data, - sub_schema, - default_name=default_name, - replace_values=replace_values, - ) - if matched == 0: - # NOTE we do not support mixed schemas with oneOf - # parameters += formatted - parameters = value - extra_imports = one_of_imports - matched_sub_schema = sub_schema - matched += 1 - except (KeyError, ValueError, TypeError) as e: - print(f"{e}") - - if matched != 1: - raise ValueError(f"[{matched}] {data} is not valid for schema {name}") - - imports |= extra_imports - - # Detect if we need to use factory method due to type erasure collision - if name: - # Use prepare_oneof_methods to detect collisions - from . import openapi - methods_info = prepare_oneof_methods(schema, openapi.type_to_java) - - # Find the method info for the matched sub_schema - for method_info in methods_info: - if method_info['schema'] == matched_sub_schema: - if method_info['use_factory']: - # Use static factory method - return name, f"{name}.{method_info['constructor_name']}(\n{parameters})", imports - else: - # Use regular constructor - return name, f"new {name}(\n{parameters})", imports - - # Fallback to regular constructor if no match found - return name, f"new {name}(\n{parameters})", imports - elif "oneOf" in schema and default_name: - imports.add(f"{default_name}Item") - return name, f"new {default_name}Item(\n{parameters})", imports - return name, parameters, imports - - -@singledispatch -def format_data_with_schema( - data, - schema, - replace_values=None, - default_name=None, -): - name, imports = get_name_and_imports(schema) - nullable = schema.get("nullable", False) - if "enum" in schema: - if nullable and data is None: - pass - elif data not in schema["enum"]: - raise ValueError(f"{data} is not valid enum value {schema['enum']}") - - if replace_values and data in replace_values: - parameters = replace_values[data] - # date time is currently retrieved as a Long. We need to convert it to a double - if isinstance(parameters, str) and schema.get("format") == "double": - parameters = f"Long.valueOf({parameters}).doubleValue()" - else: - if nullable and data is None: - parameters = "null" - else: - - def format_number(x): - if isinstance(x, bool | str): - raise TypeError(f"{x} is not supported type {schema}") - return str(x) - - def format_double(x): - if isinstance(x, bool | str): - raise TypeError(f"{x} is not supported type {schema}") - return float(x) - - def format_int(x): - if isinstance(x, bool | str): - raise TypeError(f"{x} is not supported type {schema}") - return str(x) - - def format_int64(x): - if isinstance(x, bool | str): - raise TypeError(f"{x} is not supported type {schema}") - return str(x) + "L" - - def format_string(x): - if isinstance(x, bool): - raise TypeError(f"{x} is not supported type {schema}") - if "\n" in x or '"' in x: - return f'"""\n{x}\n"""' - return f'"{x}"' if x else '""' - - def format_datetime(x): - return f"OffsetDateTime.parse({format_string(x)})" - - schema = schema.copy() - - def format_interface(x): - if isinstance(x, int): - return str(x) - if isinstance(x, float): - return str(x) - if isinstance(x, str): - return format_string(x) - raise TypeError(f"{x} is not supported type {schema}") - - def format_bool(x): - if not isinstance(x, bool): - raise TypeError(f"{x} is not supported type {schema}") - return "true" if x else "false" - - def format_uuid(x): - return f'UUID.fromString("{x}")' - - def open_file(x): - return f"new File({format_string(x)})" - - formatters = { - "int32": format_int, - "int64": format_int64, - "double": format_double, - "date-time": format_datetime, - "number": format_number, - "integer": format_number, - "boolean": format_bool, - "string": format_string, - "email": format_string, - "binary": open_file, - "uuid": format_uuid, - None: format_interface, - } - schema_type = schema.get("type") - formatter = formatters.get(schema.get("format", schema_type), formatters.get(schema_type)) - - parameters = formatter(data) - - if "enum" in schema and name: - if data is not None: - # find schema index and get name from x-enum-varnames - index = schema["enum"].index(data) - enum_varnames = schema["x-enum-varnames"][index] - parameters = f"{name}.{enum_varnames}" - imports.add(name) - - if schema.get("nullable") and schema.get("type") is not None: - return name, parameters, imports - - if "oneOf" in schema: - return _format_oneof(schema, data, name, default_name, replace_values, imports) - - return name, parameters, imports - - -@format_data_with_schema.register(list) -def format_data_with_schema_list( - data, - schema, - replace_values=None, - default_name=None, -): - name, imports = get_name_and_imports(schema) - - if "oneOf" in schema: - matched_sub_schema = None - for sub_schema in schema["oneOf"]: - try: - named, value, one_of_imports = format_data_with_schema( - data, - sub_schema, - default_name=default_name, - replace_values=replace_values, - ) - matched_sub_schema = sub_schema - except (KeyError, ValueError): - continue - - if matched_sub_schema.get("x-generate-alias-as-model"): - alias_name = schema_name(matched_sub_schema) - one_of_imports.add(alias_name) - value = f"new {alias_name}({value})" - - if name: - one_of_imports.add(f"{name}") - # Detect if we need to use factory method due to type erasure collision - from . import openapi - methods_info = prepare_oneof_methods(schema, openapi.type_to_java) - - # Find the method info for the matched sub_schema - for method_info in methods_info: - if method_info['schema'] == matched_sub_schema: - if method_info['use_factory']: - # Use static factory method - value = f"{name}.{method_info['constructor_name']}({value})" - else: - # Use regular constructor - value = f"new {name}({value})" - break - else: - # Fallback to regular constructor if no match found - value = f"new {name}({value})" - elif default_name: - one_of_imports.add(f"{default_name}Item") - value = f"new {default_name}Item({value})" - - return name, value, one_of_imports - raise ValueError(f"{data} is not valid oneOf {schema}") - - parameters = "" - param_count = 0 - for d in data: - _, value, extra_imports = format_data_with_schema( - d, - schema["items"], - replace_values=replace_values, - default_name=name, - ) - - parameters += f", {value}" if parameters else f"{value}" - param_count += 1 - imports |= extra_imports - - if param_count > 1: - parameters = f"Arrays.asList({parameters})" - elif param_count == 1: - parameters = f"Collections.singletonList({parameters})" - - return name, parameters, imports - - -@format_data_with_schema.register(dict) -def format_data_with_schema_dict( - data, - schema, - replace_values=None, - default_name=None, -): - name, imports = get_name_and_imports(schema) - - if "properties" in schema: - assert "oneOf" not in schema - - required_properties = set(schema.get("required", [])) - missing = required_properties - set(data.keys()) - if missing: - raise ValueError(f"missing required properties: {missing}") - additionalProperties = set(data.keys()) - set(schema["properties"].keys()) - if schema.get("additionalProperties") == False and additionalProperties: - raise ValueError(f"additional properties not allowed: {additionalProperties}") - - if name is None: - name = default_name - parameters = f"new {name}()" - - for k, v in data.items(): - if k not in schema["properties"]: - continue - r, value, extra_imports = format_data_with_schema( - v, - schema["properties"][k], - replace_values=replace_values, - default_name=name + upperfirst(k) if name else None, - ) - if value: - parameters += f"\n.{escape_reserved_keyword(untitle_case(camel_case(k)))}({value})" - imports |= extra_imports - - if name not in imports: - imports.add(name) - - if not schema.get("additionalProperties"): - return name, parameters, imports - - if schema.get("additionalProperties"): - assert "oneOf" not in schema - has_properties = schema.get("properties") - if has_properties: - if not parameters: - if name is None: - name = default_name - parameters = f"new {name}()" - else: - parameters = "" - - for k, v in data.items(): - if has_properties and k in schema["properties"]: - continue - r, value, extra_imports = format_data_with_schema( - v, - schema["additionalProperties"], - replace_values=replace_values, - default_name=name + untitle_case(camel_case(k)) if name else None, - ) - if has_properties: - parameters += f'\n.putAdditionalProperty("{k}", {value})' - else: - parameters += f'Map.entry("{k}", {value}),' - imports |= extra_imports - - if has_properties: - return name, parameters, imports - else: - return ( - "Map".format(openapi.type_to_java(schema["additionalProperties"])), - f"Map.ofEntries({parameters.rstrip(',')})", - imports, - ) - - if "oneOf" in schema: - return _format_oneof(schema, data, name, default_name, replace_values, imports) - - # NOTE this is a special case for unnamed objects that should be avoided in the future - if schema.get("type") == "object" and not data and "additionalProperties" not in schema: - return "Object", "new Object()", set() - - if schema.get("type") == "object" and "properties" not in schema and schema.get("additionalProperties") == {}: - parameters = "" - for k, v in data.items(): - parameters += f'Map.entry("{k}", "{v}"),' - return ( - "Map", - f"Map.ofEntries({parameters.rstrip(',')})", - imports, - ) - - raise ValueError(f"{data} is not valid for schema {name}") - - -def get_response_type(schema, version): - if "content" not in schema: - return None, None - - response_schema = list(schema["content"].values())[0]["schema"] - if response_schema.get("format") == "binary": - return "File", "java.io.File" - - if response_schema.get("type") == "array": - nested_schema = response_schema.get("items") - name = schema_name(nested_schema) - if name: - api_response_type = f"List<{name}>" - else: - api_response_type = f"List<{simple_type(nested_schema)}>" - else: - primitive = simple_type(response_schema) - name = schema_name(response_schema) - if name and not primitive: - api_response_type = name - else: - # Named primitive schemas (e.g. type: string) don't produce a class — - # use the primitive type directly and suppress the model import. - api_response_type = primitive - name = None - - if name: - return api_response_type, f"com.datadog.api.client.{version}.model.{name}" - return api_response_type, None - - -def attribute_path(attribute): - return ".".join(attribute_name(a) for a in attribute.split(".")) - - -def prepare_oneof_methods(model, get_type_func): - """ - Pre-compute method information for oneOf types to handle erasure collisions. - - Returns a list of dicts with: - - schema: the original oneOf schema - - param_type: full parameterized type (e.g., "List") - - unparam_type: unparameterized type (e.g., "List") - - use_factory: True if factory method needed (collision detected) - - constructor_name: name for constructor/factory method - - getter_name: name for getter method - """ - # Handle both dict-style and object-style access - if isinstance(model, dict): - one_of = model.get('oneOf', []) - elif hasattr(model, 'oneOf'): - one_of = model.oneOf - elif hasattr(model, 'get'): - one_of = model.get('oneOf', []) - else: - return [] - - if not one_of: - return [] - - # First pass: count unparameterized types - unparam_counts = {} - for oneOf in one_of: - param_type = get_type_func(oneOf) - unparam_type = un_parameterize_type(param_type) - unparam_counts[unparam_type] = unparam_counts.get(unparam_type, 0) + 1 - - # Second pass: compute method names - result = [] - for oneOf in one_of: - param_type = get_type_func(oneOf) - unparam_type = un_parameterize_type(param_type) - has_collision = unparam_counts[unparam_type] > 1 - - # Compute constructor/factory method name - if has_collision: - if param_type.startswith('List<'): - inner_type = param_type[5:-1] - constructor_name = f"from{inner_type}List" - else: - safe_type = param_type.replace('<', '').replace('>', '').replace(' ', '').replace(',', '') - constructor_name = f"from{safe_type}" - else: - constructor_name = None # Regular constructor - - # Compute getter method name - if has_collision: - if param_type.startswith('List<'): - inner_type = param_type[5:-1] - getter_name = f"get{inner_type}List" - else: - safe_type = param_type.replace('<', '').replace('>', '').replace(' ', '').replace(',', '') - getter_name = f"get{safe_type}" - else: - getter_name = f"get{unparam_type}" - - result.append({ - 'schema': oneOf, - 'param_type': param_type, - 'unparam_type': unparam_type, - 'use_factory': has_collision, - 'constructor_name': constructor_name, - 'getter_name': getter_name, - }) - - return result diff --git a/.generator/src/generator/openapi.py b/.generator/src/generator/openapi.py deleted file mode 100644 index 4f71c9e5865..00000000000 --- a/.generator/src/generator/openapi.py +++ /dev/null @@ -1,644 +0,0 @@ -import hashlib -import json -import pathlib -import random -import uuid - -import yaml -import warnings -from jsonref import JsonRef -from urllib.parse import urlparse -from yaml import CSafeLoader - -from . import formatter - -PRIMITIVE_TYPES = ["string", "number", "boolean", "integer"] -JAVA_TYPES = ["long", "double", "list", "map", "integer", "string", "boolean"] - - -def load(filename): - path = pathlib.Path(filename) - with path.open() as fp: - return JsonRef.replace_refs(yaml.load(fp, Loader=CSafeLoader)) - - -def is_model(schema): - if "properties" in schema or "oneOf" in schema: - return True - return False - - -def is_primitive(schema): - # We resolve enums to ClassName.ENUM so don't treat enum's as primitive - if schema.get("type") in PRIMITIVE_TYPES and "enum" not in schema: - return True - return False - - -def is_java_base_type(type): - return type in JAVA_TYPES - - -def get_required_attributes(schema): - required_attr_list = schema.get("required", []) - properties = schema.get("properties", {}) - return {k: v for k, v in properties.items() if k in required_attr_list} - - -def has_additional_properties(schema): - return schema.get("additionalProperties") not in (None, False) - - -def get_api_models(operations): - seen = set() - for _, _, operation in operations: - for response in operation.get("responses", {}).values(): - for content in response.get("content", {}).values(): - if "schema" in content: - name = formatter.schema_name(content["schema"]) - if name and name not in seen and "items" not in content["schema"]: - seen.add(name) - yield name - elif "items" in content["schema"]: - name = formatter.schema_name(content["schema"]["items"]) - if name and name not in seen: - seen.add(name) - yield name - break - for content in operation.get("parameters", []): - if "schema" in content and ( - content["schema"].get("type") in ("object", "array") or content["schema"].get("enum") - ): - name = formatter.schema_name(content["schema"]) - if name and name not in seen: - seen.add(name) - yield name - elif "items" in content["schema"]: - name = formatter.schema_name(content["schema"]["items"]) - if name and name not in seen: - seen.add(name) - yield name - if "requestBody" in operation: - for content in operation["requestBody"].get("content", {}).values(): - if ( - "schema" in content - and "items" not in content["schema"] - and (not has_additional_properties(content["schema"]) or "properties" in content["schema"]) - ): - name = formatter.schema_name(content["schema"]) - if name and name not in seen: - seen.add(name) - yield name - elif "items" in content["schema"]: - name = formatter.schema_name(content["schema"]["items"]) - if name and name not in seen: - seen.add(name) - yield name - if "additionalProperties" in content["schema"] and "items" in content["schema"]["additionalProperties"]: - name = formatter.schema_name(content["schema"]["additionalProperties"]["items"]) - if name and name not in seen: - seen.add(name) - yield name - - -def get_name(schema): - name = None - if hasattr(schema, "__reference__"): - name = schema.__reference__["$ref"].split("/")[-1] - - return name - - -def type_to_java(schema, alternative_name=None, render_new=False): - """Return Java type name for the type.""" - prefix = "" - if "enum" not in schema: - name = formatter.simple_type(schema) - if name is not None: - return name - - name = get_name(schema) - if name: - if "enum" in schema: - return prefix + name - if ( - not (has_additional_properties(schema) and not schema.get("properties")) - and schema.get("type", "object") == "object" - ): - return prefix + name - - type_ = schema.get("type") - if type_ is None: - if "items" in schema: - type_ = "array" - elif "properties" in schema: - type_ = "object" - else: - type_ = "object" - warnings.warn(f"Unknown type for schema: {schema} ({name or alternative_name})") - - if type_ == "array": - if schema.get("x-generate-alias-as-model", False): - return name - if name or alternative_name: - alternative_name = (name or alternative_name) + "Item" - name = type_to_java(schema["items"], alternative_name=alternative_name) - return "List<{}>".format(name) - elif type_ == "object": - if has_additional_properties(schema) and not schema.get("properties"): - if render_new: - return "HashMap".format(type_to_java(schema["additionalProperties"])) - return "Map".format(type_to_java(schema["additionalProperties"])) - - if schema.get("parent") and not alternative_name: - if schema["parent"].get("type") == "array": - if schema.get("type") is not None: - return get_name(schema["parent"]) + "Item" - - return ( - prefix + alternative_name - if alternative_name - and ("properties" in schema or "oneOf" in schema or "anyOf" in schema or "allOf" in schema) - else "Object" - ) - - raise ValueError(f"Unknown type {type_}") - - -def get_type_for_attribute(schema, attribute, current_name=None): - """Return Java type name for the attribute.""" - child_schema = schema.get("properties", {}).get(attribute) - alternative_name = current_name + formatter.camel_case(attribute) if current_name else None - return type_to_java(child_schema, alternative_name=alternative_name) - - -def get_type_for_parameter(parameter): - """Return Java type name for the parameter.""" - if "content" in parameter: - assert "in" not in parameter - for content in parameter["content"].values(): - return type_to_java(content["schema"]) - return type_to_java(parameter.get("schema")) - - -def get_parameter_schema_from_name(name, all_params): - for param_name, parameter in all_params: - if param_name == name: - return parameter - - -def get_type_at_path(operation, attribute_path): - content = None - for code, response in operation.get("responses", {}).items(): - if int(code) >= 300: - continue - for content in response.get("content", {}).values(): - if "schema" in content: - break - if content is None: - raise RuntimeError("Default response not found") - content = content["schema"] - if not attribute_path: - return type_to_java(content["items"]) - for attr in attribute_path.split("."): - content = content["properties"][attr] - return type_to_java(content["items"]) - - -def child_models(schema, alternative_name=None, seen=None, parent=None): - seen = seen or set() - current_name = get_name(schema) - name = current_name or alternative_name - - if parent is not None: - schema["parent"] = parent - - has_sub_models = False - if "allOf" in schema: - has_sub_models = True - for index in range(len(schema["allOf"])): - yield from child_models(schema["allOf"][index], seen=seen, parent=schema) - if "oneOf" in schema: - has_sub_models = True - for index in range(len(schema["oneOf"])): - yield from child_models(schema["oneOf"][index], seen=seen, parent=schema) - if "anyOf" in schema: - has_sub_models = True - for index in range(len(schema["anyOf"])): - yield from child_models(schema["anyOf"][index], seen=seen, parent=schema) - - if "items" in schema: - if current_name is not None and schema.get("x-generate-alias-as-model", False): - if name in seen: - return - seen.add(name) - yield name, schema - - yield from child_models( - schema["items"], - alternative_name=name + "Item" if name is not None else None, - seen=seen, - parent=schema, - ) - - if (schema.get("type") == "object" or "properties" in schema or has_sub_models) and ( - not (has_additional_properties(schema) and not schema.get("properties")) - ): - if not has_sub_models and name is None: - # this is a basic map object so we don't need a type - return - - if name is None: - raise ValueError(f"Schema {schema} has no name") - - if name in seen: - return - - if "properties" in schema or has_sub_models: - seen.add(name) - yield name, schema - - for key in schema.get("properties", {}): - yield from child_models( - schema["properties"][key], - alternative_name=name + formatter.camel_case(key), - seen=seen, - # parent=schema, - ) - - if "enum" in schema: - if name is None: - raise ValueError(f"Schema {schema} has no name") - - if name in seen: - return - - seen.add(name) - yield name, schema - - if has_additional_properties(schema): - nested_name = get_name(schema["additionalProperties"]) - if nested_name: - yield from child_models( - schema["additionalProperties"], - seen=seen, - # parent=schema, - ) - - -def models(spec): - name_to_schema = {} - - for path in spec["paths"]: - if path.startswith("x-"): - continue - for method in spec["paths"][path]: - operation = spec["paths"][path][method] - - for content in operation.get("parameters", []): - if "schema" in content: - name_to_schema.update(dict(child_models(content["schema"]))) - - for content in operation.get("requestBody", {}).get("content", {}).values(): - if "schema" in content: - name_to_schema.update(dict(child_models(content["schema"]))) - - for response in operation.get("responses", {}).values(): - for content in response.get("content", {}).values(): - if "schema" in content: - name_to_schema.update(dict(child_models(content["schema"]))) - - return name_to_schema - - -def apis(spec): - operations = {} - - for path in spec["paths"]: - if path.startswith("x-"): - continue - for method in spec["paths"][path]: - operation = spec["paths"][path][method] - tag = operation.get("tags", [None])[0] - operations.setdefault(tag, []).append((path, method, operation)) - - return operations - - -def operation(spec, operation_id): - for path in spec["paths"]: - for method in spec["paths"][path]: - operation = spec["paths"][path][method] - if operation["operationId"] == operation_id: - return operation - return None - - -def parameters(operation): - for content in operation.get("parameters", []): - if "schema" in content and content.get("required"): - yield content["name"], content - - if "requestBody" in operation: - if "multipart/form-data" in operation["requestBody"]["content"]: - parent = operation["requestBody"]["content"]["multipart/form-data"]["schema"] - for name, schema in parent["properties"].items(): - yield name, { - "in": "form", - "schema": schema, - "name": name, - "description": schema.get("description"), - "required": name in parent.get("required", []), - } - else: - name = operation.get("x-codegen-request-body-name", "body") - yield name, operation["requestBody"] - - for content in operation.get("parameters", []): - if "schema" in content and not content.get("required"): - yield content["name"], content - - -def parameter_schema(parameter): - if "schema" in parameter: - return parameter["schema"] - if "content" in parameter: - for content in parameter.get("content", {}).values(): - if "schema" in content: - return content["schema"] - raise ValueError(f"Unknown schema for parameter {parameter}") - - -def return_type(operation): - for response in operation.get("responses", {}).values(): - for content in response.get("content", {}).values(): - if "schema" in content: - return type_to_java(content["schema"]) - return - - -def accept_headers(operation): - any_type = "*/*" - seen = [] - for response in operation.get("responses", {}).values(): - if "content" in response: - for media_type in response["content"].keys(): - if media_type not in seen: - seen.append(media_type) - else: - return [any_type] - return seen - - -def collection_format(parameter): - in_to_style = { - "query": "form", - "path": "simple", - "header": "simple", - "cookie": "form", - } - schema = parameter_schema(parameter) - matrix = { - ("form", False): "csv", - ("form", True): "multi", - # TODO add more cases from https://swagger.io/specification/#parameter-style - } - if schema.get("type") == "array" or "items" in schema: - in_ = parameter.get("in", "query") - style = parameter.get("style", in_to_style[in_]) - explode = parameter.get("explode", True if style == "form" else False) - return matrix.get((style, explode), "multi") - return "" - - -def format_server(server, server_variables=None, path=""): - url = server["url"] + path - # replace potential path variables - for variable, value in (server_variables or {}).items(): - url = url.replace("{" + variable + "}", value) - # replace server variables if they were not replace before - for variable in server["variables"]: - if server_variables and variable in server_variables: - continue - url = url.replace("{" + variable + "}", server["variables"][variable]["default"]) - return urlparse(url) - - -def server_url_and_method(spec, operation_id, server_index=0, server_variables=None): - for path in spec["paths"]: - for method in spec["paths"][path]: - operation = spec["paths"][path][method] - if operation["operationId"] == operation_id: - if "servers" in operation: - server = operation["servers"][server_index] - else: - server = spec["servers"][server_index] - return ( - format_server(server, server_variables=server_variables, path=path).geturl(), - method, - ) - - raise ValueError(f"Operation {operation_id} not found") - - -def response_code_and_accept_type(operation, status_code=None): - for response in operation["responses"]: - if status_code is None: - return int(response), next(iter(operation["responses"][response].get("content", {None: None}))) - if response == str(status_code): - return status_code, next(iter(operation["responses"][response].get("content", {None: None}))) - return status_code, None - - -def request_content_type(operation, status_code=None): - return next(iter(operation.get("requestBody", {}).get("content", {None: None}))) - - -def response(operation, status_code=None): - for response in operation["responses"]: - if status_code is None or response == str(status_code): - return list(operation["responses"][response]["content"].values())[0]["schema"] - return None - - -def generate_value(schema, use_random=False, prefix=None): - spec = schema.spec - if not use_random: - if "example" in spec: - return spec["example"] - if "default" in spec: - return spec["default"] - - if spec["type"] == "string": - if use_random: - return str( - uuid.UUID( - bytes=hashlib.sha256( - str(prefix or schema.keys).encode("utf-8"), - ).digest()[:16] - ) - ) - return "string" - elif spec["type"] == "integer": - return random.randint(0, 32000) if use_random else len(str(prefix or schema.keys)) - elif spec["type"] == "number": - return random.random() if use_random else 1.0 / len(str(prefix or schema.keys)) - elif spec["type"] == "boolean": - return True - elif spec["type"] == "array": - return [generate_value(schema[0], use_random=use_random)] - elif spec["type"] == "object": - return {key: generate_value(schema[key], use_random=use_random) for key in spec["properties"]} - else: - raise TypeError(f"Unknown type: {spec['type']}") - - -class Schema: - def __init__(self, spec, value=None, keys=None): - self.spec = spec - self.value = value if value is not None else generate_value - self.keys = keys or tuple() - - def __getattr__(self, key): - return self[key] - - def __getitem__(self, key): - type_ = self.spec.get("type", "object") - if type_ == "object": - try: - return self.__class__( - self.spec["properties"][key], - value=self.value, - keys=self.keys + (key,), - ) - except KeyError: - if "oneOf" in self.spec: - for schema in self.spec["oneOf"]: - if schema.get("type", "object") == "object": - try: - return self.__class__( - schema["properties"][key], - value=self.value, - keys=self.keys + (key,), - ) - except KeyError: - pass - raise KeyError(f"{key} not found in {self.spec.get('properties', {}).keys()}: {self.spec}") - if type_ == "array": - return self.__class__(self.spec["items"], value=self.value, keys=self.keys + (key,)) - - raise KeyError(f"{key} not found in {self.spec}") - - def __repr__(self): - value = self.value(self) - if isinstance(value, (dict, list)): - return json.dumps(value, indent=2) - return str(value) - - -def get_accessors(param_path, schema={}): - if not param_path: - return False, [], [] - param_path = param_path.split(".") - optional = False - getter, setter = [], [] - if schema: - optional = not schema.get("required", True) - param_name = formatter.variable_name(param_path.pop(0)) - getter, setter = [param_name], [param_name] - - for part in param_path: - getter.append(f"get{formatter.attribute_name(part)}") - setter.append(f"set{formatter.attribute_name(part)}") - - return optional, getter, setter - - -def get_default(operation, attribute_path): - attrs = attribute_path.split(".") - for name, parameter in parameters(operation): - if name == attrs[0]: - break - if name == attribute_path: - # We found a top level attribute matching the full path, let's use the default - return formatter.format_value(parameter["schema"]["default"], schema=parameter["schema"]) - - if name == "body": - parameter = next(iter(parameter["content"].values()))["schema"] - for attr in attrs[1:]: - parameter = parameter["properties"][attr] - - return formatter.format_value(parameter["default"], schema=parameter) - - -def get_container_type(operation, attribute_path, stop=None): - attrs = attribute_path.split(".")[:stop] - for name, parameter in parameters(operation): - if name == attrs[0]: - break - - if attrs[0] == "body": - parameter = next(iter(parameter["content"].values())) - - if name == attrs[0] and len(attrs) == 1: - return type_to_java(parameter["schema"]) - - parameter = parameter["schema"] - for attr in attrs[1:]: - parameter = parameter["properties"][attr] - return type_to_java(parameter) - - -def get_security_names(security): - if security is None: - return [] - - auth_names = set() - for auth in security: - for key in auth.keys() if isinstance(auth, dict) else [auth]: - auth_names.add(key) - - return list(auth_names) - - -class Operation: - def __init__(self, name, spec, method, path): - self.name = name - self.spec = spec - self.method = method - self.path = path - - def server_url_and_method(self, spec, server_index=0, server_variables=None): - def format_server(server, path): - url = server["url"] + path - # replace potential path variables - for variable, value in server_variables.items(): - url = url.replace("{" + variable + "}", value) - # replace server variables if they were not replace before - for variable in server["variables"]: - if variable in server_variables: - continue - url = url.replace("{" + variable + "}", server["variables"][variable]["default"]) - return url - - server_variables = server_variables or {} - if "servers" in self.spec: - server = self.spec["servers"][server_index] - else: - server = spec["servers"][server_index] - return format_server(server, self.path), self.method - - def response_code_and_accept_type(self): - for response in self.spec["responses"]: - return int(response), next(iter(self.spec["responses"][response].get("content", {None: None}))) - return None, None - - def request_content_type(self): - return next(iter(self.spec.get("requestBody", {}).get("content", {None: None}))) - - def response(self): - for response in self.spec["responses"]: - return Schema(next(iter((self.spec["responses"][response]["content"].values())))["schema"]) - - def request(self): - return Schema(next(iter(self.spec["requestBody"]["content"].values()))["schema"]) diff --git a/.generator/src/generator/templates/AbstractOpenApiSchema.j2 b/.generator/src/generator/templates/AbstractOpenApiSchema.j2 deleted file mode 100644 index ffebbcbe213..00000000000 --- a/.generator/src/generator/templates/AbstractOpenApiSchema.j2 +++ /dev/null @@ -1,134 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.Map; -import java.util.Objects; -import jakarta.ws.rs.core.GenericType; - -/** Abstract class for oneOf,anyOf schemas defined in OpenAPI spec */ -@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") -public abstract class AbstractOpenApiSchema { - - // store the actual instance of the schema/object - private Object instance; - - // is nullable - private Boolean isNullable; - - // schema type (e.g. oneOf, anyOf) - private final String schemaType; - - public AbstractOpenApiSchema(String schemaType, Boolean isNullable) { - this.schemaType = schemaType; - this.isNullable = isNullable; - } - - /** - * Get the list of oneOf/anyOf composed schemas allowed to be stored in this object - * - * @return an instance of the actual schema/object - */ - public abstract Map getSchemas(); - - /** - * Get the actual instance - * - * @return an instance of the actual schema/object - */ - @JsonValue - public Object getActualInstance() { - return instance; - } - - /** - * Set the actual instance - * - * @param instance the actual instance of the schema/object - */ - public void setActualInstance(Object instance) { - this.instance = instance; - } - - /** - * Get the instant recursively when the schemas defined in oneOf/anyof happen to be oneOf/anyOf - * schema as well - * - * @return an instance of the actual schema/object - */ - public Object getActualInstanceRecursively() { - return getActualInstanceRecursively(this); - } - - private Object getActualInstanceRecursively(AbstractOpenApiSchema object) { - if (object.getActualInstance() == null) { - return null; - } else if (object.getActualInstance() instanceof AbstractOpenApiSchema) { - return getActualInstanceRecursively((AbstractOpenApiSchema) object.getActualInstance()); - } else { - return object.getActualInstance(); - } - } - - /** - * Get the schema type (e.g. anyOf, oneOf) - * - * @return the schema type - */ - public String getSchemaType() { - return schemaType; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class ").append(getClass()).append(" {\n"); - sb.append(" instance: ").append(toIndentedString(instance)).append("\n"); - sb.append(" isNullable: ").append(toIndentedString(isNullable)).append("\n"); - sb.append(" schemaType: ").append(toIndentedString(schemaType)).append("\n"); - sb.append('}'); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - AbstractOpenApiSchema a = (AbstractOpenApiSchema) o; - return Objects.equals(this.instance, a.instance) - && Objects.equals(this.isNullable, a.isNullable) - && Objects.equals(this.schemaType, a.schemaType); - } - - @Override - public int hashCode() { - return Objects.hash(instance, isNullable, schemaType); - } - - /** - * Is nullable - * - * @return true if it's nullable - */ - public Boolean isNullable() { - if (Boolean.TRUE.equals(isNullable)) { - return Boolean.TRUE; - } else { - return Boolean.FALSE; - } - } -} diff --git a/.generator/src/generator/templates/Api.j2 b/.generator/src/generator/templates/Api.j2 deleted file mode 100644 index 01b6fb678a1..00000000000 --- a/.generator/src/generator/templates/Api.j2 +++ /dev/null @@ -1,475 +0,0 @@ -{#-{% include "ApiInfo.j2" %}#} -package {{ package_name }}.api; - -import {{ common_package_name }}.ApiClient; -import {{ common_package_name }}.ApiException; -import {{ common_package_name }}.ApiResponse; -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.PaginationIterable; - -import jakarta.ws.rs.core.GenericType; -import jakarta.ws.rs.client.Invocation; - -import java.io.File; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.LinkedHashMap; -import java.util.concurrent.CompletableFuture; -import java.time.OffsetDateTime; -import java.util.UUID; - -{%- for model in get_api_models(operations) %} -import {{ package_name }}.model.{{ model }}; -{%- endfor %} -{%- for path, method, operation in operations|sort(attribute="2.operationId") %} -{%- if operation["x-pagination"] %} -{%- set pagination = operation["x-pagination"] %} -import {{ package_name }}.model.{{ get_type_at_path(operation, pagination["resultsPath"]) }}; - -{%- set limitParamParts = pagination.limitParam.split(".") %} -{%- for i in range(1, limitParamParts|length) %} -{%- set limitParam = ".".join(limitParamParts[:i]) %} -import {{ package_name }}.model.{{ get_container_type(operation, limitParam) }}; -{%- endfor %} -{%- endif %} -{%- endfor %} - - -{{ generated_annotation }} -public class {{ name }} { - private ApiClient apiClient; - public {{ name }}() { - this(ApiClient.getDefaultApiClient()); - } - - public {{ name }}(ApiClient apiClient) { - this.apiClient = apiClient; - } - - /** - * Get the API client. - * - * @return API client - */ - public ApiClient getApiClient() { - return apiClient; - } - - /** - * Set the API client. - * - * @param apiClient an instance of API client - */ - public void setApiClient(ApiClient apiClient) { - this.apiClient = apiClient; - } - - {%- for path, method, operation in operations|sort(attribute="2.operationId") %} - {%- set allParams = operation|parameters | list %} - {%- set optionalParams = allParams|rejectattr('1.required', 'equalto', true) | list %} - {%- set requiredParams = allParams|selectattr('1.required', 'equalto', true) | list %} - {%- set pathParams = allParams|selectattr('1.in', 'equalto', "path") | list %} - {%- set queryParams = allParams|selectattr('1.in', 'equalto', "query") | list %} - {%- set formParams = allParams|selectattr('1.in', 'equalto', "form") | list %} - {%- set headerParams = allParams|selectattr('1.in', 'equalto', "header") | list %} - {%- set authMethods = operation.security if "security" in operation else openapi.security %} - {%- set returnType = operation|return_type %} - {%- set operationId = operation.operationId|untitle_case %} - {%- set bodyParamName = operation.get("x-codegen-request-body-name", "body") %} - -{%- macro endpointDocAndAnnotation(async=False, showRequired=True, showOptional=False, optionalParameterArg=False, throwsApiException=True, overrideReturn=False) -%} -/** - * {{ operation.summary|escape_html }}. - * - * See {@link #{{ operationId }}WithHttpInfo{{ "Async" if async }}}. - * - {%- for name, parameter in allParams %} - {%- if parameter.required and showRequired %} - * @param {{ name|variable_name }} {{ parameter.description|inline_docstring }} (required{%- if parameter.get("schema", {}).default is defined %}, default to {{ parameter.schema.default|format_value }}{%- endif %}) - {%- elif not parameter.required and showOptional %} - * @param {{ name|variable_name }} {{ parameter.description|inline_docstring }} (optional{%- if parameter.get("schema", {}).default is defined %}, default to {{ parameter.schema.default|format_value }}{%- endif %}) - {%- endif %} - {%- endfor %} - {%- if optionalParameterArg %} - * @param parameters Optional parameters for the request. - {%- endif %} - {%- if overrideReturn %} - * @return {{ overrideReturn|escape_html }} - {%- else %} - {%- if returnType %} - * @return {% if async %}CompletableFuture<{{ returnType|escape_html }}>{% else%}{{ returnType|escape_html }}{%endif %} - {%- else %} - {%- if async %} - * @return CompletableFuture - {%- endif %} - {%- endif %} - {%- endif %} - {%- if throwsApiException %} - * @throws ApiException if fails to make API call - {%- endif %} - {%- if operation.deprecated %} - * @deprecated - {%- endif %} - */ -{%- if operation.deprecated %} -@Deprecated -{%- endif %} -{%- endmacro %} - -{%- macro paginatedEndpointMacro(pagination) -%} - {%- set _, getters, _ = get_accessors(pagination.resultsPath|default('')) %} - String resultsPath = "{{ ".".join(getters) }}"; - -{#- Pagination cursor param fields #} - {%- if pagination.cursorParam %} - {%- set _, cursorPathGetters, _ = get_accessors(pagination.cursorPath) %} - {%- set paramSchema = get_parameter_schema_from_name(pagination.cursorParam.split(".")[0], allParams) %} - {%- set cursorParamOptional, cursorParamGetters, cursorParamSetters = get_accessors(pagination.cursorParam, schema=paramSchema) %} - String valueGetterPath = "{{ ".".join(cursorPathGetters) }}"; - String valueSetterPath = "{%- if cursorParamGetters|length > 1 %}{{ ".".join(cursorParamGetters[:cursorParamGetters|length-1]) }}.{%- endif %}{{ cursorParamSetters[cursorParamSetters|length-1] }}"; - Boolean valueSetterParamOptional = {{ cursorParamOptional|lower }}; - {%- endif %} - -{#- Pagination page offset param fields #} - {%- if pagination.pageOffsetParam %} - {%- set paramSchema = get_parameter_schema_from_name(pagination.pageOffsetParam.split(".")[0], allParams) %} - {%- set optional, getters, setters = get_accessors(pagination.pageOffsetParam, schema=paramSchema) %} - String valueGetterPath = ""; - String valueSetterPath = "{%- if getters|length > 1 %}{{ ".".join(getters[:getters|length-1]) }}.{%- endif %}{{ setters[getters|length-1] }}"; - Boolean valueSetterParamOptional = {{ optional|lower }}; - {%- endif %} - - {%- if pagination.pageParam %} - {%- set paramSchema = get_parameter_schema_from_name(pagination.pageParam.split(".")[0], allParams) %} - {%- set optional, getters, setters = get_accessors(pagination.pageParam, schema=paramSchema) %} - String valueGetterPath = ""; - String valueSetterPath = "{%- if getters|length > 1 %}{{ ".".join(getters[:getters|length-1]) }}.{%- endif %}{{ setters[getters|length-1] }}"; - Boolean valueSetterParamOptional = {{ optional|lower }}; - parameters.{% if getters|length > 1 %}{{ ".".join(getters[:getters|length-1]) }}.{%- endif %}{{ setters[getters|length-1] }}({{ (pagination.pageStart | default(0))|format_value(schema=paramSchema['schema']) }}); - {%- endif %} - -{#- Limit param field #} - {{ get_container_type(operation, pagination.limitParam) }} limit; - -{#- Limit param part fields #} - {%- set limitParamParts = pagination.limitParam.split(".") %} - {%- set paramSchema = get_parameter_schema_from_name(limitParamParts[0], allParams) %} - {%- set limitParamOptional, limitParamGetters, limitParamSetters = get_accessors(pagination.limitParam, schema=paramSchema) %} - - {% if limitParamParts|length == 1 %} - {%- if limitParamOptional %} - if (parameters.{{ limitParamGetters[0] }} == null) { - limit = {{ get_default(operation, pagination.limitParam) }}; - parameters.{{ limitParamGetters[0] ~ "(limit)" }}; - } else { - limit = {% if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }}; - } - {%- endif %} - {% else %} - - {%- if limitParamOptional %} - if ({%- if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }} == null) { - {% if limitParamOptional %}parameters.{%- endif %}{{ limitParamSetters[0] ~ "(new " ~ get_container_type(operation, limitParamParts[0]) ~ "())" }}; - } - {%- endif %} - - {%- for i in range(1, limitParamGetters|length) %} - {%- if loop.nextitem %} - - if({%- if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }}.{{ "().".join(limitParamGetters[1:i+1]) }}() == null) { - {% if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }}.{{ "().".join(limitParamGetters[1:i]) }}{%- if i > 1 %}().{%- endif %}{{ limitParamSetters[i] }}(new {{ get_container_type(operation, ".".join(limitParamParts[:i+1])) }}()); - } - {%- endif %} - - {% if loop.last %} - if ({%- if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }}.{{ "().".join(limitParamGetters[1:limitParamGetters|length]) }}() == null) { - limit = {{ get_default(operation, pagination.limitParam) }}; - {% if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }}.{{ "().".join(limitParamGetters[1:limitParamGetters|length-1]) }}().{{ limitParamSetters[limitParamGetters|length-1] }}(limit); - } else { - limit = {% if limitParamOptional %}parameters.{%- endif %}{{ limitParamGetters[0] }}.{{ "().".join(limitParamGetters[1:limitParamGetters|length]) }}(); - } - {%- endif %} - - {%- endfor %} - {%- endif %} - - {# build args map #} - LinkedHashMap args = new LinkedHashMap(); - {%- for name, parameter in requiredParams %} - args.put("{{ name|variable_name }}", {{ name|variable_name }}); - {%- endfor %} - {%- if optionalParams %} - args.put("optionalParams", parameters); - {%- endif %} - - PaginationIterable iterator = new PaginationIterable(this, "{{ operationId }}", resultsPath, valueGetterPath, valueSetterPath, valueSetterParamOptional, {% if pagination.pageParam %}false{% else %}true{% endif %}, {% if pagination.cursorParam %}true{% else %}false{% endif %}, limit, args, {{ pagination.pageStart | default(0) }}); - - return iterator; -{%- endmacro %} - - {%- if optionalParams %} - - /** - * Manage optional parameters to {{ operationId }}. - */ - public static class {{ operationId|upperfirst }}OptionalParameters { - {%- for name, parameter in optionalParams %} - private {{ get_type_for_parameter(parameter) }} {{ name|variable_name }}; - {%- endfor %} - - {%- for name, parameter in optionalParams %} - {%- set paramName = name|variable_name %} - - /** - * Set {{ paramName }}. - * @param {{ paramName }} {{ parameter.description|inline_docstring }} (optional{% if parameter.get("schema", {}).default is defined %}, default to {{ parameter.schema.default|format_value }}{% endif %}) - * @return {{ operationId|upperfirst }}OptionalParameters - */ - public {{ operationId|upperfirst }}OptionalParameters {{ paramName }}({{ get_type_for_parameter(parameter) }} {{ paramName }}) { - this.{{ paramName }} = {{ paramName }}; - return this; - } - {%- endfor %} - } - {%- endif %} - - {{ endpointDocAndAnnotation() }} - {%- if optionalParams %} - public {% if returnType %}{{ returnType }}{% else %}void{% endif %} {{ operationId }} ({% for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}) throws ApiException { - {% if returnType %}return{% endif %} {{ operationId }}WithHttpInfo({% for name, parameter in requiredParams %} {{ name|variable_name }}, {% endfor %}new {{ operationId|upperfirst }}OptionalParameters()){% if returnType %}.getData(){% endif %}; - } - - {{ endpointDocAndAnnotation(async=True, throwsApiException=False) }} - public CompletableFuture<{% if returnType %}{{ returnType }}{% else %}Void{% endif %}>{{ operationId }}Async({% for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}) { - return {{ operationId }}WithHttpInfoAsync({% for name, parameter in requiredParams %}{{ name|variable_name }}, {% endfor %}new {{ operationId|upperfirst }}OptionalParameters()).thenApply(response -> { - return response.getData(); - }); - } - - {{ endpointDocAndAnnotation(optionalParameterArg=True) }} - public {% if returnType %}{{ returnType }}{% else %} void{% endif %} {{ operationId }}({% for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}, {% endfor %}{{ operationId|upperfirst }}OptionalParameters parameters) throws ApiException { - {% if returnType %}return{% endif %} {{ operationId }}WithHttpInfo({% for name, parameter in requiredParams %}{{ name|variable_name }}, {% endfor %}parameters){% if returnType %}.getData(){% endif %}; - } - - {{ endpointDocAndAnnotation(async=True, optionalParameterArg=True, throwsApiException=False) }} - public CompletableFuture<{% if returnType %}{{ returnType }}{% else %}Void{% endif %}>{{ operationId }}Async({% for name, parameter in requiredParams %} {{ get_type_for_parameter(parameter) }} {{ name|variable_name }}, {% endfor %}{{ operationId|upperfirst }}OptionalParameters parameters) { - return {{ operationId }}WithHttpInfoAsync({% for name, parameter in requiredParams %}{{ name|variable_name }}, {% endfor %}parameters).thenApply(response -> { - return response.getData(); - }); - } - {%- else %} - public {% if returnType %}{{ returnType }} {% else %} void {% endif %} {{ operationId }}({% for name, parameter in allParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}) throws ApiException { - {% if returnType %}return {% endif %}{{ operationId }}WithHttpInfo({% for name, parameter in allParams %}{{ name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}){% if returnType %}.getData(){% endif %}; - } - - {{ endpointDocAndAnnotation(async=True, throwsApiException=False) }} - public CompletableFuture<{% if returnType %}{{ returnType }}{% else %}Void{% endif %}>{{ operationId }}Async({% for name, parameter in allParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}{%- if loop.nextitem %}, {% endif %}{% endfor %}) { - return {{ operationId }}WithHttpInfoAsync({% for name, parameter in allParams %}{{ name|variable_name }}{%- if loop.nextitem %}, {% endif %}{% endfor %}).thenApply(response -> { - return response.getData(); - }); - } - {%- endif %} - - {%- if operation["x-pagination"] %} - {%- set pagination = operation["x-pagination"] %} - {%- set paginationReturnType = "PaginationIterable<" ~ get_type_at_path(operation, pagination["resultsPath"]) ~ ">" %} - - {{ endpointDocAndAnnotation(throwsApiException=False, overrideReturn=paginationReturnType) }} - public {{ paginationReturnType }} {{ operationId }}WithPagination({% for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}) { - {%- if optionalParams %} - {{ operationId|upperfirst }}OptionalParameters parameters = new {{ operationId|upperfirst }}OptionalParameters(); - return {{ operationId }}WithPagination({% for name, parameter in requiredParams %}{{ name|variable_name }}, {% endfor %}parameters); - {%- else %} - {{- paginatedEndpointMacro(pagination) }} - {%- endif %} - } - - {%- if optionalParams %} - - {{ endpointDocAndAnnotation(throwsApiException=False, overrideReturn=returnType) }} - public {{ paginationReturnType }} {{ operationId }}WithPagination({% for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}, {% endfor %}{{ operationId|upperfirst }}OptionalParameters parameters) { - {{- paginatedEndpointMacro(pagination) }} - } - {%- endif %} - {%- endif %} - - - /** -{{ operation.description|docstring }} - * - {%- for name, parameter in requiredParams %} - * @param {{ name|variable_name }} {{ parameter.description|inline_docstring }} (required) - {%- endfor %} - {%- if optionalParams %} - * @param parameters Optional parameters for the request. - {%- endif %} - * @return ApiResponse<{% if returnType %}{{ returnType|escape_html }}{% else %}Void{% endif %}> - * @throws ApiException if fails to make API call - {%- if operation.responses %} - * @http.response.details - * - * - * - {%- for responseCode, response in operation.responses.items() %} - * - {%- endfor %} - *
Response details
Status Code Description Response Headers
{{ responseCode }} {{ response.description|escape_html }} -
- {%- endif %} - {%- if operation.deprecated %} - * @deprecated - {%- endif %} - */ - {%- if operation.deprecated %} - @Deprecated - {%- endif %} - {%- if optionalParams %} - public ApiResponse<{% if returnType %}{{ returnType }}{% else %}Void{% endif %}> {{ operationId }}WithHttpInfo({%- for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{ name|variable_name }}, {% endfor %}{{ operationId|upperfirst }}OptionalParameters parameters) throws ApiException { - {%- else %} - public ApiResponse<{% if returnType %}{{ returnType }}{% else %}Void{% endif %}> {{ operationId }}WithHttpInfo({% for name, parameter in allParams %}{{ get_type_for_parameter(parameter) }} {{name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}) throws ApiException { - {%- endif %} - {%- if "x-unstable" in operation %} - // Check if unstable operation is enabled - String operationId = "{{ operationId }}"; - if (apiClient.isUnstableOperationEnabled("{{ version }}." + operationId)) { - apiClient.getLogger().warning(String.format("Using unstable operation '%s'", operationId)); - } else { - throw new ApiException(0, String.format("Unstable operation '%s' is disabled", operationId)); - } - {%- endif %} - Object localVarPostBody = {% if operation.requestBody and not "multipart/form-data" in operation.requestBody.content %}{% if operation.requestBody.required %}{{ bodyParamName }}{% else %}parameters.body{% endif %}{% else %}null{% endif %}; - {%- for name, parameter in allParams %} - {%- if parameter.required %} - - // verify the required parameter '{{ name|variable_name }}' is set - if ({{ name|variable_name }} == null) { - throw new ApiException(400, "Missing the required parameter '{{ name|variable_name }}' when calling {{ operationId }}"); - } - {%- else %} - {%- if name != bodyParamName %} - {{ get_type_for_parameter(parameter) }} {{ name|variable_name }} = parameters.{{ name|variable_name }}; - {%- endif %} - {%- endif %} - {%- endfor %} - // create path and map variables - String localVarPath = "{{ path }}" - {%- for name, parameter in pathParams %} - .replaceAll("\\{" + "{{ name }}" + "\\}", apiClient.escapeString({{ name|variable_name }}.toString())) - {%- endfor %}; - - {% if queryParams %} - List localVarQueryParams = new ArrayList(); - {%- endif %} - Map localVarHeaderParams = new HashMap(); - {%- if formParams %} - Map localVarFormParams = new HashMap(); - {%- endif %} -{# keep line #} - {%- for name, parameter in queryParams %} - localVarQueryParams.addAll(apiClient.parameterToPairs("{{ parameter|collection_format }}", "{{ name }}", {{ name|variable_name }})); - {%- endfor %} - - {%- for name, parameter in headerParams %} - if ({{ name|variable_name }} != null) { localVarHeaderParams.put("{{ name }}", apiClient.parameterToString({{ name|variable_name }})); } - {%- endfor %} - - {%- for name, parameter in formParams %} - if ({{ name|variable_name }} != null) { localVarFormParams.put("{{ name }}", {{ name|variable_name }}); } - {%- endfor %} - - Invocation.Builder builder = apiClient.createBuilder("{{ version }}.{{ name }}.{{ operationId }}", localVarPath, {% if queryParams %}localVarQueryParams{% else %}new ArrayList(){% endif %}, localVarHeaderParams, new HashMap(), new String[] { {%- for mediaType in operation|accept_headers %}"{{ mediaType }}"{% if loop.nextitem %}, {% endif %}{% endfor %} }, new String[] { {% for name in get_security_names(authMethods)|sort %}"{{ name }}"{% if loop.nextitem %}, {% endif %}{% endfor %} }); - return apiClient.invokeAPI("{{ method.upper() }}", builder, localVarHeaderParams, new String[] { {%- if operation.requestBody is defined %} {%- for mediaType in operation.requestBody.content.keys() %}"{{ mediaType }}"{% if loop.nextitem %}, {% endif %}{%- endfor %}{%- endif %} }, localVarPostBody,{% if formParams %}localVarFormParams{% else %}new HashMap(){% endif %} , {% if operation.requestBody %}{% if operation.requestBody.nullable %}true{% else %}false{% endif %}{% else %}false{% endif %}, {% if returnType %}new GenericType<{{ returnType }}>() {}{% else %}null{% endif %}); - } - - /** - * {{ operation.summary|escape_html }}. - * - * See {@link #{{ operationId }}WithHttpInfo}. - * - {%- for name, parameter in requiredParams %} - * @param {{ name|variable_name }} {{ parameter.description|inline_docstring }} (required) - {%- endfor %} - {%- if optionalParams %} - * @param parameters Optional parameters for the request. - {%- endif %} - * @return CompletableFuture<ApiResponse<{% if returnType %}{{ returnType|escape_html }}{% else %}Void{% endif %}>> - {%- if operation.deprecated %} - * @deprecated - {%- endif %} - */ - {%- if operation.deprecated %} - @Deprecated - {%- endif %} - {%- if optionalParams %} - public CompletableFuture> {{ operationId }}WithHttpInfoAsync({% for name, parameter in requiredParams %}{{ get_type_for_parameter(parameter) }} {{name|variable_name }}, {% endfor %}{{ operationId|upperfirst }}OptionalParameters parameters) { - {%- else %} - public CompletableFuture> {{ operationId }}WithHttpInfoAsync({% for name, parameter in allParams %}{{ get_type_for_parameter(parameter) }} {{name|variable_name }}{% if loop.nextitem %}, {% endif %}{% endfor %}) { - {%- endif %} - {%- if "x-unstable" in operation %} - // Check if unstable operation is enabled - String operationId = "{{ operationId }}"; - if (apiClient.isUnstableOperationEnabled("{{ version }}." + operationId)) { - apiClient.getLogger().warning(String.format("Using unstable operation '%s'", operationId)); - } else { - CompletableFuture> result = new CompletableFuture<>(); - result.completeExceptionally(new ApiException(0, String.format("Unstable operation '%s' is disabled", operationId))); - return result; - } - {%- endif %} - Object localVarPostBody = {% if operation.requestBody and not "multipart/form-data" in operation.requestBody.content %}{% if operation.requestBody.required %}{{ bodyParamName }}{% else %}parameters.body{% endif %}{% else %}null{% endif %}; - - {%- for name, parameter in allParams %} - {%- if parameter.required %} - - // verify the required parameter '{{ name|variable_name }}' is set - if ({{ name|variable_name }} == null) { - CompletableFuture> result = new CompletableFuture<>(); - result.completeExceptionally(new ApiException(400, "Missing the required parameter '{{ name|variable_name }}' when calling {{ operationId }}")); - return result; - } - {%- else %} - {%- if name != bodyParamName %} - {{ get_type_for_parameter(parameter) }} {{ name|variable_name }} = parameters.{{ name|variable_name }}; - {%- endif %} - {%- endif %} - {%- endfor %} - // create path and map variables - String localVarPath = "{{ path }}" - {%- for name, parameter in pathParams %} - .replaceAll("\\{" + "{{ name }}" + "\\}", apiClient.escapeString({{ name|variable_name }}.toString())) - {%- endfor %}; - - {% if queryParams %} - List localVarQueryParams = new ArrayList(); - {%- endif %} - Map localVarHeaderParams = new HashMap(); - {%- if formParams %} - Map localVarFormParams = new HashMap(); - {%- endif %} -{# keep line #} - {%- for name, parameter in queryParams %} - localVarQueryParams.addAll(apiClient.parameterToPairs("{{ parameter|collection_format }}", "{{ name }}", {{ name|variable_name }})); - {%- endfor %} - - {%- for name, parameter in headerParams %} - if ({{ name|variable_name }} != null) { localVarHeaderParams.put("{{ name }}", apiClient.parameterToString({{ name|variable_name }})); } - {%- endfor %} - - {%- for name, parameter in formParams %} - if ({{ name|variable_name }} != null) { localVarFormParams.put("{{ name }}", {{ name|variable_name }}); } - {% endfor %} - - Invocation.Builder builder; - try { - builder = apiClient.createBuilder("{{ version }}.{{ name }}.{{ operationId }}", localVarPath, {% if queryParams %}localVarQueryParams{% else %}new ArrayList(){% endif %}, localVarHeaderParams, new HashMap(), new String[] { {%- for mediaType in operation|accept_headers %}"{{ mediaType }}"{% if loop.nextitem %}, {% endif %}{% endfor %} }, new String[] { {% for name in get_security_names(authMethods)|sort %}"{{ name }}"{% if loop.nextitem %}, {% endif %}{% endfor %} }); - } catch (ApiException ex) { - CompletableFuture> result = new CompletableFuture<>(); - result.completeExceptionally(ex); - return result; - } - return apiClient.invokeAPIAsync("{{ method.upper() }}", builder, localVarHeaderParams, new String[] { {%- if operation.requestBody is defined %} {%- for mediaType in operation.requestBody.content.keys() %}"{{ mediaType }}"{% if loop.nextitem %}, {% endif %}{%- endfor %}{%- endif %} }, localVarPostBody,{% if formParams %}localVarFormParams{% else %}new HashMap(){% endif %} , {% if operation.requestBody %}{% if operation.requestBody.nullable %}true{% else %}false{% endif %}{% else %}false{% endif %}, {% if returnType %}new GenericType<{{ returnType }}>() {}{% else %}null{% endif %}); - } - {%- endfor %} -} diff --git a/.generator/src/generator/templates/ApiClient.j2 b/.generator/src/generator/templates/ApiClient.j2 deleted file mode 100644 index 9a8c33b0476..00000000000 --- a/.generator/src/generator/templates/ApiClient.j2 +++ /dev/null @@ -1,1584 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import jakarta.ws.rs.client.AsyncInvoker; -import jakarta.ws.rs.client.Client; -import jakarta.ws.rs.client.ClientBuilder; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.client.Invocation; -import jakarta.ws.rs.client.InvocationCallback; -import jakarta.ws.rs.client.WebTarget; -import jakarta.ws.rs.core.Form; -import jakarta.ws.rs.core.GenericType; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.Response.Status; -import jakarta.ws.rs.core.Variant; - -import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.client.filter.EncodingFilter; -import org.glassfish.jersey.client.HttpUrlConnectorProvider; -import org.glassfish.jersey.jackson.JacksonFeature; -import org.glassfish.jersey.media.multipart.Boundary; -import org.glassfish.jersey.media.multipart.FormDataBodyPart; -import org.glassfish.jersey.media.multipart.FormDataContentDisposition; -import org.glassfish.jersey.media.multipart.MultiPart; -import org.glassfish.jersey.media.multipart.MultiPartFeature; -import org.glassfish.jersey.message.GZipEncoder; -import org.glassfish.jersey.message.DeflateEncoder; - -import java.io.IOException; -import java.io.InputStream; - -import java.net.URI; -import java.net.URISyntaxException; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import java.security.cert.X509Certificate; -import java.security.KeyManagementException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import org.glassfish.jersey.logging.LoggingFeature; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.Set; -import java.util.Date; -import java.util.Properties; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; - -import java.net.URLEncoder; - -import java.io.File; -import java.io.UnsupportedEncodingException; - -import java.text.DateFormat; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import {{ common_package_name }}.ApiException; -import {{ common_package_name }}.ApiResponse; -import {{ common_package_name }}.JSON; -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.RetryConfig; -import {{ common_package_name }}.RFC3339DateFormat; -import {{ common_package_name }}.ServerConfiguration; -import {{ common_package_name }}.ServerVariable; -import {{ common_package_name }}.StringUtil; - -import {{ common_package_name }}.auth.ApiKeyAuth; -import {{ common_package_name }}.auth.Authentication; -import {{ common_package_name }}.auth.HttpBasicAuth; -import {{ common_package_name }}.auth.HttpBearerAuth; -import {{ common_package_name }}.auth.OAuth; - -{%- macro server_configuration(server) -%} - new ServerConfiguration( - "{{ server.url }}", - "{{ server.description|default("No description provided") }}", - new HashMap() { - { - {%- for name, variable in server.get("variables", {}).items() %} - put( - "{{ name }}", - new ServerVariable( - "{{ variable.description|default("No description provided") }}", - "{{ variable.default }}", - new HashSet( - {%- for value in variable.enum %} - {%- if loop.first %} - Arrays.asList( - {%- endif %} - "{{ value }}"{% if loop.nextitem %},{% endif %} - {%- if loop.last %} - ) - {%- endif %} - - {%- endfor %} - ) - ) - ); - {%- endfor%} - } - } - ) -{%- endmacro %} -{{ generated_annotation }} -public class ApiClient { - protected Map defaultHeaderMap = new HashMap(); - protected Map defaultCookieMap = new HashMap(); - {%- set default_server = openapi.servers[0]|format_server %} - protected String basePath = "{{ default_server.geturl() }}"; - protected String userAgent; - private DateTimeFormatter offsetDateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME; - - protected List servers = new ArrayList( - {%- for server in specs.v2.servers %} - {%- if loop.first %} - Arrays.asList( - {%- endif %} - {{ server_configuration(server) }}{% if loop.nextitem %},{% endif %} - {%- if loop.last %} - ) - {%- endif %} - {%- endfor%} - ); - protected Integer serverIndex = 0; - protected Map serverVariables = null; - protected Map> operationServers = new HashMap>() {{ '{{' }} - {%- for version, spec in specs.items() %} - {%- for path in spec.paths.values() %} - {%- for operation in path.values() %} - {%- for server in operation.servers %} - {%- if loop.first %} - put("{{ version }}.{{ operation.tags[0].replace(" ", "")|camel_case|upperfirst }}Api.{{ operation.operationId|untitle_case }}", new ArrayList(Arrays.asList( - {%- endif %} - {{ server_configuration(server) }}{% if loop.nextitem %},{% endif %} - {%- if loop.last %} - ))); - {%- endif %} - {%- endfor %} - {%- endfor %} - {%- endfor %} - {%- endfor %} - }}; - protected Map operationServerIndex = new HashMap(); - protected Map> operationServerVariables = new HashMap>(); - protected boolean debugging = false; - protected RetryConfig retry = new RetryConfig(false, 2, 2, 3); - protected boolean compress = true; - protected ClientConfig clientConfig; - protected int connectionTimeout = 0; - private int readTimeout = 0; - - protected Client httpClient; - protected JSON json; - protected String tempFolderPath = null; - - protected Map authentications; - - protected DateFormat dateFormat; - protected final Map unstableOperations = new HashMap() {{ '{{' }} - {%- for version, api in apis.items() %} - {%- for operations in api.values() %} - {%- for _, _, operation in operations|sort(attribute="2.operationId") %} - {%- if "x-unstable" in operation %} - put("{{ version }}.{{ operation.operationId|untitle_case }}", false); - {%- endif %} - {%- endfor %} - {%- endfor %} - {%- endfor %} - }}; - protected static final java.util.logging.Logger logger = java.util.logging.Logger.getLogger(ApiClient.class.getName()); - - private static ApiClient defaultApiClient; - - /** - * Get the default API client, which would be used when creating API instances without providing - * an API client. - * - * @return Default API client - */ - public static ApiClient getDefaultApiClient() { - if (defaultApiClient != null) { - return defaultApiClient; - } - defaultApiClient = new ApiClient(); - - // Configure the Datadog site to send API calls to - String site = System.getenv("DD_SITE"); - if (site != null) { - HashMap serverVariables = new HashMap(); - serverVariables.put("site", site); - defaultApiClient.setServerVariables(serverVariables); - } - // Configure API key authorization - HashMap secrets = new HashMap(); - String apiKeyAuth = System.getenv("DD_API_KEY"); - if (apiKeyAuth != null) { - secrets.put("apiKeyAuth", apiKeyAuth); - } - String appKeyAuth = System.getenv("DD_APP_KEY"); - if (appKeyAuth != null) { - secrets.put("appKeyAuth", appKeyAuth); - } - defaultApiClient.configureApiKeys(secrets); - - return defaultApiClient; - } - - /** - * Set the default API client, which would be used when creating API instances without providing - * an API client. - * - * @param apiClient API client - */ - public static void setDefaultApiClient(ApiClient apiClient) { - defaultApiClient = apiClient; - } - - /** - * Constructs a new ApiClient with default parameters. - */ - public ApiClient() { - this(null); - } - - /** - * Constructs a new ApiClient with the specified authentication parameters. - * - * @param authMap A hash map containing authentication parameters. - */ - public ApiClient(Map authMap) { - json = new JSON(); - httpClient = buildHttpClient(); - - this.dateFormat = new RFC3339DateFormat(); - - // Set default User-Agent. - setUserAgent(); - - // Setup authentications (key: authentication name, value: authentication). - authentications = new HashMap(); - Authentication auth = null; - {%- for name, schema in specs.v2.components.securitySchemes.items() %} - {%- if schema.type == "oauth2" %} - if (authMap != null) { - auth = authMap.get("{{ name }}"); - } - if (auth instanceof OAuth) { - authentications.put("{{ name }}", auth); - } else { - authentications.put("{{ name }}", new OAuth(basePath, "{{ schema.flows.authorizationCode.tokenUrl }}")); - } - {%- elif schema.type == "apiKey" %} - if (authMap != null) { - auth = authMap.get("{{ name }}"); - } - if (auth instanceof {{ schema.type|upperfirst ~ "Auth" }}) { - authentications.put("{{ name }}", auth); - } else { - authentications.put("{{ name }}", new {{ schema.type|upperfirst ~ "Auth" }}("{{ schema["in"] }}", "{{ schema.name }}")); - } - {%- endif %} - {%- endfor %} - // Prevent the authentications from being modified. - authentications = Collections.unmodifiableMap(authentications); - } - - /** - * Get the date format used to parse/format {@code OffsetDateTime} parameters. - * - * @return DateTimeFormatter - */ - public DateTimeFormatter getOffsetDateTimeFormatter() { - return offsetDateTimeFormatter; - } - - /** - * Add custom retry object in the client - * @param retry retry object - * */ - public void setRetry(RetryConfig retry) { - this.retry = retry; - } - - /** - * Return the retryConfig object - * @return retryConfig - */ - public RetryConfig getRetry() { - return retry; - } - - /** - * Enable retry directly on the client instead of creating a new retry object - * @param enableRetry bool, enable retry or not - */ - public void enableRetry(boolean enableRetry){ - this.retry.setEnableRetry(enableRetry); - } - - /** - * Set the date format used to parse/format {@code OffsetDateTime} parameters. - * - * @param offsetDateTimeFormatter {@code DateTimeFormatter} - */ - public void setOffsetDateTimeFormatter(DateTimeFormatter offsetDateTimeFormatter) { - this.offsetDateTimeFormatter = offsetDateTimeFormatter; - } - - /** - * Format the given {@code OffsetDateTime} object into string. - * - * @param offsetDateTime {@code OffsetDateTime} - * @return {@code OffsetDateTime} in string format - */ - public String formatOffsetDateTime(OffsetDateTime offsetDateTime) { - return offsetDateTimeFormatter.format(offsetDateTime); - } - - /** - * Gets the JSON instance to do JSON serialization and deserialization. - * - * @return JSON - */ - public JSON getJSON() { - return json; - } - - public Client getHttpClient() { - return httpClient; - } - - public ApiClient setHttpClient(Client httpClient) { - this.httpClient = httpClient; - return this; - } - - /** - * Returns the base URL to the location where the OpenAPI document is being served. - * - * @return The base URL to the target host. - */ - public String getBasePath() { - return basePath; - } - - /** - * Sets the base URL to the location where the OpenAPI document is being served. - * - * @param basePath The base URL to the target host. - * @return API client - */ - public ApiClient setBasePath(String basePath) { - this.basePath = basePath; - setOauthBasePath(basePath); - return this; - } - - public List getServers() { - return servers; - } - - public ApiClient setServers(List servers) { - this.servers = servers; - updateBasePath(); - return this; - } - - public Integer getServerIndex() { - return serverIndex; - } - - public ApiClient setServerIndex(Integer serverIndex) { - this.serverIndex = serverIndex; - updateBasePath(); - return this; - } - - public Map getServerVariables() { - return serverVariables; - } - - public ApiClient setServerVariables(Map serverVariables) { - this.serverVariables = serverVariables; - updateBasePath(); - return this; - } - - private void updateBasePath() { - if (serverIndex != null) { - setBasePath(servers.get(serverIndex).URL(serverVariables)); - } - } - - private void setOauthBasePath(String basePath) { - for(Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).setBasePath(basePath); - } - } - } - - /** - * Get authentications (key: authentication name, value: authentication). - * - * @return Map of authentication object - */ - public Map getAuthentications() { - return authentications; - } - - /** - * Get authentication for the given name. - * - * @param authName The authentication name - * @return The authentication, null if not found - */ - public Authentication getAuthentication(String authName) { - return authentications.get(authName); - } - - /** - * Helper method to set username for the first HTTP basic authentication. - * - * @param username Username - * @return API client - */ - public ApiClient setUsername(String username) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBasicAuth) { - ((HttpBasicAuth) auth).setUsername(username); - return this; - } - } - throw new RuntimeException("No HTTP basic authentication configured!"); - } - - /** - * Helper method to set password for the first HTTP basic authentication. - * - * @param password Password - * @return API client - */ - public ApiClient setPassword(String password) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBasicAuth) { - ((HttpBasicAuth) auth).setPassword(password); - return this; - } - } - throw new RuntimeException("No HTTP basic authentication configured!"); - } - - /** - * Helper method to set API key value for the first API key authentication. - * - * @param apiKey API key - * @return API client - */ - public ApiClient setApiKey(String apiKey) { - for (Authentication auth : authentications.values()) { - if (auth instanceof ApiKeyAuth) { - ((ApiKeyAuth) auth).setApiKey(apiKey); - return this; - } - } - throw new RuntimeException("No API key authentication configured!"); - } - - /** - * Helper method to configure authentications which respects aliases of API keys. - * - * @param secrets Hash map from authentication name to its secret. - * @return API client - */ - public ApiClient configureApiKeys(Map secrets) { - for (Map.Entry authEntry : authentications.entrySet()) { - Authentication auth = authEntry.getValue(); - if (auth instanceof ApiKeyAuth) { - String name = authEntry.getKey(); - if (secrets.containsKey(name)) { - ((ApiKeyAuth) auth).setApiKey(secrets.get(name)); - } - } - } - return this; - } - - /** - * Helper method to set API key prefix for the first API key authentication. - * - * @param apiKeyPrefix API key prefix - * @return API client - */ - public ApiClient setApiKeyPrefix(String apiKeyPrefix) { - for (Authentication auth : authentications.values()) { - if (auth instanceof ApiKeyAuth) { - ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); - return this; - } - } - throw new RuntimeException("No API key authentication configured!"); - } - - /** - * Helper method to set bearer token for the first Bearer authentication. - * - * @param bearerToken Bearer token - * @return API client - */ - public ApiClient setBearerToken(String bearerToken) { - for (Authentication auth : authentications.values()) { - if (auth instanceof HttpBearerAuth) { - ((HttpBearerAuth) auth).setBearerToken(bearerToken); - return this; - } - } - throw new RuntimeException("No Bearer authentication configured!"); - } - - /** - * Helper method to set access token for the first OAuth2 authentication. - * @param accessToken Access token - * @return API client - */ - public ApiClient setAccessToken(String accessToken) { - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).setAccessToken(accessToken); - return this; - } - } - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Helper method to set the credentials for the first OAuth2 authentication. - * - * @param clientId the client ID - * @param clientSecret the client secret - * @return API client - */ - public ApiClient setOauthCredentials(String clientId, String clientSecret) { - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).setCredentials(clientId, clientSecret, isDebugging()); - return this; - } - } - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Helper method to set the password flow for the first OAuth2 authentication. - * - * @param username the user name - * @param password the user password - * @return API client - */ - public ApiClient setOauthPasswordFlow(String username, String password) { - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).usePasswordFlow(username, password); - return this; - } - } - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Helper method to set the authorization code flow for the first OAuth2 authentication. - * - * @param code the authorization code - * @return API client - */ - public ApiClient setOauthAuthorizationCodeFlow(String code) { - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).useAuthorizationCodeFlow(code); - return this; - } - } - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Helper method to set the scopes for the first OAuth2 authentication. - * - * @param scope the oauth scope - * @return API client - */ - public ApiClient setOauthScope(String scope) { - for (Authentication auth : authentications.values()) { - if (auth instanceof OAuth) { - ((OAuth) auth).setScope(scope); - return this; - } - } - throw new RuntimeException("No OAuth2 authentication configured!"); - } - - /** - * Set the User-Agent header's value (by adding to the default header map). - * @param userAgent Http user agent - * @return API client - */ - public ApiClient setUserAgent(String userAgent) { - addDefaultHeader("User-Agent", userAgent); - return this; - } - - /** - * Get the User-Agent header's value. - * @return User-Agent string - */ - public String getUserAgent(){ - return userAgent; - } - - /** - * Set the default User-Agent header's value with telemetry information (by adding to the default header map). - * @return API client - */ - public ApiClient setUserAgent() { - final Properties properties = new Properties(); - try { - properties.load(getClass().getClassLoader().getResourceAsStream("com/datadog/api/project.properties")); - } catch (IOException e) { - logger.severe("Could not load client version: " + e.toString()); - } - - String userAgent = "datadog-api-client-java/" + properties.getProperty("version") - + " (" - + "java " + System.getProperty("java.version") + "; " - + "java_vendor " + System.getProperty("java.vendor") + "; " - + "os " + System.getProperty("os.name") + "; " - + "os_version " + System.getProperty("os.version") + "; " - + "arch " + System.getProperty("os.arch") - + ")"; - addDefaultHeader("User-Agent", userAgent); - this.userAgent = userAgent; - return this; - } - - /** - * Add a default header. - * - * @param key The header's key - * @param value The header's value - * @return API client - */ - public ApiClient addDefaultHeader(String key, String value) { - defaultHeaderMap.put(key, value); - return this; - } - - /** - * Add a default cookie. - * - * @param key The cookie's key - * @param value The cookie's value - * @return API client - */ - public ApiClient addDefaultCookie(String key, String value) { - defaultCookieMap.put(key, value); - return this; - } - - /** - * Gets the client config. - * @return Client config - */ - public ClientConfig getClientConfig() { - return clientConfig; - } - - /** - * Set the client config. - * - * @param clientConfig Set the client config - * @return API client - */ - public ApiClient setClientConfig(ClientConfig clientConfig) { - this.clientConfig = clientConfig; - // Rebuild HTTP Client according to the new "clientConfig" value. - this.httpClient = buildHttpClient(); - return this; - } - - /** - * Check that whether debugging is enabled for this API client. - * @return True if debugging is switched on - */ - public boolean isDebugging() { - return debugging; - } - - /** - * Enable/disable debugging for this API client. - * - * @param debugging To enable (true) or disable (false) debugging - * @return API client - */ - public ApiClient setDebugging(boolean debugging) { - this.debugging = debugging; - // Rebuild HTTP Client according to the new "debugging" value. - this.setClientConfig(null); - return this; - } - - /** - * Check that whether compress is enabled for this API client. - * - * @return True if compress is switched on - */ - public boolean isCompress() { - return compress; - } - - /** - * Enable/disable compress for this API client. - * - * @param compress To enable (true) or disable (false) compress - * @return API client - */ - public ApiClient setCompress(boolean compress) { - this.compress = compress; - // Rebuild HTTP Client according to the new "compress" value. - this.setClientConfig(null); - return this; - } - - - /** - * The path of temporary folder used to store downloaded files from endpoints - * with file response. The default value is null, i.e. using - * the system's default temporary folder. - * - * @return Temp folder path - */ - public String getTempFolderPath() { - return tempFolderPath; - } - - /** - * Set temp folder path - * @param tempFolderPath Temp folder path - * @return API client - */ - public ApiClient setTempFolderPath(String tempFolderPath) { - this.tempFolderPath = tempFolderPath; - return this; - } - - /** - * Connect timeout (in milliseconds). - * @return Connection timeout - */ - public int getConnectTimeout() { - return connectionTimeout; - } - - /** - * Set the connect timeout (in milliseconds). - * A value of 0 means no timeout, otherwise values must be between 1 and - * {@link Integer#MAX_VALUE}. - * @param connectionTimeout Connection timeout in milliseconds - * @return API client - */ - public ApiClient setConnectTimeout(int connectionTimeout) { - this.connectionTimeout = connectionTimeout; - httpClient.property(ClientProperties.CONNECT_TIMEOUT, connectionTimeout); - return this; - } - - /** - * read timeout (in milliseconds). - * @return Read timeout - */ - public int getReadTimeout() { - return readTimeout; - } - - /** - * Set the read timeout (in milliseconds). - * A value of 0 means no timeout, otherwise values must be between 1 and - * {@link Integer#MAX_VALUE}. - * @param readTimeout Read timeout in milliseconds - * @return API client - */ - public ApiClient setReadTimeout(int readTimeout) { - this.readTimeout = readTimeout; - httpClient.property(ClientProperties.READ_TIMEOUT, readTimeout); - return this; - } - - /** - * Get the date format used to parse/format date parameters. - * @return Date format - */ - public DateFormat getDateFormat() { - return dateFormat; - } - - /** - * Set the date format used to parse/format date parameters. - * @param dateFormat Date format - * @return API client - */ - public ApiClient setDateFormat(DateFormat dateFormat) { - this.dateFormat = dateFormat; - // also set the date format for model (de)serialization with Date properties - this.json.setDateFormat((DateFormat) dateFormat.clone()); - return this; - } - - /** - * Get list of all unstable operations - * @return set of all unstable operations Ids - */ - public Set getUnstableOperations() { - return unstableOperations.keySet(); - } - - /** - * Mark an unstable operation as enabled/disabled. - * @param operation operation Id - this is the name of the method on the API class, e.g. "createFoo" - * @param enabled whether to mark the operation as enabled (true) or disabled (false) - * @return true if the operation is marked as unstable and thus was enabled/disabled, false otherwise - */ - public boolean setUnstableOperationEnabled(String operation, boolean enabled) { - if (unstableOperations.containsKey(operation)) { - unstableOperations.put(operation, enabled); - return true; - } - logger.warning(String.format("'%s' is not an unstable operation, can't enable/disable", operation)); - return false; - } - - /** - * Determine whether an operation is an unstable operation. - * @param operation operation Id - this is the name of the method on the API class, e.g. "createFoo" - * @return true if the operation is an unstable operation, false otherwise - */ - public boolean isUnstableOperation(String operation) { - return unstableOperations.containsKey(operation); - } - - /** - * Determine whether an unstable operation is enabled. - * @param operation operation Id - this is the name of the method on the API class, e.g. "createFoo" - * @return true if the operation is unstable and it is enabled, false otherwise - */ - public boolean isUnstableOperationEnabled(String operation) { - if (unstableOperations.containsKey(operation)) { - return unstableOperations.get(operation); - } else { - logger.warning(String.format("'%s' is not an unstable operation, is always enabled", operation)); - return true; - } - } - - /** - * Get the ApiClient logger - * @return ApiClient logger - */ - public java.util.logging.Logger getLogger() { - return logger; - } - - /** - * Format the given Date object into string. - * @param date Date - * @return Date in string format - */ - public String formatDate(Date date) { - return dateFormat.format(date); - } - - /** - * Format the given parameter object into string. - * @param param Object - * @return Object in string format - */ - public String parameterToString(Object param) { - if (param == null) { - return ""; - } else if (param instanceof Date) { - return formatDate((Date) param); - } else if (param instanceof OffsetDateTime) { - return formatOffsetDateTime((OffsetDateTime) param); - } else if (param instanceof Collection) { - StringBuilder b = new StringBuilder(); - for(Object o : (Collection)param) { - if(b.length() > 0) { - b.append(','); - } - b.append(String.valueOf(o)); - } - return b.toString(); - } else { - return String.valueOf(param); - } - } - - /* - * Format to {@code Pair} objects. - * @param collectionFormat Collection format - * @param name Name - * @param value Value - * @return List of pairs - */ - public List parameterToPairs(String collectionFormat, String name, Object value){ - List params = new ArrayList(); - - // preconditions - if (name == null || name.isEmpty() || value == null) { return params; } - - Collection valueCollection; - if (value instanceof Collection) { - valueCollection = (Collection) value; - } else { - params.add(new Pair(name, parameterToString(value))); - return params; - } - - if (valueCollection.isEmpty()){ - return params; - } - - // get the collection format (default: csv) - String format = (collectionFormat == null || collectionFormat.isEmpty() ? "csv" : collectionFormat); - - // create the params based on the collection format - if ("multi".equals(format)) { - for (Object item : valueCollection) { - params.add(new Pair(name, parameterToString(item))); - } - - return params; - } - - String delimiter = ","; - - if ("csv".equals(format)) { - delimiter = ","; - } else if ("ssv".equals(format)) { - delimiter = " "; - } else if ("tsv".equals(format)) { - delimiter = "\t"; - } else if ("pipes".equals(format)) { - delimiter = "|"; - } - - StringBuilder sb = new StringBuilder() ; - for (Object item : valueCollection) { - sb.append(delimiter); - sb.append(parameterToString(item)); - } - - params.add(new Pair(name, sb.substring(1))); - - return params; - } - - /** - * Check if the given MIME is a JSON MIME. - * JSON MIME examples: - * application/json - * application/json; charset=UTF8 - * APPLICATION/JSON - * application/vnd.company+json - * "* / *" is also default to JSON - * @param mime MIME - * @return True if the MIME type is JSON - */ - public boolean isJsonMime(String mime) { - String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$"; - return mime != null && (mime.matches(jsonMime) || "*/*".equals(mime)); - } - - /** - * Select the Accept header's value from the given accepts array: - * if JSON exists in the given array, use it; - * otherwise use all of them (joining into a string) - * - * @param accepts The accepts array to select from - * @return The Accept header to use. If the given array is empty, - * null will be returned (not to set the Accept header explicitly). - */ - public String selectHeaderAccept(String[] accepts) { - if (accepts.length == 0) { - return null; - } - for (String accept : accepts) { - if (isJsonMime(accept)) { - return accept; - } - } - return StringUtil.join(accepts, ","); - } - - /** - * Select the Content-Type header's value from the given array: - * if JSON exists in the given array, use it; - * otherwise use the first one of the array. - * - * @param contentTypes The Content-Type array to select from - * @return The Content-Type header to use. If the given array is empty, - * JSON will be used. - */ - public String selectHeaderContentType(String[] contentTypes) { - if (contentTypes.length == 0) { - return "application/json"; - } - for (String contentType : contentTypes) { - if (isJsonMime(contentType)) { - return contentType; - } - } - return contentTypes[0]; - } - - /** - * Escape the given string to be used as URL query value. - * @param str String - * @return Escaped string - */ - public String escapeString(String str) { - try { - return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20"); - } catch (UnsupportedEncodingException e) { - return str; - } - } - - /** - * Serialize the given Java object into string entity according the given - * Content-Type (only JSON is supported for now). - * @param obj Object - * @param formParams Form parameters - * @param contentType Content type header - * @param contentEncoding Content encoding header - * @param isBodyNullable Whether the body can be null or not - * @return Entity - */ - public Entity serialize(Object obj, Map formParams, String contentType, String contentEncoding, boolean isBodyNullable) { - Entity entity; - Variant variant = new Variant(MediaType.valueOf(contentType), "", contentEncoding); - if (contentType.startsWith("multipart/form-data")) { - MultiPart multiPart = new MultiPart(); - for (Entry param: formParams.entrySet()) { - if (param.getValue() instanceof File) { - File file = (File) param.getValue(); - FormDataContentDisposition contentDisp = FormDataContentDisposition.name(param.getKey()) - .fileName(file.getName()).size(file.length()).build(); - multiPart.bodyPart(new FormDataBodyPart(contentDisp, file, MediaType.APPLICATION_OCTET_STREAM_TYPE)); - } else { - FormDataContentDisposition contentDisp = FormDataContentDisposition.name(param.getKey()).build(); - multiPart.bodyPart(new FormDataBodyPart(contentDisp, parameterToString(param.getValue()))); - } - } - MediaType mediaDataType = MediaType.MULTIPART_FORM_DATA_TYPE; - mediaDataType = Boundary.addBoundary(mediaDataType); - entity = Entity.entity(multiPart, mediaDataType); - } else if (contentType.startsWith("application/x-www-form-urlencoded")) { - Form form = new Form(); - for (Entry param: formParams.entrySet()) { - form.param(param.getKey(), parameterToString(param.getValue())); - } - entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE); - } else { - // We let jersey handle the serialization - if (isBodyNullable) { // payload is nullable - if (obj instanceof String) { - entity = Entity.entity(obj == null ? "null" : "\"" + ((String)obj).replaceAll("\"", Matcher.quoteReplacement("\\\"")) + "\"", variant); - } else { - entity = Entity.entity(obj == null ? "null" : obj, variant); - } - } else { - if (obj instanceof String) { - entity = Entity.entity(obj == null ? "" : "\"" + ((String)obj).replaceAll("\"", Matcher.quoteReplacement("\\\"")) + "\"", variant); - } else { - entity = Entity.entity(obj == null ? "" : obj, variant); - } - } - } - return entity; - } - - /** - * Deserialize response body to Java object according to the Content-Type. - * @param Type - * @param response Response - * @param returnType Return type - * @return Deserialize object - */ - @SuppressWarnings("unchecked") - public T deserialize(Response response, GenericType returnType) { - if (response == null || returnType == null) { - return null; - } - - if ("byte[]".equals(returnType.toString())) { - // Handle binary response (byte array). - return (T) response.readEntity(byte[].class); - } - - // read the entity stream multiple times - response.bufferEntity(); - - return response.readEntity(returnType); - } - - /** - * Create builder to invoke the API. - * - * @param operation The qualified name of the operation - * @param path The sub-path of the HTTP URL - * @param queryParams The query parameters - * @param headerParams The header parameters - * @param cookieParams The cookie parameters - * @param accepts The list of possible request's Accept header - * @param authNames The authentications to apply - * @return The invocation builder - * @throws ApiException API exception - */ - public Invocation.Builder createBuilder( - String operation, - String path, - List queryParams, - Map headerParams, - Map cookieParams, - String[] accepts, - String[] authNames) - throws ApiException { - - // Not using `.target(targetURL).path(path)` below, - // to support (constant) query string in `path`, e.g. "/posts?draft=1" - String targetURL; - if (serverIndex != null && operationServers.containsKey(operation)) { - Integer index = - operationServerIndex.containsKey(operation) - ? operationServerIndex.get(operation) - : serverIndex; - Map variables = - operationServerVariables.containsKey(operation) - ? operationServerVariables.get(operation) - : serverVariables; - List serverConfigurations = operationServers.get(operation); - if (index < 0 || index >= serverConfigurations.size()) { - throw new ArrayIndexOutOfBoundsException( - String.format( - "Invalid index %d when selecting the host settings. Must be less than %d", - index, serverConfigurations.size())); - } - targetURL = serverConfigurations.get(index).URL(variables) + path; - } else { - targetURL = this.basePath + path; - } - - URI parsedURI; - try { - parsedURI = new URI(targetURL); - } catch (URISyntaxException e) { - throw new ApiException(e); - } - - WebTarget target = httpClient.target(parsedURI); - - for (Pair queryParam : queryParams) { - if (queryParam.getValue() != null) { - target = target.queryParam(queryParam.getName(), escapeString(queryParam.getValue())); - } - } - - Invocation.Builder invocationBuilder = target.request().accept(selectHeaderAccept(accepts)); - - for (Entry entry : cookieParams.entrySet()) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.cookie(entry.getKey(), value); - } - } - - for (Entry entry : defaultCookieMap.entrySet()) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.cookie(entry.getKey(), value); - } - } - - // put all headers in one place - Map allHeaderParams = new HashMap<>(defaultHeaderMap); - allHeaderParams.putAll(headerParams); - - // update different parameters (e.g. headers) for authentication - updateParamsForAuth( - authNames, queryParams, allHeaderParams, cookieParams, target.getUri()); - - for (Entry entry : allHeaderParams.entrySet()) { - String value = entry.getValue(); - if (value != null) { - invocationBuilder = invocationBuilder.header(entry.getKey(), value); - } - } - - return invocationBuilder; - } - - /** - * Invoke API by sending HTTP request with the given options. - * - * @param Type - * @param invocationBuilder HTTP requests builder - * @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE" - * @param body The request body object - * @param headerParams The header parameters - * @param formParams The form parameters - * @param contentTypes The list of request Content-Type headers - * @param returnType The return type into which to deserialize the response - * @param isBodyNullable True if the body is nullable - * @return The response body in type of string - * @throws ApiException API exception - */ - public ApiResponse invokeAPI( - String method, - Invocation.Builder invocationBuilder, - Map headerParams, - String[] contentTypes, - Object body, - Map formParams, - Boolean isBodyNullable, - GenericType returnType) - throws ApiException { - - String contentEncoding = headerParams.get(HttpHeaders.CONTENT_ENCODING); - Entity entity = - serialize( - body, - formParams, - selectHeaderContentType(contentTypes), - contentEncoding, - isBodyNullable); - - Response response = null; - - try { - int currentRetry = 0; - while (true){ - response = sendRequest(method, invocationBuilder, entity); - int statusCode = response.getStatusInfo().getStatusCode(); - Map> responseHeaders = buildResponseHeaders(response); - if (response.getStatusInfo() == Status.NO_CONTENT) { - return new ApiResponse(statusCode, responseHeaders); - } else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) { - if (returnType == null) { - return new ApiResponse(statusCode, responseHeaders); - } else { - return new ApiResponse(statusCode, responseHeaders, deserialize(response, returnType)); - } - } else if (shouldRetry(currentRetry, statusCode, retry)){ - // Close the response before retry to avoid leaks - try { - response.close(); - } catch (Exception e) { - // it's not critical, since the response object is local in method invokeAPI; that's fine, - // just continue - } - retry.sleepInterval(calculateRetryInterval(responseHeaders, retry, currentRetry)); - currentRetry++; - } else { - String message = "error"; - String respBody = null; - if (response.hasEntity()) { - try { - respBody = String.valueOf(response.readEntity(String.class)); - message = respBody; - } catch (RuntimeException e) { - // e.printStackTrace(); - } - } - throw new ApiException( - response.getStatus(), message, buildResponseHeaders(response), respBody); - } - } - } finally { - try { - response.close(); - } catch (Exception e) { - // it's not critical, since the response object is local in method invokeAPI; that's fine, - // just continue - } - } - } - - private boolean shouldRetry(int retryCount, int statusCode, RetryConfig retryConfig){ - boolean statusToRetry = false; - if (statusCode == 429 || statusCode >= 500){ - statusToRetry = true; - } - return (retryConfig.maxRetries>retryCount && statusToRetry && retryConfig.isEnableRetry()); - } - - private int calculateRetryInterval(Map> responseHeaders, RetryConfig retryConfig, int retryCount){ - if ( responseHeaders.get("x-ratelimit-reset")!=null){ - List rateLimitHeader = responseHeaders.get("x-ratelimit-reset"); - return Integer.parseInt(rateLimitHeader.get(0)); - } else { - int retryInterval= (int) Math.pow (retry.backOffMultiplier, retryCount)* retryConfig.backOffBase; - if (getConnectTimeout()>0){ - retryInterval = Math.min(retryInterval, getConnectTimeout()); - } - return retryInterval; - } - } - - private Response sendRequest(String method, Invocation.Builder invocationBuilder, Entity entity) { - Response response; - if ("POST".equals(method)) { - response = invocationBuilder.post(entity); - } else if ("PUT".equals(method)) { - response = invocationBuilder.put(entity); - } else if ("DELETE".equals(method)) { - response = invocationBuilder.method("DELETE", entity); - } else if ("PATCH".equals(method)) { - response = invocationBuilder.method("PATCH", entity); - } else { - response = invocationBuilder.method(method); - } - return response; - } - - /** - * Invoke API by sending HTTP request with the given options, asynchronously. - * - * @param Type - * @param invocationBuilder HTTP requests builder - * @param method The request method, one of "GET", "POST", "PUT", "HEAD" and "DELETE" - * @param body The request body object - * @param headerParams The header parameters - * @param formParams The form parameters - * @param contentTypes The list of request Content-Type headers - * @param returnType The return type into which to deserialize the response - * @param isBodyNullable True if the body is nullable - * @return The future which be fired with the response - */ - public CompletableFuture> invokeAPIAsync( - String method, - Invocation.Builder invocationBuilder, - Map headerParams, - String[] contentTypes, - Object body, - Map formParams, - Boolean isBodyNullable, - GenericType returnType) { - - String contentEncoding = headerParams.get(HttpHeaders.CONTENT_ENCODING); - - Entity entity = serialize(body, formParams, selectHeaderContentType(contentTypes), contentEncoding, isBodyNullable); - - CompletableFuture> result = new CompletableFuture<>(); - - InvocationCallback callback = - new InvocationCallback() { - @Override - public void completed(Response response) { - int statusCode = response.getStatusInfo().getStatusCode(); - Map> responseHeaders = buildResponseHeaders(response); - - if (response.getStatusInfo() == Status.NO_CONTENT) { - result.complete(new ApiResponse(statusCode, responseHeaders)); - } else if (response.getStatusInfo().getFamily() == Status.Family.SUCCESSFUL) { - if (returnType == null) { - result.complete(new ApiResponse(statusCode, responseHeaders)); - } else { - result.complete( - new ApiResponse( - statusCode, responseHeaders, deserialize(response, returnType))); - } - } else { - String message = "error"; - String respBody = null; - if (response.hasEntity()) { - try { - respBody = String.valueOf(response.readEntity(String.class)); - message = respBody; - } catch (RuntimeException e) { - } - } - result.completeExceptionally( - new ApiException( - response.getStatus(), message, buildResponseHeaders(response), respBody)); - } - } - - @Override - public void failed(Throwable throwable) { - result.completeExceptionally(throwable); - } - }; - - // XXX Handle 401 for OAuth - sendRequestAsync(method, invocationBuilder, entity, callback); - return result; - } - - private Future sendRequestAsync( - String method, - Invocation.Builder invocationBuilder, - Entity entity, - InvocationCallback callback) { - Future response; - AsyncInvoker invoker = invocationBuilder.async(); - if ("POST".equals(method)) { - response = invoker.post(entity, callback); - } else if ("PUT".equals(method)) { - response = invoker.put(entity, callback); - } else if ("DELETE".equals(method)) { - response = invoker.method("DELETE", entity, callback); - } else if ("PATCH".equals(method)) { - response = invoker.method("PATCH", entity, callback); - } else { - response = invoker.method(method, callback); - } - return response; - } - - /** - * Build the Client used to make HTTP requests. - * @return Client - */ - protected Client buildHttpClient() { - // use the default client config if not yet initialized - if (clientConfig == null) { - clientConfig = getDefaultClientConfig(); - } - - if (compress) { - clientConfig.register(EncodingFilter.class); - } - clientConfig.register(GZipEncoder.class); - clientConfig.register(DeflateEncoder.class); - clientConfig.register(ZstdEncoder.class); - ClientBuilder clientBuilder = ClientBuilder.newBuilder(); - customizeClientBuilder(clientBuilder); - clientBuilder = clientBuilder.withConfig(clientConfig); - return clientBuilder.build(); - } - - /** - * Get the default client config. - * @return Client config - */ - public ClientConfig getDefaultClientConfig() { - ClientConfig clientConfig = new ClientConfig(); - clientConfig.register(MultiPartFeature.class); - clientConfig.register(json); - clientConfig.register(JacksonFeature.class); - clientConfig.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true); - // turn off compliance validation to be able to send payloads with DELETE calls - clientConfig.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true); - if (debugging) { - clientConfig.register(new LoggingFeature(java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), java.util.logging.Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 1024*50 /* Log payloads up to 50K */)); - clientConfig.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY, LoggingFeature.Verbosity.PAYLOAD_ANY); - // Set logger to ALL - java.util.logging.Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME).setLevel(java.util.logging.Level.ALL); - } else { - // suppress warnings for payloads with DELETE calls: - java.util.logging.Logger.getLogger("org.glassfish.jersey.client").setLevel(java.util.logging.Level.SEVERE); - } - - return clientConfig; - } - - /** - * Customize the client builder. - * - * This method can be overridden to customize the API client. For example, this can be used to: - * 1. Set the hostname verifier to be used by the client to verify the endpoint's hostname - * against its identification information. - * 2. Set the client-side key store. - * 3. Set the SSL context that will be used when creating secured transport connections to - * server endpoints from web targets created by the client instance that is using this SSL context. - * 4. Set the client-side trust store. - * - * To completely disable certificate validation (at your own risk), you can - * override this method and invoke disableCertificateValidation(clientBuilder). - * - * @param clientBuilder: HTTP client builder - */ - protected void customizeClientBuilder(ClientBuilder clientBuilder) { - // No-op extension point - } - - /** - * Disable X.509 certificate validation in TLS connections. - * - * Please note that trusting all certificates is extremely risky. - * This may be useful in a development environment with self-signed certificates. - * - * @param clientBuilder: HTTP client builder - * @throws KeyManagementException When the SSL context can't be initialized - * @throws NoSuchAlgorithmException If the environment doesn't support the required algorithm - */ - protected void disableCertificateValidation(ClientBuilder clientBuilder) throws KeyManagementException, NoSuchAlgorithmException { - TrustManager[] trustAllCerts = new X509TrustManager[] { - new X509TrustManager() { - @Override - public X509Certificate[] getAcceptedIssuers() { - return null; - } - @Override - public void checkClientTrusted(X509Certificate[] certs, String authType) { - } - @Override - public void checkServerTrusted(X509Certificate[] certs, String authType) { - } - } - }; - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(null, trustAllCerts, new SecureRandom()); - clientBuilder.sslContext(sslContext); - } - - protected Map> buildResponseHeaders(Response response) { - Map> responseHeaders = new HashMap>(); - for (Entry> entry: response.getHeaders().entrySet()) { - List values = entry.getValue(); - List headers = new ArrayList(); - for (Object o : values) { - headers.add(String.valueOf(o)); - } - responseHeaders.put(entry.getKey(), headers); - } - return responseHeaders; - } - - /** - * Update query and header parameters based on authentication settings. - * - * @param authNames The authentications to apply - * @param queryParams List of query parameters - * @param headerParams Map of header parameters - * @param cookieParams Map of cookie parameters - * @param uri HTTP URI - * @throws ApiException If one of the authentication schemes failed to be applied - */ - protected void updateParamsForAuth( - String[] authNames, - List queryParams, - Map headerParams, - Map cookieParams, - URI uri) - throws ApiException { - for (String authName : authNames) { - Authentication auth = authentications.get(authName); - if (auth == null) { - continue; - } - auth.applyToParams(queryParams, headerParams, cookieParams, "", "", uri); - } - } -} diff --git a/.generator/src/generator/templates/ApiException.j2 b/.generator/src/generator/templates/ApiException.j2 deleted file mode 100644 index e078fb7b9e0..00000000000 --- a/.generator/src/generator/templates/ApiException.j2 +++ /dev/null @@ -1,91 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.List; -import java.util.Map; - -/** API Exception */ -{{ generated_annotation }} -public class ApiException extends Exception { - private int code; - private Map> responseHeaders = null; - private String responseBody; - - public ApiException() { - super(); - } - - public ApiException(Throwable throwable) { - super(throwable); - } - - public ApiException(String message) { - super(message); - } - - public ApiException( - String message, - Throwable throwable, - int code, - Map> responseHeaders, - String responseBody) { - super(message, throwable); - this.code = code; - this.responseHeaders = responseHeaders; - this.responseBody = responseBody; - } - - public ApiException( - String message, int code, Map> responseHeaders, String responseBody) { - this(message, (Throwable) null, code, responseHeaders, responseBody); - } - - public ApiException( - String message, Throwable throwable, int code, Map> responseHeaders) { - this(message, throwable, code, responseHeaders, null); - } - - public ApiException(int code, Map> responseHeaders, String responseBody) { - this((String) null, (Throwable) null, code, responseHeaders, responseBody); - } - - public ApiException(int code, String message) { - super(message); - this.code = code; - } - - public ApiException( - int code, String message, Map> responseHeaders, String responseBody) { - this(code, message); - this.responseHeaders = responseHeaders; - this.responseBody = responseBody; - } - - /** - * Get the HTTP status code. - * - * @return HTTP status code - */ - public int getCode() { - return code; - } - - /** - * Get the HTTP response headers. - * - * @return A map of list of string - */ - public Map> getResponseHeaders() { - return responseHeaders; - } - - /** - * Get the HTTP response body. - * - * @return Response body in the form of string - */ - public String getResponseBody() { - return responseBody; - } -} diff --git a/.generator/src/generator/templates/ApiInfo.j2 b/.generator/src/generator/templates/ApiInfo.j2 deleted file mode 100644 index d206bb38f04..00000000000 --- a/.generator/src/generator/templates/ApiInfo.j2 +++ /dev/null @@ -1,5 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2019-Present Datadog, Inc. - */ diff --git a/.generator/src/generator/templates/ApiResponse.j2 b/.generator/src/generator/templates/ApiResponse.j2 deleted file mode 100644 index 61e63a2881c..00000000000 --- a/.generator/src/generator/templates/ApiResponse.j2 +++ /dev/null @@ -1,64 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.List; -import java.util.Map; - -/** - * API response returned by API call. - * - * @param The type of data that is deserialized from response body - */ -{{ generated_annotation }} -public class ApiResponse { - private final int statusCode; - private final Map> headers; - private final T data; - - /** - * @param statusCode The status code of HTTP response - * @param headers The headers of HTTP response - */ - public ApiResponse(int statusCode, Map> headers) { - this(statusCode, headers, null); - } - - /** - * @param statusCode The status code of HTTP response - * @param headers The headers of HTTP response - * @param data The object deserialized from response bod - */ - public ApiResponse(int statusCode, Map> headers, T data) { - this.statusCode = statusCode; - this.headers = headers; - this.data = data; - } - - /** - * Get the status code - * - * @return status code - */ - public int getStatusCode() { - return statusCode; - } - - /** - * Get the headers - * - * @return map of headers - */ - public Map> getHeaders() { - return headers; - } - - /** - * Get the data - * - * @return data - */ - public T getData() { - return data; - } -} diff --git a/.generator/src/generator/templates/JSON.j2 b/.generator/src/generator/templates/JSON.j2 deleted file mode 100644 index 97be2b1803c..00000000000 --- a/.generator/src/generator/templates/JSON.j2 +++ /dev/null @@ -1,259 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import com.fasterxml.jackson.annotation.*; -import com.fasterxml.jackson.databind.*; -import com.fasterxml.jackson.databind.module.SimpleModule; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import java.text.DateFormat; -import java.time.OffsetDateTime; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import jakarta.ws.rs.core.GenericType; -import jakarta.ws.rs.ext.ContextResolver; -import org.openapitools.jackson.nullable.JsonNullableModule; - -{{ generated_annotation }} -public class JSON implements ContextResolver { - private ObjectMapper mapper; - - public JSON() { - mapper = new ObjectMapper(); - mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); - mapper.configure(MapperFeature.ALLOW_COERCION_OF_SCALARS, false); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true); - mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); - mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING); - mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); - mapper.setDateFormat(new RFC3339DateFormat()); - mapper.registerModule(new JavaTimeModule()); - - SimpleModule module = new SimpleModule(); - module.addSerializer(OffsetDateTime.class, new JsonTimeSerializer()); - mapper.registerModule(module); - - JsonNullableModule jnm = new JsonNullableModule(); - mapper.registerModule(jnm); - } - - /** - * Set the date format for JSON (de)serialization with Date properties. - * @param dateFormat Date format - */ - public void setDateFormat(DateFormat dateFormat) { - mapper.setDateFormat(dateFormat); - } - - @Override - public ObjectMapper getContext(Class type) { - return mapper; - } - - /** - * Get the object mapper - * - * @return object mapper - */ - public ObjectMapper getMapper() { return mapper; } - - /** - * Returns the target model class that should be used to deserialize the input data. - * The discriminator mappings are used to determine the target model class. - * - * @param node The input data. - * @param modelClass The class that contains the discriminator mappings. - * @return The matching class - */ - public static Class getClassForElement(JsonNode node, Class modelClass) { - ClassDiscriminatorMapping cdm = modelDiscriminators.get(modelClass); - if (cdm != null) { - return cdm.getClassForElement(node, new HashSet>()); - } - return null; - } - - /** - * Helper class to register the discriminator mappings. - */ - private static class ClassDiscriminatorMapping { - // The model class name. - Class modelClass; - // The name of the discriminator property. - String discriminatorName; - // The discriminator mappings for a model class. - Map> discriminatorMappings; - - // Constructs a new class discriminator. - ClassDiscriminatorMapping(Class cls, String propertyName, Map> mappings) { - modelClass = cls; - discriminatorName = propertyName; - discriminatorMappings = new HashMap>(); - if (mappings != null) { - discriminatorMappings.putAll(mappings); - } - } - - // Return the name of the discriminator property for this model class. - String getDiscriminatorPropertyName() { - return discriminatorName; - } - - // Return the discriminator value or null if the discriminator is not - // present in the payload. - String getDiscriminatorValue(JsonNode node) { - // Determine the value of the discriminator property in the input data. - if (discriminatorName != null) { - // Get the value of the discriminator property, if present in the input payload. - node = node.get(discriminatorName); - if (node != null && node.isValueNode()) { - String discrValue = node.asText(); - if (discrValue != null) { - return discrValue; - } - } - } - return null; - } - - /** - * Returns the target model class that should be used to deserialize the input data. - * This function can be invoked for anyOf/oneOf composed models with discriminator mappings. - * The discriminator mappings are used to determine the target model class. - * - * @param node The input data. - * @param visitedClasses The set of classes that have already been visited. - * @return The target class - */ - Class getClassForElement(JsonNode node, Set> visitedClasses) { - if (visitedClasses.contains(modelClass)) { - // Class has already been visited. - return null; - } - // Determine the value of the discriminator property in the input data. - String discrValue = getDiscriminatorValue(node); - if (discrValue == null) { - return null; - } - Class cls = discriminatorMappings.get(discrValue); - // It may not be sufficient to return this cls directly because that target class - // may itself be a composed schema, possibly with its own discriminator. - visitedClasses.add(modelClass); - for (Class childClass : discriminatorMappings.values()) { - ClassDiscriminatorMapping childCdm = modelDiscriminators.get(childClass); - if (childCdm == null) { - continue; - } - if (!discriminatorName.equals(childCdm.discriminatorName)) { - discrValue = getDiscriminatorValue(node); - if (discrValue == null) { - continue; - } - } - if (childCdm != null) { - // Recursively traverse the discriminator mappings. - Class childDiscr = childCdm.getClassForElement(node, visitedClasses); - if (childDiscr != null) { - return childDiscr; - } - } - } - return cls; - } - } - - /** - * Returns true if inst is an instance of modelClass in the OpenAPI model hierarchy. - * - * The Java class hierarchy is not implemented the same way as the OpenAPI model hierarchy, - * so it's not possible to use the instanceof keyword. - * - * @param modelClass A OpenAPI model class. - * @param inst The instance object. - * @param visitedClasses Keep track of the hierarchy to break cycles - * @return Boolean indicating if the class matches - */ - public static boolean isInstanceOf(Class modelClass, Object inst, Set> visitedClasses) { - if (modelClass.isInstance(inst)) { - // This handles the 'allOf' use case with single parent inheritance. - return true; - } - if (visitedClasses.contains(modelClass)) { - // This is to prevent infinite recursion when the composed schemas have - // a circular dependency. - return false; - } - visitedClasses.add(modelClass); - - // Traverse the oneOf/anyOf composed schemas. - Map descendants = modelDescendants.get(modelClass); - if (descendants != null) { - for (GenericType childType : descendants.values()) { - if (isInstanceOf(childType.getRawType(), inst, visitedClasses)) { - return true; - } - } - } - return false; - } - - /** - * A map of discriminators for all model classes. - */ - private static Map, ClassDiscriminatorMapping> modelDiscriminators = new HashMap, ClassDiscriminatorMapping>(); - - /** - * A map of oneOf/anyOf descendants for each model class. - */ - private static Map, Map> modelDescendants = new HashMap, Map>(); - - /** - * Register a model class discriminator. - * - * @param modelClass the model class - * @param discriminatorPropertyName the name of the discriminator property - * @param mappings a map with the discriminator mappings. - */ - public static void registerDiscriminator(Class modelClass, String discriminatorPropertyName, Map> mappings) { - ClassDiscriminatorMapping m = new ClassDiscriminatorMapping(modelClass, discriminatorPropertyName, mappings); - modelDiscriminators.put(modelClass, m); - } - - /** - * Register the oneOf/anyOf descendants of the modelClass. - * - * @param modelClass the model class - * @param descendants a map of oneOf/anyOf descendants. - */ - public static void registerDescendants(Class modelClass, Map descendants) { - modelDescendants.put(modelClass, descendants); - } - - private static JSON json; - - static - { - json = new JSON(); - } - - /** - * Get the default JSON instance. - * - * @return the default JSON instance - */ - public static JSON getDefault() { - return json; - } - - /** - * Set the default JSON instance. - * - * @param json JSON instance to be used - */ - public static void setDefault(JSON json) { - JSON.json = json; - } -} diff --git a/.generator/src/generator/templates/JsonTimeSerializer.j2 b/.generator/src/generator/templates/JsonTimeSerializer.j2 deleted file mode 100644 index c339e38586f..00000000000 --- a/.generator/src/generator/templates/JsonTimeSerializer.j2 +++ /dev/null @@ -1,33 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import java.io.IOException; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; - -public class JsonTimeSerializer extends StdSerializer { - private static DateTimeFormatter msFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); - private static DateTimeFormatter missingMsFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); - - public JsonTimeSerializer() { - this(null); - } - - public JsonTimeSerializer(Class t) { - super(t); - } - - @Override - public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider arg2) throws IOException { - if (value.getNano() == 0) { - gen.writeString(missingMsFormatter.format(value)); - } else { - gen.writeString(msFormatter.format(value)); - } - } -} diff --git a/.generator/src/generator/templates/PaginationIterable.j2 b/.generator/src/generator/templates/PaginationIterable.j2 deleted file mode 100644 index 01ef1309f42..00000000000 --- a/.generator/src/generator/templates/PaginationIterable.j2 +++ /dev/null @@ -1,56 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.LinkedHashMap; - -public class PaginationIterable implements Iterable { - Object requestClass; - String requestName; - String[] resultsPath = null; - String[] valueGetterPath; - String[] valueSetterPath; - Boolean valueSetterParamOptional; - Boolean offsetPageIncrement; - Boolean cursorPagination; - Object limit; - LinkedHashMap args; - int pageStart; - - public PaginationIterable( - Object requestClass, - String requestName, - String resultsPath, - String valueGetterPath, - String valueSetterPath, - Boolean valueSetterParamOptional, - Boolean offsetPageIncrement, - Boolean cursorPagination, - Object limit, - LinkedHashMap args, - int pageStart) { - - this.requestClass = requestClass; - this.requestName = requestName; - if (resultsPath != "") { - this.resultsPath = resultsPath.split("\\."); - } - if (!valueGetterPath.isEmpty()) { - this.valueGetterPath = valueGetterPath.split("\\."); - } else { - this.valueGetterPath = new String[0]; - } - this.valueSetterPath = valueSetterPath.split("\\."); - this.valueSetterParamOptional = valueSetterParamOptional; - this.offsetPageIncrement = offsetPageIncrement; - this.cursorPagination = cursorPagination; - this.limit = limit; - this.args = args; - this.pageStart = pageStart; - } - - @Override - public PaginationIterator iterator() { - return new PaginationIterator(this); - } -} diff --git a/.generator/src/generator/templates/PaginationIterator.j2 b/.generator/src/generator/templates/PaginationIterator.j2 deleted file mode 100644 index 01a6f5aa4a6..00000000000 --- a/.generator/src/generator/templates/PaginationIterator.j2 +++ /dev/null @@ -1,166 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Iterator; - -public class PaginationIterator implements Iterator { - private int currentIndex = 0; - private int totalCount = 0; - private Boolean hasNextPage; - private ArrayList data; - private PaginationIterable iterable; - private Method requestMethod; - - PaginationIterator(PaginationIterable iterable) { - this.iterable = iterable; - this.requestMethod = buildRequestMethod(); - - // Populate initial data - getNextPage(); - } - - private Method buildRequestMethod() { - Method[] methods = this.iterable.requestClass.getClass().getDeclaredMethods(); - for (Method m : methods) { - if (m.getName().equals(this.iterable.requestName)) { - if (m.getParameterTypes().length == this.iterable.args.keySet().size()) { - m.setAccessible(true); - return m; - } - } - } - - throw new RuntimeException("Unable to find request method " + this.iterable.requestName); - } - - private void setNextPageValue(Object response) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException { - Object value = response; - - Object temp; - int i; - if (this.iterable.valueSetterParamOptional) { - temp = this.iterable.args.get("optionalParams"); - i = 0; - } else { - // First accessor is the arg. Skip the first item in list. - temp = this.iterable.args.get(this.iterable.valueSetterPath[0]); - i = 1; - } - - // Get the correct object with the setter method - for (; i < this.iterable.valueSetterPath.length - 1; i++) { - try { - // Check if field else fallback to method - Field f = temp.getClass().getDeclaredField(this.iterable.valueSetterPath[i]); - f.setAccessible(true); - temp = f.get(temp); - } catch (Exception e) { - temp = temp.getClass().getMethod(this.iterable.valueSetterPath[i]).invoke(temp); - } - } - - // Get setter method. - Method setterMethod = null; - for (Method m : temp.getClass().getDeclaredMethods()) { - if (m.getName().equals(this.iterable.valueSetterPath[i])) { - setterMethod = m; - break; - } - } - - assert setterMethod != null; - - if (this.iterable.valueGetterPath.length > 0) { - for (String path : this.iterable.valueGetterPath) { - value = value.getClass().getMethod(path).invoke(value); - } - } else { - // fallback to pageOffset = pageStart + totalCount; - // We cast the type based on the setterMethod parameter type - String pType = setterMethod.getParameterTypes()[0].getSimpleName(); - if ("Long".equals(pType)) { - value = (long) (this.iterable.pageStart + this.totalCount); - } else { - value = this.iterable.pageStart + this.totalCount; - } - } - - // Set the value - setterMethod.invoke(temp, value); - - this.hasNextPage = true; - } - - private void getNextPage() { - Object response; - try { - response = this.requestMethod.invoke(iterable.requestClass, iterable.args.values().toArray()); - Object resultData = response; - - if (this.iterable.resultsPath != null) { - for (String path : this.iterable.resultsPath) { - resultData = resultData.getClass().getMethod(path).invoke(resultData); - } - } - - this.data = ((ArrayList) resultData); - if (this.iterable.offsetPageIncrement) { - this.totalCount += this.data.size(); - } else { - this.totalCount += 1; - } - // Reset the index back to zero - this.currentIndex = 0; - } catch (Exception e) { - throw new RuntimeException("Unable to preload results: " + e.getMessage(), e); - } - - try { - setNextPageValue(response); - } catch (Exception e) { - this.hasNextPage = false; - } - } - - private static int convertToInt(Object arg) { - if ("Long".equals(arg.getClass().getSimpleName())) { - long value; - value = Long.parseLong(arg.toString()); - return (int) value; - } - return (int) arg; - } - - @Override - public boolean hasNext() { - if (this.currentIndex < this.data.size()) { - return true; - } - - if (this.iterable.cursorPagination) { - if (this.data.size() == 0) { - return false; - } - } else if (this.data.size() < convertToInt(this.iterable.limit)) { - return false; - } - - if (this.hasNextPage) { - getNextPage(); - return this.data.size() != 0; - } - - return false; - } - - @Override - public T next() { - this.currentIndex++; - return ((T) data.get(currentIndex - 1)); - } -} diff --git a/.generator/src/generator/templates/Pair.j2 b/.generator/src/generator/templates/Pair.j2 deleted file mode 100644 index 6724d4d134e..00000000000 --- a/.generator/src/generator/templates/Pair.j2 +++ /dev/null @@ -1,50 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -{{ generated_annotation }} -public class Pair { - private String name = ""; - private String value = ""; - - public Pair(String name, String value) { - setName(name); - setValue(value); - } - - private void setName(String name) { - if (!isValidString(name)) { - return; - } - - this.name = name; - } - - private void setValue(String value) { - if (!isValidString(value)) { - return; - } - - this.value = value; - } - - public String getName() { - return this.name; - } - - public String getValue() { - return this.value; - } - - private boolean isValidString(String arg) { - if (arg == null) { - return false; - } - - if (arg.trim().isEmpty()) { - return false; - } - - return true; - } -} diff --git a/.generator/src/generator/templates/RFC3339DateFormat.j2 b/.generator/src/generator/templates/RFC3339DateFormat.j2 deleted file mode 100644 index 987836fe59b..00000000000 --- a/.generator/src/generator/templates/RFC3339DateFormat.j2 +++ /dev/null @@ -1,43 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import com.fasterxml.jackson.databind.util.StdDateFormat; -import java.text.DateFormat; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.TimeZone; - -public class RFC3339DateFormat extends DateFormat { - private static final long serialVersionUID = 1L; - private static final TimeZone TIMEZONE_Z = TimeZone.getTimeZone("UTC"); - - private final StdDateFormat fmt = - new StdDateFormat().withTimeZone(TIMEZONE_Z).withColonInTimeZone(true); - - public RFC3339DateFormat() { - this.calendar = new GregorianCalendar(); - } - - @Override - public Date parse(String source) { - return parse(source, new ParsePosition(0)); - } - - @Override - public Date parse(String source, ParsePosition pos) { - return fmt.parse(source, pos); - } - - @Override - public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { - return fmt.format(date, toAppendTo, fieldPosition); - } - - @Override - public Object clone() { - return this; - } -} diff --git a/.generator/src/generator/templates/RetryConfig.j2 b/.generator/src/generator/templates/RetryConfig.j2 deleted file mode 100644 index 8e1d8c87733..00000000000 --- a/.generator/src/generator/templates/RetryConfig.j2 +++ /dev/null @@ -1,71 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class RetryConfig { - public boolean enableRetry; - public int backOffMultiplier; - public int backOffBase; - public int maxRetries; - private static final Logger log = Logger.getLogger(RetryConfig.class.getName()); - - /** - * @param enableRetry Enable retry when rate limited - * @param backOffMultiplier Multiplier for retry backoff - * @param backOffBase Base for retry backoff - * @param maxRetries Maximum number of retries - */ - public RetryConfig(boolean enableRetry, int backOffMultiplier, int backOffBase, int maxRetries) { - if (backOffBase < 2) { - throw new IllegalArgumentException("backOffBase cannot be smaller than 2"); - } - this.enableRetry = enableRetry; - this.backOffMultiplier = backOffMultiplier; - this.backOffBase = backOffBase; - this.maxRetries = maxRetries; - } - - public boolean isEnableRetry() { - return enableRetry; - } - - public int getBackOffMultiplier() { - return backOffMultiplier; - } - - public int getBackOffBase() { - return backOffBase; - } - - public int getMaxRetries() { - return maxRetries; - } - - public void setEnableRetry(boolean enableRetry) { - this.enableRetry = enableRetry; - } - - public void setBackOffMultiplier(int backOffMultiplier) { - this.backOffMultiplier = backOffMultiplier; - } - - public void setBackOffBase(int backOffBase) { - this.backOffBase = backOffBase; - } - - public void setMaxRetries(int maxRetries) { - this.maxRetries = maxRetries; - } - - public void sleepInterval(int sleepInterval) { - try { - Thread.sleep(sleepInterval * 1000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - log.log(Level.FINE, "Retry sleep interrupted", e); - } - } -} \ No newline at end of file diff --git a/.generator/src/generator/templates/ServerConfiguration.j2 b/.generator/src/generator/templates/ServerConfiguration.j2 deleted file mode 100644 index 87142b41366..00000000000 --- a/.generator/src/generator/templates/ServerConfiguration.j2 +++ /dev/null @@ -1,61 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.Map; - -/** Representing a Server configuration. */ -public class ServerConfiguration { - public String URL; - public String description; - public Map variables; - - /** - * @param URL A URL to the target host. - * @param description A description of the host designated by the URL. - * @param variables A map between a variable name and its value. The value is used for - * substitution in the server's URL template. - */ - public ServerConfiguration( - String URL, String description, Map variables) { - this.URL = URL; - this.description = description; - this.variables = variables; - } - - /** - * Format URL template using given variables. - * - * @param variables A map between a variable name and its value. - * @return Formatted URL. - */ - public String URL(Map variables) { - String url = this.URL; - - // go through variables and replace placeholders - for (Map.Entry variable : this.variables.entrySet()) { - String name = variable.getKey(); - ServerVariable serverVariable = variable.getValue(); - String value = serverVariable.defaultValue; - - if (variables != null && variables.containsKey(name)) { - value = variables.get(name); - if (serverVariable.enumValues.size() > 0 && !serverVariable.enumValues.contains(value)) { - throw new RuntimeException( - "The variable " + name + " in the server URL has invalid value " + value + "."); - } - } - url = url.replaceAll("\\{" + name + "\\}", value); - } - return url; - } - - /** - * Format URL template using default server variables. - * - * @return Formatted URL. - */ - public String URL() { - return URL(null); - } -} diff --git a/.generator/src/generator/templates/ServerVariable.j2 b/.generator/src/generator/templates/ServerVariable.j2 deleted file mode 100644 index 9cbc8dc5bd6..00000000000 --- a/.generator/src/generator/templates/ServerVariable.j2 +++ /dev/null @@ -1,24 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.HashSet; - -/** Representing a Server Variable for server URL template substitution. */ -public class ServerVariable { - public String description; - public String defaultValue; - public HashSet enumValues = null; - - /** - * @param description A description for the server variable. - * @param defaultValue The default value to use for substitution. - * @param enumValues An enumeration of string values to be used if the substitution options are - * from a limited set. - */ - public ServerVariable(String description, String defaultValue, HashSet enumValues) { - this.description = description; - this.defaultValue = defaultValue; - this.enumValues = enumValues; - } -} diff --git a/.generator/src/generator/templates/StringUtil.j2 b/.generator/src/generator/templates/StringUtil.j2 deleted file mode 100644 index 29866d0ffa8..00000000000 --- a/.generator/src/generator/templates/StringUtil.j2 +++ /dev/null @@ -1,71 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import java.util.Collection; -import java.util.Iterator; - -{{ generated_annotation }} -public class StringUtil { - /** - * Check if the given array contains the given value (with case-insensitive comparison). - * - * @param array The array - * @param value The value to search - * @return true if the array contains the value - */ - public static boolean containsIgnoreCase(String[] array, String value) { - for (String str : array) { - if (value == null && str == null) { - return true; - } - if (value != null && value.equalsIgnoreCase(str)) { - return true; - } - } - return false; - } - - /** - * Join an array of strings with the given separator. - * - *

Note: This might be replaced by utility method from commons-lang or guava someday if one of - * those libraries is added as dependency. - * - * @param array The array of strings - * @param separator The separator - * @return the resulting string - */ - public static String join(String[] array, String separator) { - int len = array.length; - if (len == 0) { - return ""; - } - - StringBuilder out = new StringBuilder(); - out.append(array[0]); - for (int i = 1; i < len; i++) { - out.append(separator).append(array[i]); - } - return out.toString(); - } - - /** - * Join a list of strings with the given separator. - * - * @param list The list of strings - * @param separator The separator - * @return the resulting string - */ - public static String join(Collection list, String separator) { - Iterator iterator = list.iterator(); - StringBuilder out = new StringBuilder(); - if (iterator.hasNext()) { - out.append(iterator.next()); - } - while (iterator.hasNext()) { - out.append(separator).append(iterator.next()); - } - return out.toString(); - } -} diff --git a/.generator/src/generator/templates/UnparsedObject.j2 b/.generator/src/generator/templates/UnparsedObject.j2 deleted file mode 100644 index 3fd9f6d2ca5..00000000000 --- a/.generator/src/generator/templates/UnparsedObject.j2 +++ /dev/null @@ -1,67 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; -import java.util.Map; - -@JsonSerialize(using = UnparsedObject.UnparsedObjectSerializer.class) -public class UnparsedObject { - Map data; - - public UnparsedObject(Map data) { - this.data = data; - } - - public static class UnparsedObjectSerializer extends StdSerializer { - public UnparsedObjectSerializer(Class t) { - super(t); - } - - public UnparsedObjectSerializer() { - this(null); - } - - @Override - public void serialize(UnparsedObject value, JsonGenerator jgen, SerializerProvider provider) - throws IOException, JsonProcessingException { - jgen.writeObject(value.data); - } - } - - public Map getData() { - return this.data; - } - - public void setData(Map data) { - this.data = data; - } - - @Override - public int hashCode() { - return data.hashCode(); - } - - /** Return true if this UnparsedObject object is equal to o. */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - return this.data.equals(((UnparsedObject) o).data); - } - - @JsonCreator - public static UnparsedObject fromValue(Map value) { - return new UnparsedObject(value); - } -} diff --git a/.generator/src/generator/templates/ZstdEncoder.j2 b/.generator/src/generator/templates/ZstdEncoder.j2 deleted file mode 100644 index 9ee8b06aa9f..00000000000 --- a/.generator/src/generator/templates/ZstdEncoder.j2 +++ /dev/null @@ -1,47 +0,0 @@ -{% include "ApiInfo.j2" %} -package {{ common_package_name }}; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.InvocationTargetException; -import jakarta.annotation.Priority; -import jakarta.ws.rs.Priorities; -import org.glassfish.jersey.spi.ContentEncoder; - -/** Zstd encoding support */ -@Priority(Priorities.ENTITY_CODER) -public class ZstdEncoder extends ContentEncoder { - public ZstdEncoder() { - super("zstd1"); - } - - @Override - public InputStream decode(String contentEncoding, InputStream encodedStream) throws IOException { - try { - Class streamClass = Class.forName("com.github.luben.zstd.ZstdInputStream"); - return (InputStream) streamClass.getConstructor(InputStream.class).newInstance(encodedStream); - } catch (ClassNotFoundException - | NoSuchMethodException - | InstantiationException - | IllegalAccessException - | InvocationTargetException e) { - throw new RuntimeException(e); - } - } - - @Override - public OutputStream encode(String contentEncoding, OutputStream entityStream) throws IOException { - try { - Class streamClass = Class.forName("com.github.luben.zstd.ZstdOutputStream"); - return (OutputStream) - streamClass.getConstructor(OutputStream.class).newInstance(entityStream); - } catch (ClassNotFoundException - | NoSuchMethodException - | InstantiationException - | IllegalAccessException - | InvocationTargetException e) { - throw new RuntimeException(e); - } - } -} diff --git a/.generator/src/generator/templates/auth/ApiKeyAuth.j2 b/.generator/src/generator/templates/auth/ApiKeyAuth.j2 deleted file mode 100644 index d27f13d9edf..00000000000 --- a/.generator/src/generator/templates/auth/ApiKeyAuth.j2 +++ /dev/null @@ -1,66 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}.auth; - -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.ApiException; -import java.net.URI; -import java.util.Map; -import java.util.List; - -{{ generated_annotation }} -public class ApiKeyAuth implements Authentication { - private final String location; - private final String paramName; - - private String apiKey; - private String apiKeyPrefix; - public ApiKeyAuth(String location, String paramName) { - this.location = location; - this.paramName = paramName; - } - - public String getLocation() { - return location; - } - - public String getParamName() { - return paramName; - } - - public String getApiKey() { - return apiKey; - } - - public void setApiKey(String apiKey) { - this.apiKey = apiKey; - } - - public String getApiKeyPrefix() { - return apiKeyPrefix; - } - - public void setApiKeyPrefix(String apiKeyPrefix) { - this.apiKeyPrefix = apiKeyPrefix; - } - - @Override - public void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException { - if (apiKey == null) { - return; - } - String value; - if (apiKeyPrefix != null) { - value = apiKeyPrefix + " " + apiKey; - } else { - value = apiKey; - } - if ("query".equals(location)) { - queryParams.add(new Pair(paramName, value)); - } else if ("header".equals(location)) { - headerParams.put(paramName, value); - } else if ("cookie".equals(location)) { - cookieParams.put(paramName, value); - } - } -} diff --git a/.generator/src/generator/templates/auth/Authentication.j2 b/.generator/src/generator/templates/auth/Authentication.j2 deleted file mode 100644 index 22c1d649cac..00000000000 --- a/.generator/src/generator/templates/auth/Authentication.j2 +++ /dev/null @@ -1,25 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}.auth; - -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.ApiException; - -import java.net.URI; -import java.util.Map; -import java.util.List; - -public interface Authentication { - /** - * Apply authentication settings to header and query params. - * - * @param queryParams List of query parameters - * @param headerParams Map of header parameters - * @param cookieParams Map of cookie parameters - * @param payload Raw payload - * @param method HTTP method to authenticate - * @param uri URI to authenticate - * @throws ApiException If the settings can't be applied - */ - void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException; -} diff --git a/.generator/src/generator/templates/auth/HttpBasicAuth.j2 b/.generator/src/generator/templates/auth/HttpBasicAuth.j2 deleted file mode 100644 index 91f5d8a10e6..00000000000 --- a/.generator/src/generator/templates/auth/HttpBasicAuth.j2 +++ /dev/null @@ -1,41 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}.auth; - -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.ApiException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.List; -import java.util.Map; - -{{ generated_annotation }} -public class HttpBasicAuth implements Authentication { - private String username; - private String password; - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException { - if (username == null && password == null) { - return; - } - String str = (username == null ? "" : username) + ":" + (password == null ? "" : password); - headerParams.put("Authorization", "Basic " + Base64.getEncoder().encodeToString(str.getBytes(StandardCharsets.UTF_8))); - } -} diff --git a/.generator/src/generator/templates/auth/HttpBearerAuth.j2 b/.generator/src/generator/templates/auth/HttpBearerAuth.j2 deleted file mode 100644 index e07552b48d0..00000000000 --- a/.generator/src/generator/templates/auth/HttpBearerAuth.j2 +++ /dev/null @@ -1,50 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}.auth; - -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.ApiException; - -import java.net.URI; -import java.util.Map; -import java.util.List; - -{{ generated_annotation }} -public class HttpBearerAuth implements Authentication { - private final String scheme; - private String bearerToken; - public HttpBearerAuth(String scheme) { - this.scheme = scheme; - } - - /** - * Gets the token, which together with the scheme, will be sent as the value of the Authorization header. - * - * @return The bearer token - */ - public String getBearerToken() { - return bearerToken; - } - - /** - * Sets the token, which together with the scheme, will be sent as the value of the Authorization header. - * - * @param bearerToken The bearer token to send in the Authorization header - */ - public void setBearerToken(String bearerToken) { - this.bearerToken = bearerToken; - } - - @Override - public void applyToParams(List queryParams, Map headerParams, Map cookieParams, String payload, String method, URI uri) throws ApiException { - if(bearerToken == null) { - return; - } - - headerParams.put("Authorization", (scheme != null ? upperCaseBearer(scheme) + " " : "") + bearerToken); - } - - private static String upperCaseBearer(String scheme) { - return ("bearer".equalsIgnoreCase(scheme)) ? "Bearer" : scheme; - } -} diff --git a/.generator/src/generator/templates/auth/OAuth.j2 b/.generator/src/generator/templates/auth/OAuth.j2 deleted file mode 100644 index 441c802d6db..00000000000 --- a/.generator/src/generator/templates/auth/OAuth.j2 +++ /dev/null @@ -1,182 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}.auth; - -import {{ common_package_name }}.Pair; -import {{ common_package_name }}.ApiException; - -import com.github.scribejava.core.builder.ServiceBuilder; -import com.github.scribejava.core.builder.api.DefaultApi20; -import com.github.scribejava.core.exceptions.OAuthException; -import com.github.scribejava.core.model.OAuth2AccessToken; -import com.github.scribejava.core.oauth.OAuth20Service; - -import jakarta.ws.rs.core.UriBuilder; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import java.util.logging.Logger; - -{{ generated_annotation }} -public class OAuth implements Authentication { - private static final Logger log = Logger.getLogger(OAuth.class.getName()); - - private String tokenUrl; - private String absoluteTokenUrl; - private OAuthFlow flow = OAuthFlow.application; - private OAuth20Service service; - private DefaultApi20 authApi; - private String scope; - private String username; - private String password; - private String code; - private volatile OAuth2AccessToken accessToken; - public OAuth(String basePath, String tokenUrl) { - this.tokenUrl = tokenUrl; - this.absoluteTokenUrl = createAbsoluteTokenUrl(basePath, tokenUrl); - authApi = new DefaultApi20() { - @Override - public String getAccessTokenEndpoint() { - return absoluteTokenUrl; - } - - @Override - protected String getAuthorizationBaseUrl() { - throw new UnsupportedOperationException("Shouldn't get there !"); - } - }; - } - - private static String createAbsoluteTokenUrl(String basePath, String tokenUrl) { - if (!URI.create(tokenUrl).isAbsolute()) { - try { - return UriBuilder.fromPath(basePath).path(tokenUrl).build().toURL().toString(); - } catch (MalformedURLException e) { - log.log(Level.SEVERE, "Couldn't create absolute token URL", e); - } - } - return tokenUrl; - } - - @Override - public void applyToParams( - List queryParams, - Map headerParams, - Map cookieParams, - String payload, - String method, - URI uri) - throws ApiException { - - if (accessToken == null) { - obtainAccessToken(null); - } - if (accessToken != null) { - headerParams.put("Authorization", "Bearer " + accessToken.getAccessToken()); - } - } - - public OAuth2AccessToken renewAccessToken() throws ApiException { - String refreshToken = null; - if (accessToken != null) { - refreshToken = accessToken.getRefreshToken(); - accessToken = null; - } - return obtainAccessToken(refreshToken); - } - - public synchronized OAuth2AccessToken obtainAccessToken(String refreshToken) throws ApiException { - if (service == null) { - log.log(Level.FINE, "service is null in obtainAccessToken."); - return null; - } - try { - if (refreshToken != null) { - return service.refreshAccessToken(refreshToken); - } - } catch (OAuthException | InterruptedException | ExecutionException | IOException e) { - log.log(Level.FINE, "Refreshing the access token using the refresh token failed", e); - } - try { - switch (flow) { - case password: - if (username != null && password != null) { - accessToken = service.getAccessTokenPasswordGrant(username, password, scope); - } - break; - case accessCode: - if (code != null) { - accessToken = service.getAccessToken(code); - code = null; - } - break; - case application: - accessToken = service.getAccessTokenClientCredentialsGrant(scope); - break; - default: - log.log(Level.SEVERE, "Invalid flow in obtainAccessToken: " + flow); - } - } catch (OAuthException | InterruptedException | ExecutionException | IOException e) { - throw new ApiException(e); - } - return accessToken; - } - - public OAuth2AccessToken getAccessToken() { - return accessToken; - } - - public OAuth setAccessToken(OAuth2AccessToken accessToken) { - this.accessToken = accessToken; - return this; - } - - public OAuth setAccessToken(String accessToken) { - this.accessToken = new OAuth2AccessToken(accessToken); - return this; - } - - public OAuth setScope(String scope) { - this.scope = scope; - return this; - } - - public OAuth setCredentials(String clientId, String clientSecret, Boolean debug) { - if (Boolean.TRUE.equals(debug)) { - service = new ServiceBuilder(clientId) - .apiSecret(clientSecret).debug() - .build(authApi); - } else { - service = new ServiceBuilder(clientId) - .apiSecret(clientSecret) - .build(authApi); - } - return this; - } - - public OAuth usePasswordFlow(String username, String password) { - this.flow = OAuthFlow.password; - this.username = username; - this.password = password; - return this; - } - - public OAuth useAuthorizationCodeFlow(String code) { - this.flow = OAuthFlow.accessCode; - this.code = code; - return this; - } - - public OAuth setFlow(OAuthFlow flow) { - this.flow = flow; - return this; - } - - public void setBasePath(String basePath) { - this.absoluteTokenUrl = createAbsoluteTokenUrl(basePath, tokenUrl); - } -} diff --git a/.generator/src/generator/templates/auth/OAuthFlow.j2 b/.generator/src/generator/templates/auth/OAuthFlow.j2 deleted file mode 100644 index 2a69c90c617..00000000000 --- a/.generator/src/generator/templates/auth/OAuthFlow.j2 +++ /dev/null @@ -1,7 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ common_package_name }}.auth; - -public enum OAuthFlow { - accessCode, implicit, password, application -} diff --git a/.generator/src/generator/templates/example.j2 b/.generator/src/generator/templates/example.j2 deleted file mode 100644 index 60d4e9aa825..00000000000 --- a/.generator/src/generator/templates/example.j2 +++ /dev/null @@ -1,110 +0,0 @@ -// {{ scenario.name|wordwrap(width=120, wrapstring='\n// ')}} - -{%- set required_parameters, optional_parameters, parameters_models = format_parameters(context.api_request.kwargs, spec=operation_spec, replace_values=context._replace_values, has_body=context.body) %} -{%- if context.body %} -{%- set body_type, body, body_models = format_data_with_schema(context.body.value, context.api_request.schema.spec, replace_values=context._replace_values) %} -{%- endif %} -{%- set api_response_type, api_reponse_type_import = get_response_type(context.api_response, version)%} - -{%- for package, imports in context._imports.items() %} -{%- for import in imports %} -import {{ package }}.{{ import }}; -{%- endfor %} -{%- endfor %} - -import com.datadog.api.client.ApiException; -import com.datadog.api.client.ApiClient; -import com.datadog.api.client.{{ version }}.api.{{ context.api_instance.name|camel_case|upperfirst }}Api; -{%- if optional_parameters %} -import com.datadog.api.client.{{ version }}.api.{{ context.api_instance.name|camel_case|upperfirst }}Api.{{ context.api_request.operation_id }}OptionalParameters; -{%- endif %} -{%- if api_reponse_type_import %} -import {{ api_reponse_type_import}}; -{%- endif %} -{%- if context.pagination %} -{%- set pagination = operation_spec["x-pagination"] %} -{%- set paginationReturnType = get_type_at_path(operation_spec, pagination["resultsPath"]) %} -import com.datadog.api.client.{{ version }}.model.{{ paginationReturnType }}; -import com.datadog.api.client.PaginationIterable; -{%- endif %} -{%- for model in (parameters_models|list + (body_models or [])|list)|unique|sort %} -import com.datadog.api.client.{{ version }}.model.{{ model }}; -{%- endfor %} -import java.io.File; -import java.time.OffsetDateTime; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -public class Example { - public static void main(String[] args) { - ApiClient defaultClient = ApiClient.getDefaultApiClient(); -{%- for operation in context._enable_operations|sort %} - defaultClient.setUnstableOperationEnabled("{{ version }}.{{ operation|untitle_case }}", true); -{%- endfor %} - {{ context.api_instance.name|camel_case|upperfirst }}Api apiInstance = new {{ context.api_instance.name|camel_case|upperfirst }}Api(defaultClient); - -{%- for name, values in context._given.items() %} - - // {{ name }} -{%- for value, schema in values.items()|sort %} -{%- if schema.type == 'string' and schema.format != 'uuid'%} - String {{ value }} = System.getenv("{{ value }}"); -{%- elif schema.type == 'string' and schema.format == 'uuid' %} - UUID {{ value }} = null; - try { - {{ value }} = UUID.fromString(System.getenv("{{ value }}")); - } catch (IllegalArgumentException e) { - System.err.println("Error parsing UUID: " + e.getMessage()); - } -{%- elif schema.type == 'integer' %} - Long {{ value }} = Long.parseLong(System.getenv("{{ value }}")); -{%- elif schema.type == 'boolean' %} - Boolean {{ value }} = Boolean.parseBoolean((System.getenv("{{ value }}"))); -{%- else %} -{{ 1/0 }} -{%- endif %} -{%- endfor %} - -{%- endfor %} - -{%- if context.body %} - - {{ body_type }} body = {{ body }}; -{%- endif %} - -{%- if context.pagination %} -{%- set pagination = operation_spec["x-pagination"] %} -{%- set paginationReturnType = get_type_at_path(operation_spec, pagination["resultsPath"]) %} - - try { - PaginationIterable<{{ paginationReturnType }}> iterable = apiInstance.{{ context.api_request.operation_id|untitle_case }}WithPagination({% if required_parameters %}{{ required_parameters }}{% endif %}{% if optional_parameters %}{% if required_parameters %}{{ "," }}{% endif %}{{ optional_parameters }}{% endif %}); - - for ({{ paginationReturnType }} item : iterable) { - System.out.println(item); - } - } catch (RuntimeException e) { - System.err.println("Exception when calling {{ context.api_instance.name|camel_case|upperfirst }}Api#{{ context.api_request.operation_id|untitle_case }}WithPagination"); - System.err.println("Reason: " + e.getMessage()); - e.printStackTrace(); - } -{%- else %} - - try { - {% if api_response_type %}{{api_response_type}} result = {% endif %}apiInstance.{{ context.api_request.operation_id|untitle_case }}({% if required_parameters %}{{ required_parameters }}{% endif %}{% if optional_parameters %}{% if required_parameters %}{{ "," }}{% endif %}{{ optional_parameters }}{% endif %}); - {%- if api_response_type %} - System.out.println(result); - {%- endif %} - } catch (ApiException e) { - System.err.println("Exception when calling {{ context.api_instance.name|camel_case|upperfirst }}Api#{{ context.api_request.operation_id|untitle_case }}"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } - -{%-endif %} - } -} diff --git a/.generator/src/generator/templates/model.j2 b/.generator/src/generator/templates/model.j2 deleted file mode 100644 index b874afa9a6f..00000000000 --- a/.generator/src/generator/templates/model.j2 +++ /dev/null @@ -1,36 +0,0 @@ -{% include "ApiInfo.j2" %} - -package {{ package_name }}.model; - -import java.io.File; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.HashMap; -import java.util.Map; -import java.util.List; -import java.util.ArrayList; -import java.util.UUID; -import java.time.OffsetDateTime; - -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyOrder; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.openapitools.jackson.nullable.JsonNullable; - -import {{ common_package_name }}.JsonTimeSerializer; - -{% if "enum" in model -%} -{% include "modelEnum.j2" %} -{%- elif "oneOf" in model -%} -{% include "modelOneOf.j2" %} -{%- else %} -{% include "modelSimple.j2" %} -{%- endif %} -{# keep new line #} diff --git a/.generator/src/generator/templates/modelEnum.j2 b/.generator/src/generator/templates/modelEnum.j2 deleted file mode 100644 index 768f13b12c2..00000000000 --- a/.generator/src/generator/templates/modelEnum.j2 +++ /dev/null @@ -1,50 +0,0 @@ -import com.datadog.api.client.ModelEnum; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import java.io.IOException; - -import java.util.Set; -import java.util.HashSet; - -/** -{{ model.description|docstring }} - */ -@JsonSerialize(using = {{ name }}.{{ name }}Serializer.class) -public class {{ name }} extends ModelEnum<{{ model|simple_type }}> { -{# keep line #} - private static final Set<{{ model|simple_type }}> allowedValues = new HashSet<{{ model|simple_type }}>(Arrays.asList({% for value in model.enum %}{{ value|format_value(schema=model) }}{% if not loop.last %}, {% endif %}{% endfor %})); -{# keep line #} -{%- for index, value in enumerate(model.enum) %} - public static final {{ name }} {{ model["x-enum-varnames"][index] or value.upper() }} = new {{ name }}({{ value|format_value(schema=model) }}); -{%- endfor %} - - - {{ name }}({{ model|simple_type }} value) { - super(value, allowedValues); - } - - public static class {{ name }}Serializer extends StdSerializer<{{ name }}> { - public {{ name }}Serializer(Class<{{ name }}> t) { - super(t); - } - - public {{ name }}Serializer() { - this(null); - } - - @Override - public void serialize({{ name }} value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { - jgen.writeObject(value.value); - } - } - - @JsonCreator - public static {{ name }} fromValue({{ model|simple_type }} value) { - return new {{ name }}(value); - } -} diff --git a/.generator/src/generator/templates/modelEnumBase.j2 b/.generator/src/generator/templates/modelEnumBase.j2 deleted file mode 100644 index f4e33b37279..00000000000 --- a/.generator/src/generator/templates/modelEnumBase.j2 +++ /dev/null @@ -1,53 +0,0 @@ -package com.datadog.api.client; - -import com.fasterxml.jackson.annotation.JsonValue; - -import java.util.Objects; -import java.util.Set; - -public abstract class ModelEnum { - - protected Set localAllowedValues; - - protected T value; - - public ModelEnum(T value, Set allowedValues) { - this.value = value; - this.localAllowedValues = allowedValues; - } - - public boolean isValid() { - return this.localAllowedValues.contains(this.value); - } - - @JsonValue - public T getValue() { - return this.value; - } - - public void setValue(T value) { - this.value = value; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - return this.value.equals(((ModelEnum) o).value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public String toString() { - return String.valueOf(value); - } -} - diff --git a/.generator/src/generator/templates/modelOneOf.j2 b/.generator/src/generator/templates/modelOneOf.j2 deleted file mode 100644 index 3348cc6f842..00000000000 --- a/.generator/src/generator/templates/modelOneOf.j2 +++ /dev/null @@ -1,277 +0,0 @@ -import jakarta.ws.rs.core.GenericType; -import jakarta.ws.rs.core.Response; -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.JsonToken; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.MapperFeature; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.fasterxml.jackson.annotation.JsonIgnore; -import {{ common_package_name }}.JSON; -import {{ common_package_name }}.AbstractOpenApiSchema; -import {{ common_package_name }}.UnparsedObject; - -{{ generated_annotation }} -@JsonDeserialize(using = {{ name }}.{{name }}Deserializer.class) -@JsonSerialize(using = {{ name }}.{{ name }}Serializer.class) -public class {{ name }} extends AbstractOpenApiSchema { - private static final Logger log = Logger.getLogger({{ name }}.class.getName()); - - @JsonIgnore - public boolean unparsed = false; - - public static class {{ name }}Serializer extends StdSerializer<{{ name }}> { - public {{ name }}Serializer(Class<{{ name }}> t) { - super(t); - } - - public {{ name }}Serializer() { - this(null); - } - - @Override - public void serialize({{ name }} value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { - jgen.writeObject(value.getActualInstance()); - } - } - - public static class {{ name }}Deserializer extends StdDeserializer<{{ name }}> { - public {{ name }}Deserializer() { - this({{ name}}.class); - } - - public {{ name }}Deserializer(Class vc) { - super(vc); - } - - @Override - public {{ name }} deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { - JsonNode tree = jp.readValueAsTree(); - Object deserialized = null; - Object tmp = null; - boolean typeCoercion = ctxt.isEnabled(MapperFeature.ALLOW_COERCION_OF_SCALARS); - int match = 0; - {%- set ns = namespace(has_model_list=false) %} - {%- for oneOf in model.oneOf %} - {%- if "items" in oneOf and oneOf.get("items")|is_model and not oneOf|is_primitive %} - {%- set ns.has_model_list = true %} - {%- endif %} - {%- endfor %} - {%- if ns.has_model_list %} - Object deserializedUnparsed = null; - int matchUnparsed = 0; - {%- endif %} - JsonToken token = tree.traverse(jp.getCodec()).nextToken(); - {%- for oneOf in model.oneOf %} - {%- set parameterizedDataType = get_type(oneOf) %} - {%- set unParameterizedDataType = parameterizedDataType|un_parameterize_type %} - {%- set isParameterized = parameterizedDataType|is_parameterized_type %} - {%- set isModelMember = not oneOf|is_primitive and not unParameterizedDataType|lower|is_java_base_type and "enum" not in oneOf %} - {%- set isComposedMember = "oneOf" in oneOf or "anyOf" in oneOf %} - {%- set isModelListMember = "items" in oneOf and oneOf.get("items")|is_model and not oneOf|is_primitive %} - // deserialize {{ parameterizedDataType }} - try { - boolean attemptParsing = true; - // ensure that we respect type coercion as set on the client ObjectMapper - if ({{ unParameterizedDataType }}.class.equals(Integer.class) || {{ unParameterizedDataType }}.class.equals(Long.class) || {{ unParameterizedDataType }}.class.equals(Float.class) || {{ unParameterizedDataType }}.class.equals(Double.class) || {{ unParameterizedDataType }}.class.equals(Boolean.class) || {{ unParameterizedDataType }}.class.equals(String.class)) { - attemptParsing = typeCoercion; - if (!attemptParsing) { - attemptParsing |= (({{ unParameterizedDataType }}.class.equals(Integer.class) || {{ unParameterizedDataType }}.class.equals(Long.class)) && token == JsonToken.VALUE_NUMBER_INT); - attemptParsing |= (({{ unParameterizedDataType }}.class.equals(Float.class) || {{ unParameterizedDataType }}.class.equals(Double.class)) && (token == JsonToken.VALUE_NUMBER_FLOAT || token == JsonToken.VALUE_NUMBER_INT)); - attemptParsing |= ({{ unParameterizedDataType }}.class.equals(Boolean.class) && (token == JsonToken.VALUE_FALSE || token == JsonToken.VALUE_TRUE)); - attemptParsing |= ({{ unParameterizedDataType }}.class.equals(String.class) && token == JsonToken.VALUE_STRING); - {%- if oneOf.nullable %} - attemptParsing |= (token == JsonToken.VALUE_NULL); - {%- endif %} - } - } - if (attemptParsing) { - {%- if isParameterized %} - tmp = tree.traverse(jp.getCodec()).readValueAs(new TypeReference<{{ parameterizedDataType }}>() {}); - {%- else %} - tmp = tree.traverse(jp.getCodec()).readValueAs({{ unParameterizedDataType }}.class); - {%- endif %} - // TODO: there is no validation against JSON schema constraints - // (min, max, enum, pattern...), this does not perform a strict JSON - // validation, which means the 'match' count may be higher than it should be. - {%- if isModelMember %} - if (!(({{ unParameterizedDataType }}) tmp).unparsed - {%- if isComposedMember %} - {#- unmatched oneOf doesn't propagate to "tmp.unparsed": we aim - for today's client to be forward-compatible with future oneOf - branches. But a oneOf branch that it itself a oneOf? We _must_ - respect the nested oneOf's `unparsed` or the oneOf branch will - always match. #} - && !((({{ unParameterizedDataType }}) tmp).getActualInstance() instanceof UnparsedObject) - {%- endif %}) { - deserialized = tmp; - match++; - } - {%- elif isModelListMember %} - {%- set itemsDataType = get_type(oneOf.get("items")) %} - // keep the matched list, but propagate 'unparsed' from any invalid item - boolean itemsUnparsed = false; - for ({{ itemsDataType }} item : ({{ parameterizedDataType }}) tmp) { - itemsUnparsed |= item.unparsed; - } - if (itemsUnparsed) { - deserializedUnparsed = tmp; - matchUnparsed++; - } else { - deserialized = tmp; - match++; - } - {%- else %} - deserialized = tmp; - match++; - {% endif %} - log.log(Level.FINER, "Input data matches schema '{{ parameterizedDataType }}'"); - } - } catch (Exception e) { - // deserialization failed, continue - log.log(Level.FINER, "Input data does not match schema '{{ parameterizedDataType }}'", e); - } - {# #} - {%- endfor %} - {{ name }} ret = new {{ name }}(); - if (match == 1) { - ret.setActualInstance(deserialized); - {%- if ns.has_model_list %} - } else if (match == 0 && matchUnparsed == 1) { - ret.setActualInstance(deserializedUnparsed); - ret.unparsed = true; - {%- endif %} - } else { - Map res = new ObjectMapper().readValue(tree.traverse(jp.getCodec()).readValueAsTree().toString(), new TypeReference>() {}); - ret.setActualInstance(new UnparsedObject(res)); - } - return ret; - } - - /** - * Handle deserialization of the 'null' value. - */ - @Override - public {{ name }} getNullValue(DeserializationContext ctxt) throws JsonMappingException { - {%- if model.nullable %} - return null; - {%- else %} - throw new JsonMappingException(ctxt.getParser(), "{{ name }} cannot be null"); - {%- endif %} - } - } - - // store a list of schema names defined in oneOf - public static final Map schemas = new HashMap(); - - public {{ name }}() { - super("oneOf", Boolean.{{ "TRUE" if model.nullable else "FALSE" }}); - } - - {%- set oneof_methods = prepare_oneof_methods(model, get_type) %} - {%- for method_info in oneof_methods %} - {%- if method_info.use_factory %} - public static {{ name }} {{ method_info.constructor_name }}({{ method_info.param_type }} o) { - {{ name }} instance = new {{ name }}(); - instance.setActualInstance(o); - return instance; - } - {%- else %} - public {{ name }}({{ method_info.param_type }} o) { - super("oneOf", Boolean.{{ "TRUE" if model.nullable else "FALSE" }}); - setActualInstance(o); - } - {%- endif %} - {%- endfor %} - - static { - {%- for oneOf in model.oneOf %} - schemas.put("{{ get_type(oneOf) }}", new GenericType<{{ get_type(oneOf) }}>() { - }); - {%- endfor %} - JSON.registerDescendants({{ name }}.class, Collections.unmodifiableMap(schemas)); - } - - @Override - public Map getSchemas() { - return {{ name }}.schemas; - } - - /** - * Set the instance that matches the oneOf child schema, check - * the instance parameter is valid against the oneOf child schemas: - * {% for oneOf in model.oneOf %}{{ get_type(oneOf)|escape_html }}{% if loop.nextitem %}, {% endif %}{% endfor %} - * - * It could be an instance of the 'oneOf' schemas. - * The oneOf child schemas may themselves be a composed schema (allOf, anyOf, oneOf). - */ - @Override - public void setActualInstance(Object instance) { - {%- if model.nullable %} - if (instance == null) { - super.setActualInstance(instance); - return; - } - - {% endif %} - {%- for oneOf in model.oneOf %} - if (JSON.isInstanceOf({{ get_type(oneOf)|un_parameterize_type }}.class, instance, new HashSet>())) { - super.setActualInstance(instance); - return; - } - - {%- if loop.last %} - - if (JSON.isInstanceOf(UnparsedObject.class, instance, new HashSet>())) { - super.setActualInstance(instance); - return; - } - {%- endif %} - {%- endfor %} - throw new RuntimeException("Invalid instance type. Must be {% for oneOf in model.oneOf %}{{ get_type(oneOf) }}{% if loop.nextitem %}, {% endif %}{% endfor %}"); - } - - /** - * Get the actual instance, which can be the following: - * {% for oneOf in model.oneOf %}{{ get_type(oneOf)|escape_html }}{% if loop.nextitem %}, {% endif %}{% endfor %} - * - * @return The actual instance ({% for oneOf in model.oneOf %}{{ get_type(oneOf)|escape_html }}{% if loop.nextitem %}, {% endif %}{% endfor %}) - */ - @Override - public Object getActualInstance() { - return super.getActualInstance(); - } - - {%- set oneof_methods = prepare_oneof_methods(model, get_type) %} - {%- for method_info in oneof_methods %} - - /** - * Get the actual instance of `{{ method_info.param_type|escape_html }}`. If the actual instance is not `{{ method_info.param_type|escape_html }}`, - * the ClassCastException will be thrown. - * - * @return The actual instance of `{{ method_info.param_type|escape_html }}` - * @throws ClassCastException if the instance is not `{{ method_info.param_type|escape_html }}` - */ - public {{ method_info.param_type }} {{ method_info.getter_name }}() throws ClassCastException { - return ({{ method_info.param_type }})super.getActualInstance(); - } - - {%- endfor %} -} diff --git a/.generator/src/generator/templates/modelSimple.j2 b/.generator/src/generator/templates/modelSimple.j2 deleted file mode 100644 index 189f88c7393..00000000000 --- a/.generator/src/generator/templates/modelSimple.j2 +++ /dev/null @@ -1,414 +0,0 @@ -/** -{{ model.description|docstring }} - {%- if model.deprecated %} - * @deprecated - {%- endif %} - */ -{%- if model.deprecated %} -@Deprecated -{%- endif %} -@JsonPropertyOrder({ -{%- for attr, schema in model.get("properties", {}).items() %} - {{ name }}.JSON_PROPERTY_{{ attr|snake_case|upper }}{%- if loop.nextitem %},{%- endif %} -{%- endfor %} -}) -{{ generated_annotation }} -public class {{ name }} {%- if model.get("x-generate-alias-as-model") %} extends {% if "items" in model %}ArrayList<{{ get_type(model["items"]) }}>{% else %}{{ get_type(model) }}{% endif %}{% endif %} { - @JsonIgnore - public boolean unparsed = false; - {%- if model.get("x-generate-alias-as-model") and "items" in model %} - - public {{ name }}() {} - - public {{ name }}(List<{{ get_type(model["items"]) }}> items) { - super(items); - } - {%- endif %} - {%- for attr, schema in model.get("properties", {}).items() %} - {%- set attributeName = attr|attribute_name %} - {%- set variableName = attr|variable_name %} - {%- set dataType = get_type(schema) %} - {%- set isRequired = attr in model.get("required", []) %} - {%- set isNullable = schema.nullable %} - {%- set defaultValue = schema.get("default", None) %} - {%- set isAdditionalPropertiesContainer = schema.properties is not defined and schema.additionalProperties is defined and schema.additionalProperties is not false %} - public static final String JSON_PROPERTY_{{ attr|snake_case|upper }} = "{{ attr }}"; - - {%- if not isRequired and isNullable %} - {%- if "items" in schema or (isAdditionalPropertiesContainer) or (schema.type is not defined and "oneOf" not in schema) %} - private JsonNullable<{{ dataType }}> {{ variableName }} = JsonNullable.<{{ dataType }}>undefined(); - {%- else %} - private JsonNullable<{{ dataType }}> {{ variableName }} = JsonNullable.<{{ dataType }}>{%- if defaultValue != None %}of({{ defaultValue|format_value(schema=schema, default_value=True, type_=dataType) }}){%- else %}undefined(){%- endif%}; - {%- endif %} - {%- else %} - {%- if "items" in schema %} - private {{ dataType }} {{ variableName }}{%- if isRequired %} = new ArrayList<>(){%- else %} = null{%- endif %}; - {%- elif (isAdditionalPropertiesContainer) or (not schema.get("type") and "oneOf" not in schema) %} - private {{ dataType }} {{ variableName }}{%- if isRequired %} = new {{ get_type(schema, render_new=True) }}(){%- else %} = null{%- endif %}; - {%- else %} - private {{ dataType }} {{ variableName }}{%- if defaultValue != None %} = {{ defaultValue|format_value(schema=schema, default_value=True, type_=dataType) }}{%- endif %}; - {%- endif %} - {%- endif %} -{# #} - {%- endfor %} - - {%- set requiredAttr = model|get_required_attributes %} - {%- if requiredAttr %} - {%- if not (model.get("x-generate-alias-as-model") and "items" in model) %} - public {{ name }}() {} - {%- endif %} - - @JsonCreator - public {{ name }}( - {%- for attr, schema in requiredAttr.items() %} - {%- set attributeName = attr|attribute_name %} - {%- set variableName = attr|variable_name %} - {%- set isRequired = attr in model.get("required", []) %} - {%- set isNullable = schema.nullable %} - {%- set dataType = get_type(schema) %} - @JsonProperty(required=true, value=JSON_PROPERTY_{{ attr|snake_case|upper }}) - {%- if not isRequired and isNullable %}JsonNullable<{{ dataType }}>{%- else %}{{ dataType }}{%- endif %} {{ variableName }}{%- if loop.nextitem %},{%- endif %} - {%- endfor %}) { - {%- for attr, schema in requiredAttr.items() %} - {%- set attributeName = attr|attribute_name %} - {%- set variableName = attr|variable_name %} - {%- set isNullable = schema.nullable %} - this.{{ variableName }} = {{ variableName }}; - {%- if isNullable %} - if ({{ variableName }} != null) { - {%- endif %} - {%- if schema.enum is defined %} - this.unparsed |= !{{ variableName }}.isValid(); - {%- endif %} - {%- if (schema.properties is defined or schema.oneOf is defined) and not schema|is_primitive %} - this.unparsed |= {{ variableName }}.unparsed; - {%- endif %} - {%- if "items" in schema and schema.get("items")|is_model and not schema|is_primitive %} - {%- set itemsDataType = get_type(schema.get("items")) %} - for ({{ itemsDataType }} item : {{ variableName }}) { - this.unparsed |= item.unparsed; - } - {%- endif %} - {%- if isNullable %} - } - {%- endif %} - {%- endfor %} - } - {%- endif %} - - {%- for attr, schema in model.get("properties", {}).items() %} - {%- set attributeName = attr|attribute_name %} - {%- set variableName = attr|variable_name %} - {%- set isNullable = schema.nullable %} - {%- set dataType = get_type(schema) %} - {%- set isArray = "items" in schema %} - {%- set isRequired = attr in model.get("required", []) %} - {%- set defaultValue = schema.get("default", None) %} - {%- set isAdditionalPropertiesContainer = schema.properties is not defined and schema.additionalProperties is defined and schema.additionalProperties is not false %} - - {%- if not schema.get("readOnly", False) %} - public {{ name }} {{ variableName }}({{ dataType }} {{ variableName }}) { - {%- if not isRequired and isNullable %} - this.{{ variableName }} = JsonNullable.<{{ dataType }}>of({{ variableName }}); - {%- else %} - this.{{ variableName }} = {{ variableName }}; - {%- if not isArray %} - {%- if isNullable %} - if ({{ variableName }} != null) { - {%- endif %} - {%- if schema.enum is defined %} - {%- if not schema|is_primitive %} - this.unparsed |= !{{ variableName }}.isValid(); - {%- endif %} - {%- endif %} - {%- if schema|is_model and not schema|is_primitive %} - this.unparsed |= {{ variableName }}.unparsed; - {%- endif %} - {%- if isNullable %} - } - {%- endif %} - {%- else %} - {%- if schema.get("items")|is_model and not schema|is_primitive %} - {%- set itemsDataType = get_type(schema.get("items")) %} - {%- if not isRequired %} - if ({{ variableName }} != null) { - {%- endif %} - for ({{ itemsDataType }} item : {{ variableName }}) { - {%- if schema.enum is defined %} - this.unparsed |= !item.isValid(); - {%- endif %} - this.unparsed |= item.unparsed; - } - {%- if not isRequired %} - } - {%- endif %} - {%- endif %} - {%- endif %} - {%- endif %} - return this; - } - {%- if isArray %} - {%- set itemsDataType = get_type(schema.get("items")) %} - public {{ name }} add{{ variableName|upperfirst }}Item({{ itemsDataType }} {{ variableName }}Item) { - {%- if not isRequired and isNullable %} - if (this.{{ variableName }} == null || !this.{{ variableName }}.isPresent()) { - this.{{ variableName }} = JsonNullable.<{{ dataType }}>of(new ArrayList<>()); - } - try { - this.{{ variableName }}.get().add({{ variableName }}Item); - } catch (java.util.NoSuchElementException e) { - // this can never happen, as we make sure above that the value is present - } - return this; - {%- else %} - {%- if not isRequired %} - if (this.{{ variableName }} == null) { - this.{{ variableName }} = new ArrayList<>(); - } - {%- endif %} - this.{{ variableName }}.add({{ variableName }}Item); - {%- if schema.get("items", {}).enum is defined %} - {%- if not schema.get("items", {})|is_primitive %} - this.unparsed |= !{{ variableName }}Item.isValid(); - {%- endif %} - {%- endif %} - {%- if schema.get("items", {})|is_model %} - {%- if not schema.get("items", {})|is_primitive %} - this.unparsed |= {{ variableName }}Item.unparsed; - {%- endif %} - {%- endif %} - return this; - {%- endif %} - } - {%- endif %} - - {%- if isAdditionalPropertiesContainer %} - public {{ name }} put{{ variableName|upperfirst }}Item(String key, {{ get_type(schema.additionalProperties) }} {{ variableName }}Item) { - {%- if not isRequired and isNullable %} - if (this.{{ variableName }} == null || !this.{{ variableName }}.isPresent()) { - this.{{ variableName }} = JsonNullable.<{{ dataType }}>of(new HashMap<>()); - } - try { - this.{{ variableName }}.get().put(key, {{ variableName }}Item); - } catch (java.util.NoSuchElementException e) { - // this can never happen, as we make sure above that the value is present - } - return this; - {%- else %} - {%- if not isRequired %} - if (this.{{ variableName }} == null) { - this.{{ variableName }} = new HashMap<>(); - } - {%- endif %} - this.{{ variableName }}.put(key, {{ variableName }}Item); - return this; - {%- endif %} - } - {%- endif %} - {%- endif %} - - /** -{{ schema.description|default("Get" ~ variableName)|docstring }} - {%- if schema.minimum is defined %} - * minimum: {{ schema.minimum }} - {%- endif %} - {%- if schema.maximum is defined %} - * maximum: {{ schema.maximum }} - {%- endif %} - * @return {{ variableName }} - {%- if schema.deprecated %} - * @deprecated - {%- endif %} - **/ - {%- if schema.deprecated %} - @Deprecated - {%- endif %} - {%- if isRequired %} - {%- if isNullable %} - @jakarta.annotation.Nullable - {%- endif %} - {%- else %} - @jakarta.annotation.Nullable - {%- endif %} - {%- if not isRequired and isNullable %} - @JsonIgnore - {%- else %} - @JsonProperty(JSON_PROPERTY_{{ attr|snake_case|upper }}) - @JsonInclude( - {%- if schema.additionalProperties is defined and schema.additionalProperties is not false %}{%- if schema.additionalProperties.nullable %}content = JsonInclude.Include.ALWAYS,{%- endif %}{%- endif %} - value = JsonInclude.Include.{%- if isRequired %}ALWAYS{%- else %}USE_DEFAULTS{%- endif %}) - {%- endif %} - public {{ dataType }} get{{ attr|camel_case|upperfirst|escape_method_reserved_name }}() { - {%- if not isRequired and isNullable %} - {%- if schema.get("readOnly", False) %} - - if ({{ variableName }} == null) { - {{ variableName }} = JsonNullable.<{{ dataType }}>{%- if defaultValue != None %}of({{ defaultValue|format_value(schema=schema, default_value=True, type_=dataType) }}){%- else %}undefined(){%- endif%}; - } - {%- endif %} - return {{ variableName }}.orElse(null); - {%- else %} - return {{ variableName }}; - {%- endif %} - } - {%- if schema.deprecated %} - @Deprecated - {%- endif %} - {%- if not isRequired and isNullable %} - @JsonProperty(JSON_PROPERTY_{{ attr|snake_case|upper }}) - @JsonInclude( - {%- if schema.additionalProperties is defined and schema.additionalProperties is not false %}{%- if schema.additionalProperties.nullable %}content = JsonInclude.Include.ALWAYS,{%- endif %}{%- endif %} - value = JsonInclude.Include.{%- if isRequired %}ALWAYS{%- else %}USE_DEFAULTS{%- endif %}) - public JsonNullable<{{ dataType }}> get{{ attr|camel_case|upperfirst }}_JsonNullable() { - return {{ variableName }}; - } - @JsonProperty(JSON_PROPERTY_{{ attr|snake_case|upper }}) - {%- if schema.get("readOnly", False) %}private{%- else %}public{%- endif %} void set{{ attr|camel_case|upperfirst }}_JsonNullable(JsonNullable<{{ dataType }}> {{ variableName }}) { - this.{{ variableName }} = {{ variableName }}; - } - {%- endif %} - - {%- if not schema.get("readOnly", False) %} - public void set{{ attr|camel_case|upperfirst|escape_method_reserved_name }}({{ dataType }} {{ variableName }}) { - {%- if schema.enum is defined %} - if (!{{ variableName }}.isValid()) { - this.unparsed = true; - } - {%- endif %} - {%- if not isRequired and isNullable %} - this.{{ variableName }} = JsonNullable.<{{ dataType }}>of({{ variableName }}); - {%- else %} - this.{{ variableName }} = {{ variableName }}; - {%- if not isArray %} - {%- if schema|is_model and not schema|is_primitive %} - if ({{ variableName }} != null) { - this.unparsed |= {{ variableName }}.unparsed; - } - {%- endif %} - {%- else %} - {%- if schema.get("items")|is_model and not schema|is_primitive %} - {%- set itemsDataType = get_type(schema.get("items")) %} - if ({{ variableName }} != null) { - for ({{ itemsDataType }} item : {{ variableName }}) { - this.unparsed |= item.unparsed; - } - } - {%- endif %} - {%- endif %} - {%- endif %} - {%- if model.get("x-keep-typed-in-additional-properties") and model.additionalProperties is not false %} - putAdditionalProperty(JSON_PROPERTY_{{ attr|snake_case|upper }}, {%- if not isRequired and isNullable %} this.{{ variableName }}.orElse(null){%- else %} {{ variableName }}{%- endif %}); - {%- endif %} - } - {%- endif %} - {%- endfor %} - {%- if model.additionalProperties is not false %} - {%- set additionalPropertiesDataType = model.get("additionalProperties", {})|simple_type or "Object" %} - - /** - * A container for additional, undeclared properties. - * This is a holder for any undeclared properties as specified with - * the 'additionalProperties' keyword in the OAS document. - */ - private Map additionalProperties; - - /** - * Set the additional (undeclared) property with the specified name and value. - * If the property does not already exist, create it otherwise replace it. - * - * @param key The arbitrary key to set - * @param value The associated value - * @return {{ name }} - */ - @JsonAnySetter - public {{ name }} putAdditionalProperty(String key, {{ additionalPropertiesDataType }} value) { - if (this.additionalProperties == null) { - this.additionalProperties = new HashMap(); - } - this.additionalProperties.put(key, value); - return this; - } - - /** - * Return the additional (undeclared) property. - * - * @return The additional properties - */ - @JsonAnyGetter - public Map getAdditionalProperties() { - return additionalProperties; - } - - /** - * Return the additional (undeclared) property with the specified name. - * - * @param key The arbitrary key to get - * @return The specific additional property for the given key - */ - public {{ additionalPropertiesDataType }} getAdditionalProperty(String key) { - if (this.additionalProperties == null) { - return null; - } - return this.additionalProperties.get(key); - } - {%- endif %} - - /** - * Return true if this {{name}} object is equal to o. - */ - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - {%- if model.properties is defined %} - {{ name }} {{ name|variable_name }} = ({{ name }}) o; - return {%- for attr, schema in model.get("properties").items() %} Objects.equals(this.{{ attr|variable_name }}, {{ name|variable_name }}.{{ attr|variable_name }}){%- if loop.nextitem or model.additionalProperties is not false %} &&{%- endif %}{%- endfor %}{% if model.additionalProperties is not false %} Objects.equals(this.additionalProperties, {{ name|variable_name }}.additionalProperties){% endif %} - {%- if model.get("x-generate-alias-as-model") %} && - super.equals(o){%- endif %}; - {%- else %} - return {%- if model.get("x-generate-alias-as-model") %} super.equals(o){%- else %} true{%-endif %}; - {%- endif %} - } - - - @Override - public int hashCode() { - return Objects.hash({%- for attr, schema in model.get("properties", {}).items() %}{{ attr|variable_name }}{%- if loop.nextitem %}, {%- endif %}{%-endfor %} - {%- if model.additionalProperties is not false %}{%- if model.properties is defined %}, {%- endif %} additionalProperties{%- endif %} - {%- if model.get("x-generate-alias-as-model") %}{%- if model.properties is defined or model.additionalProperties is not false %}, {%- endif %}super.hashCode(){%- endif %}); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("class {{ name }} {\n"); - {%- if model.get("x-generate-alias-as-model") %} - sb.append(" ").append(toIndentedString(super.toString())).append("\n"); - {%- endif %} - {%- for attr, schema in model.get("properties", {}).items() %} - sb.append(" {{ attr|variable_name }}: ").append(toIndentedString({{ attr|variable_name }})).append("\n"); - {%- endfor %} - {%- if model.additionalProperties is not false %} - sb.append(" additionalProperties: ") - .append(toIndentedString(additionalProperties)) - .append("\n"); - {%- endif %} - sb.append('}'); - return sb.toString(); - } - - /** - * Convert the given object to string with each line indented by 4 spaces - * (except the first line). - */ - private String toIndentedString(Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } -} diff --git a/.generator/tests/test_formatter.py b/.generator/tests/test_formatter.py deleted file mode 100644 index 5d7f647096f..00000000000 --- a/.generator/tests/test_formatter.py +++ /dev/null @@ -1,117 +0,0 @@ -# coding=utf-8 -"""Unit tests for the formatter module.""" - -import yaml -from click.testing import CliRunner - -from generator.cli import cli -from generator.formatter import format_data_with_schema - - -class SchemaWithRef(dict): - """A schema dict that carries a $ref, as the generator produces after resolving references.""" - - def __init__(self, *args, ref=None, **kwargs): - super().__init__(*args, **kwargs) - if ref: - self.__reference__ = {"$ref": ref} - - -def test_named_array_alias_in_oneof_uses_generated_wrapper(): - message_schema = SchemaWithRef( - { - "type": "object", - "properties": { - "role": {"type": "string"}, - "content": {"type": "string"}, - }, - "required": ["role", "content"], - }, - ref="#/components/schemas/LLMObsPromptChatMessage", - ) - chat_schema = SchemaWithRef( - { - "type": "array", - "items": message_schema, - "x-generate-alias-as-model": True, - }, - ref="#/components/schemas/LLMObsPromptChatTemplate", - ) - template_schema = SchemaWithRef( - {"oneOf": [{"type": "string"}, chat_schema]}, - ref="#/components/schemas/LLMObsPromptTemplate", - ) - - _, result, imports = format_data_with_schema( - [ - {"role": "system", "content": "You help {{company_name}} customers."}, - {"role": "user", "content": "Answer {{question}}"}, - ], - template_schema, - ) - - assert ( - "new LLMObsPromptTemplate(new LLMObsPromptChatTemplate(" - "Arrays.asList(" in result - ) - assert "new LLMObsPromptTemplate(Arrays.asList(" not in result - assert "LLMObsPromptChatTemplate" in imports - - -def test_named_array_alias_model_accepts_formatted_list(tmp_path): - spec_dir = tmp_path / "v2" - spec_dir.mkdir() - spec_path = spec_dir / "openapi.yaml" - spec_path.write_text( - yaml.safe_dump( - { - "openapi": "3.0.0", - "info": {"title": "test", "version": "1"}, - "servers": [{"url": "https://api.example.com", "variables": {}}], - "paths": { - "/test": { - "post": { - "operationId": "TestArrayAlias", - "tags": ["Test"], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LLMObsPromptChatTemplate" - } - } - } - }, - "responses": {"200": {"description": "OK"}}, - } - } - }, - "components": { - "securitySchemes": {}, - "schemas": { - "LLMObsPromptChatMessage": { - "type": "object", - "properties": { - "role": {"type": "string"}, - "content": {"type": "string"}, - }, - }, - "LLMObsPromptChatTemplate": { - "type": "array", - "items": {"$ref": "#/components/schemas/LLMObsPromptChatMessage"}, - "x-generate-alias-as-model": True, - }, - } - }, - } - ) - ) - output = tmp_path / "generated" - - result = CliRunner().invoke(cli, [str(spec_path), "--output", str(output)]) - - assert result.exit_code == 0 - model = (output / "v2/model/LLMObsPromptChatTemplate.java").read_text() - assert "public LLMObsPromptChatTemplate() {}" in model - assert "public LLMObsPromptChatTemplate(List items)" in model - assert "super(items);" in model diff --git a/.generator/tests/test_scenarios.py b/.generator/tests/test_scenarios.py deleted file mode 100644 index 9df2e25b50a..00000000000 --- a/.generator/tests/test_scenarios.py +++ /dev/null @@ -1,8 +0,0 @@ -# coding=utf-8 -"""Test scenarios.""" - -from pytest_bdd import scenarios - -scenarios( - "../../src/test/resources/com/datadog/api/client/v1/api", "../../src/test/resources/com/datadog/api/client/v2/api" -) diff --git a/.gitignore b/.gitignore index b6fda29d87b..b570c76b102 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,6 @@ gradlew gradlew.bat settings.gradle src/main/AndroidManifest.xml -.generator/lib # Ignored cassettes src/test/resources/cassettes/v1/getAllUsersTest.* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 98f3724a8a3..7bace801413 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,13 +1,6 @@ repos: - repo: local hooks: - - id: generate - name: Generate - language: system - entry: bash -c "./generate.sh" - files: '^(\.generator/.*|\.pre-commit-config\.yaml|\.prettierrc\.yml|Makefile|src/test/resources/com/datadog/api/v*/client/api/.*|src/main/.*)' - stages: [manual] - pass_filenames: false - id: docs name: Format documentation stages: [manual] @@ -21,15 +14,6 @@ repos: # When updating the version of prettier, make sure to check the pre-commit file # And keep the `entry` here up to date https://github.com/pre-commit/mirrors-prettier/blob/master/.pre-commit-hooks.yaml - prettier@3.0.0 - - id: generator - name: generator - language: python - entry: bash -c "cd .generator && poetry install && poetry run python -m generator ./schemas/v1/openapi.yaml ./schemas/v2/openapi.yaml -o ../src/main/java/com/datadog/api/client" - files: "^.generator/(config|schemas/v1|src|poetry.lock|pyproject.toml)" - stages: [manual] - pass_filenames: false - additional_dependencies: - - "poetry" - id: lint name: Format generated code language: script @@ -52,15 +36,6 @@ repos: entry: ./format.sh files: '^src/test/' types: [file, java] - - id: examples - name: examples - language: python - entry: bash -c "cd .generator && poetry install && poetry run pytest" - files: "^.generator/" - stages: [manual] - pass_filenames: false - additional_dependencies: - - "poetry" - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b9c0a256fa4..683053336f1 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,9 +1,8 @@ # Development -This repository contains code that is autogenerated via the -[openapi-generator](https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources/Java) project. +This repository contains the autogenerated Java client for the [Datadog API](https://docs.datadoghq.com/api/). The client code, feature files, and test data are generated from Datadog's public OpenAPI specifications by internal tooling. -As such, this repository should only contain development for adding or fixing tests, for improving development tooling and documentation +We welcome contributions that expand or improve the client's capabilities. As part of the pull request review process, Datadog's internal tooling may regenerate the affected code, feature files, and test data based on the proposed changes. ## Setup diff --git a/README.md b/README.md index 8718f43fa64..71d0bb69c65 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # datadog-api-client-java -This repository contains a Java API client for the [Datadog API](https://docs.datadoghq.com/api/). +This repository contains the autogenerated Java client for the [Datadog API](https://docs.datadoghq.com/api/). The client code, feature files, and test data are generated from Datadog's public OpenAPI specifications by internal tooling. + +We welcome contributions that expand or improve the client's capabilities. As part of the pull request review process, Datadog's internal tooling may regenerate the affected code, feature files, and test data based on the proposed changes. ## Requirements diff --git a/generate.sh b/generate.sh deleted file mode 100755 index a8af2573b52..00000000000 --- a/generate.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -DEFAULT_ERROR_CODES="0" - -# First arg is the command -# Second arg is the string of acceptable error codes seperated by space. E.g. "0 1" -pre_commit_wrapper () { - echo "running pre-commit run --all-files --hook-stage=manual ${1}" - - exec 5>&1 - acceptable_errors=${2:-$DEFAULT_ERROR_CODES} - out=$(pre-commit run --all-files --hook-stage=manual "${1}" | tee >(cat - >&5)) - exit_code=$( echo "$out" | grep -- "- exit code:" | cut -d":" -f2 | sed 's/[^0-9]*//g' ) - - if [[ -n $exit_code ]]; then - re="([^0-9]|^)$exit_code([^0-9]|$)" - if ! grep -qE "$re" <<< "$acceptable_errors" ; then - echo "pre-commit subcommand failed with error_code: $exit_code. See output above" - exit "$exit_code"; - fi - fi - - echo "command 'pre-commit run --all-files --hook-stage=manual ${1}' success" -} - -rm -rf src/main/java examples/* -pre_commit_wrapper generator -pre_commit_wrapper examples -pre_commit_wrapper docs -pre_commit_wrapper lint 1 -pre_commit_wrapper lint-examples 1