Could not resolve pointer: /components/schemas when nested model and custom schema Ids
Asked Answered
C

1

7

I am using Swashbuckle.AspNetCore 6.4.0

I am using a vertical slice approach where I nest models and the classes have the same name Command.

Because swagger checks the model name in order to generate the schema, I had to add this to the swager gen options

c.CustomSchemaIds(x => x.FullName);

But now my schemas are broken and, although endpoints display, nothing is shown for the schema. I can also see this error on the top of the page:

Resolver error at paths./api/agents/{aggregateId}/enrolments.post.requestBody.content.application/json.schema.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/MyProject.Features.EnrolAgent+Command does not exist in document
Resolver error at paths./api/agents/{aggregateId}/enrolments.post.requestBody.content.text/json.schema.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/MyProject.Features.EnrolAgent+Command does not exist in document
Resolver error at paths./api/agents/{aggregateId}/enrolments.post.requestBody.content.application/*+json.schema.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/MyProject.Features.EnrolAgent+Command does not exist in document
Resolver error at paths./api/clients.post.requestBody.content.application/json.schema.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/MyProject.Features.CreateClient+Command does not exist in document
Resolver error at paths./api/clients.post.requestBody.content.text/json.schema.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/MyProject.Features.CreateClient+Command does not exist in document
Resolver error at paths./api/clients.post.requestBody.content.application/*+json.schema.$ref
Could not resolve reference: Could not resolve pointer: /components/schemas/MyProject.Features.CreateClient+Command does not exist in document
MyProject.Features.EnrolAgent+Command does not exist in document

How could I have nested models generating proper schema as per xml comments?

This is my code

public class EnrolAgent
{
    private static readonly ILogger Logger = LoggerFactory.CreateLogger<EnrolAgent>();

    public class Command
        : ICommand
    {
        /// <summary>
        /// The optional message Id. If empty it will be generated at server side
        /// </summary>
        /// <example>00000000-0000-0000-0000-000000000000</example>
        public Guid MessageId { get; init; }
        
        /// <summary>
        /// The agent's Id
        /// </summary>
        /// <example>00000000-0000-0000-0000-000000000000</example>
        public Guid AggregateId { get; init; }
        
        /// <summary>
        /// The language codes supported in ISO 639-1
        /// </summary>
        /// <example>["00000000-0000-0000-0000-000000000001", "00000000-0000-0000-0000-000000000002"]</example>
        public IEnumerable<Guid> BrandIds { get; init; } = Enumerable.Empty<Guid>();

        public override string ToString()
        {
            return this.DisplayInfo();
        }
    }
    
    public class Handler
        : ICommandHandler<Command>
    {
        private readonly IDomainEventsConsumer _domainEventsConsumer;
        private readonly IDateTimeFactory _dateTimeFactory;
        private readonly IAggregateRepository<AgentAggregate> _agentRepository;

        public Handler(
            IDomainEventsConsumer domainEventsConsumer,
            IDateTimeFactory dateTimeFactory,
            IAggregateRepository<AgentAggregate> agentRepository)
        {
            _domainEventsConsumer = domainEventsConsumer;
            _dateTimeFactory = dateTimeFactory;
            _agentRepository = agentRepository;
        }
        
        public async Task<CommandResult> Handle(Command command)
        {
            Logger.LogDebug($"Handling command {command}");
            var aggregateId = command.AggregateId;
            
            var agent = await _agentRepository.Get(aggregateId);
            var createdOn = _dateTimeFactory.CreateUtcNow();
            
            agent.Enrol(createdOn, command.BrandIds);
            
            var domainEvents = await agent.ConsumeDomainEventChanges(_domainEventsConsumer);
            
            var commandResult = CommandResult.Create(aggregateId, domainEvents);
            return commandResult;
        }
    }
}

This is how I configure swagger

private static IServiceCollection AddOpenApi(this IServiceCollection services, IEnumerable<Assembly> allAssemblies)
{
    var mainAssemblyName = typeof(Startup).Assembly.GetName().Name;

    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo
        {
            Title = mainAssemblyName, 
            Version = "v1",
            Description = "Sample",
        });
        
        c.DocumentFilter<LowerCaseDocumentFilter>();
            
        var xmlCommentsWebApi = Path.Combine(AppContext.BaseDirectory, $"{mainAssemblyName}.xml");
        c.IncludeXmlComments(xmlCommentsWebApi);
        
        var allApplicationAssemblies =
            allAssemblies
                .Where(x => x.GetTypes().Any(t => typeof(ICommand).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract))
                .ToList();
        
        foreach (var applicationAssembly in allApplicationAssemblies)
        {
            var xmlCommentsApplication = Path.Combine(AppContext.BaseDirectory, $"{applicationAssembly.GetName().Name}.xml");
            c.IncludeXmlComments(xmlCommentsApplication);
        }
        
        // It fixes problem when using Command to name different nested commands
        c.CustomSchemaIds(x => x.FullName);
        
        c.AddSecurityDefinition(
            "Bearer",
            new OpenApiSecurityScheme
            {
                Name = "Authorization",
                Type = SecuritySchemeType.Http,
                Scheme = "Bearer",
                In = ParameterLocation.Header,
                Description = "JWT Authorization header"
            });

        c.AddSecurityRequirement(
            new OpenApiSecurityRequirement()
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference()
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        }
                    },
                    new string[]{}
                }
            });
    });
        
    return services;
}
Cabotage answered 1/9, 2022 at 14:34 Comment(0)
C
15

I fixed it with

c.CustomSchemaIds(s => s.FullName?.Replace("+", "."));

As per https://github.com/swagger-api/swagger-ui/issues/7911

Cabotage answered 2/9, 2022 at 8:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.