mirror of https://github.com/Squidex/squidex.git
37 changed files with 986 additions and 323 deletions
@ -0,0 +1,43 @@ |
|||
// ==========================================================================
|
|||
// ContentData.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Immutable; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Core.Contents |
|||
{ |
|||
public sealed class ContentData |
|||
{ |
|||
private readonly ImmutableDictionary<string, ContentFieldData> fields; |
|||
|
|||
public ImmutableDictionary<string, ContentFieldData> Fields |
|||
{ |
|||
get { return fields; } |
|||
} |
|||
|
|||
public ContentData(ImmutableDictionary<string, ContentFieldData> fields) |
|||
{ |
|||
Guard.NotNull(fields, nameof(fields)); |
|||
|
|||
this.fields = fields; |
|||
} |
|||
|
|||
public static ContentData Empty() |
|||
{ |
|||
return new ContentData(ImmutableDictionary<string, ContentFieldData>.Empty.WithComparers(StringComparer.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
public ContentData AddField(string fieldName, ContentFieldData data) |
|||
{ |
|||
Guard.ValidPropertyName(fieldName, nameof(fieldName)); |
|||
|
|||
return new ContentData(Fields.Add(fieldName, data)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// ==========================================================================
|
|||
// ContentFieldData.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Immutable; |
|||
using Newtonsoft.Json.Linq; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Core.Contents |
|||
{ |
|||
public sealed class ContentFieldData |
|||
{ |
|||
private readonly ImmutableDictionary<string, JToken> valueByLanguage; |
|||
|
|||
public ImmutableDictionary<string, JToken> ValueByLanguage |
|||
{ |
|||
get { return valueByLanguage; } |
|||
} |
|||
|
|||
public ContentFieldData(ImmutableDictionary<string, JToken> valueByLanguage) |
|||
{ |
|||
Guard.NotNull(valueByLanguage, nameof(valueByLanguage)); |
|||
|
|||
this.valueByLanguage = valueByLanguage; |
|||
} |
|||
|
|||
public static ContentFieldData New() |
|||
{ |
|||
return new ContentFieldData(ImmutableDictionary<string, JToken>.Empty.WithComparers(StringComparer.OrdinalIgnoreCase)); |
|||
} |
|||
|
|||
public ContentFieldData AddValue(JToken value) |
|||
{ |
|||
return new ContentFieldData(valueByLanguage.Add("iv", value)); |
|||
} |
|||
|
|||
public ContentFieldData AddValue(string language, JToken value) |
|||
{ |
|||
Guard.NotNullOrEmpty(language, nameof(language)); |
|||
|
|||
return new ContentFieldData(valueByLanguage.Add(language, value)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// ==========================================================================
|
|||
// JsonFieldModel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Core.Schemas.Json |
|||
{ |
|||
public sealed class JsonFieldModel |
|||
{ |
|||
public string Name; |
|||
|
|||
public bool IsHidden; |
|||
|
|||
public bool IsDisabled; |
|||
|
|||
public FieldProperties Properties; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
// ==========================================================================
|
|||
// JsonSchemaModel.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
|
|||
namespace Squidex.Core.Schemas.Json |
|||
{ |
|||
public sealed class JsonSchemaModel |
|||
{ |
|||
public string Name; |
|||
|
|||
public bool IsPublished; |
|||
|
|||
public SchemaProperties Properties; |
|||
|
|||
public Dictionary<long, JsonFieldModel> Fields; |
|||
} |
|||
} |
|||
@ -1,53 +0,0 @@ |
|||
// ==========================================================================
|
|||
// SwaggerIdentityUsage.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using NSwag; |
|||
using NSwag.AspNetCore; |
|||
using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors.Security; |
|||
|
|||
namespace Squidex.Config.Identity |
|||
{ |
|||
public static class SwaggerIdentityUsage |
|||
{ |
|||
private const string DescriptionPattern = |
|||
@"To retrieve an access token, the client id must make a request to the token url. For example:
|
|||
|
|||
$ curl |
|||
-X POST '{0}' |
|||
-H 'Content-Type: application/x-www-form-urlencoded' |
|||
-d 'grant_type=client_credentials& |
|||
client_id=[APP_NAME]:[CLIENT_NAME]& |
|||
client_secret=[CLIENT_SECRET]'";
|
|||
|
|||
public static SwaggerOwinSettings ConfigureIdentity(this SwaggerOwinSettings settings, MyUrlsOptions options) |
|||
{ |
|||
var tokenUrl = options.BuildUrl($"{Constants.IdentityPrefix}/connect/token"); |
|||
|
|||
var description = string.Format(CultureInfo.InvariantCulture, DescriptionPattern, tokenUrl); |
|||
|
|||
settings.DocumentProcessors.Add( |
|||
new SecurityDefinitionAppender("OAuth2", new SwaggerSecurityScheme |
|||
{ |
|||
TokenUrl = tokenUrl, |
|||
Type = SwaggerSecuritySchemeType.OAuth2, |
|||
Flow = SwaggerOAuth2Flow.Application, |
|||
Scopes = new Dictionary<string, string> |
|||
{ |
|||
{ Constants.ApiScope, "Read and write access to the API" } |
|||
}, |
|||
Description = description |
|||
})); |
|||
|
|||
settings.OperationProcessors.Add(new OperationSecurityScopeProcessor("roles")); |
|||
|
|||
return settings; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
// ==========================================================================
|
|||
// SwaggerServices.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Options; |
|||
using NJsonSchema; |
|||
using NJsonSchema.Generation.TypeMappers; |
|||
using NSwag.AspNetCore; |
|||
using NSwag.CodeGeneration.SwaggerGenerators.WebApi.Processors.Security; |
|||
using Squidex.Controllers.ContentApi.Generator; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Pipeline.Swagger; |
|||
|
|||
namespace Squidex.Config.Swagger |
|||
{ |
|||
public static class SwaggerServices |
|||
{ |
|||
public static void AddMySwaggerSettings(this IServiceCollection services) |
|||
{ |
|||
services.AddSingleton(typeof(SwaggerOwinSettings), s => |
|||
{ |
|||
var options = s.GetService<IOptions<MyUrlsOptions>>().Value; |
|||
|
|||
var settings = |
|||
new SwaggerOwinSettings { Title = "Squidex API Specification", IsAspNetCore = false } |
|||
.ConfigurePaths() |
|||
.ConfigureSchemaSettings() |
|||
.ConfigureIdentity(options); |
|||
|
|||
return settings; |
|||
}); |
|||
|
|||
services.AddTransient<SchemasSwaggerGenerator>(); |
|||
} |
|||
|
|||
private static SwaggerOwinSettings ConfigureIdentity(this SwaggerOwinSettings settings, MyUrlsOptions urlOptions) |
|||
{ |
|||
settings.DocumentProcessors.Add( |
|||
new SecurityDefinitionAppender("OAuth2", SwaggerHelper.CreateOAuthSchema(urlOptions))); |
|||
|
|||
settings.OperationProcessors.Add(new OperationSecurityScopeProcessor("roles")); |
|||
|
|||
return 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), |
|||
new PrimitiveTypeMapper(typeof(RefToken), s => s.Type = JsonObjectType.String) |
|||
}; |
|||
|
|||
settings.DocumentProcessors.Add(new XmlTagProcessor()); |
|||
|
|||
settings.OperationProcessors.Add(new XmlTagProcessor()); |
|||
settings.OperationProcessors.Add(new XmlResponseTypesProcessor()); |
|||
|
|||
return settings; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
// ==========================================================================
|
|||
// ContentSwaggerController.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using NSwag.Annotations; |
|||
using Squidex.Controllers.ContentApi.Generator; |
|||
using Squidex.Pipeline; |
|||
using Squidex.Read.Apps.Services; |
|||
using Squidex.Read.Schemas.Repositories; |
|||
|
|||
// ReSharper disable UseObjectOrCollectionInitializer
|
|||
|
|||
namespace Squidex.Controllers.ContentApi |
|||
{ |
|||
[ApiExceptionFilter] |
|||
[SwaggerIgnore] |
|||
public class ContentSwaggerController : Controller |
|||
{ |
|||
private readonly ISchemaRepository schemaRepository; |
|||
private readonly IAppProvider appProvider; |
|||
private readonly SchemasSwaggerGenerator schemasSwaggerGenerator; |
|||
|
|||
public ContentSwaggerController(ISchemaRepository schemaRepository, IAppProvider appProvider, SchemasSwaggerGenerator schemasSwaggerGenerator) |
|||
{ |
|||
this.appProvider = appProvider; |
|||
|
|||
this.schemaRepository = schemaRepository; |
|||
this.schemasSwaggerGenerator = schemasSwaggerGenerator; |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("content/{app}/docs/")] |
|||
public IActionResult Docs(string app) |
|||
{ |
|||
ViewBag.Specification = $"~/content/{app}/swagger/v1/swagger.json"; |
|||
|
|||
return View("Docs"); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("content/{app}/swagger/v1/swagger.json")] |
|||
public async Task<IActionResult> GetSwagger(string app) |
|||
{ |
|||
var appEntity = await appProvider.FindAppByNameAsync(app); |
|||
|
|||
if (appEntity == null) |
|||
{ |
|||
return NotFound(); |
|||
} |
|||
|
|||
var schemas = await schemaRepository.QueryAllWithSchemaAsync(appEntity.Id); |
|||
|
|||
var swaggerDocument = await schemasSwaggerGenerator.Generate(appEntity, schemas); |
|||
|
|||
return Content(swaggerDocument.ToJson(), "application/json"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,356 @@ |
|||
// ==========================================================================
|
|||
// SchemasSwaggerGenerator.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Http; |
|||
using Microsoft.Extensions.Options; |
|||
using NJsonSchema; |
|||
using NJsonSchema.Generation; |
|||
using NSwag; |
|||
using NSwag.AspNetCore; |
|||
using NSwag.CodeGeneration.SwaggerGenerators; |
|||
using Squidex.Config; |
|||
using Squidex.Controllers.Api; |
|||
using Squidex.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Pipeline.Swagger; |
|||
using Squidex.Read.Apps; |
|||
using Squidex.Read.Schemas.Repositories; |
|||
|
|||
// ReSharper disable SuggestBaseTypeForParameter
|
|||
// ReSharper disable PrivateFieldCanBeConvertedToLocalVariable
|
|||
|
|||
namespace Squidex.Controllers.ContentApi.Generator |
|||
{ |
|||
public sealed class SchemasSwaggerGenerator |
|||
{ |
|||
private const string BodyDescription = |
|||
@"The data of the {0} to be created or updated.
|
|||
|
|||
Please not that each field is an object with one entry per language. |
|||
If the field is not localizable you must use iv (Invariant Language) as a key. |
|||
When you change the field to be localizable the value will become the value for the master language, depending what the master language is at this point of time.";
|
|||
|
|||
private readonly SwaggerJsonSchemaGenerator schemaGenerator; |
|||
private readonly SwaggerDocument document = new SwaggerDocument { Tags = new List<SwaggerTag>() }; |
|||
private readonly HttpContext context; |
|||
private readonly JsonSchemaResolver schemaResolver; |
|||
private readonly SwaggerGenerator swaggerGenerator; |
|||
private readonly MyUrlsOptions urlOptions; |
|||
private HashSet<Language> languages; |
|||
private JsonSchema4 errorDtoSchema; |
|||
private JsonSchema4 entityCreatedDtoSchema; |
|||
private string appBasePath; |
|||
private IAppEntity app; |
|||
|
|||
public SchemasSwaggerGenerator(IHttpContextAccessor context, SwaggerOwinSettings swaggerSettings, IOptions<MyUrlsOptions> urlOptions) |
|||
{ |
|||
this.context = context.HttpContext; |
|||
|
|||
this.urlOptions = urlOptions.Value; |
|||
|
|||
schemaGenerator = new SwaggerJsonSchemaGenerator(swaggerSettings); |
|||
schemaResolver = new SwaggerSchemaResolver(document, swaggerSettings); |
|||
|
|||
swaggerGenerator = new SwaggerGenerator(schemaGenerator, swaggerSettings, schemaResolver); |
|||
|
|||
} |
|||
|
|||
public async Task<SwaggerDocument> Generate(IAppEntity appEntity, IReadOnlyCollection<ISchemaEntityWithSchema> schemas) |
|||
{ |
|||
app = appEntity; |
|||
|
|||
languages = new HashSet<Language>(appEntity.Languages); |
|||
|
|||
appBasePath = $"/content/{appEntity.Name}"; |
|||
|
|||
await GenerateBasicSchemas(); |
|||
|
|||
GenerateTitle(); |
|||
GenerateRequestInfo(); |
|||
GenerateContentTypes(); |
|||
GenerateSchemes(); |
|||
GenerateSchemasOperations(schemas); |
|||
GenerateSecurityDefinitions(); |
|||
GenerateSecurityRequirements(); |
|||
|
|||
return document; |
|||
} |
|||
|
|||
private void GenerateSchemes() |
|||
{ |
|||
document.Schemes.Add(context.Request.Scheme == "http" ? SwaggerSchema.Http : SwaggerSchema.Https); |
|||
} |
|||
|
|||
private void GenerateTitle() |
|||
{ |
|||
document.Host = context.Request.Host.Value ?? string.Empty; |
|||
|
|||
document.BasePath = "/api"; |
|||
} |
|||
|
|||
private void GenerateRequestInfo() |
|||
{ |
|||
document.Info = new SwaggerInfo |
|||
{ |
|||
Title = $"Suidex API for {app.Name} App" |
|||
}; |
|||
} |
|||
|
|||
private void GenerateContentTypes() |
|||
{ |
|||
document.Consumes = new List<string> |
|||
{ |
|||
"application/json" |
|||
}; |
|||
|
|||
document.Produces = new List<string> |
|||
{ |
|||
"application/json" |
|||
}; |
|||
} |
|||
|
|||
private void GenerateSecurityDefinitions() |
|||
{ |
|||
document.SecurityDefinitions.Add("OAuth2", SwaggerHelper.CreateOAuthSchema(urlOptions)); |
|||
} |
|||
|
|||
private async Task GenerateBasicSchemas() |
|||
{ |
|||
var errorType = typeof(ErrorDto); |
|||
var errorSchema = JsonObjectTypeDescription.FromType(errorType, new Attribute[0], EnumHandling.String); |
|||
|
|||
errorDtoSchema = await swaggerGenerator.GenerateAndAppendSchemaFromTypeAsync(errorType, errorSchema.IsNullable, null); |
|||
|
|||
var entityCreatedType = typeof(EntityCreatedDto); |
|||
var entityCreatedSchema = JsonObjectTypeDescription.FromType(entityCreatedType, new Attribute[0], EnumHandling.String); |
|||
|
|||
entityCreatedDtoSchema = await swaggerGenerator.GenerateAndAppendSchemaFromTypeAsync(entityCreatedType, entityCreatedSchema.IsNullable, null); |
|||
} |
|||
|
|||
private void GenerateSecurityRequirements() |
|||
{ |
|||
var securityRequirements = new List<SwaggerSecurityRequirement> |
|||
{ |
|||
new SwaggerSecurityRequirement |
|||
{ |
|||
{ "roles", new List<string> { "app-owner", "app-developer", "app-editor" } } |
|||
} |
|||
}; |
|||
|
|||
foreach (var operation in document.Paths.Values.SelectMany(x => x.Values)) |
|||
{ |
|||
operation.Security = securityRequirements; |
|||
} |
|||
} |
|||
|
|||
private void GenerateSchemasOperations(IEnumerable<ISchemaEntityWithSchema> schemas) |
|||
{ |
|||
foreach (var schema in schemas.Select(x => x.Schema)) |
|||
{ |
|||
GenerateSchemaOperations(schema); |
|||
} |
|||
} |
|||
|
|||
private void GenerateSchemaOperations(Schema schema) |
|||
{ |
|||
var schemaName = schema.Properties.Label ?? schema.Name; |
|||
|
|||
document.Tags.Add( |
|||
new SwaggerTag |
|||
{ |
|||
Name = schemaName, Description = $"API to managed {schemaName} content elements." |
|||
}); |
|||
|
|||
var noIdItemOperations = |
|||
document.Paths.GetOrAdd($"{appBasePath}/{schema.Name}/", k => new SwaggerOperations()); |
|||
|
|||
var idItemOperations = |
|||
document.Paths.GetOrAdd($"{appBasePath}/{schema.Name}/{{id}}/", k => new SwaggerOperations()); |
|||
|
|||
GenerateSchemaQueryOperation(noIdItemOperations, schema, schemaName); |
|||
GenerateSchemaCreateOperation(noIdItemOperations, schema, schemaName); |
|||
|
|||
GenerateSchemaGetOperation(idItemOperations, schema, schemaName); |
|||
GenerateSchemaUpdateOperation(idItemOperations, schema, schemaName); |
|||
GenerateSchemaDeleteOperation(idItemOperations, schemaName); |
|||
|
|||
foreach (var operation in idItemOperations.Values.Union(noIdItemOperations.Values)) |
|||
{ |
|||
operation.Tags = new List<string> { schemaName }; |
|||
} |
|||
|
|||
foreach (var operation in idItemOperations.Values) |
|||
{ |
|||
operation.Responses.Add("404", |
|||
new SwaggerResponse { Description = $"App, schema or {schemaName} not found." }); |
|||
|
|||
operation.Parameters.AddPathParameter("id", JsonObjectType.String, $"The id of the {schemaName} (GUID)."); |
|||
} |
|||
} |
|||
|
|||
private void GenerateSchemaQueryOperation(SwaggerOperations operations, Schema schema, string schemaName) |
|||
{ |
|||
var operation = new SwaggerOperation |
|||
{ |
|||
Summary = $"Queries {schemaName} content elements." |
|||
}; |
|||
|
|||
operation.Parameters.AddQueryParameter("take", JsonObjectType.Number, "The number of elements to take."); |
|||
operation.Parameters.AddQueryParameter("skip", JsonObjectType.Number, "The number of elements to skip."); |
|||
|
|||
operation.Parameters.AddQueryParameter("query", JsonObjectType.String, "Optional full text query skip."); |
|||
|
|||
var responseSchema = CreateContentsSchema(schema.BuildSchema(languages, AppendSchema), schemaName, schema.Name); |
|||
|
|||
operation.Responses.Add("200", |
|||
new SwaggerResponse { Description = $"{schemaName} content elements retrieved.", Schema = responseSchema }); |
|||
operation.Responses.Add("500", |
|||
new SwaggerResponse { Description = $"Querying {schemaName} element failed with internal server error.", Schema = errorDtoSchema }); |
|||
|
|||
operations[SwaggerOperationMethod.Get] = operation; |
|||
} |
|||
|
|||
private void GenerateSchemaCreateOperation(SwaggerOperations operations, Schema schema, string schemaName) |
|||
{ |
|||
var operation = new SwaggerOperation |
|||
{ |
|||
Summary = $"Create a {schemaName} content element." |
|||
}; |
|||
|
|||
var bodySchema = AppendSchema($"{schema.Name}Dto", schema.BuildSchema(languages, AppendSchema)); |
|||
|
|||
operation.Parameters.AddBodyParameter(bodySchema, "data", string.Format(BodyDescription, schemaName)); |
|||
|
|||
operation.Responses.Add("201", |
|||
new SwaggerResponse { Description = $"{schemaName} created.", Schema = entityCreatedDtoSchema }); |
|||
operation.Responses.Add("500", |
|||
new SwaggerResponse { Description = $"Creating {schemaName} element failed with internal server error.", Schema = errorDtoSchema }); |
|||
|
|||
operations[SwaggerOperationMethod.Post] = operation; |
|||
} |
|||
|
|||
private void GenerateSchemaGetOperation(SwaggerOperations operations, Schema schema, string schemaName) |
|||
{ |
|||
var operation = new SwaggerOperation |
|||
{ |
|||
Summary = $"Gets a {schemaName} content element" |
|||
}; |
|||
|
|||
var responseSchema = CreateContentSchema(schema.BuildSchema(languages, AppendSchema), schemaName, schema.Name); |
|||
|
|||
operation.Responses.Add("209", |
|||
new SwaggerResponse { Description = $"{schemaName} element found.", Schema = responseSchema }); |
|||
operation.Responses.Add("500", |
|||
new SwaggerResponse { Description = $"Retrieving {schemaName} element failed with internal server error.", Schema = errorDtoSchema }); |
|||
|
|||
operations[SwaggerOperationMethod.Get] = operation; |
|||
} |
|||
|
|||
private void GenerateSchemaUpdateOperation(SwaggerOperations operations, Schema schema, string schemaName) |
|||
{ |
|||
var operation = new SwaggerOperation |
|||
{ |
|||
Summary = $"Update {schemaName} content element." |
|||
}; |
|||
|
|||
var bodySchema = AppendSchema($"{schema.Name}Dto", schema.BuildSchema(languages, AppendSchema)); |
|||
|
|||
operation.Parameters.AddBodyParameter(bodySchema, "data", string.Format(BodyDescription, schemaName)); |
|||
|
|||
operation.Responses.Add("204", |
|||
new SwaggerResponse { Description = $"{schemaName} element updated." }); |
|||
operation.Responses.Add("500", |
|||
new SwaggerResponse { Description = $"Updating {schemaName} element failed with internal server error.", Schema = errorDtoSchema }); |
|||
|
|||
operations[SwaggerOperationMethod.Put] = operation; |
|||
} |
|||
|
|||
private void GenerateSchemaDeleteOperation(SwaggerOperations operations, string schemaName) |
|||
{ |
|||
var operation = new SwaggerOperation |
|||
{ |
|||
Summary = $"Delete a {schemaName} content element." |
|||
}; |
|||
|
|||
operation.Responses.Add("204", |
|||
new SwaggerResponse { Description = $"{schemaName} element deleted." }); |
|||
operation.Responses.Add("500", |
|||
new SwaggerResponse { Description = $"Deleting {schemaName} element failed with internal server error.", Schema = errorDtoSchema }); |
|||
|
|||
operations[SwaggerOperationMethod.Delete] = operation; |
|||
} |
|||
|
|||
private JsonSchema4 CreateContentsSchema(JsonSchema4 dataSchema, string schemaName, string id) |
|||
{ |
|||
var contentSchema = CreateContentSchema(dataSchema, schemaName, id); |
|||
|
|||
var schema = new JsonSchema4 |
|||
{ |
|||
Properties = |
|||
{ |
|||
["total"] = new JsonProperty |
|||
{ |
|||
Type = JsonObjectType.Number, IsRequired = true, Description = $"The total number of {schemaName} content elements." |
|||
}, |
|||
["items"] = new JsonProperty |
|||
{ |
|||
IsRequired = true, |
|||
Item = contentSchema, |
|||
Type = JsonObjectType.Array, |
|||
Description = $"The item of {schemaName} content elements." |
|||
} |
|||
}, |
|||
Type = JsonObjectType.Object |
|||
}; |
|||
|
|||
return schema; |
|||
} |
|||
|
|||
private JsonSchema4 CreateContentSchema(JsonSchema4 dataSchema, string schemaName, string id) |
|||
{ |
|||
var CreateProperty = |
|||
new Func<string, string, JsonProperty>((d, f) => |
|||
new JsonProperty { Description = d, Format = f, IsRequired = true, Type = JsonObjectType.String }); |
|||
|
|||
var dataProperty = new JsonProperty { Type = JsonObjectType.Object, IsRequired = true, Description = "The data of the content element" }; |
|||
|
|||
dataProperty.AllOf.Add(dataSchema); |
|||
|
|||
var schema = new JsonSchema4 |
|||
{ |
|||
Properties = |
|||
{ |
|||
["id"] = CreateProperty($"The id of the {schemaName}", null), |
|||
["data"] = dataProperty, |
|||
["created"] = CreateProperty($"The date and time when the {schemaName} content element has been created.", "date-time"), |
|||
["createdBy"] = CreateProperty($"The user that has created the {schemaName} content element.", null), |
|||
["lastModified"] = CreateProperty($"The date and time when the {schemaName} content element has been modified last.", "date-time"), |
|||
["lastModifiedBy"] = CreateProperty($"The user that has updated the {schemaName} content element.", null), |
|||
["isPublished"] = new JsonProperty |
|||
{ |
|||
Description = $"Indicates if the {schemaName} content element is publihed.", IsRequired = true, Type = JsonObjectType.Boolean |
|||
} |
|||
}, |
|||
Type = JsonObjectType.Object |
|||
}; |
|||
|
|||
return AppendSchema($"{id}ContentDto", schema); |
|||
} |
|||
|
|||
private JsonSchema4 AppendSchema(string name, JsonSchema4 schema) |
|||
{ |
|||
name = char.ToUpperInvariant(name[0]) + name.Substring(1); |
|||
|
|||
return new JsonSchema4 { SchemaReference = document.Definitions.GetOrAdd(name, x => schema) }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
// ==========================================================================
|
|||
// ContentEntryDto.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
using Squidex.Core.Contents; |
|||
using Squidex.Infrastructure; |
|||
|
|||
namespace Squidex.Controllers.ContentApi.Models |
|||
{ |
|||
public sealed class ContentEntryDto |
|||
{ |
|||
/// <summary>
|
|||
/// The if of the content element.
|
|||
/// </summary>
|
|||
public Guid Id { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The user that has created the content element.
|
|||
/// </summary>
|
|||
[Required] |
|||
public RefToken CreatedBy { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The user that has updated the content element.
|
|||
/// </summary>
|
|||
[Required] |
|||
public RefToken LastModifiedBy { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The data of the content item.
|
|||
/// </summary>
|
|||
[Required] |
|||
public ContentData Data { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The date and time when the content element has been created.
|
|||
/// </summary>
|
|||
public DateTime Created { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// The date and time when the content element has been modified last.
|
|||
/// </summary>
|
|||
public DateTime LastModified { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Indicates if the content element is publihed.
|
|||
/// </summary>
|
|||
public bool IsPublished { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
// ==========================================================================
|
|||
// SwaggerHelper.cs
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex Group
|
|||
// All rights reserved.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using NJsonSchema; |
|||
using NSwag; |
|||
using Squidex.Config; |
|||
|
|||
namespace Squidex.Pipeline.Swagger |
|||
{ |
|||
public static class SwaggerHelper |
|||
{ |
|||
private const string SecurityDescription = |
|||
@"To retrieve an access token, the client id must make a request to the token url. For example:
|
|||
|
|||
$ curl |
|||
-X POST '{0}' |
|||
-H 'Content-Type: application/x-www-form-urlencoded' |
|||
-d 'grant_type=client_credentials& |
|||
client_id=[APP_NAME]:[CLIENT_NAME]& |
|||
client_secret=[CLIENT_SECRET]'";
|
|||
|
|||
public static SwaggerSecurityScheme CreateOAuthSchema(MyUrlsOptions urlOptions) |
|||
{ |
|||
var tokenUrl = urlOptions.BuildUrl($"{Constants.IdentityPrefix}/connect/token"); |
|||
|
|||
var description = string.Format(CultureInfo.InvariantCulture, SecurityDescription, tokenUrl); |
|||
|
|||
return |
|||
new SwaggerSecurityScheme |
|||
{ |
|||
TokenUrl = tokenUrl, |
|||
Type = SwaggerSecuritySchemeType.OAuth2, |
|||
Flow = SwaggerOAuth2Flow.Application, |
|||
Scopes = new Dictionary<string, string> |
|||
{ |
|||
{ Constants.ApiScope, "Read and write access to the API" } |
|||
}, |
|||
Description = description |
|||
}; |
|||
} |
|||
|
|||
public static void AddQueryParameter(this ICollection<SwaggerParameter> parameters, string name, JsonObjectType type, string description) |
|||
{ |
|||
parameters.Add( |
|||
new SwaggerParameter |
|||
{ |
|||
Type = type, |
|||
Name = name, |
|||
Kind = SwaggerParameterKind.Query, |
|||
Description = description |
|||
}); |
|||
} |
|||
|
|||
public static void AddPathParameter(this ICollection<SwaggerParameter> parameters, string name, JsonObjectType type, string description) |
|||
{ |
|||
parameters.Add( |
|||
new SwaggerParameter |
|||
{ |
|||
Type = type, |
|||
Name = name, |
|||
Kind = SwaggerParameterKind.Path, |
|||
IsRequired = true, |
|||
IsNullableRaw = false, |
|||
Description = description |
|||
}); |
|||
} |
|||
|
|||
public static void AddBodyParameter(this ICollection<SwaggerParameter> parameters, JsonSchema4 schema, string name, string description) |
|||
{ |
|||
parameters.Add( |
|||
new SwaggerParameter |
|||
{ |
|||
Name = name, |
|||
Kind = SwaggerParameterKind.Body, |
|||
Schema = schema, |
|||
IsRequired = true, |
|||
IsNullableRaw = false, |
|||
Description = description |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue