From 3ac8000fcbe56330dbc5be0350c948501562cc33 Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Sat, 15 Jun 2024 15:36:46 +0200 Subject: [PATCH] Add ApiVersion info into generated RestEndpoint Render min/required api version info directly into the rust sdk RestEndpoint to be able to rely on the results of version discovery and not hardcode the version prefix as part of the path. Change-Id: I5491d94392a6c32b5d700e47831971f170bf737d --- codegenerator/common/__init__.py | 2 +- codegenerator/common/rust.py | 2 ++ codegenerator/rust_sdk.py | 28 ++++++++++++++++++++- codegenerator/templates/rust_sdk/impl.rs.j2 | 7 ++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/codegenerator/common/__init__.py b/codegenerator/common/__init__.py index 09c703f..877f6c5 100644 --- a/codegenerator/common/__init__.py +++ b/codegenerator/common/__init__.py @@ -20,7 +20,7 @@ import yaml from openapi_core import Spec from pydantic import BaseModel -VERSION_RE = re.compile(r"[Vv][0-9.]*") +VERSION_RE = re.compile(r"^[Vv]([0-9]+)(\.([0-9]+))?$") # RE to split name from camelCase or by [`:`,`_`,`-`] SPLIT_NAME_RE = re.compile(r"(?<=[a-z])(?=[A-Z])|:|_|-") diff --git a/codegenerator/common/rust.py b/codegenerator/common/rust.py index 333e334..29151d5 100644 --- a/codegenerator/common/rust.py +++ b/codegenerator/common/rust.py @@ -978,6 +978,8 @@ class TypeManager: """Return static lifetimes of the Structure""" lifetimes = request_model.lifetimes for param in self.parameters.values(): + if param.location == "header": + continue lt = param.lifetimes if lt: lifetimes.update(lt) diff --git a/codegenerator/rust_sdk.py b/codegenerator/rust_sdk.py index 2dfa942..c1256a5 100644 --- a/codegenerator/rust_sdk.py +++ b/codegenerator/rust_sdk.py @@ -367,6 +367,16 @@ class RustSdkGenerator(BaseGenerator): spec, args.operation_name ) + api_ver_matches: re.Match | None = None + path_elements = path.lstrip("/").split("/") + api_ver: dict[str, int] = {} + ver_prefix: str | None = None + if path_elements: + api_ver_matches = re.match(common.VERSION_RE, path_elements[0]) + if api_ver_matches and api_ver_matches.groups(): + # Remember the version prefix to discard it in the template + ver_prefix = path_elements[0] + for operation_variant in operation_variants: logging.debug("Processing variant %s" % operation_variant) # TODO(gtema): if we are in MV variants filter out unsupported query @@ -374,6 +384,14 @@ class RustSdkGenerator(BaseGenerator): # TODO(gtema): previously we were ensuring `router_id` path param # is renamed to `id` + if api_ver_matches: + api_ver = { + "major": api_ver_matches.group(1), + "minor": api_ver_matches.group(3) or 0, + } + else: + api_ver = {} + class_name = res_name.title() operation_body = operation_variant.get("body") type_manager = TypeManager() @@ -395,6 +413,13 @@ class RustSdkGenerator(BaseGenerator): min_ver = operation_body.get("x-openstack", {}).get("min-ver") if min_ver: mod_name += "_" + min_ver.replace(".", "") + v = min_ver.split(".") + if not len(v) == 2: + raise RuntimeError( + "Version information is not in format MAJOR.MINOR" + ) + api_ver = {"major": v[0], "minor": v[1]} + # There is request body. Get the ADT from jsonschema # if args.operation_type != "action": (_, all_types) = openapi_parser.parse( @@ -464,13 +489,14 @@ class RustSdkGenerator(BaseGenerator): sdk_service_name=common.get_rust_service_type_from_str( args.service_type ), - url=path[1:] if path.startswith("/") else path, + url=path.lstrip("/").lstrip(ver_prefix).lstrip("/"), method=method, type_manager=type_manager, response_key=response_key, response_list_item_key=args.response_list_item_key, mime_type=mime_type, is_json_patch=is_json_patch, + api_ver=api_ver, ) work_dir = Path(target_dir, "rust", "openstack_sdk", "src") diff --git a/codegenerator/templates/rust_sdk/impl.rs.j2 b/codegenerator/templates/rust_sdk/impl.rs.j2 index 61ee270..a2065ab 100644 --- a/codegenerator/templates/rust_sdk/impl.rs.j2 +++ b/codegenerator/templates/rust_sdk/impl.rs.j2 @@ -261,6 +261,13 @@ impl{{ type_manager.get_request_static_lifetimes(request) }} RestEndpoint for Re fn request_headers(&self) -> Option<&HeaderMap> { self._headers.as_ref() } +{%- if api_ver %} + + /// Returns required API version + fn api_version(&self) -> Option { + Some(ApiVersion::new({{ api_ver['major'] }}, {{ api_ver['minor'] }})) + } +{%- endif %} } {#- EP is pageable if operation_type is list and there is limit or marker query parameter #}