Merge "Add support for explicit empty schemas"
This commit is contained in:
@@ -18,7 +18,7 @@ import importlib
|
|||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable
|
from typing import Any, Callable, Literal
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from codegenerator.common.schema import ParameterSchema
|
from codegenerator.common.schema import ParameterSchema
|
||||||
@@ -35,6 +35,16 @@ from wsme import types as wtypes
|
|||||||
VERSION_RE = re.compile(r"[Vv][0-9\.]*")
|
VERSION_RE = re.compile(r"[Vv][0-9\.]*")
|
||||||
|
|
||||||
|
|
||||||
|
# Workaround Python's lack of an undefined sentinel
|
||||||
|
# https://python-patterns.guide/python/sentinel-object/
|
||||||
|
class Unset:
|
||||||
|
def __bool__(self) -> Literal[False]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
UNSET: Unset = Unset()
|
||||||
|
|
||||||
|
|
||||||
def get_referred_type_data(func, name: str):
|
def get_referred_type_data(func, name: str):
|
||||||
"""Get python type object referred by the function
|
"""Get python type object referred by the function
|
||||||
|
|
||||||
@@ -893,9 +903,9 @@ class OpenStackServerSourceBase:
|
|||||||
mode,
|
mode,
|
||||||
action_name,
|
action_name,
|
||||||
):
|
):
|
||||||
op_body = operation_spec.requestBody.setdefault("content", {})
|
|
||||||
mime_type: str = "application/json"
|
mime_type: str = "application/json"
|
||||||
schema_name = None
|
schema_name = None
|
||||||
|
schema_ref: str | Unset | None = None
|
||||||
# We should not modify path_resource_names of the caller
|
# We should not modify path_resource_names of the caller
|
||||||
path_resource_names = path_resource_names.copy()
|
path_resource_names = path_resource_names.copy()
|
||||||
# Create container schema with version discriminator
|
# Create container schema with version discriminator
|
||||||
@@ -909,19 +919,24 @@ class OpenStackServerSourceBase:
|
|||||||
|
|
||||||
if len(body_schemas) == 1:
|
if len(body_schemas) == 1:
|
||||||
# There is only one body known at the moment
|
# There is only one body known at the moment
|
||||||
if cont_schema_name in openapi_spec.components.schemas:
|
# None is a special case with explicitly no body supported
|
||||||
# if we have already oneOf - add there
|
if body_schemas[0] is not UNSET:
|
||||||
cont_schema = openapi_spec.components.schemas[cont_schema_name]
|
if cont_schema_name in openapi_spec.components.schemas:
|
||||||
if cont_schema.oneOf and body_schemas[0] not in [
|
# if we have already oneOf - add there
|
||||||
x["$ref"] for x in cont_schema.oneOf
|
cont_schema = openapi_spec.components.schemas[
|
||||||
]:
|
cont_schema_name
|
||||||
cont_schema.oneOf.append({"$ref": body_schemas[0]})
|
]
|
||||||
schema_ref = f"#/components/schemas/{cont_schema_name}"
|
if cont_schema.oneOf and body_schemas[0] not in [
|
||||||
else:
|
x["$ref"] for x in cont_schema.oneOf
|
||||||
# otherwise just use schema as body
|
]:
|
||||||
schema_ref = body_schemas[0]
|
cont_schema.oneOf.append({"$ref": body_schemas[0]})
|
||||||
|
schema_ref = f"#/components/schemas/{cont_schema_name}"
|
||||||
|
else:
|
||||||
|
# otherwise just use schema as body
|
||||||
|
schema_ref = body_schemas[0]
|
||||||
elif len(body_schemas) > 1:
|
elif len(body_schemas) > 1:
|
||||||
# We may end up here multiple times if we have versioned operation. In this case merge to what we have already
|
# We may end up here multiple times if we have versioned operation. In this case merge to what we have already
|
||||||
|
op_body = operation_spec.requestBody.setdefault("content", {})
|
||||||
old_schema = op_body.get(mime_type, {}).get("schema", {})
|
old_schema = op_body.get(mime_type, {}).get("schema", {})
|
||||||
old_ref = (
|
old_ref = (
|
||||||
old_schema.ref
|
old_schema.ref
|
||||||
@@ -984,16 +999,20 @@ class OpenStackServerSourceBase:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if mode == "action":
|
if mode == "action":
|
||||||
|
op_body = operation_spec.requestBody.setdefault("content", {})
|
||||||
js_content = op_body.setdefault(mime_type, {})
|
js_content = op_body.setdefault(mime_type, {})
|
||||||
body_schema = js_content.setdefault("schema", {})
|
body_schema = js_content.setdefault("schema", {})
|
||||||
one_of = body_schema.setdefault("oneOf", [])
|
one_of = body_schema.setdefault("oneOf", [])
|
||||||
if schema_ref not in [x.get("$ref") for x in one_of]:
|
if schema_ref and schema_ref not in [
|
||||||
|
x.get("$ref") for x in one_of
|
||||||
|
]:
|
||||||
one_of.append({"$ref": schema_ref})
|
one_of.append({"$ref": schema_ref})
|
||||||
os_ext = body_schema.setdefault("x-openstack", {})
|
os_ext = body_schema.setdefault("x-openstack", {})
|
||||||
os_ext["discriminator"] = "action"
|
os_ext["discriminator"] = "action"
|
||||||
if cont_schema and action_name:
|
if cont_schema and action_name:
|
||||||
cont_schema.openstack["action-name"] = action_name
|
cont_schema.openstack["action-name"] = action_name
|
||||||
elif schema_ref:
|
elif schema_ref is not None and schema_ref is not UNSET:
|
||||||
|
op_body = operation_spec.requestBody.setdefault("content", {})
|
||||||
js_content = op_body.setdefault(mime_type, {})
|
js_content = op_body.setdefault(mime_type, {})
|
||||||
body_schema = js_content.setdefault("schema", {})
|
body_schema = js_content.setdefault("schema", {})
|
||||||
operation_spec.requestBody["content"][mime_type]["schema"] = (
|
operation_spec.requestBody["content"][mime_type]["schema"] = (
|
||||||
@@ -1127,12 +1146,13 @@ class OpenStackServerSourceBase:
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"description": LiteralScalarString(description),
|
"description": LiteralScalarString(description),
|
||||||
}
|
}
|
||||||
schema = openapi_spec.components.schemas.setdefault(
|
if schema_def is not UNSET:
|
||||||
name,
|
schema = openapi_spec.components.schemas.setdefault(
|
||||||
TypeSchema(
|
name,
|
||||||
**schema_def,
|
TypeSchema(
|
||||||
),
|
**schema_def,
|
||||||
)
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if action_name:
|
if action_name:
|
||||||
if not schema.openstack:
|
if not schema.openstack:
|
||||||
@@ -1180,9 +1200,9 @@ class OpenStackServerSourceBase:
|
|||||||
"""Extract schemas from the decorated method."""
|
"""Extract schemas from the decorated method."""
|
||||||
# Unwrap operation decorators to access all properties
|
# Unwrap operation decorators to access all properties
|
||||||
expected_errors: list[str] = []
|
expected_errors: list[str] = []
|
||||||
body_schemas: list[str] = []
|
body_schemas: list[str | Unset] = []
|
||||||
query_params_versions: list[tuple] = []
|
query_params_versions: list[tuple] = []
|
||||||
response_body_schema: dict | None = None
|
response_body_schema: dict | Unset | None = UNSET
|
||||||
|
|
||||||
f = func
|
f = func
|
||||||
while hasattr(f, "__wrapped__"):
|
while hasattr(f, "__wrapped__"):
|
||||||
@@ -1214,38 +1234,47 @@ class OpenStackServerSourceBase:
|
|||||||
"request_body_schema",
|
"request_body_schema",
|
||||||
getattr(f, "_request_body_schema", {}),
|
getattr(f, "_request_body_schema", {}),
|
||||||
)
|
)
|
||||||
if obj.get("type") in ["object", "array"]:
|
if obj is not None:
|
||||||
# We only allow object and array bodies
|
if obj.get("type") in ["object", "array"]:
|
||||||
# To prevent type name collision keep module name part of the name
|
# We only allow object and array bodies
|
||||||
typ_name = (
|
# To prevent type name collision keep module name part of the name
|
||||||
"".join([x.title() for x in path_resource_names])
|
typ_name = (
|
||||||
+ func.__name__.title()
|
"".join([x.title() for x in path_resource_names])
|
||||||
+ (f"_{min_ver.replace('.', '')}" if min_ver else "")
|
+ func.__name__.title()
|
||||||
)
|
+ (
|
||||||
comp_schema = openapi_spec.components.schemas.setdefault(
|
f"_{min_ver.replace('.', '')}"
|
||||||
typ_name,
|
if min_ver
|
||||||
self._sanitize_schema(
|
else ""
|
||||||
copy.deepcopy(obj),
|
)
|
||||||
start_version=start_version,
|
)
|
||||||
end_version=end_version,
|
comp_schema = (
|
||||||
),
|
openapi_spec.components.schemas.setdefault(
|
||||||
)
|
typ_name,
|
||||||
|
self._sanitize_schema(
|
||||||
|
copy.deepcopy(obj),
|
||||||
|
start_version=start_version,
|
||||||
|
end_version=end_version,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if min_ver:
|
if min_ver:
|
||||||
if not comp_schema.openstack:
|
if not comp_schema.openstack:
|
||||||
comp_schema.openstack = {}
|
comp_schema.openstack = {}
|
||||||
comp_schema.openstack["min-ver"] = min_ver
|
comp_schema.openstack["min-ver"] = min_ver
|
||||||
if max_ver:
|
if max_ver:
|
||||||
if not comp_schema.openstack:
|
if not comp_schema.openstack:
|
||||||
comp_schema.openstack = {}
|
comp_schema.openstack = {}
|
||||||
comp_schema.openstack["max-ver"] = max_ver
|
comp_schema.openstack["max-ver"] = max_ver
|
||||||
if mode == "action":
|
if mode == "action":
|
||||||
if not comp_schema.openstack:
|
if not comp_schema.openstack:
|
||||||
comp_schema.openstack = {}
|
comp_schema.openstack = {}
|
||||||
comp_schema.openstack["action-name"] = action_name
|
comp_schema.openstack["action-name"] = action_name
|
||||||
|
|
||||||
ref_name = f"#/components/schemas/{typ_name}"
|
ref_name = f"#/components/schemas/{typ_name}"
|
||||||
body_schemas.append(ref_name)
|
body_schemas.append(ref_name)
|
||||||
|
else:
|
||||||
|
body_schemas.append(UNSET)
|
||||||
|
|
||||||
if "response_body_schema" in closure_locals or hasattr(
|
if "response_body_schema" in closure_locals or hasattr(
|
||||||
f, "_response_body_schema"
|
f, "_response_body_schema"
|
||||||
@@ -1255,7 +1284,10 @@ class OpenStackServerSourceBase:
|
|||||||
"response_body_schema",
|
"response_body_schema",
|
||||||
getattr(f, "_response_body_schema", {}),
|
getattr(f, "_response_body_schema", {}),
|
||||||
)
|
)
|
||||||
response_body_schema = obj
|
if obj is not None:
|
||||||
|
response_body_schema = obj
|
||||||
|
else:
|
||||||
|
response_body_schema = UNSET
|
||||||
if "query_params_schema" in closure_locals or hasattr(
|
if "query_params_schema" in closure_locals or hasattr(
|
||||||
f, "_request_query_schema"
|
f, "_request_query_schema"
|
||||||
):
|
):
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
#
|
||||||
import copy
|
|
||||||
import inspect
|
import inspect
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
import logging
|
import logging
|
||||||
@@ -22,7 +21,7 @@ from codegenerator.common.schema import ParameterSchema
|
|||||||
from codegenerator.common.schema import PathSchema
|
from codegenerator.common.schema import PathSchema
|
||||||
from codegenerator.common.schema import SpecSchema
|
from codegenerator.common.schema import SpecSchema
|
||||||
from codegenerator.common.schema import TypeSchema
|
from codegenerator.common.schema import TypeSchema
|
||||||
from codegenerator.openapi.base import OpenStackServerSourceBase
|
from codegenerator.openapi.base import OpenStackServerSourceBase, UNSET
|
||||||
from codegenerator.openapi.keystone_schemas import application_credential
|
from codegenerator.openapi.keystone_schemas import application_credential
|
||||||
from codegenerator.openapi.keystone_schemas import auth
|
from codegenerator.openapi.keystone_schemas import auth
|
||||||
from codegenerator.openapi.keystone_schemas import common
|
from codegenerator.openapi.keystone_schemas import common
|
||||||
@@ -368,7 +367,7 @@ class KeystoneGenerator(OpenStackServerSourceBase):
|
|||||||
start_version = None
|
start_version = None
|
||||||
end_version = None
|
end_version = None
|
||||||
deser_schema: dict = {}
|
deser_schema: dict = {}
|
||||||
ser_schema: dict = {}
|
ser_schema: dict | None = {}
|
||||||
|
|
||||||
(
|
(
|
||||||
query_params_versions,
|
query_params_versions,
|
||||||
@@ -512,7 +511,7 @@ class KeystoneGenerator(OpenStackServerSourceBase):
|
|||||||
# Invoke modularized schema _get_schema_ref
|
# Invoke modularized schema _get_schema_ref
|
||||||
for resource_mod in self.RESOURCE_MODULES:
|
for resource_mod in self.RESOURCE_MODULES:
|
||||||
hook = getattr(resource_mod, "_get_schema_ref", None)
|
hook = getattr(resource_mod, "_get_schema_ref", None)
|
||||||
if hook:
|
if hook and schema_def is not UNSET:
|
||||||
(ref, mime_type, matched) = hook(
|
(ref, mime_type, matched) = hook(
|
||||||
openapi_spec, name, description, schema_def, action_name
|
openapi_spec, name, description, schema_def, action_name
|
||||||
)
|
)
|
||||||
@@ -527,5 +526,4 @@ class KeystoneGenerator(OpenStackServerSourceBase):
|
|||||||
schema_def=schema_def,
|
schema_def=schema_def,
|
||||||
action_name=action_name,
|
action_name=action_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
return (ref, mime_type)
|
return (ref, mime_type)
|
||||||
|
Reference in New Issue
Block a user