private OpenApiSchema GenerateReferencedSchema(
            DataContract dataContract,
            SchemaRepository schemaRepository,
            Func <OpenApiSchema> definitionFactory,
            ParameterInfo parameterInfo)
        {
            var  dataContractType   = dataContract.UnderlyingType;
            var  baseControllerType = typeof(BaseApiController);
            var  baseParameterType  = typeof(RepresentationParameter);
            var  schemaId           = _generatorOptions.SchemaIdSelector(dataContract.UnderlyingType);
            Type controllerType     = null;

            if (baseParameterType.IsAssignableFrom(dataContract.UnderlyingType))
            {
                if (baseControllerType.IsAssignableFrom(parameterInfo.Member.ReflectedType))
                {
                    schemaId         = $"{parameterInfo.Member.ReflectedType.Name}{schemaId}";
                    controllerType   = parameterInfo.Member.ReflectedType;
                    dataContractType = controllerType;
                }
            }

            if (schemaRepository.TryLookupByType(dataContractType, out OpenApiSchema referenceSchema))
            {
                return(referenceSchema);
            }

            schemaRepository.RegisterType(dataContractType, schemaId);

            var schema = definitionFactory();

            ApplyFilters(schema, dataContractType, schemaRepository);
            if (controllerType != null)
            {
                var controller = (BaseApiController)_serviceProvider.GetService(controllerType);
                var scimSchema = _scimSchemaQueryRepository.FindRootSCIMSchemaByResourceType(controller.ResourceType).Result;
                if (scimSchema != null)
                {
                    Enrich(scimSchema, scimSchema.HierarchicalAttributes.Select(a => a.Leaf).ToList(), schema.Properties);
                }
                else
                {
                    _logger.LogError($"the schema '{controller.ResourceType}' doesn't exist !");
                }

                var kvp = schema.Properties.FirstOrDefault(_ => _.Key == "attributes");
                if (!kvp.Equals(default(KeyValuePair <string, OpenApiSchema>)) && !string.IsNullOrWhiteSpace(kvp.Key))
                {
                    schema.Properties.Remove(kvp);
                }
            }

            return(schemaRepository.AddDefinition(schemaId, schema));
        }
        private OpenApiSchema CreateObjectSchema(Type type, SchemaRepository schemaRepository)
        {
            if (!(_contractResolver.ResolveContract(type) is JsonObjectContract jsonObjectContract))
            {
                throw new InvalidOperationException($"Type {type} does not resolve to a JsonObjectContract");
            }

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

            // If it's a baseType with known subTypes, add the discriminator property
            if (_generatorOptions.GeneratePolymorphicSchemas && _generatorOptions.SubTypesResolver(type).Any())
            {
                var discriminatorName = _generatorOptions.DiscriminatorSelector(type);
                schema.Properties.Add(discriminatorName, new OpenApiSchema {
                    Type = "string"
                });
                schema.Required.Add(discriminatorName);
                schema.Discriminator = new OpenApiDiscriminator {
                    PropertyName = discriminatorName
                };
            }

            foreach (var jsonProperty in jsonObjectContract.Properties)
            {
                if (jsonProperty.Ignored)
                {
                    continue;
                }

                var customAttributes = jsonProperty.TryGetMemberInfo(out MemberInfo memberInfo)
                    ? memberInfo.GetInlineOrMetadataTypeAttributes()
                    : Enumerable.Empty <object>();

                if (_generatorOptions.IgnoreObsoleteProperties && customAttributes.OfType <ObsoleteAttribute>().Any())
                {
                    continue;
                }
                var required = jsonProperty.IsRequiredSpecified()
                    ? jsonProperty.Required
                    : jsonObjectContract.ItemRequired ?? Required.Default;

                schema.Properties[jsonProperty.PropertyName] = CreatePropertySchema(jsonProperty, customAttributes, required, schemaRepository);

                if (required == Required.Always || required == Required.AllowNull || customAttributes.OfType <RequiredAttribute>().Any())
                {
                    schema.Required.Add(jsonProperty.PropertyName);
                }
            }

            if (jsonObjectContract.ExtensionDataValueType != null)
            {
                schema.AdditionalProperties        = _schemaGenerator.GenerateSchema(jsonObjectContract.ExtensionDataValueType, schemaRepository);
                schema.AdditionalPropertiesAllowed = true;
            }

            // If it's a known subType, reference the baseType for inheritied properties
            if (_generatorOptions.GeneratePolymorphicSchemas && type.BaseType != null && _generatorOptions.SubTypesResolver(type.BaseType).Contains(type))
            {
                var baseType = type.BaseType;

                var baseSchemaReference = schemaRepository.GetOrAdd(
                    type: baseType,
                    schemaId: _generatorOptions.SchemaIdSelector(baseType),
                    factoryMethod: () => CreateObjectSchema(baseType, schemaRepository));

                var baseSchema = schemaRepository.Schemas[baseSchemaReference.Reference.Id];

                schema.AllOf = new[] { baseSchemaReference };

                foreach (var basePropertyName in baseSchema.Properties.Keys)
                {
                    schema.Properties.Remove(basePropertyName);
                }
            }

            return(schema);
        }
Esempio n. 3
0
        /// <inheritdoc />
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (context.Type.IsAssignableTo <IPropertyContainer>())
            {
                IPropertySet?propertySet     = null;
                Type?        knownSchemaType = null;

                // Get by provided attribute [PropertySet].
                var propertySetAttribute = context.MemberInfo?.GetCustomAttribute <PropertySetAttribute>();
                if (propertySetAttribute != null)
                {
                    knownSchemaType = propertySetAttribute.Type;
                    propertySet     = propertySetAttribute.GetPropertySet();
                }

                // Get by IKnownPropertySet
                if (propertySet == null)
                {
                    propertySet = context.Type.GetSchemaByKnownPropertySet();
                }

                // "$ref": "#/components/schemas/KnownSchema"
                if (_options.GenerateKnownSchemasAsRefs && propertySet != null)
                {
                    knownSchemaType ??= propertySet.GetType();
                    string knownSchemaId = _schemaGeneratorOptions.SchemaIdSelector(knownSchemaType);

                    if (!context.SchemaRepository.Schemas.TryGetValue(knownSchemaId, out OpenApiSchema knownSchema))
                    {
                        // Generate and fill knownSchema once for type.
                        knownSchema = context.SchemaGenerator.GenerateSchema(knownSchemaType, context.SchemaRepository);
                        FillSchema(knownSchema, context, propertySet);
                        context.SchemaRepository.Schemas[knownSchemaId] = knownSchema;
                    }

                    schema.Type       = null;
                    schema.Items      = null;
                    schema.Properties = null;

                    // "$ref": "#/components/schemas/KnownSchema"
                    schema.Reference = new OpenApiReference
                    {
                        Type = ReferenceType.Schema,
                        Id   = knownSchemaId,
                    };
                }
                else if (propertySet != null)
                {
                    // Generate inlined schema
                    FillSchema(schema, context, propertySet);
                }
                else
                {
                    // No known schema for property container.
                    schema.Type       = "object";
                    schema.Items      = null;
                    schema.Properties = new Dictionary <string, OpenApiSchema>();
                    schema.AdditionalPropertiesAllowed = true;
                }
            }
        }