public override void CreateModelTypeForOptionalClientProperties(CodeModelTS cm) { List <string> predefinedOptionalProperties = new List <string>() { "requestOptions", "filters", "noRetryPolicy", "apiVersion", "acceptLanguage", "longRunningOperationRetryTimeout", "generateClientRequestId", "rpRegistrationRetryTimeout" }; var optionalProperitesOnClient = cm.Properties.Where( p => (!p.IsRequired || p.IsRequired && !string.IsNullOrEmpty(p.DefaultValue)) && !p.IsConstant && !predefinedOptionalProperties.Contains(p.Name)); if (optionalProperitesOnClient.Count() > 0) { string modelTypeName = cm.Name + "Options"; var modelType = new CompositeTypeTS(modelTypeName); modelType.BaseModelType = New <CompositeType>(new { Name = "AzureServiceClientOptions", SerializedName = "AzureServiceClientOptions" }); // We could end up having a property that is required but has a default value based on the above condition. If so then make it optional. optionalProperitesOnClient.Where(p => p.IsRequired && !string.IsNullOrEmpty(p.DefaultValue)).ForEach(prop => prop.IsRequired = false); modelType.AddRange(optionalProperitesOnClient); var modelTypeFound = cm.ModelTypes.FirstOrDefault(m => m.Name.EqualsIgnoreCase(modelTypeName)); if (modelTypeFound != null) { cm.Remove(modelTypeFound); } cm.Add(modelType); cm.OptionalParameterTypeForClientConstructor = "Models." + modelTypeName; } }
/// <summary> /// Finds the UberParent for a given Composite type. /// An uber parent is the closest parent that defines the polymorphicDiscriminator /// </summary> /// <param name="composite">The composite type to find the uberParent for</param> /// <returns>The uberParent or itself if it has no uberParent</returns> private static CompositeType GetUberParent(CompositeTypeTS composite) { CompositeType uberParent = composite; while (uberParent.BaseModelType != null && string.IsNullOrWhiteSpace(uberParent.PolymorphicDiscriminator)) { uberParent = uberParent.BaseModelType; } return(uberParent); }
public static CompositeTypeTS CompositeType(string name = null, IEnumerable <PropertyTS> properties = null) { CompositeTypeTS compositeType = new CompositeTypeTS(name); if (properties != null) { foreach (PropertyTS property in properties) { compositeType.Add(property); } } return(compositeType); }
public virtual void CreateModelTypeForOptionalClientProperties(CodeModelTS cm) { List <string> predefinedOptionalProperties = new List <string>() { "requestOptions", "filters", "noRetryPolicy" }; var optionalProperitesOnClient = cm.Properties.Where( p => (!p.IsRequired || p.IsRequired && !string.IsNullOrEmpty(p.DefaultValue)) && !p.IsConstant && !predefinedOptionalProperties.Contains(p.Name)); if (optionalProperitesOnClient.Count() > 0) { string modelTypeName = cm.Name + "Options"; var modelType = new CompositeTypeTS(modelTypeName); modelType.BaseModelType = New <CompositeType>(new { Name = "ServiceClientOptions", SerializedName = "ServiceClientOptions" }); // We could end up having a property that is required but has a default value based on the above condition. If so then make it optional. optionalProperitesOnClient.Where(p => p.IsRequired && !string.IsNullOrEmpty(p.DefaultValue)).ForEach(prop => prop.IsRequired = false); modelType.AddRange(optionalProperitesOnClient); cm.Add(modelType); cm.OptionalParameterTypeForClientConstructor = "Models." + modelTypeName; } }
public static CompositeTypeTS CompositeType(string name = null, IEnumerable <PropertyTS> properties = null, string xmlPrefix = null, string xmlName = null) { CompositeTypeTS compositeType = new CompositeTypeTS(name); if (!string.IsNullOrEmpty(xmlPrefix)) { compositeType.XmlProperties = new XmlProperties { Name = xmlName, Prefix = xmlPrefix, }; } if (properties != null) { foreach (PropertyTS property in properties) { compositeType.Add(property); } } return(compositeType); }
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 = composite; while (baseType.BaseModelType != null) { baseType = baseType.BaseModelType; } 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."); } }
/// <summary> /// Return the TypeScript type (as a string) for specified type. /// </summary> /// <param name="type">IType to query</param> /// <param name="inModelsModule">Pass true if generating the code for the models module, thus model types don't need a "models." prefix</param> /// <returns>TypeScript type string for type</returns> public static string TSType(this IModelType type, bool inModelsModule) { CompositeTypeTS composite = type as CompositeTypeTS; SequenceType sequence = type as SequenceType; DictionaryType dictionary = type as DictionaryType; PrimaryType primary = type as PrimaryType; EnumType enumType = type as EnumType; string tsType; if (primary != null) { tsType = primary.PrimaryTSType(); } else if (enumType != null) { string enumName = enumType.DeclarationName; if (inModelsModule || enumName.Contains('.') || enumName == "string") { tsType = enumName; } else { tsType = "Models." + enumName.ToPascalCase(); } } else if (composite != null) { // ServiceClientCredentials starts with the "msRest." prefix, so strip msRest./msRestAzure. as we import those // types with no module prefix needed var compositeName = composite.UnionTypeName; if (compositeName.StartsWith("msRest.") || compositeName.StartsWith("msRestAzure.")) { tsType = compositeName.Substring(compositeName.IndexOf('.') + 1); } else if (inModelsModule || compositeName.Contains('.')) { tsType = compositeName; } else { tsType = "Models." + compositeName; } } else if (sequence != null) { if (sequence.IsSequenceContainingDateKind()) { tsType = sequence.ElementType.TSType(inModelsModule) + "[]" + " | string[]"; } else { tsType = sequence.ElementType.TSType(inModelsModule) + "[]"; } } else if (dictionary != null) { if (dictionary.IsDictionaryContainingDateKind()) //then provide a union of Date and string { tsType = "{ [propertyName: string]: " + dictionary.ValueType.TSType(inModelsModule) + " }" + " | { [propertyName: string]: string }"; } else { tsType = "{ [propertyName: string]: " + dictionary.ValueType.TSType(inModelsModule) + " }"; } } else { throw new NotImplementedException($"Type '{type}' not implemented"); } return(tsType); }