Browse Source

NSwag

pull/336/head
Sebastian Stehle 8 years ago
parent
commit
9035d60063
  1. 6
      src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml
  2. 21
      src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xsd
  3. 29
      src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsageRepository.cs
  4. 10
      src/Squidex.Infrastructure/HashSet.cs
  5. 2
      src/Squidex.Infrastructure/UsageTracking/IUsageRepository.cs
  6. 41
      src/Squidex/Areas/Api/Config/Swagger/LogoProcessor.cs
  7. 20
      src/Squidex/Areas/Api/Config/Swagger/ODataQueryParamsProcessor.cs
  8. 7
      src/Squidex/Areas/Api/Config/Swagger/ScopesProcessor.cs
  9. 13
      src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs
  10. 100
      src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs
  11. 3
      src/Squidex/Areas/Api/Config/Swagger/XmlResponseTypesProcessor.cs
  12. 2
      src/Squidex/Areas/Api/Controllers/Contents/ContentSwaggerController.cs
  13. 21
      src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs
  14. 6
      src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs

6
src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xml

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<Freezable/>
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Freezable />
</Weavers>

21
src/Squidex.Domain.Apps.Core.Model/FodyWeavers.xsd

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuild. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="Freezable" minOccurs="0" maxOccurs="1" type="xs:anyType" />
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification on the target assembly after all weavers have been finished.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

29
src/Squidex.Infrastructure.MongoDb/UsageTracking/MongoUsageRepository.cs

@ -35,30 +35,35 @@ namespace Squidex.Infrastructure.UsageTracking
new CreateIndexModel<MongoUsage>(Index.Ascending(x => x.Key).Ascending(x => x.Category).Ascending(x => x.Date)), cancellationToken: ct);
}
public async Task TrackUsagesAsync(params UsageUpdate[] updates)
public async Task TrackUsagesAsync(UsageUpdate update)
{
if (updates.Length == 1)
Guard.NotNull(update, nameof(update));
if (update.Counters.Count > 0)
{
var value = updates[0];
var (filter, updateStatement) = CreateOperation(update);
if (value.Counters.Count > 0)
{
var (filter, update) = CreateOperation(value);
await Collection.UpdateOneAsync(filter, updateStatement);
}
}
await Collection.UpdateOneAsync(filter, update, Upsert);
}
public async Task TrackUsagesAsync(params UsageUpdate[] updates)
{
if (updates.Length == 1)
{
await TrackUsagesAsync(updates[0]);
}
else if (updates.Length > 0)
{
var writes = new List<WriteModel<MongoUsage>>();
foreach (var value in updates)
foreach (var update in updates)
{
if (value.Counters.Count > 0)
if (update.Counters.Count > 0)
{
var (filter, update) = CreateOperation(value);
var (filter, updateStatement) = CreateOperation(update);
writes.Add(new UpdateOneModel<MongoUsage>(filter, update) { IsUpsert = true });
writes.Add(new UpdateOneModel<MongoUsage>(filter, updateStatement) { IsUpsert = true });
}
}

10
src/Squidex.Infrastructure/HashSet.cs

@ -15,5 +15,15 @@ namespace Squidex.Infrastructure
{
return new HashSet<T>(items);
}
public static HashSet<T> Of<T>(T item1)
{
return new HashSet<T> { item1 };
}
public static HashSet<T> Of<T>(T item1, T item2)
{
return new HashSet<T> { item1, item2 };
}
}
}

2
src/Squidex.Infrastructure/UsageTracking/IUsageRepository.cs

