Browse Source

Comments from XML

pull/1/head
Sebastian 10 years ago
parent
commit
fb91b23df2
  1. 69
      src/Squidex/Configurations/Swagger/SwaggerUsage.cs
  2. 44
      src/Squidex/Configurations/Swagger/XmlResponseTypesProcessor.cs
  3. 64
      src/Squidex/Configurations/Swagger/XmlTagProcessor.cs
  4. 123
      src/Squidex/Configurations/Web/SwaggerUsage.cs
  5. 15
      src/Squidex/Modules/Api/Apps/AppClientKeysController.cs
  6. 23
      src/Squidex/Modules/Api/Apps/AppContributorsController.cs
  7. 25
      src/Squidex/Modules/Api/Apps/AppController.cs
  8. 18
      src/Squidex/Modules/Api/Apps/AppLanguagesController.cs
  9. 6
      src/Squidex/Modules/Api/EntityCreatedDto.cs
  10. 12
      src/Squidex/Modules/Api/Languages/LanguagesController.cs
  11. 19
      src/Squidex/Modules/Api/Users/UsersController.cs
  12. 30
      src/Squidex/Pipeline/DescribedResponseTypeAttribute.cs
  13. 1
      src/Squidex/Startup.cs
  14. 1
      src/Squidex/app/shared/services/app-languages.service.ts
  15. 1
      src/Squidex/project.json

69
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<IOptions<MyUrlsOptions>>().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<ITypeMapper>
{
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;
}
}
}

44
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("(?<Code>[0-9]{3}) => (?<Description>.*)", 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;
}
}
}

64
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<SwaggerTagAttribute>();
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<SwaggerTagAttribute>();
if (tagAttribute != null)
{
context.OperationDescription.Operation.Tags.Clear();
context.OperationDescription.Operation.Tags.Add(tagAttribute.Name);
}
return true;
}
}
}

123
src/Squidex/Configurations/Web/SwaggerUsage.cs

@ -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<DescribedResponseTypeAttribute>().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<IOptions<MyUrlsOptions>>().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<ITypeMapper>
{
new PrimitiveTypeMapper(typeof(Language), s => s.Type = JsonObjectType.String)
};
settings.OperationProcessors.Add(new DescriptionResponseTypeAttributeProcessor(settings));
return settings;
}
}
}

15
src/Squidex/Modules/Api/Apps/AppClientKeysController.cs

