private OpenApiSchema GenerateArraySchema(SerializerMetadata serializerMetadata, SchemaRepository schemaRepository)
 {
     return(new OpenApiSchema
     {
         Type = "array",
         Items = GenerateSchema(serializerMetadata.ArrayItemType, schemaRepository),
         UniqueItems = serializerMetadata.Type.IsSet() ? (bool?)true : null
     });
 }
 private OpenApiSchema GenerateReferencedSchema(SerializerMetadata serializerMetadata, SchemaRepository schemaRepository)
 {
     return(schemaRepository.GetOrAdd(
                serializerMetadata.Type,
                _generatorOptions.SchemaIdSelector(serializerMetadata.Type),
                () =>
     {
         var schema = GenerateInlineSchema(serializerMetadata, schemaRepository);
         ApplyFilters(schema, serializerMetadata.Type, schemaRepository);
         return schema;
     }));
 }
        private OpenApiSchema GeneratePrimitiveSchema(SerializerMetadata serializerMetadata)
        {
            var schema = new OpenApiSchema
            {
                Type   = serializerMetadata.DataType,
                Format = serializerMetadata.DataFormat
            };

            if (serializerMetadata.EnumValues != null)
            {
                schema.Enum = serializerMetadata.EnumValues
                              .Select(value => OpenApiAnyFactory.CreateFor(schema, value))
                              .ToList();
            }

            return(schema);
        }
        private OpenApiSchema GenerateDictionarySchema(SerializerMetadata serializerMetadata, SchemaRepository schemaRepository)
        {
            if (serializerMetadata.DictionaryKeyType.IsEnum)
            {
                // This is a special case where we can include named properties based on the enum values
                return(new OpenApiSchema
                {
                    Type = "object",
                    Properties = serializerMetadata.DictionaryKeyType.GetEnumNames()
                                 .ToDictionary(
                        name => name,
                        name => GenerateSchema(serializerMetadata.DictionaryValueType, schemaRepository)
                        )
                });
            }

            return(new OpenApiSchema
            {
                Type = "object",
                AdditionalPropertiesAllowed = true,
                AdditionalProperties = GenerateSchema(serializerMetadata.DictionaryValueType, schemaRepository)
            });
        }
        private OpenApiSchema GenerateInlineSchema(SerializerMetadata serializerMetadata, SchemaRepository schemaRepository)
        {
            if (serializerMetadata.IsPrimitive)
            {
                return(GeneratePrimitiveSchema(serializerMetadata));
            }

            if (serializerMetadata.IsDictionary)
            {
                return(GenerateDictionarySchema(serializerMetadata, schemaRepository));
            }

            if (serializerMetadata.IsArray)
            {
                return(GenerateArraySchema(serializerMetadata, schemaRepository));
            }

            if (serializerMetadata.IsObject)
            {
                return(GenerateObjectSchema(serializerMetadata, schemaRepository));
            }

            return(new OpenApiSchema());
        }
        public SerializerMetadata GetSerializerMetadataForType(Type type)
        {
            var underlyingType = type.IsNullable(out Type innerType) ? innerType : type;

            if (PrimitiveTypesAndFormats.ContainsKey(underlyingType))
            {
                var primitiveTypeAndFormat = PrimitiveTypesAndFormats[underlyingType];
                return(SerializerMetadata.ForPrimitive(underlyingType, primitiveTypeAndFormat.Item1, primitiveTypeAndFormat.Item2));
            }

            if (underlyingType.IsEnum)
            {
                var enumValues = GetSerializeEnumValuesFor(underlyingType);

                var primitiveTypeAndFormat = (enumValues.Any(value => value.GetType() == typeof(string)))
                    ? PrimitiveTypesAndFormats[typeof(string)]
                    : PrimitiveTypesAndFormats[underlyingType.GetEnumUnderlyingType()];

                return(SerializerMetadata.ForPrimitive(underlyingType, primitiveTypeAndFormat.Item1, primitiveTypeAndFormat.Item2, enumValues));
            }

            if (underlyingType.IsDictionary(out Type keyType, out Type valueType))
            {
                return(SerializerMetadata.ForDictionary(underlyingType, keyType, valueType));
            }

            if (underlyingType.IsEnumerable(out Type itemType))
            {
                return(SerializerMetadata.ForArray(underlyingType, itemType));
            }

            return(SerializerMetadata.ForObject(
                       underlyingType,
                       GetSerializerPropertiesFor(underlyingType, out Type extensionDataValueType),
                       extensionDataValueType));
        }
        private OpenApiSchema GenerateObjectSchema(SerializerMetadata serializerMetadata, SchemaRepository schemaRepository)
        {
            if (serializerMetadata.Properties == null)
            {
                return(new OpenApiSchema {
                    Type = "object"
                });
            }

            var schema = new OpenApiSchema
            {
                Type       = "object",
                Properties = new Dictionary <string, OpenApiSchema>(),
                Required   = new SortedSet <string>()
            };

            // If it's a baseType with known subTypes, add the discriminator property
            if (_generatorOptions.GeneratePolymorphicSchemas && _generatorOptions.SubTypesResolver(serializerMetadata.Type).Any())
            {
                var discriminatorName = _generatorOptions.DiscriminatorSelector(serializerMetadata.Type);

                if (!schema.Properties.ContainsKey(discriminatorName))
                {
                    schema.Properties.Add(discriminatorName, new OpenApiSchema {
                        Type = "string"
                    });
                }

                schema.Required.Add(discriminatorName);
                schema.Discriminator = new OpenApiDiscriminator {
                    PropertyName = discriminatorName
                };
            }

            foreach (var serializerPropertyMetadata in serializerMetadata.Properties)
            {
                var customAttributes = serializerPropertyMetadata.MemberInfo?.GetInlineOrMetadataTypeAttributes() ?? Enumerable.Empty <object>();

                if (_generatorOptions.IgnoreObsoleteProperties && customAttributes.OfType <ObsoleteAttribute>().Any())
                {
                    continue;
                }

                var propertySchema = GenerateSchema(serializerPropertyMetadata.MemberType, schemaRepository, memberInfo: serializerPropertyMetadata.MemberInfo);

                schema.Properties.Add(serializerPropertyMetadata.Name, propertySchema);

                if (serializerPropertyMetadata.IsRequired || customAttributes.OfType <RequiredAttribute>().Any())
                {
                    schema.Required.Add(serializerPropertyMetadata.Name);
                }

                if (propertySchema.Reference == null)
                {
                    propertySchema.Nullable = propertySchema.Nullable && serializerPropertyMetadata.AllowNull;
                }
            }

            if (serializerMetadata.ExtensionDataValueType != null)
            {
                schema.AdditionalProperties = GenerateSchema(serializerMetadata.ExtensionDataValueType, schemaRepository);
            }

            // If it's a known subType, reference the baseType for inheritied properties
            if (_generatorOptions.GeneratePolymorphicSchemas &&
                (serializerMetadata.Type.BaseType != null) &&
                _generatorOptions.SubTypesResolver(serializerMetadata.Type.BaseType).Contains(serializerMetadata.Type))
            {
                var baseSerializerContract = _serializerMetadataResolver.GetSerializerMetadataForType(serializerMetadata.Type.BaseType);
                var baseSchemaReference    = GenerateReferencedSchema(baseSerializerContract, schemaRepository);

                var baseSchema = schemaRepository.Schemas[baseSchemaReference.Reference.Id];
                foreach (var basePropertyName in baseSchema.Properties.Keys)
                {
                    schema.Properties.Remove(basePropertyName);
                }

                return(new OpenApiSchema
                {
                    AllOf = new List <OpenApiSchema> {
                        baseSchemaReference, schema
                    }
                });
            }

            return(schema);
        }