2

When I generate an API spec on SwaggerHub, I can declare the schemas, including user-friendly examples, as follows:

components:
  schemas:
    Job:
      type: object
      required:
        - position
        - department
        - startDate
      properties:
        jobId:
          type: integer
          format: int32
        position:
          type: string
          example: Copy Boy III
        department:
          type: string
          example: Legal
        startDate:
          type: string
          format: date
          example: '2019-10-01'

I can't figure out how to generate the same using the attributes in ServiceStack OpenAPI. Do I put attributes on my Response DTO? Or on my type? I can't find any attribute that seems to correspond to the "example" field.

I've tried using [ApiMember], which looks like the closest possible option, on both the Response DTO and the type the response is tied to, but neither seems to make a difference. Here are a few things I've tried, just in the hopes of seeing a change in the Swagger UI, but neither has worked:

// In the DTO
public class JobResponse
{
    [ApiMember(Name = "Job", DataType = "array")] // Will this do anything?
    public Job Job { get; set; }
    public ResponseStatus ResponseStatus { get; set; } // inject structured errors
}

// In the Type
public class Job : IEntity
{
    [Required][DataMember(Name = "jobId")]
    public int Id { get; set; }
    [ServiceStack.ApiMember(Name = "test", DataType = "string", IsRequired = true)] // Will this do anything?
    public string Position { get; set; }
    public string Department { get; set; }
    public DateTime? StartDate { get; set; }
}
Jeff Rosenberg
  • 3,522
  • 1
  • 18
  • 38

1 Answers1

1

You'd typically use Open API Attributes in order to customize the metadata returned in the generated /openapi specification of your Services.

Attributes to describe the Operation should be on the Request DTO, here's an example of an annotated Request DTO:

[Api("Service Description")]
[Tag("Core Requests")]
[ApiResponse(HttpStatusCode.BadRequest, "Your request was not understood")]
[ApiResponse(HttpStatusCode.InternalServerError, "Oops, something broke")]
[Route("/swagger/{Name}", "GET", Summary = "GET Summary", Notes = "Notes")]
[Route("/swagger/{Name}", "POST", Summary = "POST Summary", Notes="Notes")]
public class MyRequestDto
{
    [ApiMember(Name="Name", Description = "Name Description",
        ParameterType = "path", DataType = "string", IsRequired = true)]
    [ApiAllowableValues("Name", typeof(Color))] //Enum
    public string Name { get; set; }
}

You can also use [ApiMember] on normal DTO Type properties if you want to override their default representation.

Whenever you need more fine-grained control over the generated /openapi you can use Operation Filters on the OpenApiFeature plugin, e.g:

Plugins.Add(new OpenApiFeature
{
    OperationFilter = (verb, operation) => operation.Tags.Add("all operations")
});

Available configuration options:

  • ApiDeclarationFilter - allows to modify final result of returned OpenAPI json
  • OperationFilter - allows to modify operations
  • SchemaFilter - allows to modify OpenAPI schema for user types
  • SchemaPropertyFilter - allows to modify propery declarations in OpenAPI schema
mythz
  • 141,670
  • 29
  • 246
  • 390
  • Thanks! So if I'm understanding correctly, it looks like [ApiMember] is intended to annotate the request DTO, and generates the `paths` section of the OpenAPI spec. The `components/schemas` section, on the other hand, isn't generated through attributes, but simply declared using the `SchemaFilter` configuration. Is that right? – Jeff Rosenberg Aug 06 '19 at 02:23
  • @JeffRosenberg All the attributes on the `MyRequestDto` type above need to be annotated on Request DTO's but the `[ApiMember]` and `[ApiAllowableValues]` property attributes can be used to annotate on any DTO Type. The filters provide even further customization. – mythz Aug 06 '19 at 02:27
  • okay, so two follow-up questions on that: 1. how do I properly annotate my response DTOs? I tried `[ServiceStack.ApiMember(Name = "test")]`, but it didn't change the representation. 2. is there any documentation on how to use the filters other than OperationFilter? I didn't see any on the ServiceStack Open API page. – Jeff Rosenberg Aug 06 '19 at 02:29
  • @JeffRosenberg The filters let you modify the [Open API Specification Typed DTOs](https://github.com/ServiceStack/ServiceStack/tree/master/src/ServiceStack.Api.OpenApi/Specification), you'll need to refer to the [Open API v2 specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) to find out what each type/property does. You should use `[DataMember(Name = "test")]` to change the property name, the `[Api*]` attrs only documents the API, they have no effect on serialization, which you need if you want to change the schema name. – mythz Aug 06 '19 at 02:43
  • Thanks, I'll look at the spec and those DTOs. As far as the Api* attributes, I'm not trying to affect serialization, just the documentation. I would have thought using ApiMember would have kept the database mapping and serialization the same, but displayed "test" in Swagger UI. Am I misunderstanding? Thanks for your patience in explaining this! – Jeff Rosenberg Aug 06 '19 at 02:59
  • @JeffRosenberg Schema changes like changing the name in the specification but not serialization would break all clients generated from the specification which is why it needs to match the serialized name. – mythz Aug 06 '19 at 03:02
  • Thanks again! This gives me the general idea. I'll admit that I'm still a bit lost on using the SchemaFilters still, but hopefully I have enough to figure it out. I'm going to hold this open until I have it fully working though :-) – Jeff Rosenberg Aug 06 '19 at 04:20