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

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",
}
}
}
});

Related

ServiceStack's JsConfig.TextCase no long honored in v6.0

We have existing code that worked as desired in ServiceStack v5.13.2, but had unexpected breaking behavior after upgrading to v6.0.0.
Here is our service implementation:
public async Task<object> Get(IsAuthenticated request)
{
var session = await this.GetSessionAsync();
bool isAuth = await AuthUserSessionExtensions.AuthenticateAsync(Request, Request.Dto);
if (!isAuth) throw new UnauthorizedAccessException();
var sanitizedSession = new AuthUserSession()
{
FirstName = session.FirstName,
LastName = session.LastName,
Email = session.Email,
Permissions = session.Permissions,
Roles = session.Roles,
UserName = session.UserName,
};
return sanitizedSession;
}
Originally, we would receive the response back in camelCase, per our JsConfig settings. After upgrading, the AuthUserSession was ALWAYS in PascalCase. Even wrapping a manual serialization in a JsConfig scope and explicitly requesting camelCase, serialization only ever provided Pascal.
Example output post-upgrade:
{
"ReferrerUrl": null,
"Id": null,
"UserAuthId": "5",
"UserAuthName": null,
"UserName": "admin",
"TwitterUserId": null,
}
My only thought is the ServiceStack v6 libraries perform special serialization for AuthUserSession and/or ServiceStack's own POCOs.
A work-around is to create a custom POCO that matches field-for-field the AuthUserSession. Serialization of our own classes works without issue.
public class CustomSession
{
public string UserAuthId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public List<string> Permissions { get; set; }
public List<string> Roles { get; set; }
public string UserName { get; set; }
}
results in the properly serialized output:
{
"userAuthId": "5",
"firstName": "Admin",
"lastName": "Admin",
"email": "admin#",
...

How to work with Gmail attachments on Azure Logic App

I have created a Logic app where I get emails from a Gmail Account and I want to post the attachment of the email to my rest API. But I don't understand which type I get as the attachment. I have seen: if I use the Outlook.com trigger I get a base64String but from Gmail I get something else.
Is there an example how to work with Gmail attachments.
Thanks for the input SahadevSinh. I have changed my workflow like this:
And in my endpoint I do this:
public async System.Threading.Tasks.Task<MissionOutputDto> CreateMissionFromMail(HttpRequestMessage req)
{
string body = await req.Content.ReadAsStringAsync();
dynamic fileData = JObject.Parse(body);
string email = fileData.email;
JArray files = fileData.files;
string fileString = null;
string fileName = null;
string mimeType = null;
foreach (dynamic file in files)
{
fileString = file.ContentBytes;
fileName = file.Name;
mimeType = file.ContentType;
}
i have to one example to show you how you can get gmail attachment
enter image description here
1) receive email trigger :
Step 1 details
2) get email details :
Step 2 details
3) pass attachment details in HTTP request
Step 3 details
[
{
"Name": "test (2).txt",
"ContentBytes": "dGVzdA==",
"ContentType": "text/plain; charset=\"US-ASCII\"; name=\"test (2).txt\"",
"ContentId": "",
"Size": 4
},
{
"Name": "test (2) - Copy.txt",
"ContentBytes": "dGVzdA==",
"ContentType": "text/plain; charset=\"US-ASCII\"; name=\"test (2) - Copy.txt\"",
"ContentId": "",
"Size": 4
}
]
"contentbyte" : is base64Strig
WebAPI changes :
you have create one more class to retrieve this attachment data
public class GmailAttechment
{
public string FileName { get; set; }
public string ContentBytes { get; set; }
public string ContentType { get; set; }
public string ContentId { get; set; }
public int Size { get; set; }
}
this class use to retrieve attachment details from your request
add above class into your webapi request parameter
public class GetEmailDetails
{
public string file { get; set; }
public string fileName { get; set; }
public string from { get; set; }
public string mimeType { get; set; }
**public List<GmailAttechment> GmailAttechmentList { get; set; }**
}
example of action
public void GetGmailDetails(GetEmailDetails gmailDetails)
{
foreach (var item in gmailDetails.GmailAttechmentList)
{
//here you can get all file content
string base6String = item.ContentBytes;
}
}

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

