Finalize openstack_sdk crate features work

Finishing work on introducing features in the sdk/cli it is necessary to
populate sync/async feature macros already in templates. Since this is
anyway causing major regeneration pull object-store (the sdk part) and
fix new issues (caused by sequential updates without mass regeneration -
to be fixed by introducing a separate job).

Change-Id: I4f69325cf31a1ab677b4da96bf2dc92f17e85637
This commit is contained in:
Artem Goncharov 2024-06-14 16:30:45 +02:00
parent e084a3334e
commit 3bc9e13176
14 changed files with 137 additions and 17 deletions

View File

@ -427,6 +427,9 @@ def get_operation_variants(spec: dict, operation_name: str):
elif "application/json-patch+json" in content:
mime_type = "application/json-patch+json"
operation_variants.append({"mime_type": mime_type})
elif "*" in content:
mime_type = "*"
operation_variants.append({"mime_type": mime_type})
elif content == {}:
operation_variants.append({"body": None})
else:
@ -521,6 +524,12 @@ def get_resource_names_from_url(path: str):
path_resource_names.remove("lbaa")
if path.startswith("/v2/octavia/amphorae"):
path_resource_names.remove("octavia")
if path == "/v1/{account}":
return ["account"]
elif path == "/v1/{account}/{container}":
return ["container"]
elif path == "/v1/{account}/{container}/{object}":
return ["object"]
if len(path_resource_names) == 0:
return ["version"]

View File

@ -909,6 +909,8 @@ class TypeManager:
name
and name in unique_models
and unique_models[name].hash_ == model_.reference.hash_
# image.metadef.namespace have weird occurences of itself
and model_.reference != unique_models[name]
):
# Ignore duplicated (or more precisely same) model
self.ignored_models.append(model_.reference)

View File

