ServiceStack Open API - Swagger UI not displaying enum values as dropdown - servicestack

I have recently upgraded my ServiceStack libraries from 5.10.4 to 6.5.0 and implemented Open API as specified in the documentation. But my Enum properties are displayed as Textbox instead of Dropdown in the /swagger-ui page.
This was working as expected previously when I was using 'ServiceStack.Api.Swagger' instead of 'ServiceStack.Api.OpenApi'.
Can someone please help me in this?
Thanks and Regards,
Sibin
Below are the code I am using:
// Configuration
Plugins.Add(new OpenApiFeature());
// DTO
[Route("my-route", "GET", Summary = "My summary")]
public class MyClass: IReturn<MyResponse>
{
[ApiMember(Name = "Alphabet", Description = "Alphabet",
ParameterType = "path", DataType = "string", IsRequired = true)]
[ApiAllowableValues("Alphabet", typeof(Alphabets))]
public string Alphabet { get; set; }
}
// Enum
public enum Alphabets
{
A,
B,
C,
D
}

This should now be resolved in the latest v6.5.1+ that's now available on MyGet.
Alternatively you can change your DTO to have an Enum property, which also doesn't require an [ApiAllowableValues] attribute, e.g:
[Route("/my-route", "GET", Summary = "My summary")]
public class MyClass: IReturn<MyResponse>
{
[ApiMember(Name = "Alphabet", Description = "Alphabet",
ParameterType = "path", DataType = "string", IsRequired = true)]
public Alphabets Alphabet { get; set; }
}
If you haven't already, checkout API Explorer that's built into ServiceStack v6+ which you can view from /ui or a specific API from /ui/{RequestDto} e.g. /ui/MyClass.

Related

Defining OpenApi response schemas - particularly the example field - with ServiceStack.Api.OpenApi

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; }
}
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

ServiceStack - extending AutoQuery Metadata with

