Skip to content

Commit

Permalink
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python
Browse files Browse the repository at this point in the history
…into fix_credential_ordering

* 'autorestv3' of https://github.com/Azure/autorest.python:
  LLC (#875)
  • Loading branch information
iscai-msft committed Jul 14, 2021
2 parents 549dd62 + bacf5cf commit b6e3891
Show file tree
Hide file tree
Showing 2,998 changed files with 298,026 additions and 122,882 deletions.
15 changes: 15 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Change Log

### 2021-xx-xx - 5.9.0

| Library | Min Version
| --------------- | -------
|`@autorest/core` | `3.4.5`
|`@autorest/modelerfour` | `4.19.1`
|`azure-core` dep of generated code | `1.16.0`
|`msrest` dep of generated code | `0.6.21`
|`azure-mgmt-core` dep of generated code (If generating mgmt plane code) | `1.2.1`

**New Features**

- We have added a **provisional** `rest` layer to our generated code. We have also added the following **provisional** flags listed [here](https://github.com/Azure/autorest.python/wiki/Generating-Low-Level-Client#generate-a-low-level-client). #875
- With this new release, we are also dropping support for Python 3.5 + async. #875

### 2021-07-13 - 5.8.4

min Autorest core version: 3.4.5
Expand Down
92 changes: 69 additions & 23 deletions autorest/codegen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,40 @@
from .models import build_schema
from .models.operation_group import OperationGroup
from .models.parameter import Parameter
from .models.parameter_list import ParameterList
from .models.credential_schema import AzureKeyCredentialSchema, TokenCredentialSchema
from .models.credential_schema_policy import get_credential_schema_policy, CredentialSchemaPolicy
from .models.parameter_list import GlobalParameterList
from .models.rest import Rest
from .serializers import JinjaSerializer
from .models.credential_schema_policy import CredentialSchemaPolicy, get_credential_schema_policy
from .models.credential_schema import AzureKeyCredentialSchema, TokenCredentialSchema


def _get_credential_default_policy_type_has_async_version(credential_default_policy_type: str) -> bool:
mapping = {
"BearerTokenCredentialPolicy": True,
"AzureKeyCredentialPolicy": False
}
return mapping[credential_default_policy_type]

def _build_convenience_layer(yaml_data: Dict[str, Any], code_model: CodeModel) -> None:
# Create operations
if code_model.show_operations and yaml_data.get("operationGroups"):
code_model.operation_groups = [
OperationGroup.from_yaml(code_model, op_group) for op_group in yaml_data["operationGroups"]
]
if code_model.show_models and yaml_data.get("schemas"):
# sets the enums property in our code_model variable, which will later be passed to EnumSerializer

code_model.add_inheritance_to_models()
code_model.sort_schemas()
code_model.link_operation_to_request_builder()
code_model.add_schema_link_to_operation()
code_model.generate_single_parameter_from_multiple_media_types_operation()

if code_model.show_operations:
# LRO operation
code_model.format_lro_operations()
code_model.remove_next_operation()

_LOGGER = logging.getLogger(__name__)
class CodeGenerator(Plugin):
@staticmethod
Expand Down Expand Up @@ -56,7 +84,35 @@ def _build_exceptions_set(yaml_data: List[Dict[str, Any]]) -> Set[int]:

def _create_code_model(self, yaml_data: Dict[str, Any], options: Dict[str, Union[str, bool]]) -> CodeModel:
# Create a code model
code_model = CodeModel(options)
low_level_client = self._autorestapi.get_boolean_value("low-level-client", False)
show_models = self._autorestapi.get_boolean_value(
"show-models",
not low_level_client
)
show_builders = self._autorestapi.get_boolean_value(
"show-builders",
low_level_client
)
show_operations = self._autorestapi.get_boolean_value(
"show-operations",
not low_level_client
)
show_send_request = self._autorestapi.get_boolean_value(
"show-send-request",
low_level_client
)
only_path_and_body_params_positional = self._autorestapi.get_boolean_value(
"only-path-and-body-params-positional",
low_level_client
)
code_model = CodeModel(
show_builders=show_builders,
show_models=show_models,
show_operations=show_operations,
show_send_request=show_send_request,
only_path_and_body_params_positional=only_path_and_body_params_positional,
options=options,
)
if code_model.options['credential']:
self._handle_default_authentication_policy(code_model)
code_model.module_name = yaml_data["info"]["python_title"]
Expand All @@ -66,9 +122,8 @@ def _create_code_model(self, yaml_data: Dict[str, Any], options: Dict[str, Union
)

# Global parameters
code_model.global_parameters = ParameterList(
code_model.global_parameters = GlobalParameterList(
[Parameter.from_yaml(param) for param in yaml_data.get("globalParameters", [])],
implementation="Client",
)

# Custom URL
Expand All @@ -79,17 +134,13 @@ def _create_code_model(self, yaml_data: Dict[str, Any], options: Dict[str, Union
# UGLY as hell.....
if yaml_data.get("operationGroups"):
first_req_of_first_op_of_first_grp = yaml_data["operationGroups"][0]["operations"][0]["requests"][0]
code_model.custom_base_url = first_req_of_first_op_of_first_grp["protocol"]["http"]["uri"]
code_model.service_client.custom_base_url = (
first_req_of_first_op_of_first_grp["protocol"]["http"]["uri"]
)
else:
for host in dollar_host:
code_model.global_parameters.remove(host)
code_model.base_url = dollar_host[0].yaml_data["clientDefaultValue"]

# Create operations
if yaml_data.get("operationGroups"):
code_model.operation_groups = [
OperationGroup.from_yaml(code_model, op_group) for op_group in yaml_data["operationGroups"]
]
code_model.service_client.base_url = dollar_host[0].yaml_data["clientDefaultValue"]

# Get my namespace
namespace = self._autorestapi.get_value("namespace")
Expand All @@ -98,23 +149,17 @@ def _create_code_model(self, yaml_data: Dict[str, Any], options: Dict[str, Union
namespace = yaml_data["info"]["python_title"]
code_model.namespace = namespace

code_model.rest = Rest.from_yaml(yaml_data, code_model=code_model)
if yaml_data.get("schemas"):
exceptions_set = CodeGenerator._build_exceptions_set(yaml_data=yaml_data["operationGroups"])

for type_list in yaml_data["schemas"].values():
for schema in type_list:
build_schema(yaml_data=schema, exceptions_set=exceptions_set, code_model=code_model)
# sets the enums property in our code_model variable, which will later be passed to EnumSerializer

code_model.add_inheritance_to_models()
code_model.sort_schemas()
code_model.add_schema_link_to_operation()
code_model.add_schema_link_to_request_builder()
code_model.add_schema_link_to_global_parameters()
code_model.generate_single_parameter_from_multiple_media_types()

# LRO operation
code_model.format_lro_operations()
code_model.remove_next_operation()
_build_convenience_layer(yaml_data=yaml_data, code_model=code_model)

if options["credential"]:
code_model.add_credential_global_parameter()
Expand Down Expand Up @@ -230,6 +275,7 @@ def _build_code_model_options(self) -> Dict[str, Any]:
"client_side_validation": self._autorestapi.get_boolean_value("client-side-validation", False),
"tracing": self._autorestapi.get_boolean_value("trace", False),
"multiapi": self._autorestapi.get_boolean_value("multiapi", False),
"polymorphic_examples": self._autorestapi.get_value("polymorphic-examples") or 5,
}

if options["basic_setup_py"] and not options["package_version"]:
Expand Down
39 changes: 34 additions & 5 deletions autorest/codegen/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Dict
from typing import Any, Dict, TypeVar, Union
from .base_model import BaseModel
from .code_model import CodeModel
from .credential_schema import AzureKeyCredentialSchema, TokenCredentialSchema
Expand All @@ -22,8 +22,13 @@
from .property import Property
from .operation_group import OperationGroup
from .schema_response import SchemaResponse
from .parameter_list import ParameterList

from .parameter_list import GlobalParameterList, ParameterList
from .request_builder_parameter_list import RequestBuilderParameterList
from .request_builder import RequestBuilder
from .base_builder import BaseBuilder
from .lro_paging_operation import LROPagingOperation
from .request_builder_parameter import RequestBuilderParameter
from .schema_request import SchemaRequest

__all__ = [
"AzureKeyCredentialSchema",
Expand All @@ -47,8 +52,13 @@
"ParameterList",
"OperationGroup",
"Property",
"RequestBuilder",
"SchemaResponse",
"TokenCredentialSchema",
"LROPagingOperation",
"BaseBuilder",
"SchemaRequest",
"RequestBuilderParameter",
]

def _generate_as_object_schema(yaml_data: Dict[str, Any]) -> bool:
Expand Down Expand Up @@ -90,7 +100,7 @@ def build_schema(yaml_data: Dict[str, Any], **kwargs) -> BaseSchema:
schema = DictionarySchema.from_yaml(namespace=namespace, yaml_data=yaml_data, **kwargs)
code_model.primitives[yaml_id] = schema

elif schema_type in ["object", "and", "group"]:
elif schema_type in ["object", "and", "group", "any-object"]:
if _generate_as_object_schema(yaml_data):
# To avoid infinite loop, create the right instance in memory,
# put it in the index, and then parse the object.
Expand All @@ -100,9 +110,28 @@ def build_schema(yaml_data: Dict[str, Any], **kwargs) -> BaseSchema:
else:
schema = AnySchema.from_yaml(namespace=namespace, yaml_data=yaml_data)
code_model.primitives[yaml_id] = schema

else:
schema = get_primitive_schema(namespace=namespace, yaml_data=yaml_data)
code_model.primitives[yaml_id] = schema

return schema

BuilderType = TypeVar(
"BuilderType",
bound=Union[
RequestBuilder,
Operation,
LROPagingOperation,
LROOperation,
PagingOperation,
]
)

ParameterListType = TypeVar(
"ParameterListType",
bound=Union[
ParameterList,
GlobalParameterList,
RequestBuilderParameterList,
],
)
96 changes: 96 additions & 0 deletions autorest/codegen/models/base_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
from abc import abstractmethod
from .base_model import BaseModel
from .schema_response import SchemaResponse

if TYPE_CHECKING:
from . import ParameterListType


_M4_HEADER_PARAMETERS = ["content_type", "accept"]

def get_converted_parameters(yaml_data: Dict[str, Any], parameter_converter: Callable):
multiple_requests = len(yaml_data["requests"]) > 1

multiple_media_type_parameters = []
parameters = [parameter_converter(yaml) for yaml in yaml_data.get("parameters", [])]

for request in yaml_data["requests"]:
for yaml in request.get("parameters", []):
parameter = parameter_converter(yaml)
name = yaml["language"]["python"]["name"]
if name in _M4_HEADER_PARAMETERS:
parameters.append(parameter)
elif multiple_requests:
parameter.has_multiple_media_types = True
multiple_media_type_parameters.append(parameter)
else:
parameters.append(parameter)

if multiple_media_type_parameters:
body_parameters_name_set = set(
p.serialized_name for p in multiple_media_type_parameters
)
if len(body_parameters_name_set) > 1:
raise ValueError(
f"The body parameter with multiple media types has different names: {body_parameters_name_set}"
)


parameters_index = {id(parameter.yaml_data): parameter for parameter in parameters}

# Need to connect the groupBy and originalParameter
for parameter in parameters:
parameter_grouped_by_id = id(parameter.grouped_by)
if parameter_grouped_by_id in parameters_index:
parameter.grouped_by = parameters_index[parameter_grouped_by_id]

parameter_original_id = id(parameter.original_parameter)
if parameter_original_id in parameters_index:
parameter.original_parameter = parameters_index[parameter_original_id]

return parameters, multiple_media_type_parameters

class BaseBuilder(BaseModel):
"""Base class for Operations and Request Builders"""

def __init__(
self,
yaml_data: Dict[str, Any],
name: str,
description: str,
parameters: "ParameterListType",
responses: Optional[List[SchemaResponse]] = None,
summary: Optional[str] = None,
) -> None:
super().__init__(yaml_data=yaml_data)
self.name = name
self.description = description
self.parameters = parameters
self.responses = responses or []
self.summary = summary

@property
def default_content_type_declaration(self) -> str:
return f'"{self.parameters.default_content_type}"'

def get_response_from_status(self, status_code: int) -> SchemaResponse:
for response in self.responses:
if status_code in response.status_codes:
return response
raise ValueError(f"Incorrect status code {status_code}, operation {self.name}")

@property
def success_status_code(self) -> List[Union[str, int]]:
"""The list of all successfull status code."""
return [code for response in self.responses for code in response.status_codes if code != "default"]

@staticmethod
@abstractmethod
def get_parameter_converter() -> Callable:
...
13 changes: 11 additions & 2 deletions autorest/codegen/models/base_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ def has_xml_serialization_ctxt(self) -> bool:
return bool(self.xml_metadata)

def xml_serialization_ctxt(self) -> Optional[str]:
"""Return the serialization context in case this schema is used in an operation.
"""
"""Return the serialization context in case this schema is used in an operation."""
attrs_list = []
if self.xml_metadata.get("name"):
attrs_list.append(f"'name': '{self.xml_metadata['name']}'")
Expand Down Expand Up @@ -127,3 +126,13 @@ def validation_map(self) -> Optional[Dict[str, Union[bool, int, str]]]: # pylin
@property
def serialization_constraints(self) -> Optional[List[str]]: # pylint: disable=no-self-use
return None

@abstractmethod
def get_json_template_representation(self, **kwargs: Any) -> Any:
"""Template of what this schema would look like as JSON input"""
...

@abstractmethod
def get_files_template_representation(self, **kwargs: Any) -> Any:
"""Template of what this schema would look like as files input"""
...
Loading

0 comments on commit b6e3891

Please sign in to comment.