@ -352,6 +352,34 @@ class MetadataGenerator(BaseGenerator):
):
# No need in HEAD defaults
continue
if args.service_type == "object-store":
if resource_name == "object":
mapping_obj: dict[str, str] = {
"head": "head",
"get": "get",
"delete": "delete",
"put": "put",
"post": "update",
}
operation_key = mapping_obj[method]
elif resource_name == "container":
mapping_cont: dict[str, str] = {
"head": "head",
"get": "get",
"delete": "delete",
"put": "create",
"post": "update",
}
operation_key = mapping_cont[method]
elif resource_name == "account":
mapping_account: dict[str, str] = {
"head": "head",
"get": "get",
"delete": "delete",
"put": "create",
"post": "update",
}
operation_key = mapping_account[method]
if operation_key in resource_model:
raise RuntimeError("Operation name conflict")
@ -740,6 +768,10 @@ def post_process_operation(
operation = post_process_network_operation(
resource_name, operation_name, operation
)
elif service_type == "object-store":
operation = post_process_object_store_operation(
resource_name, operation_name, operation
)
return operation
@ -1092,3 +1124,29 @@ def post_process_network_operation(
].cli_full_command.replace("delete-all", "purge")
return operation
def post_process_object_store_operation(
resource_name: str, operation_name: str, operation
):
if resource_name == "account":
if operation_name == "get":
operation.targets["rust-cli"].cli_full_command = "container list"
elif operation_name == "head":
operation.targets["rust-cli"].cli_full_command = "account show"
elif resource_name == "container":
if operation_name == "get":
operation.targets["rust-cli"].cli_full_command = "object list"
elif operation_name == "head":
operation.targets["rust-cli"].cli_full_command = "container show"
elif resource_name == "object":
if operation_name == "get":
operation.targets["rust-cli"].cli_full_command = "object download"
operation.operation_type = "download"
elif operation_name == "head":
operation.targets["rust-cli"].cli_full_command = "object show"
elif operation_name == "put":
operation.targets["rust-cli"].cli_full_command = "object upload"
operation.operation_type = "upload"
return operation

View File

@ -673,8 +673,9 @@ class OpenAPISchemaParser(JsonSchemaParser):
param_schema = schema.get("schema")
param_typ = param_schema.get("type")
dt: PrimitiveType | ADT | None = None
if isinstance(param_typ, list) and "null" in param_typ:
param_typ.remove("null")
if isinstance(param_typ, list):
if "null" in param_typ:
param_typ.remove("null")
if len(param_typ) == 1:
param_typ = param_typ[0]
if param_typ == "string":

View File

@ -640,8 +640,6 @@ class NovaGenerator(OpenStackServerSourceBase):
schema.openstack = {}
schema.openstack.setdefault("action-name", action_name)
if schema:
print(schema.model_dump())
return (ref, mime_type)
def _post_process_operation_hook(

View File

@ -657,10 +657,23 @@ class RequestTypeManager(common_rust.TypeManager):
field_data_type = field_data_type.item_type
elif isinstance(field_data_type, EnumGroupStruct):
field_data_type.is_required = field.is_required
elif isinstance(
field_data_type, DictionaryInput
) and not isinstance(
field_data_type.value_type, common_rust.BasePrimitiveType
elif (
# is Dictionary
isinstance(field_data_type, DictionaryInput)
# of Primitives
and not isinstance(
field_data_type.value_type, common_rust.BasePrimitiveType
)
and not (
# and not Option<Primitive>
isinstance(
field_data_type.value_type, self.option_type_class
)
and isinstance(
field_data_type.value_type.item_type,
common_rust.BasePrimitiveType,
)
)
):
dict_type_model = self._get_adt_by_reference(field.data_type)
simplified_data_type = JsonValue()
@ -1201,11 +1214,25 @@ class RustCliGenerator(BaseGenerator):
response_def
)
if isinstance(root, model.Dictionary):
value_type = (
response_type_manager.convert_model(
root.value_type
value_type: (
common_rust.BasePrimitiveType
| common_rust.BaseCombinedType
| common_rust.BaseCompoundType
| None
) = None
try:
value_type = (
response_type_manager.convert_model(
root.value_type
)
)
)
except Exception:
# In rare cases we can not conter
# value_type since it depends on different
# types. We are here in the output
# simplification, so just downcast it to
# JsonValue (what is anyway our goal)
value_type = JsonValue()
# if not isinstance(value_type, common_rust.BasePrimitiveType):
# value_type = JsonValue(original_data_type=value_type)
root_dict = HashMapResponse(

View File

@ -176,13 +176,20 @@ class BTreeMap(common_rust.Dictionary):
".map(|(k, v)| (k, v.map(Into::into)))"
)
else:
type_hint: str
if isinstance(self.value_type, BTreeMap):
type_hint = self.value_type.value_type.type_hint.replace(
"Cow<'a, str>", "String"
)
return (
f"BTreeMap::<String, BTreeMap<String, {self.value_type.value_type.type_hint}>>::new().into_iter()"
f"BTreeMap::<String, BTreeMap<String, {type_hint}>>::new().into_iter()"
f".map(|(k, v)| (k, v.into_iter()))"
)
else:
return f"BTreeMap::<String, {self.value_type.type_hint}>::new().into_iter()"
type_hint = self.value_type.type_hint.replace(
"Cow<'a, str>", "String"
)
return f"BTreeMap::<String, {type_hint}>::new().into_iter()"
def get_mandatory_init(self):
return ""

View File

@ -250,6 +250,8 @@ impl {{ target_class_name }}Command {
let rsp: Response<Bytes> = ep.raw_query_async(client).await?;
let data: serde_json::Value = serde_json::from_slice(rsp.body())?;
op.output_machine(data)?;
{%- else %}
// not implemented
{%- endif %}
{%- endwith %}

View File

@ -16,4 +16,10 @@
.collect();
op.output_list::<ResponseData>(split)?;
{%- elif data_type.__class__.__name__ in ["HashMapResponse"] %}
let data: serde_json::Value = ep.query_async(client).await?;
op.output_single::<ResponseData>(data)?;
{%- else %}
let data: serde_json::Value = ep.query_async(client).await?;
op.output_list::<ResponseData>(data)?;
{%- endif %}

View File

@ -273,12 +273,15 @@ mod tests {
#![allow(unused_imports)]
use super::*;
{%- if method.upper() == "HEAD" %}
#[cfg(feature = "sync")]
use crate::api::RawQuery;
{%- else %}
#[cfg(feature = "sync")]
use crate::api::Query;
use serde_json::json;
{%- endif %}
use crate::types::ServiceType;
#[cfg(feature = "sync")]
use crate::test::client::MockServerClient;
use http::{HeaderName, HeaderValue};
{%- if is_json_patch %}
@ -312,6 +315,7 @@ mod tests {
{%- endif %}
}
#[cfg(feature = "sync")]
#[test]
fn endpoint() {
let client = MockServerClient::new();
@ -343,6 +347,7 @@ mod tests {
mock.assert();
}
#[cfg(feature = "sync")]
#[test]
fn endpoint_headers() {
let client = MockServerClient::new();

View File

@ -15,9 +15,11 @@ pub struct {{ data_type.name }}{{ type_manager.get_request_static_lifetimes(dat
{%- endfor %}
{%- for name, param in type_manager.parameters | dictsort %}
{%- if param.location != "header" %}
{{ macros.docstring(param.description, indent=4) }}
{{ param.builder_macros }}
{{ param.local_name }}: {{ param.type_hint }},
{%- endif %}
{%- endfor %}
{% if is_json_patch %}

View File

@ -5,6 +5,7 @@ METADATA=metadata
DST=~/workspace/github/gtema/openstack
NET_RESOURCES=(
"image"
"metadef"
"schema"
)

View File

@ -4,6 +4,8 @@ WRK_DIR=wrk
METADATA=metadata
DST=~/workspace/github/gtema/openstack
NET_RESOURCES=(
"address_group"
"address_scope"
"availability_zone"
"extension"
"floatingip"
@ -18,10 +20,8 @@ openstack-codegenerator --work-dir ${WRK_DIR} --target rust-cli --metadata ${MET
for resource in "${NET_RESOURCES[@]}"; do
# openstack-codegenerator --work-dir ${WRK_DIR} --target rust-sdk --metadata ${METADATA}/network_metadata.yaml --service network # --resource ${resource}
# openstack-codegenerator --work-dir ${WRK_DIR} --target rust-cli --metadata ${METADATA}/network_metadata.yaml --service network # --resource ${resource}
cp -av "${WRK_DIR}/rust/openstack_sdk/src/api/network/v2/${resource}" ${DST}/openstack_sdk/src/api/network/v2
cp -av "${WRK_DIR}/rust/openstack_sdk/src/api/network/v2/${resource}.rs" ${DST}/openstack_sdk/src/api/network/v2
cp -av "${WRK_DIR}/rust/openstack_cli/src/network/v2/${resource}" ${DST}/openstack_cli/src/network/v2
cp -av "${WRK_DIR}/rust/openstack_cli/tests/network/v2/${resource}" ${DST}/openstack_cli/tests/network/v2
done;

View File

@ -6,6 +6,8 @@ METADATA=metadata
DST=~/workspace/github/gtema/openstack
NET_RESOURCES=(
"account"
"container"
"object"
)
openstack-codegenerator --work-dir ${WRK_DIR} --target rust-sdk --metadata ${METADATA}/object-store_metadata.yaml --service object-store