/// <summary> /// Adds the parameter groups to operation parameters. /// </summary> /// <param name="codeModelient"></param> public static void AddParameterGroups(CodeModel codeModel) { if (codeModel == null) { throw new ArgumentNullException("codeModel"); } HashSet<CompositeType> generatedParameterGroups = new HashSet<CompositeType>(); foreach (Method method in codeModel.Methods) { //Copy out flattening transformations as they should be the last List<ParameterTransformation> flatteningTransformations = method.InputParameterTransformation.ToList(); method.InputParameterTransformation.Clear(); //This group name is normalized by each languages code generator later, so it need not happen here. IEnumerable<ParameterGroup> parameterGroups = ExtractParameterGroups(method); List<Parameter> parametersToAddToMethod = new List<Parameter>(); List<Parameter> parametersToRemoveFromMethod = new List<Parameter>(); foreach (ParameterGroup parameterGroup in parameterGroups) { CompositeType parameterGroupType = generatedParameterGroups.FirstOrDefault(item => item.Name.RawValue == parameterGroup.Name); if (parameterGroupType == null) { IEnumerable<Method> methodsWhichUseGroup = GetMethodsUsingParameterGroup(codeModel.Methods, parameterGroup); parameterGroupType = New<CompositeType>(parameterGroup.Name,new { Documentation = GenerateParameterGroupModelText(methodsWhichUseGroup) }); generatedParameterGroups.Add(parameterGroupType); //Add to the service client codeModel.Add(parameterGroupType); } foreach (Property property in parameterGroup.ParameterMapping.Keys) { Property matchingProperty = parameterGroupType.Properties.FirstOrDefault( item => item.Name.RawValue == property.Name.RawValue && item.IsReadOnly == property.IsReadOnly && item.DefaultValue .RawValue== property.DefaultValue.RawValue && item.SerializedName.RawValue == property.SerializedName.RawValue); if (matchingProperty == null) { parameterGroupType.Add(property); } } bool isGroupParameterRequired = parameterGroupType.Properties.Any(p => p.IsRequired); //Create the new parameter object based on the parameter group type Parameter newParameter = New<Parameter>(new { Name = parameterGroup.Name, IsRequired = isGroupParameterRequired, Location = ParameterLocation.None, SerializedName = string.Empty, ModelType = parameterGroupType, Documentation = "Additional parameters for the operation" }); parametersToAddToMethod.Add(newParameter); //Link the grouped parameters to their parent, and remove them from the method parameters foreach (Property property in parameterGroup.ParameterMapping.Keys) { Parameter p = parameterGroup.ParameterMapping[property]; var parameterTransformation = new ParameterTransformation { OutputParameter = p }; parameterTransformation.ParameterMappings.Add(new ParameterMapping { InputParameter = newParameter, InputParameterProperty = property.GetClientName() }); method.InputParameterTransformation.Add(parameterTransformation); parametersToRemoveFromMethod.Add(p); } } method.Remove(p => parametersToRemoveFromMethod.Contains(p)); method.AddRange(parametersToAddToMethod); // Copy back flattening transformations if any flatteningTransformations.ForEach(t => method.InputParameterTransformation.Add(t)); } }
private static Parameter CreateParameterFromGrouping(IEnumerable<ParameterTransformation> grouping, Method method, CodeModel codeModel) { var properties = new List<Property>(); string parameterGroupName = null; foreach (var parameter in grouping.Select(g => g.OutputParameter)) { Newtonsoft.Json.Linq.JContainer extensionObject = parameter.Extensions[ParameterGroupExtension] as Newtonsoft.Json.Linq.JContainer; string specifiedGroupName = extensionObject.Value<string>("name"); if (specifiedGroupName == null) { string postfix = extensionObject.Value<string>("postfix") ?? "Parameters"; parameterGroupName = method.Group + "-" + method.Name + "-" + postfix; } else { parameterGroupName = specifiedGroupName; } Property groupProperty = New<Property>(new { IsReadOnly = false, //Since these properties are used as parameters they are never read only Name = parameter.Name, IsRequired = parameter.IsRequired, DefaultValue = parameter.DefaultValue, //Constraints = parameter.Constraints, Omit these since we don't want to perform parameter validation Documentation = parameter.Documentation, ModelType = parameter.ModelType, SerializedName = default(string) //Parameter is never serialized directly }); properties.Add(groupProperty); } var parameterGroupType = New <CompositeType>(parameterGroupName, new { Documentation = "Additional parameters for the " + method.Name + " operation." }); //Add to the service client codeModel.Add(parameterGroupType); foreach (Property property in properties) { parameterGroupType.Add(property); } bool isGroupParameterRequired = parameterGroupType.Properties.Any(p => p.IsRequired); //Create the new parameter object based on the parameter group type return New<Parameter>(new { Name = parameterGroupName, IsRequired = isGroupParameterRequired, Location = ParameterLocation.None, SerializedName = string.Empty, ModelType = parameterGroupType, Documentation = "Additional parameters for the operation" }); }
/// <summary> /// Adds ListNext() method for each List method with x-ms-pageable extension. /// </summary> /// <param name="codeModelient"></param> /// <param name="codeNamer"></param> public static void AddPageableMethod(CodeModel codeModel) { if (codeModel == null) { throw new ArgumentNullException("codeModel"); } foreach (var method in codeModel.Methods.Distinct(each => each.Group + each.Name).ToArray()) { if (method.Extensions.ContainsKey(PageableExtension)) { var pageableExtension = JsonConvert.DeserializeObject<PageableExtension>(method.Extensions[PageableExtension].ToString()); if (string.IsNullOrWhiteSpace(pageableExtension.NextLinkName)) { continue; } Method nextLinkMethod = null; if (!string.IsNullOrEmpty(pageableExtension.OperationName)) { nextLinkMethod = codeModel.Methods.FirstOrDefault(m => pageableExtension.OperationName.EqualsIgnoreCase(m.SerializedName)); if (nextLinkMethod != null) { nextLinkMethod.Extensions["nextLinkMethod"] = true; method.Extensions["nextMethodName"] = nextLinkMethod.Name; method.Extensions["nextMethodGroup"] = nextLinkMethod.Group; } } if (nextLinkMethod == null) { nextLinkMethod = Duplicate<Method>(method); if (!string.IsNullOrEmpty(pageableExtension.OperationName)) { nextLinkMethod.Name = CodeNamer.Instance.GetMethodName(SwaggerModeler.GetMethodName( new Operation { OperationId = pageableExtension.OperationName })); nextLinkMethod.Group = CodeNamer.Instance.GetMethodGroupName(SwaggerModeler.GetMethodGroup( new Operation { OperationId = pageableExtension.OperationName })); } else { nextLinkMethod.Name = nextLinkMethod.Name + "Next"; } method.Extensions["nextMethodName"] = nextLinkMethod.Name; method.Extensions["nextMethodGroup"] = nextLinkMethod.Group; nextLinkMethod.Extensions["nextLinkMethod"] = true; nextLinkMethod.ClearParameters(); nextLinkMethod.Url = "{nextLink}"; nextLinkMethod.IsAbsoluteUrl = true; var nextLinkParameter = New<Parameter>(new { Name = "nextPageLink", SerializedName = "nextLink", ModelType = New<PrimaryType>(KnownPrimaryType.String), Documentation = "The NextLink from the previous successful call to List operation.", IsRequired = true, Location = ParameterLocation.Path }); nextLinkParameter.Extensions[SkipUrlEncodingExtension] = true; nextLinkMethod.Add(nextLinkParameter); // Need copy all the header parameters from List method to ListNext method foreach (var param in method.Parameters.Where(p => p.Location == ParameterLocation.Header)) { nextLinkMethod.Add(Duplicate(param)); } // Copy all grouped parameters that only contain header parameters nextLinkMethod.InputParameterTransformation.Clear(); method.InputParameterTransformation.GroupBy(t => t.ParameterMappings[0].InputParameter) .ForEach(grouping => { if (grouping.All(t => t.OutputParameter.Location == ParameterLocation.Header)) { // All grouped properties were header parameters, reuse data type nextLinkMethod.Add(grouping.Key); grouping.ForEach(t => nextLinkMethod.InputParameterTransformation.Add(t)); } else if (grouping.Any(t => t.OutputParameter.Location == ParameterLocation.Header)) { // Some grouped properties were header parameters, creating new data types var headerGrouping = grouping.Where(t => t.OutputParameter.Location == ParameterLocation.Header); headerGrouping.ForEach(t => nextLinkMethod.InputParameterTransformation.Add((ParameterTransformation) t.Clone())); var newGroupingParam = CreateParameterFromGrouping(headerGrouping, nextLinkMethod, codeModel); nextLinkMethod.Add(newGroupingParam); //grouping.Key.Name = newGroupingParam.Name; // var inputParameter = (Parameter) nextLinkMethod.InputParameterTransformation.First().ParameterMappings[0].InputParameter.Clone(); var inputParameter = Duplicate(nextLinkMethod.InputParameterTransformation.First().ParameterMappings[0].InputParameter); inputParameter.Name = CodeNamer.Instance.GetParameterName(newGroupingParam.Name); inputParameter.IsRequired = newGroupingParam.IsRequired; nextLinkMethod.InputParameterTransformation.ForEach(t => t.ParameterMappings[0].InputParameter = inputParameter); } }); codeModel.Add( nextLinkMethod); } } } }
/// <summary> /// Creates azure specific properties. /// </summary> /// <param name="codeModelient"></param> public static void AddAzureProperties(CodeModel codeModel) { if (codeModel == null) { throw new ArgumentNullException("codeModel"); } var apiVersion = codeModel.Properties .FirstOrDefault(p => ApiVersion.EqualsIgnoreCase(p.SerializedName)); if (apiVersion != null) { apiVersion.DefaultValue = codeModel.ApiVersion; apiVersion.IsReadOnly = true; apiVersion.IsRequired = false; } var subscriptionId = codeModel.Properties.FirstOrDefault( p => p.Name.EqualsIgnoreCase( "subscriptionId")); if (subscriptionId != null) { subscriptionId.IsRequired = true; } var acceptLanguage = codeModel.Properties .FirstOrDefault(p => AcceptLanguage.EqualsIgnoreCase(p.SerializedName)); if (acceptLanguage == null) { acceptLanguage = New<Property>(new { Name = AcceptLanguage, Documentation = "Gets or sets the preferred language for the response.", SerializedName = AcceptLanguage, DefaultValue = "en-US" }); codeModel.Add( acceptLanguage); } acceptLanguage.IsReadOnly = false; acceptLanguage.IsRequired = false; acceptLanguage.ModelType = New<PrimaryType>(KnownPrimaryType.String); codeModel.Methods .Where(m => !m.Parameters.Any(p => AcceptLanguage.EqualsIgnoreCase(p.SerializedName))) .ForEach(m2 => m2.Add(New<Parameter>(new { ClientProperty = acceptLanguage, Location = ParameterLocation.Header }).LoadFrom(acceptLanguage))); codeModel.Insert(New<Property>(new { Name = "Credentials", SerializedName = "credentials", ModelType = New<PrimaryType>(KnownPrimaryType.Credentials), IsRequired = true, IsReadOnly = true, Documentation = "Credentials needed for the client to connect to Azure." })); codeModel.Add(New<Property>(new { Name = "LongRunningOperationRetryTimeout", SerializedName = "longRunningOperationRetryTimeout", ModelType = New<PrimaryType>(KnownPrimaryType.Int), Documentation = "Gets or sets the retry timeout in seconds for Long Running Operations. Default value is 30.", DefaultValue = "30" })); codeModel.Add( New<Property>(new { Name = "GenerateClientRequestId", SerializedName = "generateClientRequestId", ModelType = New<PrimaryType>(KnownPrimaryType.Boolean), Documentation = "When set to true a unique x-ms-client-request-id value is generated and included in each request. Default is true.", DefaultValue = "true" })); }
/// <summary> /// Creates long running operation methods. /// </summary> /// <param name="codeModelient"></param> public static void AddLongRunningOperations(CodeModel codeModel) { if (codeModel == null) { throw new ArgumentNullException("codeModel"); } foreach( var method in codeModel.Methods.Where( each => each.Extensions.ContainsKey(LongRunningExtension)).Distinct( each => each.Group + each.Name ).ToArray()) { var isLongRunning = method.Extensions[LongRunningExtension]; if (true == isLongRunning as bool?) { // copy the method var m = Duplicate(method); // change the name, remove the extension. m.Name = "Begin" + m.Name.ToPascalCase(); m.Extensions.Remove(LongRunningExtension); codeModel.Add(m); } } }
/// <summary> /// Set default response to CloudError if not defined explicitly. /// </summary> /// <param name="codeModelient"></param> public static void SetDefaultResponses(CodeModel codeModel) { if (codeModel == null) { throw new ArgumentNullException("codeModel"); } // Create CloudError if not already defined CompositeType cloudError = codeModel.ModelTypes.FirstOrDefault(c => c.Name.EqualsIgnoreCase("cloudError")); if (cloudError == null) { cloudError = New<CompositeType>( new { Name = "cloudError", SerializedName = "cloudError" }); cloudError.Extensions[ExternalExtension] = true; codeModel.Add(cloudError); } // Set default response if not defined explicitly foreach (var method in codeModel.Methods) { if (method.DefaultResponse.Body == null && method.ReturnType.Body != null) { method.DefaultResponse = new Response(cloudError, method.ReturnType.Headers); } } }