Skip to content
This repository has been archived by the owner on Apr 10, 2024. It is now read-only.

feat: process identity.federation resource #15

Merged
merged 1 commit into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions codegenerator/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ def find_resource_schema(
return (props[resource_name]["items"], resource_name)
return (props[resource_name], resource_name)
for name, item in props.items():
if name == "additionalProperties" and isinstance(item, bool):
# Some schemas are broken
continue
(r, path) = find_resource_schema(item, name, resource_name)
if r:
return (r, path)
Expand Down
100 changes: 96 additions & 4 deletions codegenerator/common/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ class Struct(BaseCompoundType):
base_type: str = "struct"
fields: dict[str, StructField] = {}
field_type_class_: Type[StructField] | StructField = StructField
additional_fields_type: BasePrimitiveType | BaseCombinedType | BaseCompoundType | None = (
None
)
additional_fields_type: (
BasePrimitiveType | BaseCombinedType | BaseCompoundType | None
) = None

@property
def type_hint(self):
Expand Down Expand Up @@ -436,6 +436,8 @@ class TypeManager:
option_type_class: Type[Option] | Option = Option
string_enum_class: Type[StringEnum] | StringEnum = StringEnum

ignored_models: list[model.Reference] = []

def __init__(self):
self.models = []
self.refs = {}
Expand Down Expand Up @@ -778,6 +780,7 @@ def set_models(self, models):
"""Process (translate) ADT models into Rust SDK style"""
self.models = models
self.refs = {}
self.ignored_models = []
unique_model_names: set[str] = set()
for model_ in models:
model_data_type = self.convert_model(model_)
Expand All @@ -791,13 +794,47 @@ def set_models(self, models):
# New name is still unused
model_data_type.name = new_name
unique_model_names.add(new_name)
elif isinstance(model_data_type, Struct):
# This is already an exceptional case (identity.mapping
# with remote being oneOf with multiple structs)
# Try to make a name consisting of props
props = model_data_type.fields.keys()
new_new_name = name + "".join(
x.title() for x in props
).replace("_", "")
if new_new_name not in unique_model_names:
for other_ref, other_model in self.refs.items():
other_name = getattr(other_model, "name", None)
if not other_name:
continue
if other_name in [
name,
new_name,
] and isinstance(other_model, Struct):
# rename first occurence to the same scheme
props = other_model.fields.keys()
new_other_name = name + "".join(
x.title() for x in props
).replace("_", "")
other_model.name = new_other_name
unique_model_names.add(new_other_name)

model_data_type.name = new_new_name
unique_model_names.add(new_new_name)
else:
raise RuntimeError(
"Model name %s is already present" % new_name
)
else:
raise RuntimeError(
"Model name %s is already present" % name
"Model name %s is already present" % new_name
)
elif name:
unique_model_names.add(name)

for ignore_model in self.ignored_models:
self.discard_model(ignore_model)

def get_subtypes(self):
"""Get all subtypes excluding TLA"""
for k, v in self.refs.items():
Expand Down Expand Up @@ -903,6 +940,61 @@ def get_parameters(
if v.location == location:
yield (k, v)

def discard_model(
self,
type_model: model.PrimitiveType | model.ADT | model.Reference,
):
"""Discard model from the manager"""
logging.debug(f"Request to discard {type_model}")
if isinstance(type_model, model.Reference):
type_model = self._get_adt_by_reference(type_model)
if not hasattr(type_model, "reference"):
return
for ref, data in list(self.refs.items()):
if ref == type_model.reference:
sub_ref: model.Reference | None = None
if ref.type == model.Struct:
logging.debug(
"Element is a struct. Purging also field types"
)
# For struct type we cascadely discard all field types as
# well
for v in type_model.fields.values():
if isinstance(v.data_type, model.Reference):
sub_ref = v.data_type
else:
sub_ref = getattr(v.data_type, "reference", None)
if sub_ref:
logging.debug(f"Need to purge also {sub_ref}")
self.discard_model(sub_ref)
elif ref.type == model.OneOfType:
logging.debug(
"Element is a OneOf. Purging also kinds types"
)
for v in type_model.kinds:
if isinstance(v, model.Reference):
sub_ref = v
else:
sub_ref = getattr(v, "reference", None)
if sub_ref:
logging.debug(f"Need to purge also {sub_ref}")
self.discard_model(sub_ref)
elif ref.type == model.Array:
logging.debug(
f"Element is a Array. Purging also item type {type_model.item_type}"
)
if isinstance(type_model.item_type, model.Reference):
sub_ref = type_model.item_type
else:
sub_ref = getattr(
type_model.item_type, "reference", None
)
if sub_ref:
logging.debug(f"Need to purge also {sub_ref}")
self.discard_model(sub_ref)
logging.debug(f"Purging {ref} from models")
self.refs.pop(ref, None)


def sanitize_rust_docstrings(doc: str | None) -> str | None:
"""Sanitize the string to be a valid rust docstring"""
Expand Down
41 changes: 38 additions & 3 deletions codegenerator/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,13 +280,28 @@ def generate(
f"Cannot identify op name for {path}:{method}"
)

# Next hacks
if args.service_type == "identity" and resource_name in [
"OS_FEDERATION/identity_provider",
"OS_FEDERATION/identity_provider/protocol",
"OS_FEDERATION/mapping",
"OS_FEDERATION/service_provider",
]:
if method == "put":
operation_key = "create"
elif method == "patch":
operation_key = "update"

if operation_key in resource_model:
raise RuntimeError("Operation name conflict")
else:
if (
operation_key == "action"
and args.service_type
in ["compute", "block-storage"]
in [
"compute",
"block-storage",
]
):
# For action we actually have multiple independent operations
try:
Expand All @@ -302,7 +317,7 @@ def generate(
).get("discriminator")
if discriminator != "action":
raise RuntimeError(
"Cannot generate metadata for %s since requet body is not having action discriminator"
"Cannot generate metadata for %s since request body is not having action discriminator"
% path
)
for body in bodies:
Expand All @@ -317,7 +332,11 @@ def generate(
if (
resource_name == "flavor"
and action_name
in ["update", "create", "delete"]
in [
"update",
"create",
"delete",
]
):
# Flavor update/create/delete
# operations are exposed ALSO as wsgi
Expand Down Expand Up @@ -467,6 +486,12 @@ def generate(
if "id" not in response_schema.get("properties", {}).keys():
# Resource has no ID in show method => find impossible
continue
elif (
"name" not in response_schema.get("properties", {}).keys()
and res_name != "floatingip"
):
# Resource has no NAME => find useless
continue

list_op_ = list_detailed_op or list_op
if not list_op_:
Expand Down Expand Up @@ -623,6 +648,10 @@ def post_process_operation(
operation = post_process_compute_operation(
resource_name, operation_name, operation
)
elif service_type == "identity":
operation = post_process_identity_operation(
resource_name, operation_name, operation
)
elif service_type == "image":
operation = post_process_image_operation(
resource_name, operation_name, operation
Expand Down Expand Up @@ -666,6 +695,12 @@ def post_process_compute_operation(
return operation


def post_process_identity_operation(
resource_name: str, operation_name: str, operation
):
return operation


def post_process_image_operation(
resource_name: str, operation_name: str, operation
):
Expand Down
3 changes: 3 additions & 0 deletions codegenerator/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ def parse_object(
if properties:
obj = Struct()
for k, v in properties.items():
if k == "additionalProperties" and isinstance(v, bool):
# Some schemas (in keystone) are Broken
continue
if ignore_read_only and v.get("readOnly", False):
continue
data_type = self.parse_schema(
Expand Down
Loading