// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using Microsoft.AspNetCore.Mvc; using Microsoft.Net.Http.Headers; using NSwag.Annotations; using Squidex.Areas.Api.Controllers.Schemas.Models; using Squidex.Domain.Apps.Core.GenerateFilters; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Queries; using Squidex.Shared; using Squidex.Web; namespace Squidex.Areas.Api.Controllers.Schemas { /// /// Manages and retrieves information about schemas. /// [ApiExplorerSettings(GroupName = nameof(Schemas))] public sealed class SchemasController : ApiController { private readonly IAppProvider appProvider; public SchemasController(ICommandBus commandBus, IAppProvider appProvider) : base(commandBus) { this.appProvider = appProvider; } /// /// Get schemas. /// /// The name of the app. /// /// 200 => Schemas returned. /// 404 => App not found. /// [HttpGet] [Route("apps/{app}/schemas/")] [ProducesResponseType(typeof(SchemasDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasRead)] [ApiCosts(0)] public async Task GetSchemas(string app) { var schemas = await appProvider.GetSchemasAsync(AppId, HttpContext.RequestAborted); var response = Deferred.Response(() => { return SchemasDto.FromDomain(schemas, Resources); }); Response.Headers[HeaderNames.ETag] = schemas.ToEtag(); return Ok(response); } /// /// Get a schema by name. /// /// The name of the app. /// The name of the schema to retrieve. /// /// 200 => Schema found. /// 404 => Schema or app not found. /// [HttpGet] [Route("apps/{app}/schemas/{schema}/")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasRead)] [ApiCosts(0)] public IActionResult GetSchema(string app, string schema) { var response = Deferred.Response(() => { return SchemaDto.FromDomain(Schema, Resources); }); Response.Headers[HeaderNames.ETag] = Schema.ToEtag(); return Ok(response); } /// /// Create a new schema. /// /// The name of the app. /// The schema object that needs to be added to the app. /// /// 201 => Schema created. /// 400 => Schema request not valid. /// 409 => Schema name already in use. /// [HttpPost] [Route("apps/{app}/schemas/")] [ProducesResponseType(typeof(SchemaDto), 201)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasCreate)] [ApiCosts(1)] public async Task PostSchema(string app, [FromBody] CreateSchemaDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return CreatedAtAction(nameof(GetSchema), new { app, schema = request.Name }, response); } /// /// Update a schema. /// /// The name of the app. /// The name of the schema. /// The schema object that needs to updated. /// /// 200 => Schema updated. /// 400 => Schema request not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchema(string app, string schema, [FromBody] UpdateSchemaDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Synchronize a schema. /// /// The name of the app. /// The name of the schema. /// The schema object that needs to updated. /// /// 200 => Schema updated. /// 400 => Schema request not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/sync")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutSchemaSync(string app, string schema, [FromBody] SynchronizeSchemaDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Update a schema category. /// /// The name of the app. /// The name of the schema. /// The schema object that needs to updated. /// /// 200 => Schema updated. /// 400 => Schema request not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/category")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutCategory(string app, string schema, [FromBody] ChangeCategoryDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Update the preview urls. /// /// The name of the app. /// The name of the schema. /// The preview urls for the schema. /// /// 200 => Schema updated. /// 400 => Schema request not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/preview-urls")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutPreviewUrls(string app, string schema, [FromBody] ConfigurePreviewUrlsDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Update the scripts. /// /// The name of the app. /// The name of the schema. /// The schema scripts object that needs to updated. /// /// 200 => Schema updated. /// 400 => Schema request not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/scripts/")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasScripts)] [ApiCosts(1)] public async Task PutScripts(string app, string schema, [FromBody] SchemaScriptsDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Update the rules. /// /// The name of the app. /// The name of the schema. /// The schema rules object that needs to updated. /// /// 200 => Schema updated. /// 400 => Schema request not valid. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/rules/")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasUpdate)] [ApiCosts(1)] public async Task PutRules(string app, string schema, [FromBody] ConfigureFieldRulesDto request) { var command = request.ToCommand(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Publish a schema. /// /// The name of the app. /// The name of the schema to publish. /// /// 200 => Schema published. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/publish/")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasPublish)] [ApiCosts(1)] public async Task PublishSchema(string app, string schema) { var command = new PublishSchema(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Unpublish a schema. /// /// The name of the app. /// The name of the schema to unpublish. /// /// 200 => Schema unpublished. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{schema}/unpublish/")] [ProducesResponseType(typeof(SchemaDto), StatusCodes.Status200OK)] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasPublish)] [ApiCosts(1)] public async Task UnpublishSchema(string app, string schema) { var command = new UnpublishSchema(); var response = await InvokeCommandAsync(command); return Ok(response); } /// /// Delete a schema. /// /// The name of the app. /// The name of the schema to delete. /// /// 204 => Schema deleted. /// 404 => Schema or app not found. /// [HttpDelete] [Route("apps/{app}/schemas/{schema}/")] [ApiPermissionOrAnonymous(PermissionIds.AppSchemasDelete)] [ApiCosts(1)] public async Task DeleteSchema(string app, string schema) { var command = new DeleteSchema(); await CommandBus.PublishAsync(command, HttpContext.RequestAborted); return NoContent(); } [HttpGet] [Route("apps/{app}/schemas/{schema}/completion")] [ApiPermissionOrAnonymous] [ApiCosts(1)] [OpenApiIgnore] public async Task GetScriptCompletion(string app, string schema, [FromServices] ScriptingCompleter completer) { var completion = completer.ContentScript(await BuildModel()); return Ok(completion); } [HttpGet] [Route("apps/{app}/schemas/{schema}/completion/triggers")] [ApiPermissionOrAnonymous] [ApiCosts(1)] [OpenApiIgnore] public async Task GetScriptTriggerCompletion(string app, string schema, [FromServices] ScriptingCompleter completer) { var completion = completer.ContentTrigger(await BuildModel()); return Ok(completion); } [HttpGet] [Route("apps/{app}/schemas/{schema}/filters")] [ApiPermissionOrAnonymous] [ApiCosts(1)] [OpenApiIgnore] public async Task GetFilters(string app, string schema) { var components = await appProvider.GetComponentsAsync(Schema, HttpContext.RequestAborted); var filters = ContentQueryModel.Build(Schema.SchemaDef, App.PartitionResolver(), components).Flatten(); return Ok(filters); } private async Task BuildModel() { var components = await appProvider.GetComponentsAsync(Schema, HttpContext.RequestAborted); return Schema.SchemaDef.BuildDataSchema(App.PartitionResolver(), components); } private Task GetSchemaAsync(string schema) { if (Guid.TryParse(schema, out var guid)) { var schemaId = DomainId.Create(guid); return appProvider.GetSchemaAsync(AppId, schemaId, ct: HttpContext.RequestAborted); } else { return appProvider.GetSchemaAsync(AppId, schema, ct: HttpContext.RequestAborted); } } private async Task InvokeCommandAsync(ICommand command) { var context = await CommandBus.PublishAsync(command, HttpContext.RequestAborted); var result = context.Result(); var response = SchemaDto.FromDomain(result, Resources); return response; } } }