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": "..."}
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 want to take the raw JSON body from an HTTP post and write it directly into my CosmosDB.
Let's say the data looks like this:
{
"id": "123456",
"storeName": "City Bar and Grille",
"invoiceTotal": 65
}
However, the documentsOut.AddAsync command uses a format like this:
wait documentsOut.AddAsync(new
{
// create a random ID
id = System.Guid.NewGuid().ToString(),
body = sourceJson
});
And then I end up with a document that looks like this:
{
"id": "0e99d3ab-1956-4c0a-8ec1-99de5c987555",
"body": {
"id": "123456",
"storeName": "City Bar and Grille",
"invoiceTotal": 65
}
}
What I really want is to end up with this:
{
"id": "123456",
"storeName": "City Bar and Grille",
"invoiceTotal": 65
}
I'd like to drop the id = System.Guid.NewGuid().ToString() completely (which should not be hard).
How can I pass the raw JSON through without needing to add it to some parent node (such as body)?
Just to formalize my comment as an answer: You're specifically creating the body property of the Cosmos DB document you're creating:
wait documentsOut.AddAsync(new
{
// create a random ID
id = System.Guid.NewGuid().ToString(),
body = sourceJson
});
At the same time, you're ignoring the incoming ID. Since you wanted to preserve that ID, You can copy the ID over (as long as it remains unique within the partition) instead of generating a new GUID, and also grab individual properties from sourceJson to avoid the nested body element:
wait documentsOut.AddAsync(new
{
id = sourceJson.id,
storeName = sourceJson.storeName,
invoiceTotal = sourceJson.invoiceTotal
});
Using a similar example as shared by you in the question.
We can create a Model class with properties which want to store in database.
public class StoreDetails : TableEntity
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("storeName")]
public string StoreName { get; set; }
[JsonProperty("invoiceTotal")]
public string InvoiceTotal { get; set; }
}
Then can create object of model and pass that object to AddAsync()method. As shown in below screenshot.
var item = new StoreDetails
{
Id = Guid.NewGuid().ToString(),
StoreName = data.StoreName,
InvoiceTotal = data.InvoiceTotal
};
await storeDetailOut.AddAsync(item);
and when we triggered post API, got response as shown below.
As finally we can see the same record is stored in CosmosDB.
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
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.
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.