Skip to content

Commit

Permalink
Add support for OpenApi Tags
Browse files Browse the repository at this point in the history
  • Loading branch information
mythz committed Jun 27, 2017
1 parent 8bae718 commit a71a8a5
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 28 deletions.
4 changes: 2 additions & 2 deletions src/ServiceStack.Api.OpenApi/OpenApiFeature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class OpenApiFeature : IPlugin, IPreInitPlugin

public Action<OpenApiProperty> SchemaPropertyFilter { get; set; }

public Dictionary<string, string> RouteSummary { get; set; }
public List<OpenApiTag> Tags { get; set; }

public List<string> AnyRouteVerbs { get; set; }

Expand All @@ -42,7 +42,7 @@ public class OpenApiFeature : IPlugin, IPreInitPlugin
public OpenApiFeature()
{
LogoUrl = "//raw.githubusercontent.com/ServiceStack/Assets/master/img/artwork/logo-24.png";
RouteSummary = new Dictionary<string, string>();
Tags = new List<OpenApiTag>();
AnyRouteVerbs = new List<string> { HttpMethods.Get, HttpMethods.Post, HttpMethods.Put, HttpMethods.Delete };
}

Expand Down
60 changes: 35 additions & 25 deletions src/ServiceStack.Api.OpenApi/OpenApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using ServiceStack.Web;
using ServiceStack.Api.OpenApi.Support;
using ServiceStack.Api.OpenApi.Specification;
using ServiceStack;

namespace ServiceStack.Api.OpenApi
{
Expand Down Expand Up @@ -57,8 +56,9 @@ public object Get(OpenApiSpecification request)
paths.AddRange(visiblePaths);
}

var definitions = new Dictionary<string, OpenApiSchema>() {
{ "Object", new OpenApiSchema() {Description = "Object", Type = OpenApiType.Object, Properties = new OrderedDictionary<string, OpenApiProperty>() } }
var definitions = new Dictionary<string, OpenApiSchema>
{
{ "Object", new OpenApiSchema { Description = "Object", Type = OpenApiType.Object, Properties = new OrderedDictionary<string, OpenApiProperty>() } },
};

foreach (var restPath in paths.SelectMany(x => x.Verbs.Select(y => new { Value = x, Verb = y })))
Expand Down Expand Up @@ -88,7 +88,6 @@ public object Get(OpenApiSpecification request)
SecurityDefinitions = new Dictionary<string, OpenApiSecuritySchema> { { "basic", new OpenApiSecuritySchema { Type = "basic" } } }
};


if (OperationFilter != null)
apiPaths.Each(x => GetOperations(x.Value).Each(o => OperationFilter(o.Item1, o.Item2)));

