Skip to content

OpenAPI: document.Workspace has schemas duplicated #63598

@desjoerd

Description

@desjoerd

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

While playing around hacking SchemaReferenceTransformers I got an itch around OpenApiDocumentExtensions.AddOpenApiSchemaByReference:

        document.Workspace ??= new();
        var location = document.BaseUri + "/components/schemas/" + schemaId;
        document.Workspace.RegisterComponentForDocument(document, schema, location);

So when debugging some unit tests I found that schemas are added twice. For OpenApiSchemaServiceTests.GetOpenApiRequestBody_GeneratesSchemaForPoco it adds the Todo schema 2 times:

  1. https://openapi.net/d6bd3466-1e30-4009-a52a-c5e42c50c184#/components/schemas/https://openapi.net/d6bd3466-1e30-4009-a52a-c5e42c50c184/components/schemas/Todo
  2. https://openapi.net/d6bd3466-1e30-4009-a52a-c5e42c50c184#/components/schemas/Todo

The first is added in the OpenApiDocumentExtensions.AddOpenApiSchemaByReference method. The second is added in the OpenApiDocumentService.GetOpenApiDocumentAsync (document.Workspace.RegisterComponents(document)). This last one scans all components and adds them to the workspace (so they are correct from a OpenAPI.NET point of view).

I tried changing OpenApiDocumentExtensions.AddOpenApiSchemaByReference to one of the following:

  • document.Workspace.RegisterComponentForDocument(document, schema, schemaId);
  • document.AddComponent(schemaId, schema);

Both add the schema as expected to the workspace but it fails some tests caused by circular references. This is because the moment they are added correctly to the workspace they are used when resolving references. The way how RegisterComponent works is different than just assigning the schema in document.Components.Schemas[schemaId] = schema;. RegisterComponent does not overwrite a component (TryAdd), but assigning does.

Related method:

    public static IOpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument document, string schemaId, IOpenApiSchema schema)
    {
        if(schema is OpenApiSchemaReference)
        {
            throw new ArgumentException("Schema is already a reference.", nameof(schema));
        }

        document.Components ??= new();
        document.Components.Schemas ??= new Dictionary<string, IOpenApiSchema>();
        document.Components.Schemas[schemaId] = schema;
        document.Workspace ??= new();
        var location = document.BaseUri + "/components/schemas/" + schemaId;
        document.Workspace.RegisterComponentForDocument(document, schema, location);
        
        object? description = null;
        object? example = null;
        if (schema is OpenApiSchema actualSchema)
        {
            actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDescriptionAnnotation, out description);
            actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefExampleAnnotation, out example);
        }

        return new OpenApiSchemaReference(schemaId, document)
        {
            Description = description as string,
            Examples = example is JsonNode exampleJson ? [exampleJson] : null,
        };
    }

My proposed fix is to remove the whole part around the workspace in AddOpenApiSchemaByReference as it's not used anyway for resolving references. In the OpenApiDocumentService we still keep the initialization of the workspaces which gives is a correctly initialized workspace.

The below passes all tests:

    public static IOpenApiSchema AddOpenApiSchemaByReference(this OpenApiDocument document, string schemaId, IOpenApiSchema schema)
    {
        if(schema is OpenApiSchemaReference)
        {
            throw new ArgumentException("Schema is already a reference.", nameof(schema));
        }

        document.Components ??= new();
        document.Components.Schemas ??= new Dictionary<string, IOpenApiSchema>();
        document.Components.Schemas[schemaId] = schema;
        
        object? description = null;
        object? example = null;
        if (schema is OpenApiSchema actualSchema)
        {
            actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefDescriptionAnnotation, out description);
            actualSchema.Metadata?.TryGetValue(OpenApiConstants.RefExampleAnnotation, out example);
        }

        return new OpenApiSchemaReference(schemaId, document)
        {
            Description = description as string,
            Examples = example is JsonNode exampleJson ? [exampleJson] : null,
        };
    }

Another fix could be just removing the initialization of the Workspace completly as it's not required for the functionality.

@captainsafia let me know what you think 😊. I can make the change, I just don't know where you would like the unit tests for the Workspace initialization (as there are none right now).

Expected Behavior

No response

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

10.0.100-rc.1.25420.111

Anything else?

It's not a breaking bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-openapi

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions