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
Related
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.
I am a .Net developer and is currently exploring on ArangoDB. I have played around with the arangod web user interface and arangod and like this NoSql very much until I delve into the detail of coding. I could not find the .Net driver working properly. Even for simple CRUD operation. Here's the problem.
ArangoClient.AddConnection("127.0.0.1", 8529, false, "Sample", "Sample");
var db = new ArangoDatabase("Sample");
string collectionName = "MyTestCollection";
var collection = new ArangoCollection();
collection.Name = collectionName;
collection.Type = ArangoCollectionType.Document;
if (db.Collection.Get(collectionName) == null)
{
db.Collection.Create(collection);
}
var employee = new Employee();
employee.Id = "1234";
employee.Name = "My Name";
employee.Salary = 33333;
employee.DateOfBirth = new DateTime(1979, 7, 22);
db.Document.Create<Employee>("MyTestCollection", employee);
employee.Name = "Tan";
db.Document.Update(employee);
It thrown the error for db.Document.Update(employee). Here's the error message: Field '_id' does not exist.
Then I tried to add the field _id though I think it is weird, it prompted me another error message.
Arango.Client.ArangoException : ArangoDB responded with error code BadRequest:
expecting PATCH /_api/document/<document-handle> [error number 400]
at Arango.Client.Protocol.DocumentOperation.Patch(Document document, Boolean waitForSync, String revision)
at Arango.Client.ArangoDocumentOperation.Update[T](T genericObject, Boolean waitForSync, String revision) ...
I have no clues at all and do not know how to to proceed further. Any help will be much appreciated. Thanks.
This is likely due to the definition of the Employee class, which is not contained in the above snippet.
To identify a document in a collection, documents have special system attributes, such as _id, _key and _rev. These attributes should be mapped to properties in .NET classes, even if not used explicitly. So one property in the class should be tagged with "Identity", one with "Key", and one with "Revision". Here is an example class definition that should work:
public class Employee
{
/* this will map the _id attribute from the database to ThisIsId property */
[ArangoProperty(Identity = true)]
public string ThisIsId { get; set; }
/* this will map the _key attribute from the database to the Id property */
[ArangoProperty(Key = true)]
public string Id { get; set; }
/* here is _rev */
[ArangoProperty(Revision = true)]
public string ThisIsRevision { get; set; }
public DateTime DateOfBirth { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
public Employee()
{
}
}
The ThisIsId property will contain the automatically assigned _id value, and can also be used to retrieve the document easily later:
var employeeFromDatabase = db.Document.Get<Employee>(employee.ThisIsId);
You can of course rename the properties to your like.
In my example I have the following database structure. Order has many OrderLine, which has one Product.
I am trying to return the following DTO:
public class OrderLineDto {
public int Id { get; set; }
public int Quantity { get; set; }
public string OrderType { get; set; }
public string ProductName { get; set; }
}
This should be possible by use of the following Query Route:
[Route("/orderlines")]
public class FindOrderLines : QueryBase<OrderLine, OrderLineDto>,
IJoin<OrderLine, Order>,
IJoin<OrderLine, Product>
{ }
What I am trying to do here is join OrderLine in both directions to bring in Type from Order, and Name from Product and return it in an OrderLineDto.
I am able to do these things individually by only using one IJoin, however AutoQuery appears only to use the first IJoin interface declaration, and does not perform the second join.
If I attempt to do a join like this: IJoin<OrderLine, Order, Product>
I get the following exception: Could not infer relationship between Order and Product
Is it possible to achieve what I am trying to do here with auto query or should I go back to writing standard REST services, abandoning AutoQuery?
I have submitted a pull request to ServiceStack which will now allow this behavior.
https://github.com/ServiceStack/ServiceStack/pull/955
I love the simplicity of using servicestack's IRestClient to test my api, but I need to replicate a test scenario when someone sends an incomplete object. For instance if my dto looks like this:
public class Todo
{
public long? Id { get; set; }
public string Content { get; set; }
public int? Order { get; set; }
public bool? Done { get; set; }
}
And I want to do a PUT with partial json like this:
{"Id": 99999, "Order":1}
How do I send just the above with the json client. If I use the typed version the client sends the entire object with defaults.
I think it might be easier to use ServiceStack's Http Utils (link is to POST but applies to PUT) to PUT the data.
"http://localhost/todo"
.PutJsonToUrl(#"{""Id"":9999,""Order"":1}");
Also, I'm not sure exactly what you are trying to test. But, if you're testing your Service the request (your json string) will be deserialized (pretty sure using ServiceStack.Text) into the Todo class so it will get the object with defaults for missing fields. See the test below.
[Test]
public void TestJson()
{
var json = #"{""Id"": 99999, ""Order"":1}";
var todo = new ServiceStack.Text.JsonSerializer<Todo>().DeserializeFromString(json);
Assert.IsNull(todo.Done);
Assert.IsNull(todo.Content);
}
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.