Example #1
0
 /// <summary>
 /// Creates an instance of <see cref="SchemaReferenceRegistry"/>.
 /// </summary>
 /// <param name="schemaGenerationSettings">The schema generation settings.</param>
 /// <param name="propertyDescriptionMap">The property description map.</param>
 public SchemaReferenceRegistry(SchemaGenerationSettings schemaGenerationSettings,
                                Dictionary <string, string> propertyDescriptionMap)
 {
     _schemaGenerationSettings = schemaGenerationSettings
                                 ?? throw new ArgumentNullException(nameof(schemaGenerationSettings));
     _propertyNameResolver   = schemaGenerationSettings.PropertyNameResolver;
     _propertyDescriptionMap = propertyDescriptionMap
                               ?? throw new ArgumentNullException(nameof(propertyDescriptionMap));
 }
Example #2
0
 public ServiceConfigurator(IPropertyNameResolver propertyNameResolver)
 {
     _propertyNameResolver = propertyNameResolver;
 }
        /// <summary>
        /// Finds the existing reference object based on the key from the input or creates a new one.
        /// </summary>
        /// <returns>The existing or created reference object.</returns>
        internal override OpenApiSchema FindOrAddReference(Type input)
        {
            // Return empty schema when the type does not have a name.
            // This can occur, for example, when a generic type without the generic argument specified
            // is passed in.
            if (input == null || input.FullName == null)
            {
                return(new OpenApiSchema());
            }

            var key = GetKey(input);

            // If the schema already exists in the References, simply return.
            if (References.ContainsKey(key))
            {
                return(new OpenApiSchema
                {
                    Reference = new OpenApiReference
                    {
                        Id = key,
                        Type = ReferenceType.Schema
                    }
                });
            }

            try
            {
                // There are multiple cases for input types that should be handled differently to match the OpenAPI spec.
                //
                // 1. Simple Type
                // 2. Enum Type
                // 3. Dictionary Type
                // 4. Enumerable Type
                // 5. Object Type
                var schema = new OpenApiSchema();

                if (input.IsSimple())
                {
                    schema = input.MapToOpenApiSchema();

                    // Certain simple types yield more specific information.
                    if (input == typeof(char))
                    {
                        schema.MinLength = 1;
                        schema.MaxLength = 1;
                    }
                    else if (input == typeof(Guid))
                    {
                        schema.Example = new OpenApiString(Guid.Empty.ToString());
                    }

                    return(schema);
                }

                if (input.IsEnum)
                {
                    schema.Type = "string";
                    foreach (var name in Enum.GetNames(input))
                    {
                        schema.Enum.Add(new OpenApiString(name));
                    }

                    return(schema);
                }

                if (input.IsDictionary())
                {
                    schema.Type = "object";
                    schema.AdditionalProperties = FindOrAddReference(input.GetGenericArguments()[1]);

                    return(schema);
                }

                if (input.IsEnumerable())
                {
                    schema.Type = "array";

                    schema.Items = FindOrAddReference(input.GetEnumerableItemType());

                    return(schema);
                }

                schema.Type = "object";

                // Note this assignment is necessary to allow self-referencing type to finish
                // without causing stack overflow.
                // We can also assume that the schema is an object type at this point.
                References[key] = schema;

                var propertyNameDeclaringTypeMap = new Dictionary <string, Type>();
                var typeAttributes = input.GetCustomAttributes(false);

                foreach (var typeAttribute in typeAttributes)
                {
                    if (typeAttribute.GetType().FullName == "Newtonsoft.Json.JsonObjectAttribute")
                    {
                        var type = typeAttribute.GetType();
                        var namingStrategyInfo = type.GetProperty("NamingStrategyType");
                        if (namingStrategyInfo != null)
                        {
                            var namingStrategyValue = namingStrategyInfo.GetValue(typeAttribute, null);

                            if (namingStrategyValue?.ToString()
                                == "Newtonsoft.Json.Serialization.CamelCaseNamingStrategy")
                            {
                                _propertyNameResolver = new CamelCasePropertyNameResolver();
                            }
                        }
                    }
                }

                foreach (var propertyInfo in input.GetProperties())
                {
                    var ignoreProperty = false;

                    var innerSchema = FindOrAddReference(propertyInfo.PropertyType);

                    var propertyName = _propertyNameResolver.ResolvePropertyName(propertyInfo);

                    var attributes = propertyInfo.GetCustomAttributes(false);

                    foreach (var attribute in attributes)
                    {
                        if (attribute.GetType().FullName == "Newtonsoft.Json.JsonPropertyAttribute")
                        {
                            var type = attribute.GetType();
                            var requiredPropertyInfo = type.GetProperty("Required");

                            if (requiredPropertyInfo != null)
                            {
                                var requiredValue = Enum.GetName(
                                    requiredPropertyInfo.PropertyType,
                                    requiredPropertyInfo.GetValue(attribute, null));

                                if (requiredValue == "Always")
                                {
                                    schema.Required.Add(propertyName);
                                }
                            }
                        }

                        if (attribute.GetType().FullName == "Newtonsoft.Json.JsonIgnoreAttribute")
                        {
                            ignoreProperty = true;
                        }
                    }

                    if (ignoreProperty)
                    {
                        continue;
                    }

                    var propertyDeclaringType = propertyInfo.DeclaringType;

                    if (propertyNameDeclaringTypeMap.ContainsKey(propertyName))
                    {
                        var existingPropertyDeclaringType = propertyNameDeclaringTypeMap[propertyName];
                        var duplicateProperty             = true;

                        if (existingPropertyDeclaringType != null && propertyDeclaringType != null)
                        {
                            if (propertyDeclaringType.IsSubclassOf(existingPropertyDeclaringType) ||
                                (existingPropertyDeclaringType.IsInterface &&
                                 propertyDeclaringType.ImplementInterface(existingPropertyDeclaringType)))
                            {
                                // Current property is on a derived class and hides the existing
                                schema.Properties[propertyName] = innerSchema;
                                duplicateProperty = false;
                            }

                            if (existingPropertyDeclaringType.IsSubclassOf(propertyDeclaringType) ||
                                (propertyDeclaringType.IsInterface &&
                                 existingPropertyDeclaringType.ImplementInterface(propertyDeclaringType)))
                            {
                                // current property is hidden by the existing so don't add it
                                continue;
                            }
                        }

                        if (duplicateProperty)
                        {
                            throw new AddingSchemaReferenceFailedException(
                                      key,
                                      string.Format(
                                          SpecificationGenerationMessages.DuplicateProperty,
                                          propertyName,
                                          input));
                        }
                    }

                    schema.Properties[propertyName] = innerSchema;
                    propertyNameDeclaringTypeMap.Add(propertyName, propertyDeclaringType);
                }

                References[key] = schema;

                return(new OpenApiSchema
                {
                    Reference = new OpenApiReference
                    {
                        Id = key,
                        Type = ReferenceType.Schema
                    }
                });
            }
            catch (Exception e)
            {
                // Something went wrong while fetching schema, so remove the key if exists from the references.
                if (References.ContainsKey(key))
                {
                    References.Remove(key);
                }

                throw new AddingSchemaReferenceFailedException(key, e.Message);
            }
        }
 /// <summary>
 /// Creates an instance of <see cref="SchemaReferenceRegistry"/>.
 /// </summary>
 /// <param name="schemaGenerationSettings">The schema generation settings.</param>
 public SchemaReferenceRegistry(SchemaGenerationSettings schemaGenerationSettings)
 {
     _schemaGenerationSettings = schemaGenerationSettings
                                 ?? throw new ArgumentNullException(nameof(schemaGenerationSettings));
     _propertyNameResolver = schemaGenerationSettings.PropertyNameResolver;
 }
 /// <summary>
 /// Creates an instance of <see cref="SchemaGenerationSettings"/> class.
 /// </summary>
 /// <param name="propertyNameResolver">The property name resolver.</param>
 public SchemaGenerationSettings(IPropertyNameResolver propertyNameResolver)
 {
     PropertyNameResolver = propertyNameResolver
                            ?? throw new ArgumentNullException(nameof(propertyNameResolver));
 }