public void GenerateOperationSpec(TSObject operationSpec) { operationSpec.QuotedStringProperty("httpMethod", HttpMethod.ToString().ToUpper()); if (IsAbsoluteUrl) { operationSpec.QuotedStringProperty("baseUrl", CodeModelTS.SchemeHostAndPort); } string path = Path; if (!string.IsNullOrEmpty(path)) { operationSpec.QuotedStringProperty("path", path); } Parameter[] logicalParameters = LogicalParameters.ToArray(); AddParameterRefs(operationSpec, "urlParameters", logicalParameters.Where(p => p.Location == ParameterLocation.Path)); AddParameterRefs(operationSpec, "queryParameters", logicalParameters.Where(p => p.Location == ParameterLocation.Query)); AddParameterRefs(operationSpec, "headerParameters", logicalParameters.Where(p => p.Location == ParameterLocation.Header)); bool addContentTypeProperty = (!string.IsNullOrEmpty(RequestContentType) && RequestContentType != CodeModelTS.RequestContentType); if (Body != null) { operationSpec.ObjectProperty("requestBody", requestBodyObject => { GenerateRequestParameterPath(requestBodyObject, Body, GetParameterTransformations()); requestBodyObject.Property("mapper", requestBodyMapper => ClientModelExtensions.ConstructRequestBodyMapper(requestBodyMapper, Body)); }); } else { IEnumerable <Parameter> formDataParameters = logicalParameters.Where(p => p.Location == ParameterLocation.FormData); if (formDataParameters.Any()) { AddParameterRefs(operationSpec, "formDataParameters", formDataParameters); addContentTypeProperty = true; } } if (addContentTypeProperty) { operationSpec.QuotedStringProperty("contentType", RequestContentType); } operationSpec.ObjectProperty("responses", responses => { bool isXml = CodeModelTS.ShouldGenerateXmlSerialization; foreach (KeyValuePair <HttpStatusCode, Response> statusCodeResponsePair in Responses) { HttpStatusCode statusCode = statusCodeResponsePair.Key; Response response = statusCodeResponsePair.Value; responses.ObjectProperty(((int)statusCode).ToString(), responseObject => { if (response.Body != null) { responseObject.Property("bodyMapper", responseBodyMapper => ClientModelExtensions.ConstructResponseBodyMapper(responseBodyMapper, response, this)); } if (response.Headers != null) { responseObject.Property("headersMapper", responseHeadersMapper => responseHeadersMapper.Text($"Mappers.{response.Headers.Name}")); } }); } responses.ObjectProperty("default", defaultResponseObject => { Response defaultResponse = DefaultResponse; if (defaultResponse != null && defaultResponse.Body != null) { defaultResponseObject.Property("bodyMapper", responseBodyMapper => ClientModelExtensions.ConstructResponseBodyMapper(responseBodyMapper, defaultResponse, this)); } }); }); if (CodeModel.ShouldGenerateXmlSerialization) { operationSpec.BooleanProperty("isXML", true); } operationSpec.TextProperty("serializer", "serializer"); }
public static void ConstructMapper(TSObject mapper, IModelType type, string serializedName, IVariable parameter, bool isPageable, bool expandComposite, bool isXML, bool isCaseSensitive = true, string xmlName = null) { string defaultValue = null; bool isRequired = false; bool isConstant = false; bool isReadOnly = false; Dictionary <Constraint, string> constraints = null; var property = parameter as Property; if (parameter != null) { defaultValue = parameter.DefaultValue; isRequired = parameter.IsRequired; isConstant = parameter.IsConstant; constraints = parameter.Constraints; } string xmlPrefix = !isXML ? null : property?.XmlPrefix ?? type?.XmlPrefix; bool addXmlNameFromParameterValue = isXML && !string.IsNullOrEmpty(xmlName) && xmlName != serializedName; if (addXmlNameFromParameterValue) { if (!string.IsNullOrEmpty(xmlPrefix)) { xmlName = $"{xmlPrefix}:{xmlName}"; } mapper.QuotedStringProperty("xmlName", xmlName); } if (isXML && !string.IsNullOrEmpty(serializedName) && !string.IsNullOrEmpty(xmlPrefix)) { serializedName = $"{xmlPrefix}:{serializedName}"; } if (property != null) { isReadOnly = property.IsReadOnly; if (isXML) { if (property.XmlIsAttribute) { mapper.BooleanProperty("xmlIsAttribute", true); } if (property.XmlIsWrapped) { mapper.BooleanProperty("xmlIsWrapped", true); } string propertyXmlName = property.ModelType.XmlProperties?.Name ?? property.XmlName; if (!addXmlNameFromParameterValue && !string.IsNullOrEmpty(propertyXmlName)) { if (!string.IsNullOrEmpty(xmlPrefix)) { propertyXmlName = $"{xmlPrefix}:{propertyXmlName}"; } // For some reason we can't omit xmlName in this scenario if it is equal to // serializedName. It might have to do with whether or not xmlElementName // is present, but I'm not sure at this time. mapper.QuotedStringProperty("xmlName", propertyXmlName); } } } CompositeTypeTS composite = type as CompositeTypeTS; if (composite != null && composite.ContainsConstantProperties && (parameter != null && parameter.IsRequired)) { defaultValue = "{}"; } SequenceType sequence = type as SequenceType; if (sequence != null && isXML) { if (sequence.ElementXmlIsWrapped) { mapper.BooleanProperty("xmlElementIsWrapped", true); } string xmlElementName = sequence.ElementType.XmlProperties?.Name ?? sequence.ElementXmlName; if (!string.IsNullOrEmpty(xmlElementName)) { mapper.QuotedStringProperty("xmlElementName", xmlElementName); } } if (isRequired) { mapper.BooleanProperty("required", true); } if (parameter?.IsXNullable != null) { if (parameter.IsXNullable.Value) { mapper.BooleanProperty("nullable", true); } else { mapper.BooleanProperty("nullable", false); } } if (isReadOnly) { mapper.BooleanProperty("readOnly", true); } if (isConstant) { mapper.BooleanProperty("isConstant", true); } if (serializedName != null) { if (!isCaseSensitive) { serializedName = serializedName.ToLower(); } mapper.QuotedStringProperty("serializedName", serializedName); } if (!string.IsNullOrEmpty(defaultValue)) { mapper.TextProperty("defaultValue", defaultValue); } DictionaryType dictionary = type as DictionaryType; PrimaryType primary = type as PrimaryType; EnumType enumType = type as EnumType; void applyConstraints(TSObject obj) { bool useClientSideValidation = (bool)(Settings.Instance?.CustomSettings[CodeModelTS.ClientSideValidationSettingName] ?? false); if (useClientSideValidation && constraints != null && constraints.Any()) { obj.ObjectProperty("constraints", constraintsObject => { foreach (KeyValuePair <Constraint, string> constraintEntry in constraints) { Constraint constraint = constraintEntry.Key; string constraintValue = constraintEntry.Value; if (constraint == Constraint.Pattern) { constraintValue = CreateRegexPatternConstraintValue(constraintValue); } constraintsObject.TextProperty(constraint.ToString(), constraintValue); } }); } } // Apply header collection constraints only to dictionary values, not the dictionary itself string prefix = parameter?.Extensions?.GetValue <string>(SwaggerExtensions.HeaderCollectionPrefix); bool skipConstraints = !string.IsNullOrEmpty(prefix) && dictionary != null; if (!skipConstraints) { applyConstraints(mapper); } if (primary != null) { switch (primary.KnownPrimaryType) { case KnownPrimaryType.Base64Url: case KnownPrimaryType.Boolean: case KnownPrimaryType.ByteArray: case KnownPrimaryType.Date: case KnownPrimaryType.DateTime: case KnownPrimaryType.DateTimeRfc1123: case KnownPrimaryType.Object: case KnownPrimaryType.Stream: case KnownPrimaryType.String: case KnownPrimaryType.TimeSpan: case KnownPrimaryType.UnixTime: case KnownPrimaryType.Uuid: AddTypeProperty(mapper, primary.KnownPrimaryType.ToString()); break; case KnownPrimaryType.Int: case KnownPrimaryType.Long: case KnownPrimaryType.Decimal: case KnownPrimaryType.Double: AddTypeProperty(mapper, "Number"); break; default: throw new NotImplementedException($"{primary} is not a supported Type."); } } else if (enumType != null) { if (enumType.ModelAsString) { AddTypeProperty(mapper, "String"); } else { AddTypeProperty(mapper, "Enum", typeObject => { typeObject.ArrayProperty("allowedValues", allowedValues => { foreach (EnumValue enumValue in enumType.Values) { allowedValues.QuotedString(enumValue.SerializedName); } }); }); } } else if (sequence != null) { AddTypeProperty(mapper, "Sequence", typeObject => { typeObject.Property("element", element => { ConstructMapper(element, sequence.ElementType, null, null, false, false, isXML, isCaseSensitive); }); }); } else if (dictionary != null) { AddTypeProperty(mapper, "Dictionary", typeObject => { typeObject.ObjectProperty("value", dictionaryValue => { ConstructMapper(dictionaryValue, dictionary.ValueType, null, null, false, false, isXML, isCaseSensitive); applyConstraints(dictionaryValue); }); }); if (!string.IsNullOrEmpty(prefix)) { mapper.QuotedStringProperty("headerCollectionPrefix", prefix); } } else if (composite != null) { AddTypeProperty(mapper, "Composite", typeObject => { if (expandComposite) { if (composite.IsPolymorphic) { // Note: If the polymorphicDiscriminator has a dot in it's name then do not escape that dot for // it's serializedName, the way it is done for other properties. This makes it easy to find the // discriminator property from the responseBody during deserialization. Please, do not get confused // between the definition of the discriminator and the definition of the property that is // marked as the discriminator. typeObject.ObjectProperty("polymorphicDiscriminator", polymorphicDiscriminator => { polymorphicDiscriminator.QuotedStringProperty("serializedName", composite.PolymorphicDiscriminator); polymorphicDiscriminator.QuotedStringProperty("clientName", Singleton <CodeNamerTS> .Instance.GetPropertyName(composite.PolymorphicDiscriminator)); }); typeObject.QuotedStringProperty("uberParent", composite.Name); } else { CompositeType baseType = GetUberParent(composite); if (baseType.IsPolymorphic) { typeObject.TextProperty("polymorphicDiscriminator", baseType.Name + ".type.polymorphicDiscriminator"); typeObject.QuotedStringProperty("uberParent", baseType.Name); } } } typeObject.QuotedStringProperty("className", composite.Name); if (expandComposite) { typeObject.ObjectProperty("modelProperties", modelProperties => { if (composite.BaseModelType != null && composite.BaseModelType.ComposedProperties.Any()) { modelProperties.Spread(composite.BaseModelType.Name + ".type.modelProperties"); } foreach (Property prop in composite.Properties) { var serializedPropertyName = prop.SerializedName; if (isPageable) { PropertyInfo itemName = composite.GetType().GetProperty("ItemName"); PropertyInfo nextLinkName = composite.GetType().GetProperty("NextLinkName"); string nextLinkNameValue = (string)nextLinkName.GetValue(composite); if (itemName != null && ((string)itemName.GetValue(composite) == prop.Name)) { serializedPropertyName = ""; } if (prop.Name.Contains("nextLink") && nextLinkName != null && nextLinkNameValue == null) { continue; } } if (modelProperties.ContainsProperty(prop.Name)) { // throw new InvalidOperationException($"Mapper \"{serializedName}\" contains multiple modelProperties with the name \"{prop.Name}\"."); } else { modelProperties.Property(prop.Name, propertyValue => ConstructMapper(propertyValue, prop.ModelType, serializedPropertyName, prop, false, false, isXML, isCaseSensitive)); } } }); } if (composite.AdditionalProperties != null) { typeObject.ObjectProperty("additionalProperties", additionalProperties => { ConstructMapper(additionalProperties, composite.AdditionalProperties, serializedName: null, parameter: null, isPageable: false, expandComposite: false, isXML: isXML); }); } else { CompositeTypeTS baseType = composite; while (true) { baseType = (CompositeTypeTS)baseType.BaseModelType; if (baseType == null) { break; } else if (baseType.AdditionalProperties != null) { typeObject.TextProperty("additionalProperties", $"{baseType.Name}.type.additionalProperties"); break; } } } }); } else { throw new NotImplementedException($"{type} is not a supported Type."); } }