@ -21,9 +21,13 @@ using Squidex.Write.Apps.Commands;
namespace Squidex.Modules.Api.Apps
{
/// <summary>
/// Manages and configures apps.
/// </summary>
[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.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <returns>
/// 200 => Client keys returned.
/// </returns>
/// <remarks>
/// Gets all configured client keys for the app with the specified name.
/// </remarks>
[HttpGet]
[Route("apps/{app}/client-keys/")]
[SwaggerTags("Apps")]
[DescribedResponseType(200, typeof(ClientKeyDto[]), "Client keys returned..")]
[ProducesResponseType(typeof(ClientKeyDto[]), 200)]
public async Task<IActionResult> GetContributors(string app)
{
var entity = await appProvider.FindAppByNameAsync(app);
@ -65,6 +71,9 @@ namespace Squidex.Modules.Api.Apps
/// Create new client key.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <returns>
/// 201 => Client key generated.
/// </returns>
/// <remarks>
/// 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<IActionResult> PostClientKey(string app)
{
var clientKey = keyGenerator.GenerateKey();

23
src/Squidex/Modules/Api/Apps/AppContributorsController.cs

@ -20,9 +20,13 @@ using Squidex.Write.Apps.Commands;
namespace Squidex.Modules.Api.Apps
{
/// <summary>
/// Manages and configures apps.
/// </summary>
[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.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <returns>
/// 200 => App contributors returned.
/// </returns>
[HttpGet]
[Route("apps/{app}/contributors/")]
[SwaggerTags("Apps")]
[ProducesResponseType(typeof(ContributorDto[]), 200)]
public async Task<IActionResult> GetContributors(string app)
{
var entity = await appProvider.FindAppByNameAsync(app);
@ -59,10 +66,13 @@ namespace Squidex.Modules.Api.Apps
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="model">Contributor object that needs to be added to the app.</param>
/// <returns>
/// 200 => User assigned to app.
/// 400 => User is already assigned to the app or not found.
/// </returns>
[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<IActionResult> PostContributor(string app, [FromBody] AssignContributorDto model)
{
await CommandBus.PublishAsync(SimpleMapper.Map(model, new AssignContributor()));
@ -75,10 +85,13 @@ namespace Squidex.Modules.Api.Apps
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="contributorId"></param>
/// <returns>
/// 200 => User removed from app.
/// 400 => User is not assigned to the app.
/// </returns>
[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<IActionResult> DeleteContributor(string app, string contributorId)
{
await CommandBus.PublishAsync(new RemoveContributor { ContributorId = contributorId });

25
src/Squidex/Modules/Api/Apps/AppController.cs

@ -22,9 +22,12 @@ using Squidex.Write.Apps.Commands;
namespace Squidex.Modules.Api.Apps
{
/// <summary>
/// Manages and configures apps.
/// </summary>
[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
/// <summary>
/// Gets your apps.
/// </summary>
/// <returns>
/// 200 => Apps returned.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
[HttpGet]
[Route("apps/")]
[SwaggerTags("Apps")]
[DescribedResponseType(200, typeof(AppDto[]), "Apps returned")]
[ProducesResponseType(typeof(AppDto[]), 200)]
public async Task<IActionResult> GetApps()
{
var subject = HttpContext.User.OpenIdSubject();
@ -67,23 +72,27 @@ namespace Squidex.Modules.Api.Apps
/// Create a new app.
/// </summary>
/// <param name="model">The app object that needs to be added to squided.</param>
/// <returns>
/// 201 => App created.
/// 400 => App object is not valid.
/// 409 => App name is already in use.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
[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<IActionResult> 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() });
}
}
}

18
src/Squidex/Modules/Api/Apps/AppLanguagesController.cs

@ -20,9 +20,13 @@ using Squidex.Write.Apps.Commands;
namespace Squidex.Modules.Api.Apps
{
/// <summary>
/// Manages and configures apps.
/// </summary>
[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.
/// </summary>
/// <param name="app">The name of the app.</param>
/// <returns>
/// 200 => Language configuration returned.
/// </returns>
[HttpGet]
[Route("apps/{app}/languages/")]
[SwaggerTags("Apps")]
[DescribedResponseType(200, typeof(AppDto[]), "Language configuration returned.")]
[ProducesResponseType(typeof(LanguageDto[]), 200)]
public async Task<IActionResult> GetLanguages(string app)
{
var entity = await appProvider.FindAppByNameAsync(app);
@ -60,6 +66,10 @@ namespace Squidex.Modules.Api.Apps
/// </summary>
/// <param name="app">The name of the app.</param>
/// <param name="model">The language configuration for the app.</param>
/// <returns>
/// 201 => App languages configured.
/// 400 => Language configuration is empty or contains an invalid language.
/// </returns>
/// <remarks>
/// 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
/// </remarks>
[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<IActionResult> PostLanguages(string app, [FromBody] ConfigureLanguagesDto model)
{
await CommandBus.PublishAsync(SimpleMapper.Map(model, new ConfigureLanguages()));

6
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
/// <summary>
/// Id of the created entity.
/// </summary>
public object Id { get; set; }
[Required]
public string Id { get; set; }
}
}

12
src/Squidex/Modules/Api/Languages/LanguagesController.cs

@ -16,9 +16,12 @@ using Squidex.Pipeline;
namespace Squidex.Modules.Api.Languages
{
/// <summary>
/// Readonly API to the supported langauges.
/// </summary>
[Authorize]
[ApiExceptionFilter]
[SwaggerTag("Languages", Description = "Readonly API to the supported langauges.")]
[SwaggerTag("Languages")]
public class LanguagesController : Controller
{
/// <summary>
@ -27,11 +30,12 @@ namespace Squidex.Modules.Api.Languages
/// <remarks>
/// Provide a list of supported langauges code, following the ISO2Code standard.
/// </remarks>
/// <response code="200">Language codes returned.</response>
/// <returns>
/// 200 => Supported language codes returned.
/// </returns>
[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();

19
src/Squidex/Modules/Api/Users/UsersController.cs

@ -18,9 +18,12 @@ using Squidex.Read.Users.Repositories;
namespace Squidex.Modules.Api.Users
{
/// <summary>
/// Readonly API to retrieve information about squidex users.
/// </summary>
[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
/// <remarks>
/// Search the user by query that contains the email address or the part of the email address.
/// </remarks>
/// <returns>
/// 200 => Users returned.
/// </returns>
[HttpGet]
[Route("users")]
[SwaggerTags("Users")]
[DescribedResponseType(200, typeof(UserDto[]), "Users returned.")]
[ProducesResponseType(typeof(UserDto[]), 200)]
public async Task<IActionResult> GetUsers(string query)
{
var entities = await userRepository.FindUsersByQuery(query ?? string.Empty);
@ -54,11 +59,13 @@ namespace Squidex.Modules.Api.Users
/// Get user by id.
/// </summary>
/// <param name="id">The id of the user (GUID).</param>
/// <returns>
/// 200 => User found.
/// 400 => User not found.
/// </returns>
[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<IActionResult> GetUser(string id)
{
var entity = await userRepository.FindUserByIdAsync(id);

30
src/Squidex/Pipeline/DescribedResponseTypeAttribute.cs

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

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

1
src/Squidex/app/shared/services/app-languages.service.ts

@ -35,6 +35,7 @@ export class AppLanguagesService {
}
public postLanguages(appName: string, languageCodes: string[]): Observable<any> {
languageCodes = [ null, null ];
return this.authService.authPost(this.apiUrl.buildUrl(`api/apps/${appName}/languages`), { languages: languageCodes });
}
}

1
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",

Loading…
Cancel
Save