Expand Down Expand Up @@ -532,16 +531,16 @@ private OpenApiSchema GetSchemaForResponseType(Type schemaType, IDictionary<stri
ParseDefinitions(schemas, schemaType, null, null);

var schema = GetDictionarySchema(schemas, schemaType, null, null)
?? GetKeyValuePairSchema(schemas, schemaType, null, null)
?? GetListSchema(schemas, schemaType, null, null)
?? (IsSwaggerScalarType(schemaType)
? new OpenApiSchema
{
Title = GetSchemaTypeName(schemaType),
Type = GetSwaggerTypeName(schemaType),
Format = GetSwaggerTypeFormat(schemaType)
}
: new OpenApiSchema { Ref = "#/definitions/" + GetSchemaTypeName(schemaType) });
?? GetKeyValuePairSchema(schemas, schemaType, null, null)
?? GetListSchema(schemas, schemaType, null, null)
?? (IsSwaggerScalarType(schemaType)
? new OpenApiSchema
{
Title = GetSchemaTypeName(schemaType),
Type = GetSwaggerTypeName(schemaType),
Format = GetSwaggerTypeFormat(schemaType)
}
: new OpenApiSchema { Ref = "#/definitions/" + GetSchemaTypeName(schemaType) });

schemaDescription = schema.Description ?? schemaType.GetDescription() ?? string.Empty;

Expand Down Expand Up @@ -588,6 +587,7 @@ private OpenApiSchema GetSchemaForResponseType(Type schemaType, IDictionary<stri

private OrderedDictionary<string, OpenApiPath> ParseOperations(List<RestPath> restPaths, Dictionary<string, OpenApiSchema> schemas, Dictionary<string, OpenApiTag> tags)
{
var feature = HostContext.GetPlugin<OpenApiFeature>();
var apiPaths = new OrderedDictionary<string, OpenApiPath>();

foreach (var restPath in restPaths)
Expand All @@ -606,7 +606,13 @@ private OpenApiSchema GetSchemaForResponseType(Type schemaType, IDictionary<stri

if (!apiPaths.TryGetValue(restPath.Path, out curPath))
{
curPath = new OpenApiPath() { Parameters = new List<OpenApiParameter> { new OpenApiParameter { Ref = "#/parameters/Accept" } } };
curPath = new OpenApiPath
{
Parameters = new List<OpenApiParameter>
{
new OpenApiParameter { Ref = "#/parameters/Accept" }
}
};
apiPaths.Add(restPath.Path, curPath);
}

Expand All @@ -616,19 +622,18 @@ private OpenApiSchema GetSchemaForResponseType(Type schemaType, IDictionary<stri
var authAttrs = new[] { op.ServiceType, op.RequestType }
.SelectMany(x => x.AllAttributes().OfType<AuthenticateAttribute>()).ToList();

authAttrs.AddRange(
actions.Where(x => x.Name.ToUpperInvariant() == "ANY")
authAttrs.AddRange(actions
.Where(x => x.Name.ToUpperInvariant() == "ANY")
.SelectMany(x => x.AllAttributes<AuthenticateAttribute>())
);
);

var annotatingTagAttributes = requestType.AllAttributes<TagAttribute>();

foreach (var verb in verbs)
{
var needAuth = authAttrs.Count > 0
|| actions.Where(x => x.Name.ToUpperInvariant() == verb)
.SelectMany(x => x.AllAttributes<AuthenticateAttribute>())
.Count() > 0;
.SelectMany(x => x.AllAttributes<AuthenticateAttribute>()).Any();

var userTags = new List<string>();
ApplyTo applyToVerb;
Expand Down Expand Up @@ -663,7 +668,12 @@ private OpenApiSchema GetSchemaForResponseType(Type schemaType, IDictionary<stri
foreach (var tag in operation.Tags)
{
if (!tags.ContainsKey(tag))
tags.Add(tag, new OpenApiTag { Name = tag });
{
var tagObject = feature.Tags.FirstOrDefault(x => x.Name == tag)
?? new OpenApiTag { Name = tag };

tags.Add(tag, tagObject);
}
}

switch (verb)
Expand Down Expand Up @@ -718,7 +728,7 @@ private string GetOperationName(string name, string route, string verb)

var entries = route.Replace("{", string.Empty)
.Replace("}", string.Empty)
.Split(new [] { '/'}, StringSplitOptions.RemoveEmptyEntries);
.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

if (entries.Length > 1)
pathPostfix = string.Join(string.Empty, entries, 1, entries.Length - 1);
Expand Down Expand Up @@ -785,7 +795,7 @@ private List<OpenApiParameter> ParseParameters(IDictionary<string, OpenApiSchema
var inPath = (route ?? "").ToLowerInvariant().Contains("{" + propertyName.ToLowerInvariant() + "}");
var paramType = inPath
? "path"
: IsFormData(verb, apiAttr) ? "formData": "query";
: IsFormData(verb, apiAttr) ? "formData" : "query";


var parameter = GetParameter(schemas, property.PropertyType,
Expand Down Expand Up @@ -913,7 +923,7 @@ private List<string> GetTags(string path)
private string GetTagName(string path)
{
var tags = path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);

return tags.Length > 0 ? tags[0] : null;
}

Expand Down Expand Up @@ -946,7 +956,7 @@ private OpenApiParameter GetAcceptHeaderParameter()
Type = OpenApiType.String,
Name = "Accept",
Description = "Accept Header",
Enum = new List<string>() { "application/json"},
Enum = new List<string>() { "application/json" },
In = "header",
Required = true,
};
Expand Down
2 changes: 2 additions & 0 deletions src/ServiceStack.Api.OpenApi/Specification/OpenApiTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ public class OpenApiTag
{
[DataMember(Name = "name")]
public string Name { get; set; }

[DataMember(Name = "description")]
public string Description { get; set; }

[DataMember(Name = "externalDocs")]
public OpenApiExternalDocumentation ExternalDocs { get; set; }
}
Expand Down
18 changes: 17 additions & 1 deletion tests/CheckWeb/Global.asax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using ServiceStack;
using ServiceStack.Admin;
using ServiceStack.Api.OpenApi;
using ServiceStack.Api.OpenApi.Specification;
using ServiceStack.Api.Swagger;
using ServiceStack.Auth;
using ServiceStack.Data;
Expand Down Expand Up @@ -241,7 +242,22 @@ private void ConfigureView(Container container)
razor.Deny.RemoveAt(0);
Plugins.Add(razor);

Plugins.Add(new OpenApiFeature());
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",
}
}
}
});

// Enable support for Swagger API browser
//Plugins.Add(new SwaggerFeature
Expand Down
1 change: 1 addition & 0 deletions tests/CheckWeb/SwaggerTestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum MyColor
Blue
}

[Tag("TheTag")]
[Api("SwaggerTest Service Description")]
[ApiResponse(HttpStatusCode.BadRequest, "Your request was not understood")]
[ApiResponse(HttpStatusCode.InternalServerError, "Oops, something broke")]
Expand Down

0 comments on commit a71a8a5

Please sign in to comment.