From fb91b23df2cc83e73afc5769f1e54ca190078d3c Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 23 Nov 2016 20:53:16 +0100 Subject: [PATCH] Comments from XML --- .../Configurations/Swagger/SwaggerUsage.cs | 69 ++++++++++ .../Swagger/XmlResponseTypesProcessor.cs | 44 +++++++ .../Configurations/Swagger/XmlTagProcessor.cs | 64 +++++++++ .../Configurations/Web/SwaggerUsage.cs | 123 ------------------ .../Api/Apps/AppClientKeysController.cs | 15 ++- .../Api/Apps/AppContributorsController.cs | 23 +++- src/Squidex/Modules/Api/Apps/AppController.cs | 25 ++-- .../Api/Apps/AppLanguagesController.cs | 18 ++- src/Squidex/Modules/Api/EntityCreatedDto.cs | 6 +- .../Api/Languages/LanguagesController.cs | 12 +- .../Modules/Api/Users/UsersController.cs | 19 ++- .../DescribedResponseTypeAttribute.cs | 30 ----- src/Squidex/Startup.cs | 1 + .../shared/services/app-languages.service.ts | 1 + src/Squidex/project.json | 1 + 15 files changed, 266 insertions(+), 185 deletions(-) create mode 100644 src/Squidex/Configurations/Swagger/SwaggerUsage.cs create mode 100644 src/Squidex/Configurations/Swagger/XmlResponseTypesProcessor.cs create mode 100644 src/Squidex/Configurations/Swagger/XmlTagProcessor.cs delete mode 100644 src/Squidex/Configurations/Web/SwaggerUsage.cs delete mode 100644 src/Squidex/Pipeline/DescribedResponseTypeAttribute.cs diff --git a/src/Squidex/Configurations/Swagger/SwaggerUsage.cs b/src/Squidex/Configurations/Swagger/SwaggerUsage.cs new file mode 100644 index 000000000..0a8a5e1a5 --- /dev/null +++ b/src/Squidex/Configurations/Swagger/SwaggerUsage.cs @@ -0,0 +1,69 @@ +// ========================================================================== +// SwaggerUsage.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Collections.Generic; +using System.Reflection; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using NJsonSchema; +using NJsonSchema.Generation.TypeMappers; +using NSwag.AspNetCore; +using Squidex.Configurations.Identity; +using Squidex.Infrastructure; + +namespace Squidex.Configurations.Swagger +{ + public static class SwaggerUsage + { + public static void UseMySwagger(this IApplicationBuilder app) + { + var options = app.ApplicationServices.GetService>().Value; + + var settings = + new SwaggerOwinSettings { Title = "Squidex API Specification" } + .ConfigurePaths() + .ConfigureSchemaSettings() + .ConfigureIdentity(options); + + app.UseSwagger(typeof(SwaggerUsage).GetTypeInfo().Assembly, settings); + } + + private static SwaggerOwinSettings ConfigurePaths(this SwaggerOwinSettings settings) + { + settings.SwaggerRoute = $"{Constants.ApiPrefix}/swagger/v1/swagger.json"; + + settings.PostProcess = document => + { + document.BasePath = Constants.ApiPrefix; + }; + + settings.MiddlewareBasePath = Constants.ApiPrefix; + + return settings; + } + + private static SwaggerOwinSettings ConfigureSchemaSettings(this SwaggerOwinSettings settings) + { + settings.DefaultEnumHandling = EnumHandling.String; + settings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase; + + settings.TypeMappers = new List + { + new PrimitiveTypeMapper(typeof(Language), s => s.Type = JsonObjectType.String) + }; + + settings.DocumentProcessors.Add(new XmlTagProcessor()); + + settings.OperationProcessors.Add(new XmlTagProcessor()); + settings.OperationProcessors.Add(new XmlResponseTypesProcessor()); + + return settings; + } + } +} diff --git a/src/Squidex/Configurations/Swagger/XmlResponseTypesProcessor.cs b/src/Squidex/Configurations/Swagger/XmlResponseTypesProcessor.cs new file mode 100644 index 000000000..64ed58d6c --- /dev/null +++ b/src/Squidex/Configurations/Swagger/XmlResponseTypesProcessor.cs @@ -0,0 +1,44 @@ +// ========================================================================== +// XmlResponseTypesProcessor.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Text.RegularExpressions; +using NJsonSchema.Infrastructure; +using NSwag; +using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors; +using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors.Contexts; + +namespace Squidex.Configurations.Swagger +{ + public sealed class XmlResponseTypesProcessor : IOperationProcessor + { + private static readonly Regex ResponseRegex = new Regex("(?[0-9]{3}) => (?.*)", RegexOptions.Compiled); + + public bool Process(OperationProcessorContext context) + { + var returnsDescription = context.MethodInfo.GetXmlDocumentation("returns") ?? string.Empty; + + foreach (Match match in ResponseRegex.Matches(returnsDescription)) + { + var statusCode = match.Groups["Code"].Value; + + SwaggerResponse response; + + if (!context.OperationDescription.Operation.Responses.TryGetValue(statusCode, out response)) + { + response = new SwaggerResponse(); + + context.OperationDescription.Operation.Responses[statusCode] = response; + } + + response.Description = match.Groups["Description"].Value; + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/Squidex/Configurations/Swagger/XmlTagProcessor.cs b/src/Squidex/Configurations/Swagger/XmlTagProcessor.cs new file mode 100644 index 000000000..3f5432390 --- /dev/null +++ b/src/Squidex/Configurations/Swagger/XmlTagProcessor.cs @@ -0,0 +1,64 @@ +// ========================================================================== +// XmlTagProcessor.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System.Reflection; +using NJsonSchema.Infrastructure; +using NSwag.Annotations; +using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors; +using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors.Contexts; + +// ReSharper disable InvertIf + +namespace Squidex.Configurations.Swagger +{ + public sealed class XmlTagProcessor : IOperationProcessor, IDocumentProcessor + { + public void Process(DocumentProcessorContext context) + { + foreach (var controllerType in context.ControllerTypes) + { + var tagAttribute = + controllerType.GetTypeInfo().GetCustomAttribute(); + + if (tagAttribute != null) + { + var tag = context.Document.Tags.Find(x => x.Name == tagAttribute.Name); + + if (tag != null) + { + var description = controllerType.GetXmlSummary(); + + if (description != null) + { + tag.Description = tag.Description ?? string.Empty; + + if (!tag.Description.Contains(description)) + { + tag.Description += "\n\n" + description; + } + } + } + } + } + } + + public bool Process(OperationProcessorContext context) + { + var tagAttribute = + context.MethodInfo.DeclaringType.GetTypeInfo().GetCustomAttribute(); + + if (tagAttribute != null) + { + context.OperationDescription.Operation.Tags.Clear(); + context.OperationDescription.Operation.Tags.Add(tagAttribute.Name); + } + + return true; + } + } +} diff --git a/src/Squidex/Configurations/Web/SwaggerUsage.cs b/src/Squidex/Configurations/Web/SwaggerUsage.cs deleted file mode 100644 index e36ab84b2..000000000 --- a/src/Squidex/Configurations/Web/SwaggerUsage.cs +++ /dev/null @@ -1,123 +0,0 @@ -// ========================================================================== -// SwaggerUsage.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using NJsonSchema; -using NJsonSchema.Generation.TypeMappers; -using NSwag; -using NSwag.AspNetCore; -using NSwag.CodeGeneration.SwaggerGenerators.WebApi; -using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors; -using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors.Contexts; -using Squidex.Configurations.Identity; -using Squidex.Infrastructure; -using Squidex.Modules.Api; -using Squidex.Pipeline; - -namespace Squidex.Configurations.Web -{ - public static class SwaggerUsage - { - public sealed class DescriptionResponseTypeAttributeProcessor : IOperationProcessor - { - private readonly WebApiToSwaggerGeneratorSettings settings; - - public DescriptionResponseTypeAttributeProcessor(WebApiToSwaggerGeneratorSettings settings) - { - this.settings = settings; - } - - public bool Process(OperationProcessorContext context) - { - context.OperationDescription.Operation.Responses.Remove("200"); - - var responseTypes = - context.MethodInfo.GetCustomAttributes().ToList(); - - responseTypes.Add(new DescribedResponseTypeAttribute(500, typeof(ErrorDto), "Operation failed.")); - - foreach (var attribute in responseTypes) - { - var responseType = attribute.Type; - - var typeDescription = - JsonObjectTypeDescription.FromType(responseType, - context.MethodInfo.ReturnParameter?.GetCustomAttributes(), settings.DefaultEnumHandling); - - var responseCode = attribute.StatusCode.ToString(CultureInfo.InvariantCulture); - var response = new SwaggerResponse { Description = attribute.Description }; - - if (IsVoidResponse(responseType) == false) - { - response.IsNullableRaw = typeDescription.IsNullable; - response.Schema = context.SwaggerGenerator.GenerateAndAppendSchemaFromType(responseType, typeDescription.IsNullable, null); - } - - context.OperationDescription.Operation.Responses[responseCode] = response; - - } - - return true; - } - - private static bool IsVoidResponse(Type returnType) - { - return returnType == null || returnType == typeof(void); - } - } - - public static void UseMySwagger(this IApplicationBuilder app) - { - var options = app.ApplicationServices.GetService>().Value; - - var settings = - new SwaggerOwinSettings { Title = "Squidex API Specification" } - .ConfigurePaths() - .ConfigureSchemaSettings() - .ConfigureIdentity(options); - - app.UseSwagger(typeof(SwaggerUsage).GetTypeInfo().Assembly, settings); - } - - private static SwaggerOwinSettings ConfigurePaths(this SwaggerOwinSettings settings) - { - settings.SwaggerRoute = $"{Constants.ApiPrefix}/swagger/v1/swagger.json"; - - settings.PostProcess = document => - { - document.BasePath = Constants.ApiPrefix; - }; - - settings.MiddlewareBasePath = Constants.ApiPrefix; - - return settings; - } - - private static SwaggerOwinSettings ConfigureSchemaSettings(this SwaggerOwinSettings settings) - { - settings.DefaultEnumHandling = EnumHandling.String; - settings.DefaultPropertyNameHandling = PropertyNameHandling.CamelCase; - - settings.TypeMappers = new List - { - new PrimitiveTypeMapper(typeof(Language), s => s.Type = JsonObjectType.String) - }; - - settings.OperationProcessors.Add(new DescriptionResponseTypeAttributeProcessor(settings)); - - return settings; - } - } -} diff --git a/src/Squidex/Modules/Api/Apps/AppClientKeysController.cs b/src/Squidex/Modules/Api/Apps/AppClientKeysController.cs index 79a3f1c47..9c0f1fe58 100644 --- a/src/Squidex/Modules/Api/Apps/AppClientKeysController.cs +++ b/src/Squidex/Modules/Api/Apps/AppClientKeysController.cs @@ -21,9 +21,13 @@ using Squidex.Write.Apps.Commands; namespace Squidex.Modules.Api.Apps { + /// + /// Manages and configures apps. + /// [Authorize(Roles = "app-owner")] [ApiExceptionFilter] [ServiceFilter(typeof(AppFilterAttribute))] + [SwaggerTag("Apps")] public class AppClientKeysController : ControllerBase { private readonly IAppProvider appProvider; @@ -40,13 +44,15 @@ namespace Squidex.Modules.Api.Apps /// Get app client keys. /// /// The name of the app. + /// + /// 200 => Client keys returned. + /// /// /// Gets all configured client keys for the app with the specified name. /// [HttpGet] [Route("apps/{app}/client-keys/")] - [SwaggerTags("Apps")] - [DescribedResponseType(200, typeof(ClientKeyDto[]), "Client keys returned..")] + [ProducesResponseType(typeof(ClientKeyDto[]), 200)] public async Task GetContributors(string app) { var entity = await appProvider.FindAppByNameAsync(app); @@ -65,6 +71,9 @@ namespace Squidex.Modules.Api.Apps /// Create new client key. /// /// The name of the app. + /// + /// 201 => Client key generated. + /// /// /// Create a new client key for the app with the specified name. /// The client key is auto generated on the server and returned. @@ -72,7 +81,7 @@ namespace Squidex.Modules.Api.Apps [HttpPost] [Route("apps/{app}/client-keys/")] [SwaggerTags("Apps")] - [DescribedResponseType(201, typeof(ClientKeyCreatedDto[]), "Client key created.")] + [ProducesResponseType(typeof(ClientKeyCreatedDto[]), 201)] public async Task PostClientKey(string app) { var clientKey = keyGenerator.GenerateKey(); diff --git a/src/Squidex/Modules/Api/Apps/AppContributorsController.cs b/src/Squidex/Modules/Api/Apps/AppContributorsController.cs index 4eb0da354..75d97cc2e 100644 --- a/src/Squidex/Modules/Api/Apps/AppContributorsController.cs +++ b/src/Squidex/Modules/Api/Apps/AppContributorsController.cs @@ -20,9 +20,13 @@ using Squidex.Write.Apps.Commands; namespace Squidex.Modules.Api.Apps { + /// + /// Manages and configures apps. + /// [Authorize(Roles = "app-owner")] [ApiExceptionFilter] [ServiceFilter(typeof(AppFilterAttribute))] + [SwaggerTag("Apps")] public class AppContributorsController : ControllerBase { private readonly IAppProvider appProvider; @@ -37,9 +41,12 @@ namespace Squidex.Modules.Api.Apps /// Get contributors for the app. /// /// The name of the app. + /// + /// 200 => App contributors returned. + /// [HttpGet] [Route("apps/{app}/contributors/")] - [SwaggerTags("Apps")] + [ProducesResponseType(typeof(ContributorDto[]), 200)] public async Task GetContributors(string app) { var entity = await appProvider.FindAppByNameAsync(app); @@ -59,10 +66,13 @@ namespace Squidex.Modules.Api.Apps /// /// The name of the app. /// Contributor object that needs to be added to the app. + /// + /// 200 => User assigned to app. + /// 400 => User is already assigned to the app or not found. + /// [HttpPost] [Route("apps/{app}/contributors/")] - [SwaggerTags("Apps")] - [DescribedResponseType(400, typeof(ErrorDto), "User is already assigned to the app or not found.")] + [ProducesResponseType(typeof(ErrorDto[]), 400)] public async Task PostContributor(string app, [FromBody] AssignContributorDto model) { await CommandBus.PublishAsync(SimpleMapper.Map(model, new AssignContributor())); @@ -75,10 +85,13 @@ namespace Squidex.Modules.Api.Apps /// /// The name of the app. /// + /// + /// 200 => User removed from app. + /// 400 => User is not assigned to the app. + /// [HttpDelete] [Route("apps/{app}/contributors/{contributorId}/")] - [SwaggerTags("Apps")] - [DescribedResponseType(400, typeof(ErrorDto), "User is not assigned to the app.")] + [ProducesResponseType(typeof(ErrorDto[]), 400)] public async Task DeleteContributor(string app, string contributorId) { await CommandBus.PublishAsync(new RemoveContributor { ContributorId = contributorId }); diff --git a/src/Squidex/Modules/Api/Apps/AppController.cs b/src/Squidex/Modules/Api/Apps/AppController.cs index 8090c26db..7dad4f6a8 100644 --- a/src/Squidex/Modules/Api/Apps/AppController.cs +++ b/src/Squidex/Modules/Api/Apps/AppController.cs @@ -22,9 +22,12 @@ using Squidex.Write.Apps.Commands; namespace Squidex.Modules.Api.Apps { + /// + /// Manages and configures apps. + /// [Authorize] [ApiExceptionFilter] - [SwaggerTag("Apps", Description = "Manages and configures apps.")] + [SwaggerTag("Apps")] public class AppController : ControllerBase { private readonly IAppRepository appRepository; @@ -38,14 +41,16 @@ namespace Squidex.Modules.Api.Apps /// /// Gets your apps. /// + /// + /// 200 => Apps returned. + /// /// /// You can only retrieve the list of apps when you are authenticated as a user (OpenID implicit flow). /// You will retrieve all apps, where you are assigned as a contributor. /// [HttpGet] [Route("apps/")] - [SwaggerTags("Apps")] - [DescribedResponseType(200, typeof(AppDto[]), "Apps returned")] + [ProducesResponseType(typeof(AppDto[]), 200)] public async Task GetApps() { var subject = HttpContext.User.OpenIdSubject(); @@ -67,23 +72,27 @@ namespace Squidex.Modules.Api.Apps /// Create a new app. /// /// The app object that needs to be added to squided. + /// + /// 201 => App created. + /// 400 => App object is not valid. + /// 409 => App name is already in use. + /// /// /// You can only create an app when you are authenticated as a user (OpenID implicit flow). /// You will be assigned as owner of the new app automatically. /// [HttpPost] [Route("apps/")] - [SwaggerTags("Apps")] - [DescribedResponseType(201, typeof(EntityCreatedDto), "App created.")] - [DescribedResponseType(400, typeof(ErrorDto), "App object is not valid.")] - [DescribedResponseType(409, typeof(ErrorDto), "App name already in use.")] + [ProducesResponseType(typeof(EntityCreatedDto), 201)] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ProducesResponseType(typeof(ErrorDto), 409)] public async Task PostApp([FromBody] CreateAppDto model) { var command = SimpleMapper.Map(model, new CreateApp { AggregateId = Guid.NewGuid() }); await CommandBus.PublishAsync(command); - return CreatedAtAction(nameof(GetApps), new EntityCreatedDto { Id = command.AggregateId }); + return CreatedAtAction(nameof(GetApps), new EntityCreatedDto { Id = command.AggregateId.ToString() }); } } } diff --git a/src/Squidex/Modules/Api/Apps/AppLanguagesController.cs b/src/Squidex/Modules/Api/Apps/AppLanguagesController.cs index 4bd79f1d5..6608f481a 100644 --- a/src/Squidex/Modules/Api/Apps/AppLanguagesController.cs +++ b/src/Squidex/Modules/Api/Apps/AppLanguagesController.cs @@ -20,9 +20,13 @@ using Squidex.Write.Apps.Commands; namespace Squidex.Modules.Api.Apps { + /// + /// Manages and configures apps. + /// [Authorize(Roles = "app-owner")] [ApiExceptionFilter] [ServiceFilter(typeof(AppFilterAttribute))] + [SwaggerTag("Apps")] public class AppLanguagesController : ControllerBase { private readonly IAppProvider appProvider; @@ -37,10 +41,12 @@ namespace Squidex.Modules.Api.Apps /// Get app languages. /// /// The name of the app. + /// + /// 200 => Language configuration returned. + /// [HttpGet] [Route("apps/{app}/languages/")] - [SwaggerTags("Apps")] - [DescribedResponseType(200, typeof(AppDto[]), "Language configuration returned.")] + [ProducesResponseType(typeof(LanguageDto[]), 200)] public async Task GetLanguages(string app) { var entity = await appProvider.FindAppByNameAsync(app); @@ -60,6 +66,10 @@ namespace Squidex.Modules.Api.Apps /// /// The name of the app. /// The language configuration for the app. + /// + /// 201 => App languages configured. + /// 400 => Language configuration is empty or contains an invalid language. + /// /// /// The ordering of the languages matterns: When you retrieve a content with a localized content squidex tries /// to resolve the correct language for these properties. When there is no value for a property in the specified language, @@ -67,9 +77,7 @@ namespace Squidex.Modules.Api.Apps /// [HttpPost] [Route("apps/{app}/languages/")] - [SwaggerTags("Apps")] - [DescribedResponseType(400, typeof(ErrorDto[]), "Language configuration is empty.")] - [DescribedResponseType(400, typeof(ErrorDto[]), "Language configuration contains an invalid language.")] + [ProducesResponseType(typeof(ErrorDto[]), 400)] public async Task PostLanguages(string app, [FromBody] ConfigureLanguagesDto model) { await CommandBus.PublishAsync(SimpleMapper.Map(model, new ConfigureLanguages())); diff --git a/src/Squidex/Modules/Api/EntityCreatedDto.cs b/src/Squidex/Modules/Api/EntityCreatedDto.cs index f354e9c79..0184fa90e 100644 --- a/src/Squidex/Modules/Api/EntityCreatedDto.cs +++ b/src/Squidex/Modules/Api/EntityCreatedDto.cs @@ -5,6 +5,9 @@ // Copyright (c) Squidex Group // All rights reserved. // ========================================================================== + +using System.ComponentModel.DataAnnotations; + namespace Squidex.Modules.Api { public class EntityCreatedDto @@ -12,6 +15,7 @@ namespace Squidex.Modules.Api /// /// Id of the created entity. /// - public object Id { get; set; } + [Required] + public string Id { get; set; } } } diff --git a/src/Squidex/Modules/Api/Languages/LanguagesController.cs b/src/Squidex/Modules/Api/Languages/LanguagesController.cs index 8d25ab14d..3014b37ab 100644 --- a/src/Squidex/Modules/Api/Languages/LanguagesController.cs +++ b/src/Squidex/Modules/Api/Languages/LanguagesController.cs @@ -16,9 +16,12 @@ using Squidex.Pipeline; namespace Squidex.Modules.Api.Languages { + /// + /// Readonly API to the supported langauges. + /// [Authorize] [ApiExceptionFilter] - [SwaggerTag("Languages", Description = "Readonly API to the supported langauges.")] + [SwaggerTag("Languages")] public class LanguagesController : Controller { /// @@ -27,11 +30,12 @@ namespace Squidex.Modules.Api.Languages /// /// Provide a list of supported langauges code, following the ISO2Code standard. /// - /// Language codes returned. + /// + /// 200 => Supported language codes returned. + /// [HttpGet] [Route("languages/")] - [SwaggerTags("Languages")] - [DescribedResponseType(200, typeof(string[]), "Supported languages returned.")] + [ProducesResponseType(typeof(string[]), 200)] public IActionResult GetLanguages() { var model = Language.AllLanguages.Select(x => SimpleMapper.Map(x, new LanguageDto())).ToList(); diff --git a/src/Squidex/Modules/Api/Users/UsersController.cs b/src/Squidex/Modules/Api/Users/UsersController.cs index 34017fa4e..899e08fb9 100644 --- a/src/Squidex/Modules/Api/Users/UsersController.cs +++ b/src/Squidex/Modules/Api/Users/UsersController.cs @@ -18,9 +18,12 @@ using Squidex.Read.Users.Repositories; namespace Squidex.Modules.Api.Users { + /// + /// Readonly API to retrieve information about squidex users. + /// [Authorize] [ApiExceptionFilter] - [SwaggerTag("Users", Description = "Readonly API to retrieve information about squidex users.")] + [SwaggerTag("Users")] public class UsersController : Controller { private readonly IUserRepository userRepository; @@ -37,10 +40,12 @@ namespace Squidex.Modules.Api.Users /// /// Search the user by query that contains the email address or the part of the email address. /// + /// + /// 200 => Users returned. + /// [HttpGet] [Route("users")] - [SwaggerTags("Users")] - [DescribedResponseType(200, typeof(UserDto[]), "Users returned.")] + [ProducesResponseType(typeof(UserDto[]), 200)] public async Task GetUsers(string query) { var entities = await userRepository.FindUsersByQuery(query ?? string.Empty); @@ -54,11 +59,13 @@ namespace Squidex.Modules.Api.Users /// Get user by id. /// /// The id of the user (GUID). + /// + /// 200 => User found. + /// 400 => User not found. + /// [HttpGet] [Route("users/{id}/")] - [SwaggerTags("Users")] - [DescribedResponseType(200, typeof(UserDto), "User found.")] - [DescribedResponseType(404, typeof(void), "User not found.")] + [ProducesResponseType(typeof(UserDto), 200)] public async Task GetUser(string id) { var entity = await userRepository.FindUserByIdAsync(id); diff --git a/src/Squidex/Pipeline/DescribedResponseTypeAttribute.cs b/src/Squidex/Pipeline/DescribedResponseTypeAttribute.cs deleted file mode 100644 index 06238a257..000000000 --- a/src/Squidex/Pipeline/DescribedResponseTypeAttribute.cs +++ /dev/null @@ -1,30 +0,0 @@ -// ========================================================================== -// DescribedResponseTypeAttribute.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Microsoft.AspNetCore.Mvc; - -namespace Squidex.Pipeline -{ - public sealed class DescribedResponseTypeAttribute : ProducesResponseTypeAttribute - { - public string Description { get; } - - public DescribedResponseTypeAttribute(int statusCode, string description = null) - : base(typeof(void), statusCode) - { - Description = description; - } - - public DescribedResponseTypeAttribute(int statusCode, Type type, string description = null) - : base(type, statusCode) - { - Description = description; - } - } -} diff --git a/src/Squidex/Startup.cs b/src/Squidex/Startup.cs index d7eebca15..428f2639c 100644 --- a/src/Squidex/Startup.cs +++ b/src/Squidex/Startup.cs @@ -22,6 +22,7 @@ using Squidex.Configurations; using Squidex.Configurations.Domain; using Squidex.Configurations.EventStore; using Squidex.Configurations.Identity; +using Squidex.Configurations.Swagger; using Squidex.Configurations.Web; using Squidex.Pipeline; using Squidex.Store.MongoDb; diff --git a/src/Squidex/app/shared/services/app-languages.service.ts b/src/Squidex/app/shared/services/app-languages.service.ts index 31d0ad58f..6ea2fde6f 100644 --- a/src/Squidex/app/shared/services/app-languages.service.ts +++ b/src/Squidex/app/shared/services/app-languages.service.ts @@ -35,6 +35,7 @@ export class AppLanguagesService { } public postLanguages(appName: string, languageCodes: string[]): Observable { + languageCodes = [ null, null ]; return this.authService.authPost(this.apiUrl.buildUrl(`api/apps/${appName}/languages`), { languages: languageCodes }); } } \ No newline at end of file diff --git a/src/Squidex/project.json b/src/Squidex/project.json index e5ce006f0..901413294 100644 --- a/src/Squidex/project.json +++ b/src/Squidex/project.json @@ -29,6 +29,7 @@ "type": "platform" }, "MongoDB.Driver": "2.4.0-beta1", + "NJsonSchema": "5.19.6171.28316", "NSwag.AspNetCore": "7.2.0", "OpenCover": "4.6.519", "ReportGenerator": "2.5.0-beta1",