Is there a way to have a ServiceStack metadata page show all the options for an enum request or response property

I'd like to be able to have the code below
[Route("/Incidents", "Get")]
public class GetViewConfig
{
public List<Filter> Filters { get; set; }
}
public class Filter
{
public string Property { get; set; }
public FilterType Type { get; set; }
public string Value { get; set; }
}
public enum FilterType
{
IsBetween,
Is,
IsNot
}
public class GetViewConfigResponse
{
public List<Filter> Filters { get; set; }
}
public class ViewConfigService : Service
{
public object Get(GetViewConfig request)
{
return null;
}
}
Show all the values for the FilterType on the metadata page. Is there a way to do this?
Not on the metadata pages, but you can view this using the Swagger API and the [ApiAllowableValues] attribute, e.g:
[Api("Service Description")]
[Route("/swagger/{Name}", "GET", Summary = #"GET Summary", Notes = "GET 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; }
}

MVC 4 DropDownListFor Set Selected Item Based on Text

Normally when I use the DropDownListFor helper I'm selecting which item is selected based on an ID (int), but now I have a situation where I need to display which item is selected based on the text (string) and not an ID. In the controller the model is being set correctly to the value that I want with this property:
model.Title
An example title would be "Front Office". I have the following code in my Controller:
ViewBag.Titles = new SelectList(jobTitles, "Name", "Name");
and on the view I have this:
#Html.DropDownListFor(model => model.Title, ViewBag.Titles as SelectList)
The DropDownList is populating correctly with all the expected job titles, it just isn't selecting the correct job title based on model.Title. What am I doing wrong?
UPDATE:
There seems to be something else potentially going wrong in my code, so I'm putting all of it here to see if I'm doing something wrong.
Controller:
public ActionResult Edit(int id = 0)
{
StaffMember staffmember = StaffMember.SelectByID(id); // gets staff member from db
ViewBag.Titles = new SelectList(JobTitle.SelectAll(), "Name", "Name", staffmember.Title); // JobTitle.SelectAll() returns List<JobTitle>
StaffEditModel model = new StaffEditModel();
model.ID = staffmember.ID;
model.ClientID = staffmember.ClientID;
model.FirstName = staffmember.FirstName;
model.MiddleInitial = staffmember.MiddleInitial;
model.LastName = staffmember.LastName;
model.Title = staffmember.Title;
model.Phone = staffmember.Phone;
model.Email = staffmember.Email;
model.Birthday = staffmember.Birthday;
model.HireDate = staffmember.HireDate;
model.Biography = staffmember.Biography;
if (staffmember == null)
{
return HttpNotFound();
}
return View(model);
}
Model:
public class StaffEditModel
{
public int ID { get; set; }
public int ClientID { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Display(Name = "Middle Initial")]
public string MiddleInitial { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set; }
public string Title { get; set; } // this is the property I'm trying to show in DropDown
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public string Birthday { get; set; }
[Display(Name = "Hire Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public string HireDate { get; set; }
public string Email { get; set; }
[MaxLength(14)]
public string Phone { get; set; }
public string Biography { get; set; }
}
View:
#Html.DropDownListFor(model => model.Title, new SelectList(ViewBag.Titles,"Value","Text", Model.Title))
This should work.
You can pass ViewBag.Titles as List<SelectListItem> from controller
var jobList = new List<SelectListItem>();
foreach (var job in jobTitles)
{
var item = new SelectListItem();
item.Value = job.PropertyName; //the property you want to display i.e. Title
item.Text = job.PropertyName;
jobList.Add(item);
}
ViewBag.Title = jobList;
and then in the view,
#Html.DropDownListFor(model => model.Title, new SelectList(ViewBag.Titles,"Value","Text", Model.Title))
I know I'm late to the party on this, but I had a similar problem and thought I would add what I've found.
I haven't figured out the why yet - I'm still researching (that's how I ended up here) - but I think it has something to do with the fact that you're using the word Title
I've got the exact same scenario and as soon as I changed my model to CustomerTitle instead of Title things worked as expected.
Initial thoughts are that the model binder is finding Title somewhere else in the DOM and getting tripped up. Complete speculation. If I have more time I'll keep digging for the why

Resources