ServiceStack documenting body parameters in Open API (Swagger UI) Issue - servicestack

I am looking for any way to document body parameters in ServiceStack API with Open API plugin. It is showing proper documentation when written for query or path parameters but is there any way to do it for parameters in body?

If you have all of the attributes assigned then it should be visible in the Swagger UI, you just have to click the "model" tab that sits above each example value.
The above was created using the types below:
public enum TestType
{
ValueA,
ValueB,
ValueC,
ValueD
}
[DataContract]
public class TestA
{
[
DataMember(Order = 1),
ApiMember(
Name = "PropertyA",
Description = "A test property showing an integer type",
ParameterType = "model",
DataType = "integer",
Format = "int32",
IsRequired = true
)
]
public int PropertyA { get; set; }
[
DataMember(Order = 2),
ApiMember(
Name = "PropertyB",
Description = "A second test property showing a string type",
ParameterType = "model",
DataType = "string",
IsRequired = true
)
]
public string PropertyB { get; set; }
[
DataMember(Order = 3),
ApiMember(
Name = "PropertyC",
Description = "A third test property showing a string enum type.",
ParameterType = "model",
DataType = "string",
IsRequired = true
),
ApiAllowableValues("PropertyC", typeof(TestType))
]
public string PropertyC { get; set; }
[
DataMember(Order = 4),
ApiMember(
Name = "PropertyD",
Description = "A fourth test property showing a nested type.",
ParameterType = "model",
IsRequired = true
)
]
public TestB PropertyD { get; set; }
}
[DataContract]
public class TestB
{
[
DataMember(Order = 1),
ApiMember(
Name = "PropertyA",
Description = "A test property showing an integer type",
ParameterType = "model",
DataType = "integer",
Format = "int32",
IsRequired = true
)
]
public int PropertyA { get; set; }
[
DataMember(Order = 2),
ApiMember(
Name = "PropertyB",
Description = "A second test property showing a string type",
ParameterType = "model",
DataType = "string",
IsRequired = true
)
]
public string PropertyB { get; set; }
[
DataMember(Order = 3),
ApiMember(
Name = "PropertyC",
Description = "A third test property showing a string enum type.",
ParameterType = "model",
DataType = "string",
IsRequired = true
),
ApiAllowableValues("PropertyC", typeof(TestType))
]
public string PropertyC { get; set; }
}
[
Route(
"/test",
"POST",
Summary = "POST to test documentation in Swagger UI."
),
Api("Test"),
Tag("Swagger UI Documentation"),
ApiResponse(
HttpStatusCode.BadRequest,
"Your request was not understood or failed validation.",
ResponseType = typeof(PostTestResponse)
),
ApiResponse(
HttpStatusCode.InternalServerError,
"Oops, something broke.",
ResponseType = typeof(PostTestResponse)
),
DataContract
]
public class PostTest : IReturn<PostTestResponse>
{
[
DataMember(Order = 1),
ApiMember(
Name = "QueryStringA",
Description = "A query string parameter",
ParameterType = "query",
DataType = "string",
IsRequired = false,
ExcludeInSchema = true
)
]
public string QueryStringA { get; set; }
[
DataMember(Order = 2),
ApiMember(
Name = "Test",
Description = "Posted test data",
ParameterType = "model",
IsRequired = true
)
]
public TestA Test { get; set; }
}
[DataContract]
public class PostTestResponse : IHasResponseStatus
{
[
DataMember(Order = 1),
ApiMember(
Name = "ResponseStatus",
Description = "Information about any issue that may have occurred",
ParameterType = "model",
IsRequired = false
)
]
public ResponseStatus ResponseStatus { get; set; }
[
DataMember(Order = 2),
ApiMember(
Name = "Test",
Description = "Returned test",
ParameterType = "model",
IsRequired = false
)
]
public TestA Test { get; set; }
}
Since ServiceStack just generates OpenApi 2.0, you can use any Swagger UI (or reader that supports it). Using the newer Swagger UI you get a less cramped representation of your body schema.

Related

Acumatica Rest API get customer attribute name

