Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic-typed response object not accurately documented in Swagger (ServiceStack)

I'm having an issue with the ServiceStack implementation of Swagger with regards to the documentation of generic-typed response objects. Strongly-typed response objects are correctly documented and displayed, however once a generic-typed object is used as a response, the documentation is inaccurate and misleading.

Request DTO

[Route("/users/{UserId}", "GET", Summary = "Get a specific User Profile")]
public class GetUser : IReturn<ServiceResponse<UserProfile>>
{
    [ApiMember(Description = "User Id", ParameterType = "path", IsRequired = true)]
    public int UserId { get; set; }
}

Response DTO

public class ServiceResponse<T> : IServiceResponse<T>
{
    public IList<string> Errors { get; set; }
    public bool Successful { get; set; }
    public string Message { get; set; }
    public string StackTrace { get; set; }
    public T Data { get; set; }

    public ServiceResponse()
    {
        Errors = new List<string>();
    }
}

Response DTO Type

public class UserProfile : RavenDocument
{
    public UserProfile()
    {
        Races = new List<UserRace>();
        Workouts = new List<Workout>();
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string DisplayName { get; set; }
    public DateTime? BirthDate { get; set; }
    public Gender? Gender { get; set; }
    public string UltracartPassword { get; set; }
    public string UltracartCartId { get; set; }

    [UniqueConstraint]
    public string Email { get; set; }

    public string ImageUrl { get; set; }

    public FacebookUserInfo FacebookData { get; set; }
    public GoogleUserInfo GoogleData { get; set; }

    public DateTime CreatedOn { get; set; }
    public DateTime? LastUpdated { get; set; }
    public UserAddress ShippingAddress { get; set; }
    public UserAddress BillingAddress { get; set; }
    public IList<UserRace> Races { get; set; }
    public IList<Workout> Workouts { get; set; }
}

The examples are pretty straight forward. Nothing really hacky or clever going on, however this is the sample documentation I get from Swagger out of the box:

Swagger Example

As you can see, the generic type isn't documented correctly and some other type is used instead. As I am using this same ServiceResponse wrapper for all my responses, this is happening across the board.

like image 319
Jason Castellano Avatar asked Jan 06 '14 14:01

Jason Castellano


Video Answer


1 Answers

As you have found, the ServiceStack swagger plugin does not currently attempt to handle generic types cleanly. A simple alternative that should work better is to make concrete subclasses of the generic types. e.g.:

public class UserProfileResponse : ServiceResponse<UserProfile> { ... }

public class GetUser : IReturn<UserProfileResponse> ...

This should be handled properly by Swagger.

I've found generic types aren't always a great fit for ServiceStack DTOs. You'll find many discussions (for example here, here and here) on StackOverflow that discuss this, and the reasons why concrete types and generally avoiding inheritance is a good idea for ServiceStack DTOs.

It takes effort to overcome the temptation to apply the DRY principle to request/respone DTOs. The way I think about it is that generics and inheritance are language features that facilitate implementation of algorithms in generic, reusable ways, where the generic method or base class doesn't need to know about the details of the concrete type. While DTOs may superficially have common structures that look like opportunities for inheritance or generics, in this case the implementation and semantics of each DTO is different for each concrete usage, so the details of each request/response message deserve to be defined explicitly.

like image 158
Mike Mertsock Avatar answered Sep 28 '22 12:09

Mike Mertsock