From da28f1b1ccf2be867b4b2597734c997e601dac21 Mon Sep 17 00:00:00 2001 From: AllyW Date: Wed, 28 May 2025 08:48:56 +0800 Subject: [PATCH 1/4] add bytes arg --- .../cli/controller/az_arg_group_generator.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/aaz_dev/cli/controller/az_arg_group_generator.py b/src/aaz_dev/cli/controller/az_arg_group_generator.py index cdd336cb..8a12042a 100644 --- a/src/aaz_dev/cli/controller/az_arg_group_generator.py +++ b/src/aaz_dev/cli/controller/az_arg_group_generator.py @@ -350,7 +350,18 @@ def render_arg_base(arg, cmd_ctx, arg_kwargs=None): elif isinstance(arg, CMDByteArgBase): raise NotImplementedError() elif isinstance(arg, CMDBinaryArgBase): - raise NotImplementedError() + arg_type = "AAZFileBytesArg" + if arg.fmt and isinstance(arg.fmt, CMDStringFormat): + arg_kwargs['fmt'] = fmt = { + "cls": "AAZFileBytesArgFormat", + "kwargs": {} + } + if arg.fmt.pattern is not None: + fmt['kwargs']["pattern"] = arg.fmt.pattern + if arg.fmt.max_length is not None: + fmt['kwargs']["max_length"] = arg.fmt.max_length + if arg.fmt.min_length is not None: + fmt['kwargs']["min_length"] = arg.fmt.min_length elif isinstance(arg, CMDDurationArgBase): arg_type = "AAZDurationArg" elif isinstance(arg, CMDDateArgBase): From caaa42559600febe8d1d939ced4667cfff4a7560 Mon Sep 17 00:00:00 2001 From: AllyW Date: Wed, 28 May 2025 22:00:15 +0800 Subject: [PATCH 2/4] add bytes arg --- .../cli/controller/az_arg_group_generator.py | 6 --- .../cli/controller/az_operation_generator.py | 4 +- .../command/model/configuration/__init__.py | 4 +- .../command/model/configuration/_arg.py | 8 +-- .../model/configuration/_arg_builder.py | 7 ++- .../command/model/configuration/_content.py | 54 ++++++++++++++++++- .../model/configuration/_http_request_body.py | 21 +++++++- .../command/model/configuration/_schema.py | 18 +++++-- src/aaz_dev/swagger/model/schema/operation.py | 5 +- src/aaz_dev/swagger/model/schema/parameter.py | 8 ++- 10 files changed, 111 insertions(+), 24 deletions(-) diff --git a/src/aaz_dev/cli/controller/az_arg_group_generator.py b/src/aaz_dev/cli/controller/az_arg_group_generator.py index 8a12042a..2dcbf10a 100644 --- a/src/aaz_dev/cli/controller/az_arg_group_generator.py +++ b/src/aaz_dev/cli/controller/az_arg_group_generator.py @@ -356,12 +356,6 @@ def render_arg_base(arg, cmd_ctx, arg_kwargs=None): "cls": "AAZFileBytesArgFormat", "kwargs": {} } - if arg.fmt.pattern is not None: - fmt['kwargs']["pattern"] = arg.fmt.pattern - if arg.fmt.max_length is not None: - fmt['kwargs']["max_length"] = arg.fmt.max_length - if arg.fmt.min_length is not None: - fmt['kwargs']["min_length"] = arg.fmt.min_length elif isinstance(arg, CMDDurationArgBase): arg_type = "AAZDurationArg" elif isinstance(arg, CMDDateArgBase): diff --git a/src/aaz_dev/cli/controller/az_operation_generator.py b/src/aaz_dev/cli/controller/az_operation_generator.py index bec6177a..6bbc3799 100644 --- a/src/aaz_dev/cli/controller/az_operation_generator.py +++ b/src/aaz_dev/cli/controller/az_operation_generator.py @@ -2,7 +2,7 @@ CMDHttpOperation, CMDHttpRequestJsonBody, CMDArraySchema, CMDInstanceUpdateOperation, CMDRequestJson, CMDHttpResponseJsonBody, CMDObjectSchema, CMDSchema, CMDStringSchemaBase, CMDIntegerSchemaBase, CMDFloatSchemaBase, CMDBooleanSchemaBase, CMDObjectSchemaBase, CMDArraySchemaBase, CMDClsSchemaBase, CMDJsonInstanceUpdateAction, - CMDObjectSchemaDiscriminator, CMDSchemaEnum, CMDJsonInstanceCreateAction, CMDJsonInstanceDeleteAction, + CMDObjectSchemaDiscriminator, CMDSchemaEnum, CMDJsonInstanceCreateAction, CMDJsonInstanceDeleteAction, CMDBinarySchema, CMDInstanceCreateOperation, CMDInstanceDeleteOperation, CMDClientEndpointsByTemplate, CMDIdentityObjectSchemaBase, CMDAnyTypeSchemaBase) from utils import exceptions from utils.case import to_snake_case @@ -990,6 +990,8 @@ def render_schema_base(schema, cls_map, schema_kwargs=None): schema_type = "AAZFloatType" elif isinstance(schema, CMDIdentityObjectSchemaBase): schema_type = "AAZIdentityObjectType" + elif isinstance(schema, CMDBinarySchema): + schema_type = "AAZFileBytesType" elif isinstance(schema, CMDObjectSchemaBase): if schema.props or schema.discriminators: schema_type = "AAZObjectType" diff --git a/src/aaz_dev/command/model/configuration/__init__.py b/src/aaz_dev/command/model/configuration/__init__.py index 5c7e23fe..09d6bf33 100644 --- a/src/aaz_dev/command/model/configuration/__init__.py +++ b/src/aaz_dev/command/model/configuration/__init__.py @@ -35,7 +35,7 @@ CMDConditionAndOperator, CMDConditionOrOperator, CMDConditionNotOperator, CMDConditionHasValueOperator, \ CMDCondition from ._configuration import CMDConfiguration -from ._content import CMDRequestJson, CMDResponseJson +from ._content import CMDRequestJson, CMDResponseJson, CMDRequestBytes from ._example import CMDCommandExample from ._fields import CMDBooleanField, CMDStageField, CMDVariantField, CMDClassField, \ CMDPrimitiveField, CMDRegularExpressionField, CMDVersionField, CMDResourceIdField, CMDCommandNameField, \ @@ -47,7 +47,7 @@ CMDHttpRequest, \ CMDHttpResponseHeaderItem, CMDHttpResponseHeader, CMDHttpResponse, \ CMDHttpAction -from ._http_request_body import CMDHttpRequestBody, CMDHttpRequestJsonBody +from ._http_request_body import CMDHttpRequestBody, CMDHttpRequestJsonBody, CMDHttpRequestBytesBody from ._http_response_body import CMDHttpResponseBody, CMDHttpResponseJsonBody from ._instance_create import CMDInstanceCreateAction, CMDJsonInstanceCreateAction from ._instance_delete import CMDInstanceDeleteAction, CMDJsonInstanceDeleteAction diff --git a/src/aaz_dev/command/model/configuration/_arg.py b/src/aaz_dev/command/model/configuration/_arg.py index fe409f13..d7d3f126 100644 --- a/src/aaz_dev/command/model/configuration/_arg.py +++ b/src/aaz_dev/command/model/configuration/_arg.py @@ -384,20 +384,20 @@ def _reformat(self, **kwargs): # byte: base64 encoded characters -class CMDByteArgBase(CMDStringArgBase): +class CMDByteArgBase(CMDArg): TYPE_VALUE = "byte" -class CMDByteArg(CMDByteArgBase, CMDStringArg): +class CMDByteArg(CMDByteArgBase): pass # binary: any sequence of octets -class CMDBinaryArgBase(CMDStringArgBase): +class CMDBinaryArgBase(CMDArgBase): TYPE_VALUE = "binary" -class CMDBinaryArg(CMDBinaryArgBase, CMDStringArg): +class CMDBinaryArg(CMDBinaryArgBase, CMDArg): pass diff --git a/src/aaz_dev/command/model/configuration/_arg_builder.py b/src/aaz_dev/command/model/configuration/_arg_builder.py index e20e592d..d9ea5cd7 100644 --- a/src/aaz_dev/command/model/configuration/_arg_builder.py +++ b/src/aaz_dev/command/model/configuration/_arg_builder.py @@ -7,7 +7,7 @@ from ._format import CMDFormat from ._schema import CMDObjectSchema, CMDSchema, CMDSchemaBase, CMDObjectSchemaBase, CMDObjectSchemaDiscriminator, \ CMDArraySchemaBase, CMDArraySchema, CMDObjectSchemaAdditionalProperties, CMDResourceIdSchema, \ - CMDResourceLocationSchemaBase, CMDPasswordSchema, CMDBooleanSchemaBase + CMDResourceLocationSchemaBase, CMDPasswordSchema, CMDBooleanSchemaBase, CMDBinarySchema from ..configuration._schema import CMDIdentityObjectSchema, CMDStringSchemaBase, \ CMDStringSchema @@ -197,7 +197,8 @@ def get_sub_args(self): item.arg = discriminator_mapping[prop.name][item.value] continue sub_builder = self.get_sub_builder(schema=prop, ref_args=sub_ref_args) - sub_args.extend(sub_builder.get_args()) + sub_arg = sub_builder.get_args() + sub_args.extend(sub_arg) self.schema.props = [prop for prop in self.schema.props if prop not in removed] if isinstance(self.schema, CMDIdentityObjectSchema) and (not self._is_update_action or self.schema.action): @@ -381,6 +382,8 @@ def get_options(self): if self.schema.action is not None and self.schema.name in ["userAssigned", "systemAssigned"]: return [opt_name, "mi-" + opt_name] + if isinstance(self.schema, CMDBinarySchema): + return [opt_name, "body-file", "body-file-path"] else: raise NotImplementedError() return [opt_name, ] diff --git a/src/aaz_dev/command/model/configuration/_content.py b/src/aaz_dev/command/model/configuration/_content.py index ec36df5e..3b8c2a95 100644 --- a/src/aaz_dev/command/model/configuration/_content.py +++ b/src/aaz_dev/command/model/configuration/_content.py @@ -4,7 +4,7 @@ from ._arg_builder import CMDArgBuilder from ._fields import CMDVariantField from ._schema import CMDSchemaBaseField, CMDSchema, CMDClsSchema, CMDClsSchemaBase, \ - CMDObjectSchemaBase, CMDArraySchemaBase, CMDObjectSchemaDiscriminator + CMDObjectSchemaBase, CMDArraySchemaBase, CMDObjectSchemaDiscriminator, CMDBinarySchema from ._utils import CMDDiffLevelEnum @@ -63,6 +63,58 @@ def reformat(self, schema_cls_map, **kwargs): def register_cls(self, cls_register_map, **kwargs): _iter_over_schema_for_cls_register(self.schema, cls_register_map) +class CMDRequestBytes(Model): + """Used for Request Bytes Body""" + + ref = CMDVariantField() + + schema = PolyModelType(CMDBinarySchema, allow_subclasses=False) + + class Options: + serialize_when_none = False + + def generate_args(self, ref_args, var_prefix=None, is_update_action=False): + if not self.schema: + return [] + assert isinstance(self.schema, CMDBinarySchema) + builder = CMDArgBuilder.new_builder( + schema=self.schema, + ref_args=ref_args, + var_prefix=var_prefix, + is_update_action=is_update_action + ) + args = builder.get_args() + return args + + def diff(self, old, level): + diff = {} + if level >= CMDDiffLevelEnum.BreakingChange: + if (self.ref is not None) != (old.ref is not None): + diff["ref"] = f"{old.ref} != {self.ref}" + schema_diff = self.schema.diff(old.schema, level) + if schema_diff: + diff["schema"] = schema_diff + + if level >= CMDDiffLevelEnum.Associate: + if self.ref != old.ref: + diff["ref"] = f"{old.ref} != {self.ref}" + return diff + + def reformat(self, schema_cls_map, **kwargs): + if self.schema: + if getattr(self.schema, 'cls', None): + if not schema_cls_map.get(self.schema.cls, None): + schema_cls_map[self.schema.cls] = self.schema + else: + # replace by CMDClsSchema + self.schema = CMDClsSchema.build_from_schema(self.schema, schema_cls_map[self.schema.cls]) + + _iter_over_schema(self.schema, schema_cls_map) + + self.schema.reformat(**kwargs) + + def register_cls(self, cls_register_map, **kwargs): + _iter_over_schema_for_cls_register(self.schema, cls_register_map) class CMDResponseJson(Model): # properties as tags diff --git a/src/aaz_dev/command/model/configuration/_http_request_body.py b/src/aaz_dev/command/model/configuration/_http_request_body.py index 74df7e5d..e5500fec 100644 --- a/src/aaz_dev/command/model/configuration/_http_request_body.py +++ b/src/aaz_dev/command/model/configuration/_http_request_body.py @@ -1,7 +1,7 @@ from schematics.models import Model from schematics.types import ModelType -from ._content import CMDRequestJson +from ._content import CMDRequestJson, CMDRequestBytes class CMDHttpRequestBody(Model): @@ -50,3 +50,22 @@ def reformat(self, **kwargs): def register_cls(self, **kwargs): self.json.register_cls(**kwargs) + +class CMDHttpRequestBytesBody(CMDHttpRequestBody): + POLYMORPHIC_KEY = "bytes" + + bytes = ModelType(CMDRequestBytes, required=True) + + def generate_args(self, ref_args, var_prefix=None): + return self.bytes.generate_args(ref_args=ref_args, var_prefix=var_prefix) + + def diff(self, old, level): + if not isinstance(old, self.__class__): + return f"Response type changed: '{type(old)}' != '{self.__class__}'" + return self.bytes.diff(old.bytes, level) + + def reformat(self, **kwargs): + self.bytes.reformat(**kwargs) + + def register_cls(self, **kwargs): + self.bytes.register_cls(**kwargs) diff --git a/src/aaz_dev/command/model/configuration/_schema.py b/src/aaz_dev/command/model/configuration/_schema.py index ab75ab7a..97cda46b 100644 --- a/src/aaz_dev/command/model/configuration/_schema.py +++ b/src/aaz_dev/command/model/configuration/_schema.py @@ -489,22 +489,32 @@ class CMDStringSchema(CMDStringSchemaBase, CMDSchema): # byte: base64 encoded characters -class CMDByteSchemaBase(CMDStringSchemaBase): +class CMDByteSchemaBase(CMDSchemaBase): TYPE_VALUE = "byte" ARG_TYPE = CMDByteArgBase -class CMDByteSchema(CMDByteSchemaBase, CMDStringSchema): +class CMDByteSchema(CMDByteSchemaBase, CMDSchema): ARG_TYPE = CMDByteArg # binary: any sequence of octets -class CMDBinarySchemaBase(CMDStringSchemaBase): +class CMDBinarySchemaBase(CMDSchemaBase): TYPE_VALUE = "binary" ARG_TYPE = CMDBinaryArgBase + name = StringType(required=True) + arg = CMDVariantField() + required = CMDBooleanField() + + description = CMDDescriptionField() + fmt = ModelType( + CMDStringFormat, + serialized_name='format', + deserialize_from='format' + ) -class CMDBinarySchema(CMDBinarySchemaBase, CMDStringSchema): +class CMDBinarySchema(CMDBinarySchemaBase, CMDSchema): ARG_TYPE = CMDBinaryArg diff --git a/src/aaz_dev/swagger/model/schema/operation.py b/src/aaz_dev/swagger/model/schema/operation.py index d41654a3..bc1e1634 100644 --- a/src/aaz_dev/swagger/model/schema/operation.py +++ b/src/aaz_dev/swagger/model/schema/operation.py @@ -5,7 +5,7 @@ from schematics.types import StringType, ModelType, ListType, DictType, BooleanType, PolyModelType from command.model.configuration import CMDHttpOperation, CMDHttpAction, CMDHttpRequest, CMDHttpRequestPath, \ - CMDHttpRequestQuery, CMDHttpRequestHeader, CMDHttpRequestJsonBody, CMDRequestJson, CMDHttpOperationLongRunning + CMDHttpRequestQuery, CMDHttpRequestHeader, CMDHttpRequestJsonBody, CMDRequestJson, CMDRequestBytes, CMDHttpRequestBytesBody, CMDHttpOperationLongRunning from swagger.utils import exceptions from swagger.utils.tools import swagger_resource_path_to_resource_id_template from .example_item import XmsExamplesField @@ -225,6 +225,9 @@ def to_cmd(self, builder, parent_parameters, host_path, **kwargs): if isinstance(model, CMDRequestJson): request.body = CMDHttpRequestJsonBody() request.body.json = model + elif isinstance(model, CMDRequestBytes): + request.body = CMDHttpRequestBytesBody() + request.body.bytes = model else: raise NotImplementedError() diff --git a/src/aaz_dev/swagger/model/schema/parameter.py b/src/aaz_dev/swagger/model/schema/parameter.py index 9ff675e6..00c57929 100644 --- a/src/aaz_dev/swagger/model/schema/parameter.py +++ b/src/aaz_dev/swagger/model/schema/parameter.py @@ -1,7 +1,7 @@ import logging from command.model.configuration import CMDRequestJson, CMDBooleanSchema, CMDStringSchema, CMDObjectSchema, \ - CMDArraySchema, CMDFloatSchema, CMDIntegerSchema + CMDArraySchema, CMDFloatSchema, CMDIntegerSchema, CMDBinarySchema, CMDRequestBytes from schematics.models import Model from schematics.types import StringType, BooleanType, ModelType, PolyModelType, BaseType from swagger.utils import exceptions @@ -205,7 +205,11 @@ def to_cmd(self, builder, **kwargs): msg=f"Request Body Parameter is None: {self.traces}" ) return None - if isinstance(v, ( + if isinstance(v, CMDBinarySchema): + model = CMDRequestBytes() + model.schema = v + v.required = self.required + elif isinstance(v, ( CMDStringSchema, CMDObjectSchema, CMDArraySchema, From f748ebd49be733e9417735121bddaafc82bea72d Mon Sep 17 00:00:00 2001 From: AllyW Date: Thu, 29 May 2025 14:14:05 +0800 Subject: [PATCH 3/4] add generator --- .../cli/controller/az_arg_group_generator.py | 13 ++----- .../cli/controller/az_operation_generator.py | 37 ++++++++++++++++++- .../command/model/configuration/_arg.py | 4 +- 3 files changed, 41 insertions(+), 13 deletions(-) diff --git a/src/aaz_dev/cli/controller/az_arg_group_generator.py b/src/aaz_dev/cli/controller/az_arg_group_generator.py index 2dcbf10a..0a8a5fb3 100644 --- a/src/aaz_dev/cli/controller/az_arg_group_generator.py +++ b/src/aaz_dev/cli/controller/az_arg_group_generator.py @@ -294,6 +294,10 @@ def render_arg_base(arg, cmd_ctx, arg_kwargs=None): if isinstance(arg, CMDAnyTypeArgBase): arg_type = "AAZAnyTypeArg" + elif isinstance(arg, CMDByteArgBase): + raise NotImplementedError() + elif isinstance(arg, CMDBinaryArgBase): + arg_type = "AAZFileBytesArg" elif isinstance(arg, CMDStringArgBase): arg_type = "AAZStrArg" enum_kwargs = parse_arg_enum(arg.enum) @@ -347,15 +351,6 @@ def render_arg_base(arg, cmd_ctx, arg_kwargs=None): "resource_group_arg": resource_group_arg } } - elif isinstance(arg, CMDByteArgBase): - raise NotImplementedError() - elif isinstance(arg, CMDBinaryArgBase): - arg_type = "AAZFileBytesArg" - if arg.fmt and isinstance(arg.fmt, CMDStringFormat): - arg_kwargs['fmt'] = fmt = { - "cls": "AAZFileBytesArgFormat", - "kwargs": {} - } elif isinstance(arg, CMDDurationArgBase): arg_type = "AAZDurationArg" elif isinstance(arg, CMDDateArgBase): diff --git a/src/aaz_dev/cli/controller/az_operation_generator.py b/src/aaz_dev/cli/controller/az_operation_generator.py index 6bbc3799..f0731ef6 100644 --- a/src/aaz_dev/cli/controller/az_operation_generator.py +++ b/src/aaz_dev/cli/controller/az_operation_generator.py @@ -1,5 +1,5 @@ from command.model.configuration import ( - CMDHttpOperation, CMDHttpRequestJsonBody, CMDArraySchema, CMDInstanceUpdateOperation, CMDRequestJson, + CMDHttpOperation, CMDHttpRequestJsonBody, CMDArraySchema, CMDInstanceUpdateOperation, CMDRequestJson, CMDHttpRequestBytesBody, CMDRequestBytes, CMDHttpResponseJsonBody, CMDObjectSchema, CMDSchema, CMDStringSchemaBase, CMDIntegerSchemaBase, CMDFloatSchemaBase, CMDBooleanSchemaBase, CMDObjectSchemaBase, CMDArraySchemaBase, CMDClsSchemaBase, CMDJsonInstanceUpdateAction, CMDObjectSchemaDiscriminator, CMDSchemaEnum, CMDJsonInstanceCreateAction, CMDJsonInstanceDeleteAction, CMDBinarySchema, @@ -118,6 +118,8 @@ def __init__(self, name, cmd_ctx, operation, client_endpoints): body = self._operation.http.request.body if isinstance(body, CMDHttpRequestJsonBody): self.content = AzHttpRequestContentGenerator(self._cmd_ctx, body) + elif isinstance(body, CMDHttpRequestBytesBody): + self.content = AzHttpRequestContentBytesGenerator(self._cmd_ctx, body) else: raise NotImplementedError() @@ -260,6 +262,13 @@ def header_parameters(self): True, {} ]) + elif isinstance(body, CMDHttpRequestBytesBody): + parameters.append([ + "Content-Type", + "application/octet-stream", + True, + {} + ]) if self.success_responses: for response in self.success_responses: if response._response.body is not None and isinstance(response._response.body, CMDHttpResponseJsonBody): @@ -510,6 +519,30 @@ def iter_scopes(self): for scopes in _iter_request_scopes_by_schema_base(self.schema, self.BUILDER_NAME, None, arg_key, self._cmd_ctx): yield scopes +class AzHttpRequestContentBytesGenerator: + VALUE_NAME = "_content_value" + BUILDER_NAME = "_builder" + + def __init__(self, cmd_ctx, body): + self._cmd_ctx = cmd_ctx + assert isinstance(body.bytes, CMDRequestBytes) + self._bodycontent = body.bytes + self.ref = None + if self._bodycontent.ref: + self.ref, is_selector = self._cmd_ctx.get_variant(self._bodycontent.ref) + assert not is_selector + self.arg_key = "self.ctx.args" + if self.ref is None: + assert isinstance(self._bodycontent.schema, CMDSchema) + if self._bodycontent.schema.arg: + self.arg_key, hide = self._cmd_ctx.get_argument(self._bodycontent.schema.arg) + assert not hide + self.typ, self.typ_kwargs, self.cls_builder_name = render_schema( + self._bodycontent.schema, self._cmd_ctx.update_clses, name=self._bodycontent.schema.name) + + def iter_scopes(self): + pass + class AzHttpResponseGenerator: @@ -991,7 +1024,7 @@ def render_schema_base(schema, cls_map, schema_kwargs=None): elif isinstance(schema, CMDIdentityObjectSchemaBase): schema_type = "AAZIdentityObjectType" elif isinstance(schema, CMDBinarySchema): - schema_type = "AAZFileBytesType" + schema_type = "AAZBytesType" elif isinstance(schema, CMDObjectSchemaBase): if schema.props or schema.discriminators: schema_type = "AAZObjectType" diff --git a/src/aaz_dev/command/model/configuration/_arg.py b/src/aaz_dev/command/model/configuration/_arg.py index d7d3f126..38e7d417 100644 --- a/src/aaz_dev/command/model/configuration/_arg.py +++ b/src/aaz_dev/command/model/configuration/_arg.py @@ -384,11 +384,11 @@ def _reformat(self, **kwargs): # byte: base64 encoded characters -class CMDByteArgBase(CMDArg): +class CMDByteArgBase(CMDArgBase): TYPE_VALUE = "byte" -class CMDByteArg(CMDByteArgBase): +class CMDByteArg(CMDByteArgBase, CMDArg): pass From 298f56a6ecd768b7aa317bc9f2b6e0dd67cfdc5b Mon Sep 17 00:00:00 2001 From: AllyW Date: Thu, 29 May 2025 14:14:37 +0800 Subject: [PATCH 4/4] add ge --- .../cli/controller/az_arg_group_generator.py | 2 +- .../cli/controller/az_operation_generator.py | 25 ++++----- .../cli/templates/aaz/command/_cmd.py.j2 | 26 +++++++++- .../command/model/configuration/__init__.py | 4 +- .../model/configuration/_arg_builder.py | 3 +- .../command/model/configuration/_content.py | 4 +- .../model/configuration/_http_request_body.py | 6 +-- .../swagger/controller/command_generator.py | 52 ++++++++++--------- src/aaz_dev/swagger/model/schema/operation.py | 6 +-- src/aaz_dev/swagger/model/schema/parameter.py | 4 +- 10 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/aaz_dev/cli/controller/az_arg_group_generator.py b/src/aaz_dev/cli/controller/az_arg_group_generator.py index 0a8a5fb3..62057b9e 100644 --- a/src/aaz_dev/cli/controller/az_arg_group_generator.py +++ b/src/aaz_dev/cli/controller/az_arg_group_generator.py @@ -297,7 +297,7 @@ def render_arg_base(arg, cmd_ctx, arg_kwargs=None): elif isinstance(arg, CMDByteArgBase): raise NotImplementedError() elif isinstance(arg, CMDBinaryArgBase): - arg_type = "AAZFileBytesArg" + arg_type = "AAZFileUploadArg" elif isinstance(arg, CMDStringArgBase): arg_type = "AAZStrArg" enum_kwargs = parse_arg_enum(arg.enum) diff --git a/src/aaz_dev/cli/controller/az_operation_generator.py b/src/aaz_dev/cli/controller/az_operation_generator.py index f0731ef6..f7908894 100644 --- a/src/aaz_dev/cli/controller/az_operation_generator.py +++ b/src/aaz_dev/cli/controller/az_operation_generator.py @@ -1,5 +1,5 @@ from command.model.configuration import ( - CMDHttpOperation, CMDHttpRequestJsonBody, CMDArraySchema, CMDInstanceUpdateOperation, CMDRequestJson, CMDHttpRequestBytesBody, CMDRequestBytes, + CMDHttpOperation, CMDHttpRequestJsonBody, CMDArraySchema, CMDInstanceUpdateOperation, CMDRequestJson, CMDHttpRequestBinaryBody, CMDRequestBinary, CMDHttpResponseJsonBody, CMDObjectSchema, CMDSchema, CMDStringSchemaBase, CMDIntegerSchemaBase, CMDFloatSchemaBase, CMDBooleanSchemaBase, CMDObjectSchemaBase, CMDArraySchemaBase, CMDClsSchemaBase, CMDJsonInstanceUpdateAction, CMDObjectSchemaDiscriminator, CMDSchemaEnum, CMDJsonInstanceCreateAction, CMDJsonInstanceDeleteAction, CMDBinarySchema, @@ -114,11 +114,13 @@ def __init__(self, name, cmd_ctx, operation, client_endpoints): self.content = None self.form_content = None self.stream_content = None + self.content_as_binary = None if self._operation.http.request.body: body = self._operation.http.request.body if isinstance(body, CMDHttpRequestJsonBody): self.content = AzHttpRequestContentGenerator(self._cmd_ctx, body) - elif isinstance(body, CMDHttpRequestBytesBody): + elif isinstance(body, CMDHttpRequestBinaryBody): + self.content_as_binary = True self.content = AzHttpRequestContentBytesGenerator(self._cmd_ctx, body) else: raise NotImplementedError() @@ -262,13 +264,19 @@ def header_parameters(self): True, {} ]) - elif isinstance(body, CMDHttpRequestBytesBody): + elif isinstance(body, CMDHttpRequestBinaryBody): parameters.append([ "Content-Type", "application/octet-stream", True, {} ]) + parameters.append([ + "Content-Length", + "self.ctx.file_length", + False, + {} + ]) if self.success_responses: for response in self.success_responses: if response._response.body is not None and isinstance(response._response.body, CMDHttpResponseJsonBody): @@ -520,12 +528,10 @@ def iter_scopes(self): yield scopes class AzHttpRequestContentBytesGenerator: - VALUE_NAME = "_content_value" - BUILDER_NAME = "_builder" def __init__(self, cmd_ctx, body): self._cmd_ctx = cmd_ctx - assert isinstance(body.bytes, CMDRequestBytes) + assert isinstance(body.bytes, CMDRequestBinary) self._bodycontent = body.bytes self.ref = None if self._bodycontent.ref: @@ -537,11 +543,6 @@ def __init__(self, cmd_ctx, body): if self._bodycontent.schema.arg: self.arg_key, hide = self._cmd_ctx.get_argument(self._bodycontent.schema.arg) assert not hide - self.typ, self.typ_kwargs, self.cls_builder_name = render_schema( - self._bodycontent.schema, self._cmd_ctx.update_clses, name=self._bodycontent.schema.name) - - def iter_scopes(self): - pass class AzHttpResponseGenerator: @@ -1024,7 +1025,7 @@ def render_schema_base(schema, cls_map, schema_kwargs=None): elif isinstance(schema, CMDIdentityObjectSchemaBase): schema_type = "AAZIdentityObjectType" elif isinstance(schema, CMDBinarySchema): - schema_type = "AAZBytesType" + schema_type = "AAZFileUploadType" elif isinstance(schema, CMDObjectSchemaBase): if schema.props or schema.discriminators: schema_type = "AAZObjectType" diff --git a/src/aaz_dev/cli/templates/aaz/command/_cmd.py.j2 b/src/aaz_dev/cli/templates/aaz/command/_cmd.py.j2 index eaf2520d..e77d2b34 100644 --- a/src/aaz_dev/cli/templates/aaz/command/_cmd.py.j2 +++ b/src/aaz_dev/cli/templates/aaz/command/_cmd.py.j2 @@ -236,11 +236,21 @@ class {{ leaf.cls_name }}( @register_callback def pre_operations(self): + {%- if leaf.http_operations[0].content is not none and leaf.http_operations[0].content_as_binary is not none %} + self.ctx.file_content, self.ctx.file_handler, self.ctx.file_length = {{ leaf.http_operations[0].content.arg_key }}.to_serialized_data() + {%- else %} pass + {%- endif %} @register_callback def post_operations(self): + {%- if leaf.http_operations[0].content is not none and leaf.http_operations[0].content_as_binary is not none %} + if self.ctx.file_handler is not None: + self.ctx.file_handler.close() + self.ctx.file_handler = None + {%- else %} pass + {%- endif %} {%- for op in leaf.operations %} {%- if op.name in ("pre_instance_update", "post_instance_update", "post_instance_create") %} @@ -485,7 +495,7 @@ class {{ leaf.cls_name }}( return parameters {%- endif %} - {%- if op.content is not none %} + {%- if op.content is not none and op.content_as_binary is none %} @property def content(self): @@ -548,6 +558,13 @@ class {{ leaf.cls_name }}( return self.serialize_content({{ op.content.VALUE_NAME }}) {%- endif %} + {%- if op.content is not none and op.content_as_binary is not none %} + + @property + def content(self): + return self.ctx.file_content + {%- endif %} + {%- if op.form_content is not none %} @property @@ -556,6 +573,13 @@ class {{ leaf.cls_name }}( return None {%- endif %} + {%- if op.content is not none and op.content_as_binary is not none %} + + @property + def stream_content(self): + return self.ctx.file_handler + {%- endif %} + {%- if op.stream_content is not none %} @property diff --git a/src/aaz_dev/command/model/configuration/__init__.py b/src/aaz_dev/command/model/configuration/__init__.py index 09d6bf33..3cee8c48 100644 --- a/src/aaz_dev/command/model/configuration/__init__.py +++ b/src/aaz_dev/command/model/configuration/__init__.py @@ -35,7 +35,7 @@ CMDConditionAndOperator, CMDConditionOrOperator, CMDConditionNotOperator, CMDConditionHasValueOperator, \ CMDCondition from ._configuration import CMDConfiguration -from ._content import CMDRequestJson, CMDResponseJson, CMDRequestBytes +from ._content import CMDRequestJson, CMDResponseJson, CMDRequestBinary from ._example import CMDCommandExample from ._fields import CMDBooleanField, CMDStageField, CMDVariantField, CMDClassField, \ CMDPrimitiveField, CMDRegularExpressionField, CMDVersionField, CMDResourceIdField, CMDCommandNameField, \ @@ -47,7 +47,7 @@ CMDHttpRequest, \ CMDHttpResponseHeaderItem, CMDHttpResponseHeader, CMDHttpResponse, \ CMDHttpAction -from ._http_request_body import CMDHttpRequestBody, CMDHttpRequestJsonBody, CMDHttpRequestBytesBody +from ._http_request_body import CMDHttpRequestBody, CMDHttpRequestJsonBody, CMDHttpRequestBinaryBody from ._http_response_body import CMDHttpResponseBody, CMDHttpResponseJsonBody from ._instance_create import CMDInstanceCreateAction, CMDJsonInstanceCreateAction from ._instance_delete import CMDInstanceDeleteAction, CMDJsonInstanceDeleteAction diff --git a/src/aaz_dev/command/model/configuration/_arg_builder.py b/src/aaz_dev/command/model/configuration/_arg_builder.py index d9ea5cd7..5439cc44 100644 --- a/src/aaz_dev/command/model/configuration/_arg_builder.py +++ b/src/aaz_dev/command/model/configuration/_arg_builder.py @@ -197,8 +197,7 @@ def get_sub_args(self): item.arg = discriminator_mapping[prop.name][item.value] continue sub_builder = self.get_sub_builder(schema=prop, ref_args=sub_ref_args) - sub_arg = sub_builder.get_args() - sub_args.extend(sub_arg) + sub_args.extend(sub_builder.get_args()) self.schema.props = [prop for prop in self.schema.props if prop not in removed] if isinstance(self.schema, CMDIdentityObjectSchema) and (not self._is_update_action or self.schema.action): diff --git a/src/aaz_dev/command/model/configuration/_content.py b/src/aaz_dev/command/model/configuration/_content.py index 3b8c2a95..db4a6fbc 100644 --- a/src/aaz_dev/command/model/configuration/_content.py +++ b/src/aaz_dev/command/model/configuration/_content.py @@ -63,8 +63,8 @@ def reformat(self, schema_cls_map, **kwargs): def register_cls(self, cls_register_map, **kwargs): _iter_over_schema_for_cls_register(self.schema, cls_register_map) -class CMDRequestBytes(Model): - """Used for Request Bytes Body""" +class CMDRequestBinary(Model): + """Used for Request Binary Body""" ref = CMDVariantField() diff --git a/src/aaz_dev/command/model/configuration/_http_request_body.py b/src/aaz_dev/command/model/configuration/_http_request_body.py index e5500fec..6adaae36 100644 --- a/src/aaz_dev/command/model/configuration/_http_request_body.py +++ b/src/aaz_dev/command/model/configuration/_http_request_body.py @@ -1,7 +1,7 @@ from schematics.models import Model from schematics.types import ModelType -from ._content import CMDRequestJson, CMDRequestBytes +from ._content import CMDRequestJson, CMDRequestBinary class CMDHttpRequestBody(Model): @@ -51,10 +51,10 @@ def reformat(self, **kwargs): def register_cls(self, **kwargs): self.json.register_cls(**kwargs) -class CMDHttpRequestBytesBody(CMDHttpRequestBody): +class CMDHttpRequestBinaryBody(CMDHttpRequestBody): POLYMORPHIC_KEY = "bytes" - bytes = ModelType(CMDRequestBytes, required=True) + bytes = ModelType(CMDRequestBinary, required=True) def generate_args(self, ref_args, var_prefix=None): return self.bytes.generate_args(ref_args=ref_args, var_prefix=var_prefix) diff --git a/src/aaz_dev/swagger/controller/command_generator.py b/src/aaz_dev/swagger/controller/command_generator.py index b5002626..99844b16 100644 --- a/src/aaz_dev/swagger/controller/command_generator.py +++ b/src/aaz_dev/swagger/controller/command_generator.py @@ -6,7 +6,7 @@ from command.model.configuration import CMDCommandGroup, CMDCommand, CMDHttpOperation, CMDHttpRequest, \ CMDSchemaDefault, CMDHttpResponseJsonBody, CMDArrayOutput, CMDJsonInstanceUpdateAction, \ CMDInstanceUpdateOperation, CMDRequestJson, DEFAULT_CONFIRMATION_PROMPT, CMDClsSchemaBase, CMDHttpResponse, \ - CMDResponseJson, CMDResource + CMDResponseJson, CMDResource, CMDHttpRequestBinaryBody from swagger.model.schema.cmd_builder import CMDBuilder from swagger.model.schema.fields import MutabilityEnum from swagger.model.schema.path_item import PathItem @@ -404,10 +404,13 @@ def create_draft_command_group(self, resource, delete_command.confirmation = DEFAULT_CONFIRMATION_PROMPT # add confirmation for delete command by default command_group.commands.append(delete_command) + skip_update = False if path_item.put is not None and 'put' in methods: cmd_builder = CMDBuilder(path=resource.path, method='put', mutability=MutabilityEnum.Create, parameterized_host=parameterized_host) op = self.generate_operation(cmd_builder, path_item, instance_var) + if isinstance(op.http.request.body, CMDHttpRequestBinaryBody): + skip_update = True create_command = self.generate_command(path_item, resource, instance_var, cmd_builder, op) command_group.commands.append(create_command) @@ -426,29 +429,30 @@ def create_draft_command_group(self, resource, command_group.commands.append(head_command) # update command - if update_by is None: - update_by_patch_command = None - update_by_generic_command = None - if path_item.patch is not None and 'patch' in methods: - cmd_builder = CMDBuilder(path=resource.path, method='patch', mutability=MutabilityEnum.Update, - parameterized_host=parameterized_host) - op = self.generate_operation(cmd_builder, path_item, instance_var) - update_by_patch_command = self.generate_command(path_item, resource, instance_var, cmd_builder, op) - if path_item.get is not None and path_item.put is not None and 'get' in methods and 'put' in methods: - cmd_builder = CMDBuilder(path=resource.path, - parameterized_host=parameterized_host) - get_op = self.generate_operation( - cmd_builder, path_item, instance_var, method='get', mutability=MutabilityEnum.Read) - put_op = self.generate_operation( - cmd_builder, path_item, instance_var, method='put', mutability=MutabilityEnum.Update) - update_by_generic_command = self.generate_generic_update_command(path_item, resource, instance_var, cmd_builder, get_op, put_op) - # generic update command first, patch update command after that - if update_by_generic_command: - command_group.commands.append(update_by_generic_command) - elif update_by_patch_command: - command_group.commands.append(update_by_patch_command) - else: - if update_by == 'GenericOnly': + if not skip_update: + if update_by is None: + update_by_patch_command = None + update_by_generic_command = None + if path_item.patch is not None and 'patch' in methods: + cmd_builder = CMDBuilder(path=resource.path, method='patch', mutability=MutabilityEnum.Update, + parameterized_host=parameterized_host) + op = self.generate_operation(cmd_builder, path_item, instance_var) + update_by_patch_command = self.generate_command(path_item, resource, instance_var, cmd_builder, op) + if path_item.get is not None and path_item.put is not None and 'get' in methods and 'put' in methods: + cmd_builder = CMDBuilder(path=resource.path, + parameterized_host=parameterized_host) + get_op = self.generate_operation( + cmd_builder, path_item, instance_var, method='get', mutability=MutabilityEnum.Read) + put_op = self.generate_operation( + cmd_builder, path_item, instance_var, method='put', mutability=MutabilityEnum.Update) + update_by_generic_command = self.generate_generic_update_command(path_item, resource, instance_var, + cmd_builder, get_op, put_op) + # generic update command first, patch update command after that + if update_by_generic_command: + command_group.commands.append(update_by_generic_command) + elif update_by_patch_command: + command_group.commands.append(update_by_patch_command) + elif update_by == 'GenericOnly': if path_item.get is None or path_item.put is None: raise exceptions.InvalidAPIUsage(f"Invalid update_by resource: resource needs to have 'get' and 'put' operations: '{resource}'") if 'get' not in methods or 'put' not in methods: diff --git a/src/aaz_dev/swagger/model/schema/operation.py b/src/aaz_dev/swagger/model/schema/operation.py index bc1e1634..1468a26e 100644 --- a/src/aaz_dev/swagger/model/schema/operation.py +++ b/src/aaz_dev/swagger/model/schema/operation.py @@ -5,7 +5,7 @@ from schematics.types import StringType, ModelType, ListType, DictType, BooleanType, PolyModelType from command.model.configuration import CMDHttpOperation, CMDHttpAction, CMDHttpRequest, CMDHttpRequestPath, \ - CMDHttpRequestQuery, CMDHttpRequestHeader, CMDHttpRequestJsonBody, CMDRequestJson, CMDRequestBytes, CMDHttpRequestBytesBody, CMDHttpOperationLongRunning + CMDHttpRequestQuery, CMDHttpRequestHeader, CMDHttpRequestJsonBody, CMDRequestJson, CMDRequestBinary, CMDHttpRequestBinaryBody, CMDHttpOperationLongRunning from swagger.utils import exceptions from swagger.utils.tools import swagger_resource_path_to_resource_id_template from .example_item import XmsExamplesField @@ -225,8 +225,8 @@ def to_cmd(self, builder, parent_parameters, host_path, **kwargs): if isinstance(model, CMDRequestJson): request.body = CMDHttpRequestJsonBody() request.body.json = model - elif isinstance(model, CMDRequestBytes): - request.body = CMDHttpRequestBytesBody() + elif isinstance(model, CMDRequestBinary): + request.body = CMDHttpRequestBinaryBody() request.body.bytes = model else: raise NotImplementedError() diff --git a/src/aaz_dev/swagger/model/schema/parameter.py b/src/aaz_dev/swagger/model/schema/parameter.py index 00c57929..ec50cf6b 100644 --- a/src/aaz_dev/swagger/model/schema/parameter.py +++ b/src/aaz_dev/swagger/model/schema/parameter.py @@ -1,7 +1,7 @@ import logging from command.model.configuration import CMDRequestJson, CMDBooleanSchema, CMDStringSchema, CMDObjectSchema, \ - CMDArraySchema, CMDFloatSchema, CMDIntegerSchema, CMDBinarySchema, CMDRequestBytes + CMDArraySchema, CMDFloatSchema, CMDIntegerSchema, CMDBinarySchema, CMDRequestBinary from schematics.models import Model from schematics.types import StringType, BooleanType, ModelType, PolyModelType, BaseType from swagger.utils import exceptions @@ -206,7 +206,7 @@ def to_cmd(self, builder, **kwargs): ) return None if isinstance(v, CMDBinarySchema): - model = CMDRequestBytes() + model = CMDRequestBinary() model.schema = v v.required = self.required elif isinstance(v, (