@ -13,6 +13,8 @@ namespace Squidex.Infrastructure.UsageTracking
{
public interface IUsageRepository
{
Task TrackUsagesAsync(UsageUpdate update);
Task TrackUsagesAsync(params UsageUpdate[] updates);
Task<IReadOnlyList<StoredUsage>> QueryAsync(string key, DateTime fromDate, DateTime toDate);

41
src/Squidex/Areas/Api/Config/Swagger/LogoProcessor.cs

@ -0,0 +1,41 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using NSwag.SwaggerGeneration.Processors;
using NSwag.SwaggerGeneration.Processors.Contexts;
using Squidex.Config;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Areas.Api.Config.Swagger
{
public class LogoProcessor : IDocumentProcessor
{
private const string Background = "#3f83df";
private readonly string logo;
public LogoProcessor(IOptions<MyUrlsOptions> urlOptions)
{
logo = urlOptions.Value.BuildUrl("images/logo-white.png", false);
}
public Task ProcessAsync(DocumentProcessorContext context)
{
context.Document.BasePath = Constants.ApiPrefix;
context.Document.Info.ExtensionData = new Dictionary<string, object>
{
["x-logo"] = new { url = logo, backgroundColor = Background }
};
return TaskHelper.Done;
}
}
}

20
src/Squidex/Areas/Api/Config/Swagger/ODataQueryParamsProcessor.cs

@ -16,30 +16,32 @@ namespace Squidex.Areas.Api.Config.Swagger
{
public sealed class ODataQueryParamsProcessor : IOperationProcessor
{
private readonly string path;
private readonly string supportedPath;
private readonly string entity;
private readonly bool supportSearch;
public ODataQueryParamsProcessor(string path, string entity, bool supportSearch)
public ODataQueryParamsProcessor(string supportedPath, string entity, bool supportSearch)
{
this.path = path;
this.entity = entity;
this.supportSearch = supportSearch;
this.supportedPath = supportedPath;
}
public Task<bool> ProcessAsync(OperationProcessorContext context)
{
if (context.OperationDescription.Path == path)
if (context.OperationDescription.Path == supportedPath)
{
var operation = context.OperationDescription.Operation;
if (supportSearch)
{
context.OperationDescription.Operation.AddQueryParameter("$search", JsonObjectType.String, "Optional OData full text search.");
operation.AddQueryParameter("$search", JsonObjectType.String, "Optional OData full text search.");
}
context.OperationDescription.Operation.AddQueryParameter("$top", JsonObjectType.Number, $"Optional number of {entity} to take.");
context.OperationDescription.Operation.AddQueryParameter("$skip", JsonObjectType.Number, $"Optional number of {entity} to skip.");
context.OperationDescription.Operation.AddQueryParameter("$orderby", JsonObjectType.String, "Optional OData order definition.");
context.OperationDescription.Operation.AddQueryParameter("$filter", JsonObjectType.String, "Optional OData filter definition.");
operation.AddQueryParameter("$top", JsonObjectType.Number, $"Optional number of {entity} to take.");
operation.AddQueryParameter("$skip", JsonObjectType.Number, $"Optional number of {entity} to skip.");
operation.AddQueryParameter("$orderby", JsonObjectType.String, "Optional OData order definition.");
operation.AddQueryParameter("$filter", JsonObjectType.String, "Optional OData filter definition.");
}
return TaskHelper.True;

7
src/Squidex/Areas/Api/Config/Swagger/ScopesProcessor.cs

@ -18,7 +18,7 @@ using Squidex.Infrastructure.Tasks;
namespace Squidex.Areas.Api.Config.Swagger
{
public class ScopesProcessor : IOperationProcessor
public sealed class ScopesProcessor : IOperationProcessor
{
public Task<bool> ProcessAsync(OperationProcessorContext context)
{
@ -28,8 +28,9 @@ namespace Squidex.Areas.Api.Config.Swagger
}
var authorizeAttributes =
context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Union(
context.MethodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType<AuthorizeAttribute>()).ToArray();
context.MethodInfo.GetCustomAttributes<AuthorizeAttribute>(true).Union(
context.MethodInfo.DeclaringType.GetCustomAttributes<AuthorizeAttribute>(true))
.ToArray();
if (authorizeAttributes.Any())
{

13
src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs

@ -6,9 +6,6 @@
// ==========================================================================
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NSwag.AspNetCore;
using Squidex.Config;
namespace Squidex.Areas.Api.Config.Swagger
@ -17,15 +14,9 @@ namespace Squidex.Areas.Api.Config.Swagger
{
public static void UseMySwagger(this IApplicationBuilder app)
{
var urlOptions = app.ApplicationServices.GetService<IOptions<MyUrlsOptions>>().Value;
app.UseSwaggerWithApiExplorer(settings =>
app.UseSwagger(settings =>
{
settings.AddAssetODataParams();
settings.ConfigureNames();
settings.ConfigurePaths(urlOptions);
settings.ConfigureSchemaSettings();
settings.ConfigureIdentity(urlOptions);
settings.Path = $"{Constants.ApiPrefix}/swagger/v1/swagger.json";
});
}
}

100
src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs

@ -13,6 +13,7 @@ using NJsonSchema.Generation.TypeMappers;
using NodaTime;
using NSwag.AspNetCore;
using NSwag.SwaggerGeneration;
using NSwag.SwaggerGeneration.Processors;
using NSwag.SwaggerGeneration.Processors.Security;
using Squidex.Areas.Api.Controllers.Contents.Generator;
using Squidex.Areas.Api.Controllers.Rules.Models;
@ -26,73 +27,72 @@ namespace Squidex.Areas.Api.Config.Swagger
{
public static void AddMySwaggerSettings(this IServiceCollection services)
{
services.AddSingleton(typeof(SwaggerSettings<SwaggerGeneratorSettings>), s =>
services.AddSingletonAs<RuleActionProcessor>()
.As<IDocumentProcessor>();
services.AddSingletonAs<XmlTagProcessor>()
.As<IDocumentProcessor>();
services.AddSingletonAs<TagByGroupNameProcessor>()
.As<IOperationProcessor>();
services.AddSingletonAs<XmlResponseTypesProcessor>()
.As<IOperationProcessor>();
services.AddSingleton(c =>
{
var urlOptions = s.GetService<IOptions<MyUrlsOptions>>().Value;
var settings = new SwaggerDocumentSettings { SchemaType = SchemaType.OpenApi3 };
return new SwaggerDocumentRegistration(settings.DocumentName, generator);
}))
var settings = new SwaggerSettings<SwaggerGeneratorSettings>()
.AddAssetODataParams()
.ConfigureNames()
.ConfigurePaths(urlOptions)
.ConfigureSchemaSettings()
.ConfigureIdentity(urlOptions);
settings.DocumentProcessors.Add(new RuleActionProcessor());
settings.DocumentProcessors.Add(new XmlTagProcessor());
return settings;
settings.OperationProcessors.Add(new TagByGroupNameProcessor());
settings.OperationProcessors.Add(new XmlResponseTypesProcessor());
services.AddOpenApiDocument(configure =>
{
var urlOptions = configure.GetService<IOptions<MyUrlsOptions>>().Value;
configure.AddAssetODataParams();
configure.ConfigureNames();
configure.ConfigurePaths(urlOptions);
configure.ConfigureSchemaSettings();
configure.ConfigureIdentity(urlOptions);
});
services.AddTransient<SchemasSwaggerGenerator>();
}
public static SwaggerSettings<T> ConfigureNames<T>(this SwaggerSettings<T> settings) where T : SwaggerGeneratorSettings, new()
public static void AddAssetODataParams<T>(this T settings) where T : SwaggerGeneratorSettings
{
settings.GeneratorSettings.Title = "Squidex API";
settings.GeneratorSettings.Version = "1.0";
return settings;
settings.OperationProcessors.Add(new ODataQueryParamsProcessor("/apps/{app}/assets", "assets", false));
}
public static SwaggerSettings<T> AddAssetODataParams<T>(this SwaggerSettings<T> settings) where T : SwaggerGeneratorSettings, new()
public static void ConfigureNames<T>(this T settings) where T : SwaggerGeneratorSettings
{
settings.GeneratorSettings.OperationProcessors.Add(new ODataQueryParamsProcessor("/apps/{app}/assets", "assets", false));
return settings;
settings.Title = "Squidex API";
}
public static SwaggerSettings<T> ConfigureIdentity<T>(this SwaggerSettings<T> settings, MyUrlsOptions urlOptions) where T : SwaggerGeneratorSettings, new()
public static void ConfigureIdentity<T>(this T settings, MyUrlsOptions urlOptions) where T : SwaggerGeneratorSettings
{
settings.GeneratorSettings.DocumentProcessors.Add(
settings.DocumentProcessors.Add(
new SecurityDefinitionAppender(
Constants.SecurityDefinition, SwaggerHelper.CreateOAuthSchema(urlOptions)));
settings.GeneratorSettings.OperationProcessors.Add(new ScopesProcessor());
return settings;
settings.OperationProcessors.Add(new ScopesProcessor());
}
public static SwaggerSettings<T> ConfigurePaths<T>(this SwaggerSettings<T> settings, MyUrlsOptions urlOptions) where T : SwaggerGeneratorSettings, new()
public static void ConfigureSchemaSettings<T>(this T settings) where T : SwaggerGeneratorSettings
{
settings.SwaggerRoute = $"{Constants.ApiPrefix}/swagger/v1/swagger.json";
settings.DefaultEnumHandling = EnumHandling.String;
settings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
settings.PostProcess = document =>
{
document.BasePath = Constants.ApiPrefix;
document.Info.ExtensionData = new Dictionary<string, object>
{
["x-logo"] = new { url = urlOptions.BuildUrl("images/logo-white.png", false), backgroundColor = "#3f83df" }
};
};
settings.MiddlewareBasePath = Constants.ApiPrefix;
return settings;
}
public static SwaggerSettings<T> ConfigureSchemaSettings<T>(this SwaggerSettings<T> settings) where T : SwaggerGeneratorSettings, new()
{
settings.GeneratorSettings.DefaultEnumHandling = EnumHandling.String;
settings.GeneratorSettings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase;
settings.GeneratorSettings.TypeMappers = new List<ITypeMapper>
settings.TypeMappers = new List<ITypeMapper>
{
new PrimitiveTypeMapper(typeof(Instant), schema =>
{
@ -103,13 +103,11 @@ namespace Squidex.Areas.Api.Config.Swagger
new PrimitiveTypeMapper(typeof(RefToken), s => s.Type = JsonObjectType.String)
};
settings.GeneratorSettings.DocumentProcessors.Add(new RuleActionProcessor());
settings.GeneratorSettings.DocumentProcessors.Add(new XmlTagProcessor());
settings.GeneratorSettings.OperationProcessors.Add(new TagByGroupNameProcessor());
settings.GeneratorSettings.OperationProcessors.Add(new XmlResponseTypesProcessor());
settings.DocumentProcessors.Add(new RuleActionProcessor());
settings.DocumentProcessors.Add(new XmlTagProcessor());
return settings;
settings.OperationProcessors.Add(new TagByGroupNameProcessor());
settings.OperationProcessors.Add(new XmlResponseTypesProcessor());
}
}
}

3
src/Squidex/Areas/Api/Config/Swagger/XmlResponseTypesProcessor.cs

@ -70,8 +70,7 @@ namespace Squidex.Areas.Api.Config.Swagger
private static void RemoveOkResponse(SwaggerOperation operation)
{
if (operation.Responses.TryGetValue("200", out var response) &&
response.Description?.Contains("=>") == true)
if (operation.Responses.TryGetValue("200", out var response) && response.Description?.Contains("=>") == true)
{
operation.Responses.Remove("200");
}

2
src/Squidex/Areas/Api/Controllers/Contents/ContentSwaggerController.cs

@ -47,7 +47,7 @@ namespace Squidex.Areas.Api.Controllers.Contents
{
var schemas = await appProvider.GetSchemasAsync(AppId);
var swaggerDocument = await schemasSwaggerGenerator.Generate(App, schemas);
var swaggerDocument = await schemasSwaggerGenerator.Generate(HttpContext, App, schemas);
return Content(swaggerDocument.ToJson(), "application/json");
}

21
src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs

@ -14,6 +14,7 @@ using NJsonSchema;
using NSwag;
using NSwag.AspNetCore;
using NSwag.SwaggerGeneration;
using Squidex.Areas.Api.Config.Swagger;
using Squidex.Config;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Schemas;
@ -24,26 +25,26 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator
{
public sealed class SchemasSwaggerGenerator
{
private readonly HttpContext context;
private readonly SwaggerSettings<SwaggerGeneratorSettings> settings;
private readonly MyUrlsOptions urlOptions;
private readonly SwaggerDocumentSettings settings = new SwaggerDocumentSettings();
private SwaggerJsonSchemaGenerator schemaGenerator;
private JsonSchemaResolver schemaResolver;
private SwaggerDocument document;
private JsonSchemaResolver schemaResolver;
public SchemasSwaggerGenerator(IHttpContextAccessor context, SwaggerSettings<SwaggerGeneratorSettings> settings, IOptions<MyUrlsOptions> urlOptions)
public SchemasSwaggerGenerator(IOptions<MyUrlsOptions> urlOptions)
{
this.context = context.HttpContext;
this.settings = settings;
this.urlOptions = urlOptions.Value;
settings.ConfigureNames();
settings.Conf
}
public async Task<SwaggerDocument> Generate(IAppEntity app, IEnumerable<ISchemaEntity> schemas)
public async Task<SwaggerDocument> Generate(HttpContext httpContext, IAppEntity app, IEnumerable<ISchemaEntity> schemas)
{
document = SwaggerHelper.CreateApiDocument(context, urlOptions, app.Name);
document = SwaggerHelper.CreateApiDocument(httpContext, urlOptions, app.Name);
schemaGenerator = new SwaggerJsonSchemaGenerator(settings.GeneratorSettings);
schemaResolver = new SwaggerSchemaResolver(document, settings.GeneratorSettings);
schemaGenerator = new SwaggerJsonSchemaGenerator(settings);
schemaResolver = new SwaggerSchemaResolver(document, settings);
GenerateSchemasOperations(schemas, app);

6
src/Squidex/Areas/Api/Controllers/Rules/Models/RuleActionProcessor.cs

@ -25,15 +25,13 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models
{
var discriminator = new OpenApiDiscriminator
{
JsonInheritanceConverter = new JsonInheritanceConverter("actionType", typeof(RuleAction)),
PropertyName = "actionType"
JsonInheritanceConverter = new JsonInheritanceConverter("actionType", typeof(RuleAction)), PropertyName = "actionType"
};
schema.DiscriminatorObject = discriminator;
schema.Properties["actionType"] = new JsonProperty
{
Type = JsonObjectType.String,
IsRequired = true
Type = JsonObjectType.String, IsRequired = true
};
foreach (var derived in RuleElementRegistry.Actions)

Loading…
Cancel
Save