I am trying to obtain values for Customer attributes via the Rest API. Currently, I can retrieve customer attributes, but am unable to determine which specific attribute is associated. For example, given this code (based on https://github.com/Acumatica/AcumaticaRESTAPIClientForCSharp, Endpoint = "Default", EndpointVersion = "18.200.001", Endpoint Library = Acumatica.Default_20.200.001, Acumatica version = Cloud ERP 2020 R1, Build 20.110.0017):
var customerApi = new CustomerApi(configuration);
var customers = customerApi.GetList(top: 5, expand: "Attributes", select: "Attributes/Attribute,Attributes/Value");
foreach (var cust in customers)
{
Console.WriteLine(cust.ToString());
}
Yields this output:
{
"AccountRef": {},
"Attributes": [
{
"Value": {
"value": "True"
},
"id": "8de7a85d-6d60-4235-9d35-74a9d08d1cc6",
"rowNumber": 1,
"custom": {}
},
{
"Value": {
"value": "Sample Email Body"
},
"id": "8da2a21c-2ba3-45ba-9e12-02122c626e11",
"rowNumber": 2,
"custom": {}
}, ...
What am I missing to be able to get the attribute name returned as well? Or how I am supposed to correlate the given values back to a given attribute?
The default configuration of the Customers API is returning the Attributes array as below for the customerApi.GetList(top: 5, expand: "Attributes", select: "Attributes/Attribute,Attributes/Value"); request
"Attributes": [
{
"Attribute": {
"value": "Company Revenue"
},
"Value": {
"value": "1,000,000 to 5,000,000"
},
"id": "6df69428-7157-438f-8b61-99b2d7d1a3ad",
"rowNumber": 1,
"custom": {}
},
{
"Attribute": {
"value": "Number of Employees"
},
"Value": {
"value": "1-100"
},
"id": "15c3f47f-36eb-481b-92c0-f6b2f738732f",
"rowNumber": 2,
"custom": {}
}
]
Attributes->Attribute->Value is the identifier for the Attribute which is corresponding to the Description of the Attribute record.
Your result is returned for customerApi.GetList(top: 5, expand: "Attributes", select: "Attributes/Value"); request. Please make sure that you have included the Attributes/Attribute in the select part if you are specifying it.
UPDATE
There is a small difference in naming between 18.200 and 20.200.
In 18.200 the Attribute ID is actually named Attribute
In 20.200 the Attribute ID is renamed to Attribute ID
That is why this request is working correctly for 18.200
namespace Acumatica.Default_18_200_001.Model
{
[DataContract]
public class AttributeDetail : Entity_v3
{
[DataMember(Name="Attribute", EmitDefaultValue=false)]
public StringValue Attribute { get; set; }
[DataMember(Name="RefNoteID", EmitDefaultValue=false)]
public GuidValue RefNoteID { get; set; }
[DataMember(Name="Required", EmitDefaultValue=false)]
public BooleanValue Required { get; set; }
[DataMember(Name="Value", EmitDefaultValue=false)]
public StringValue Value { get; set; }
}
}
namespace Acumatica.Default_20_200_001.Model
{
[DataContract]
public class AttributeValue : Entity_v4
{
[DataMember(Name="AttributeID", EmitDefaultValue=false)]
public StringValue AttributeID { get; set; }
[DataMember(Name="AttributeDescription", EmitDefaultValue=false)]
public StringValue AttributeDescription { get; set; }
[DataMember(Name="RefNoteID", EmitDefaultValue=false)]
public GuidValue RefNoteID { get; set; }
[DataMember(Name="Required", EmitDefaultValue=false)]
public BooleanValue Required { get; set; }
[DataMember(Name="Value", EmitDefaultValue=false)]
public StringValue Value { get; set; }
[DataMember(Name="ValueDescription", EmitDefaultValue=false)]
public StringValue ValueDescription { get; set; }
}
}

How to use ServiceStack OpenApiFeature/Swagger with api description and response examples?

Is there a way to add a description to the api (not just to individual routes) and update api version and add example responses/resquests using the OpenApiFeature in ServiceStack? I can't find anything in the documentation about these pieces of the swagger ui.
I tried using the Api attribute to add the description but that doesn't seem to work.
The only declarative Attributes that can annotate individual Services are documented on the Open API docs. Here's a fully annotated example:
[Tag("TheTag")]
[Api("SwaggerTest Service Description")]
[ApiResponse(HttpStatusCode.BadRequest, "Your request was not understood")]
[ApiResponse(HttpStatusCode.InternalServerError, "Oops, something broke")]
[Route("/swagger", "GET", Summary = #"GET / Summary", Notes = "GET / Notes")]
[Route("/swagger/{Name}", "GET", Summary = #"GET Summary", Notes = "GET /Name Notes")]
[Route("/swagger/{Name}", "POST", Summary = #"POST Summary", Notes = "POST /Name Notes")]
public class SwaggerExample
{
[ApiMember(Description = "Color Description",
ParameterType = "path", DataType = "string", IsRequired = true)]
[ApiAllowableValues("Name", typeof(MyColor))] //Enum
public string Name { get; set; }
[ApiMember]
[ApiAllowableValues("Color", typeof(MyColor))] //Enum
public MyColor Color { get; set; }
[ApiMember(Description = "Aliased Description", DataType="string", IsRequired=true)]
[DataMember(Name = "Aliased")]
public string Original { get; set; }
[ApiMember(Description = "Not Aliased", DataType="string", IsRequired=true)]
public string NotAliased { get; set; }
[ApiMember(IsRequired = false, AllowMultiple = true)]
public DateTime[] MyDateBetween { get; set; }
[ApiMember(Description = "Nested model 1", DataType = "SwaggerNestedModel")]
public SwaggerNestedModel NestedModel1 { get; set; }
[ApiMember(Description = "Nested model 2", DataType = "SwaggerNestedModel2")]
public SwaggerNestedModel2 NestedModel2 { get; set; }
}
The other annotation Open API allows is grouping logical operations by Tag which you would use annotate in ServiceStack with the [Tag] attribute that you can then provide a description for when registering the OpenApiFeature Plugin, e.g:
Plugins.Add(new OpenApiFeature
{
Tags =
{
new OpenApiTag
{
Name = "TheTag",
Description = "TheTag Description",
ExternalDocs = new OpenApiExternalDocumentation
{
Description = "Link to External Docs Desc",
Url = "http://example.org/docs/path",
}
}
}
});

Servicestack AutoQuery not filtering results

my query
/API/Json/GetJson?Desc=test1
I get all records not just the test1 records
[Route("/API/Json/GetJson", "GET")]
public class GetJson : QueryDb<JsonModel>
{
public int? Id { get; set; }
public int? RefId { get; set; }
public int? SecondRefId { get; set; }
public int? ThirdRefId { get; set; }
public int? FourthRefId { get; set; }
public string Name { get; set; }
public string JSON { get; set; }
public string JsonType { get; set; }
public string Desc { get; set; }
public int? AuditId { get; set; }
}
public class JsonModel
{
[AutoIncrement]
[PrimaryKey]
[IgnoreOnUpdate]
public int Id { get; set; }
/// <summary>
/// Other tables the this data is relevant to
/// </summary>
public int? RefId { get; set; }
public int? SecondRefId { get; set; }
public int? ThirdRefId { get; set; }
public int? FourthRefId { get; set; }
/// <summary>
/// name that is displayed to users
/// </summary>
[Required]
public string Name { get; set; }
public string JSON { get; set; }
/// <summary>
/// Tells what data type the JSON is storing
/// </summary>
[Required]
public string JsonType { get; set; }
public string Desc { get; set; }
public int AuditId { get; set; }
public DateTime AuditStamp { get; set; } = DateTime.UtcNow;
}
also my return data has extra fields. Starting at Skip
{
"id": 4,
"refId": 9,
"secondRefId": 3,
"thirdRefId": 100,
"fourthRefId": null,
"name": "test",
"json": "JSON STRING DATA",
"jsonType": "test",
"desc": "test3",
"auditId": 0,
**"skip": null,
"take": null,
"orderBy": null,
"orderByDesc": null,
"include": null,
"fields": null,
"meta": null**
},
I updated my model to nullables and its is till returing all records. My seed data and I am using SS 5.6.0
WireUpService<IntegrationService>();
using (var db = HostContext.Resolve<IDbConnectionFactory>().Open())
{
string JSON = " \"Columns\": [\r\n {\r\n \"CompanyName\": [\r\n {\r\n \"name\": \"Company Name\",\r\n \"Visible\": \"True\",\r\n \"Sort\": \"U,A,Z[Unsorted, A-Z, Z-A]\",\r\n \"Filter\": \"Test Company\"\r\n }\r\n ],\r\n \"ParentCompnay\": [\r\n {\r\n \"name\": \"Company Name\",\r\n \"Visible\": \"True\",\r\n \"Sort\": \"U,A,Z[Unsorted, A-Z, Z-A]\",\r\n \"Filter\": \"Test Company\"\r\n }\r\n ]\r\n }";
db.DropAndCreateTable<JsonModel>();
db.Insert(new JsonModel { Desc = "test",Name = "test",JsonType = "test", JSON = JSON,RefId = 10,SecondRefId = 3, AuditId = 0, AuditStamp = DateTime.Now });
db.Insert(new JsonModel { Desc = "test1", Name = "test", JsonType = "test", JSON = JSON, RefId = 10, SecondRefId = 3, AuditId = 0, AuditStamp = DateTime.Now });
db.Insert(new JsonModel { Desc = "test2", Name = "test", JsonType = "test", JSON = JSON, RefId = 5, SecondRefId = 3, AuditId = 0, AuditStamp = DateTime.Now });
db.Insert(new JsonModel { Desc = "test3", Name = "test", JsonType = "test", JSON = JSON, RefId = 9, SecondRefId = 3,ThirdRefId = 100, AuditId = 0, AuditStamp = DateTime.Now });
}
I wasn't able to reproduce this issue using the classes provided, which I've seeded with test data that matches the query and non-matching test data that you've included in your JSON response:
db.CreateTable<JsonModel>();
db.Insert(new JsonModel { RefId = 1, SecondRefId = 1, ThirdRefId = 111, Name = "test1", Desc = "test1", JsonType = "test", JSON = "TEST1"});
db.Insert(new JsonModel { RefId = 9, SecondRefId = 3, ThirdRefId = 100, Name = "test1", Desc = "test3", JsonType = "test", JSON = "JSON STRING DATA"});
There is an issue with your GetJson model where you've specified Id and AuditId as non-nullable int properties which if not specified are populated in your GetJson Request DTO as 0 (default int).
If you're going to include required value types on your GetJson AutoQuery Service you should always be providing values for them otherwise change them into int? so they're not added to the query filter when they're not specified, e.g:
public class GetJson : QueryDb<JsonModel>
{
public int? Id { get; set; }
public int? AuditId { get; set; }
}
After doing this, it works as expected where I can query the Auto Query Service using your specified query, i.e:
var url = baseUrl.CombineWith("/API/Json/GetJson").AddQueryParam("Desc", "test1");
var json = url.GetJsonFromUrl();
json.Print();
Which works as expected returning the matching result in a QueryResponse<T> DTO, i.e:
{"Offset":0,"Total":0,"Results":[{"Id":1,"RefId":1,"SecondRefId":1,"ThirdRefId":111,"Name":"test1","JSON":"TEST1","JsonType":"test","Desc":"test1","AuditId":0,"AuditStamp":"\/Date(1577190306230-0000)\/"}],"Meta":{}}

ServiceStack 'ExcludePropertyReferences' dynamically if decorate with datamember attribute

I want to ignore some properties from my Object during run time. The properties are decorated with data member attribute (Without data member attribute excludepropertyreferencces is working fine). Can you please provide some insight? Thanks
Question : HOW TO EXCLUDE PROPERTIES AT RUN TIME, IF THEY ARE DECORATE WITH DATAMEMBER ATTRIBUTE ?
ServiceStack , ExcludePropertyReferences
var model = new Model {
FirstName = "First Name",
LastName = "Last Name",
Children = new List<ModelChild>{
new ModelChild { ChildFirstName = "ChildFirstName 1", ChildLastName = "ChildLastName 1" },
new ModelChild { ChildFirstName = "ChildFirstName 2", ChildLastName = "ChildLastName 2" }
}
};
var model1 = new Model1 {
FirstName = "First Name",
LastName = "Last Name",
Children = new List<Model1Child>{
new Model1Child { ChildFirstName = "ChildFirstName 1", ChildLastName = "ChildLastName 1" },
new Model1Child { ChildFirstName = "ChildFirstName 2", ChildLastName = "ChildLastName 2" }
}
};
Console.WriteLine("Properties won't get ignored because the Model is decorated with Serialization Attributes");
using(MemoryStream stream = new MemoryStream())
using (var jsConfig = JsConfig.BeginScope()) {
jsConfig.ExcludeTypeInfo = true;
jsConfig.ExcludePropertyReferences = new [] { "Model.LastName", "ModelChild.ChildLastName" }.ToArray();
JsonSerializer.SerializeToStream(model, model.GetType(), stream);
LINQPad.Extensions.Dump(System.Text.Encoding.Default.GetString(stream.ToArray()));
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Properties will get ignored because the Model is not decorated with Serialization Attributes");
using(MemoryStream stream = new MemoryStream())
using (var jsConfig = JsConfig.BeginScope()) {
jsConfig.ExcludeTypeInfo = true;
jsConfig.ExcludePropertyReferences = new [] { "Model1.LastName", "Model1Child.ChildLastName" }.ToArray();
JsonSerializer.SerializeToStream(model1, model1.GetType(), stream);
LINQPad.Extensions.Dump(System.Text.Encoding.Default.GetString(stream.ToArray()));
}
// Define other methods and classes here
[DataContract()]
public class Model {
[DataMember(Name = "first_name",EmitDefaultValue = false )]
public string FirstName { get; set; }
[DataMember(Name = "last_name")]
public string LastName { get; set; }
[DataMember(Name = "collections")]
public List<ModelChild> Children { get; set; }
}
[DataContract()]
public class ModelChild {
[DataMember(Name = "child_first_name")]
public string ChildFirstName { get; set; }
[DataMember(Name = "child_last_name")]
public string ChildLastName { get; set; }
}
public class Model1 {
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Model1Child> Children { get; set; }
}
public class Model1Child {
public string ChildFirstName { get; set; }
public string ChildLastName { get; set; }
}

Serialize an object with DataContractJsonSerializer

I have a class which contains some items. I want to serialize an instance of this class to json using the DataContractJsonSerializer :
[DataContract]
public class Policy
{
private string expiration { get; set; }
private List<List<string>> conditions { get; set; }
public Policy(){}
public Policy(string expiration, List<List<string>> conditions){
this.expiration = expiration;
this.conditions = conditions;
}
[DataMember]
public string DateExpiration
{
get{ return expiration;}
set{expiration = value;}
}
[DataMember]
public List<List<string>> Conditions
{
get{return conditions;}
set{conditions = value;}
}
}
When serialized to json it should be like this :
{
"expiration": "2011-04-20T11:54:21.032Z",
"conditions": [
["eq", "acl", "private"],
["eq", "bucket": "myas3bucket"],
["eq", "$key", "myfilename.jpg"],
["content-length-range", 0, 20971520],
["eq", "$redirect", "myredirecturl"],
]
}
I tried like this but nothing :
string expiration = "2012-12-01T12:00:00.000Z";
List<List<string>> conditions = new List<List<string>>()
{
new List<string>(){ "[ eq", "acl", "private ]" },
new List<string>(){ "[ eq", "bucket", "myas3bucket]" },
new List<string>(){ "[ eq", "$key", "myfilename.jpg ]" },
new List<string>(){ "[ content-length-range", "0", "20971520]" },
new List<string>(){ "[ eq", "$redirect", "myredirecturl]" }
};
Policy myPolicy = new Policy(expiration,conditions);
string policy = JSONHelper.Serialize<Policy>(myPolicy);
thanks
First off... your class needs a little help:
[DataContract]
public class Policy
{
public Policy(){}
public Policy(string expiration, List<List<string>> conditions){
this.DateExpiration = expiration;
this.Conditions = conditions;
}
[DataMember]
public string DateExpiration { get; set; }
[DataMember]
public List<List<string>> Conditions { get; set; }
}
Try removing the brackets in your lists:
string expiration = "2012-12-01T12:00:00.000Z";
List<List<string>> conditions = new List<List<string>>()
{
new List<string>(){ "eq", "acl", "private" },
new List<string>(){ "eq", "bucket", "myas3bucket" },
new List<string>(){ "eq", "$key", "myfilename.jpg" },
new List<string>(){ "content-length-range", "0", "20971520" },
new List<string>(){ "eq", "$redirect", "myredirecturl" }
};
Policy myPolicy = new Policy(expiration,conditions);
string policy = JSONHelper.Serialize<Policy>(myPolicy);

Resources