Using the SwaggerFeature plugin in ServiceStack, I can annotate the properties of a DTO using the ApiMember attribute.
Example:
[Route("/swagger/{Name}", "POST"]
public class MyRequestDto
{
[ApiMember(Name="Name", Description = "Name Description",
ParameterType = "path", DataType = "string", IsRequired = true)]
public string Name { get; set; }
}
Is it possible to include these additional annotations for properties in the AutoQuery metadata service?
The schema for AutoQuery Metadata doesn't allow for this metadata which would change the scope and purpose of the AutoQuery Metadata.
This information is also redundant as this metadata about Services is maintained in the /types/metadata route which returns the MetadataTypes DTO.

Swagger UI displaying extra parameter using ServiceStack

I'm having issues with displaying the correct notes in swagger using ServiceStack.
Given this structure:
[Route("/Widget/{WidgetId}", Summary = "Updates a widget by id", Verbs = "POST",
Notes = "Updates a widget by id.")]
public class UpdateReqWidget : IReturnVoid
{
[ApiMember(Name = "WidgetId", Description = "The id of widget to delete.")]
public int WidgetId { get; set; }
}
public class WidgetService
{
public void Put(UpdateReqWidget req)
{
//do code here
}
}
It's producing:
I would expect the parameters list only to have WidgetId, but it's displaying WidgetId and UpdageReqWidget, the class name for the request. any ideas what I'm doing wrong?
EDIT: I'm using versions 3.9.55 for both ServiceStack and ServiceStack.API.Swagger. I've changed the templates to better suit our needs.
There is a recent addition to ServiceStack.Api.Swagger to automatically generate a request body parameter in the Swagger UI. I think there's an edge case in which the request DTO has no properties that aren't designated as path or query parameters, as in your case here, the code will still generate that UpdateReqWidget request body parameter, but it will be an empty object in Swagger.

Swagger with ServiceStack does not send elements to server on POST

I have a simple session object which looks like this
[Route("/Session", Summary = "Creates a security session", Notes = "Some session related notes here")]
public class Session : IReturn<SessionResponse>
{
[ApiMember(Name = "DomainName", Description = "The Security Domain", ParameterType = "path", DataType = "string", IsRequired = true)]
public string DomainName { get; set; }
[ApiMember(Name = "UserName", Description = "The User Name", ParameterType = "path", DataType = "string", IsRequired = true)]
public string UserName { get; set; }
[ApiMember(Name = "Password", Description = "The password", ParameterType = "path", DataType = "string", IsRequired = true)]
public string Password { get; set; }
}
When I go to swagger UI I can see the elements
However when I enter the element and press Try it now, I see that the request contents are not sent to the server.
Have I configured my poco correctly with parameterType="path" or should I be doing something else here? Please advise.
Has there been any update on getting this to work without seeing your path parameters in the body parameter definition?
[Route("/users/{UserId}/races", "POST", Summary = "Associates a race with a user")]
public class AddUserRace : IReturnVoid
{
[ApiMember(Description = "User Id to associate a race with to", ParameterType = "path", Verb="POST", DataType = "int", IsRequired = true)]
public int UserId { get; set; }
[Description("Race Id of race to associate with user")]
public int RaceId { get; set; }
}
Ideally, since UserId is a path param, I'd want it hidden from the DTO body.
Have I configured my poco correctly with parameterType="path" or should I be doing something else here?
I don't think so. Configuring each of your Session properties to ParameterType='path' suggests that you want each property to be a variable/field with the path/url (documentation here). So you would want your ServiceStack route to look something like this if you want to use 'path'.
[Route("/Session/{DomainName}/{UserName}/{Password}"]
To me, 'query' or 'body' would be a better choice. Also, This might contain useful information as well.
I got this to work , here is what I had to do
[Route("/Session", "POST", Summary = "Creates a security session", Notes = "Some session related notes here")]
public class Session : IReturn<SessionResponse>
{
[ApiMember(Name = "SessionData", Description = "The Session Data", ParameterType = "body", DataType = "SessionData", IsRequired = true)]
public string DomainName { get; set; }
//[ApiMember(Name = "UserName", Description = "The User Name", ParameterType = "path", DataType = "string", IsRequired = true)]
public string UserName { get; set; }
//[ApiMember(Name = "Password", Description = "The password", ParameterType = "path", DataType = "string", IsRequired = true)]
public string Password { get; set; }
}
So now I get a text area box and I need to enter the JSON for the entire session object so that the DomainName, UserName and Password get transferred, but somehow this does not seem right.
To support sending data in the request body with the current implementation of Swagger in ServiceStack, you should have exactly one ApiMember with ParameterType = "body" (you can still have other ApiMember attributes with other other ParameterType values, if you have variables in your URL for example).
The ApiMember with ParameterType = "body" will represent the entire content of your request body. It doesn't really matter which property of your request DTO you decorate with this attribute. We do something like this to make it clear in the Swagger UI that the textarea generated for this ApiMember should be used to populate the entire request body:
[ApiMember(Name = "RequestBody", ParameterType = "body", IsRequired = true,
Description = SomeLongStringConstantThatDescribesTheEntireDTO]
In the Swagger UI, this will display a textarea, you will need to enter the entire DTO as JSON in this text area. In your case, it would be something like:
{"DomainName": "...", "UserName": "...", "Password": "..."}

Swagger UI and ServiceStack

Does Service Stack support Models in Swagger.
In the sample code below
[Route("/User", "GET", Summary = "Get all the users available")]
[Route("/User", "POST, PUT", Summary = "Create a new user")]
[Route("/User/{Id}", "GET", Summary = "Get user with a specific id")]
public class User : RequestBase, IReturn<UserResponse>
{
[ApiMember(Name = "Id", Description = "The User Id", ParameterType = "path", DataType = "int", IsRequired = false)]
public int Id { get; set; }
[ApiMember(Name = "UserData", Description = "The User Data", ParameterType = "body", DataType = "complex", IsRequired = false)]
public UserData Entry { get; set; }
}
I would like UserData to be a complex type or container type. However if I define it to be one of these, all I get in the SwaggerUI is a text area box. I do not get the Model and ModelSchema links that I see in the petstore example online.
http://petstore.swagger.wordnik.com/#!/pet/addPet_post_1
Parameters of type "body" are properly supported as of the latest ServiceStack releases. You will need to set DataType = "User" in your second ApiMember attribute. This will allow the Swagger UI to correctly display model schema information. Also, for simple types, you can use constants in the SwaggerType class; for example you can change the first Apimember attribute to have DataType = SwaggerType.Int.

Resources