Headless CMS and Content Managment Hub
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

229 lines
8.1 KiB

// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Microsoft.OData;
using Microsoft.OData.Edm;
using NJsonSchema;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Queries;
using Squidex.Infrastructure.Queries.Json;
using Squidex.Infrastructure.Queries.OData;
using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.Validation;
using Squidex.Log;
using Squidex.Text;
namespace Squidex.Domain.Apps.Entities.Assets.Queries
{
public class AssetQueryParser
{
private readonly JsonSchema jsonSchema = BuildJsonSchema();
private readonly IEdmModel edmModel = BuildEdmModel();
private readonly IJsonSerializer jsonSerializer;
private readonly ITagService tagService;
private readonly AssetOptions options;
public AssetQueryParser(IJsonSerializer jsonSerializer, ITagService tagService, IOptions<AssetOptions> options)
{
Guard.NotNull(jsonSerializer, nameof(jsonSerializer));
Guard.NotNull(options, nameof(options));
Guard.NotNull(tagService, nameof(tagService));
this.jsonSerializer = jsonSerializer;
this.tagService = tagService;
this.options = options.Value;
}
public virtual async Task<Q> ParseAsync(Context context, Q q)
{
Guard.NotNull(context, nameof(context));
Guard.NotNull(q, nameof(q));
using (Profiler.TraceMethod<AssetQueryParser>())
{
var query = ParseClrQuery(q);
await TransformTagAsync(context, query);
WithSorting(query);
WithPaging(query);
q = q.WithQuery(query);
if (context.ShouldSkipTotal())
{
q = q.WithoutTotal();
}
return q;
}
}
private ClrQuery ParseClrQuery(Q q)
{
var query = q.Query;
if (!string.IsNullOrWhiteSpace(q?.JsonQueryString))
{
query = ParseJson(q.JsonQueryString);
}
else if (!string.IsNullOrWhiteSpace(q?.ODataQuery))
{
query = ParseOData(q.ODataQuery);
}
return query;
}
private void WithPaging(ClrQuery query)
{
if (query.Take == long.MaxValue)
{
query.Take = options.DefaultPageSize;
}
else if (query.Take > options.MaxResults)
{
query.Take = options.MaxResults;
}
}
private static void WithSorting(ClrQuery query)
{
if (query.Sort.Count == 0)
{
query.Sort.Add(new SortNode(new List<string> { "lastModified" }, SortOrder.Descending));
}
if (!query.Sort.Any(x => string.Equals(x.Path.ToString(), "id", StringComparison.OrdinalIgnoreCase)))
{
query.Sort.Add(new SortNode(new List<string> { "id" }, SortOrder.Ascending));
}
}
private async Task TransformTagAsync(Context context, ClrQuery query)
{
if (query.Filter != null)
{
query.Filter = await FilterTagTransformer.TransformAsync(query.Filter, context.App.Id, tagService);
}
}
private ClrQuery ParseJson(string json)
{
return jsonSchema.Parse(json, jsonSerializer);
}
private ClrQuery ParseOData(string odata)
{
try
{
return edmModel.ParseQuery(odata).ToQuery();
}
catch (NotSupportedException)
{
throw new ValidationException(T.Get("common.odataNotSupported", new { odata }));
}
catch (ODataException ex)
{
var message = ex.Message;
throw new ValidationException(T.Get("common.odataFailure", new { odata, message }), ex);
}
catch (Exception)
{
throw new ValidationException(T.Get("common.odataNotSupported", new { odata }));
}
}
private static JsonSchema BuildJsonSchema()
{
var schema = new JsonSchema { Title = "Asset", Type = JsonObjectType.Object };
void AddProperty(string name, JsonObjectType type, string? format = null)
{
var property = new JsonSchemaProperty { Type = type, Format = format };
schema.Properties[name.ToCamelCase()] = property;
}
AddProperty("id", JsonObjectType.String);
AddProperty("version", JsonObjectType.Integer);
AddProperty("created", JsonObjectType.String, JsonFormatStrings.DateTime);
AddProperty("createdBy", JsonObjectType.String);
AddProperty("fileHash", JsonObjectType.String);
AddProperty("fileName", JsonObjectType.String);
AddProperty("fileSize", JsonObjectType.Integer);
AddProperty("fileVersion", JsonObjectType.Integer);
AddProperty("isDeleted", JsonObjectType.Boolean);
AddProperty("isProtected", JsonObjectType.Boolean);
AddProperty("lastModified", JsonObjectType.String, JsonFormatStrings.DateTime);
AddProperty("lastModifiedBy", JsonObjectType.String);
AddProperty("metadata", JsonObjectType.None);
AddProperty("mimeType", JsonObjectType.String);
AddProperty("slug", JsonObjectType.String);
AddProperty("tags", JsonObjectType.String);
AddProperty("type", JsonObjectType.String);
return schema;
}
private static IEdmModel BuildEdmModel()
{
var entityType = new EdmEntityType("Squidex", "Asset");
void AddProperty(string name, EdmPrimitiveTypeKind type)
{
entityType.AddStructuralProperty(name.ToCamelCase(), type);
}
void AddPropertyReference(string name, IEdmTypeReference reference)
{
entityType.AddStructuralProperty(name.ToCamelCase(), reference);
}
var jsonType = new EdmComplexType("Squidex", "Json", null, false, true);
AddPropertyReference("Metadata", new EdmComplexTypeReference(jsonType, false));
AddProperty("id", EdmPrimitiveTypeKind.String);
AddProperty("version", EdmPrimitiveTypeKind.Int64);
AddProperty("created", EdmPrimitiveTypeKind.DateTimeOffset);
AddProperty("createdBy", EdmPrimitiveTypeKind.String);
AddProperty("fileHash", EdmPrimitiveTypeKind.String);
AddProperty("fileName", EdmPrimitiveTypeKind.String);
AddProperty("isDeleted", EdmPrimitiveTypeKind.Boolean);
AddProperty("isProtected", EdmPrimitiveTypeKind.Boolean);
AddProperty("fileSize", EdmPrimitiveTypeKind.Int64);
AddProperty("fileVersion", EdmPrimitiveTypeKind.Int64);
AddProperty("lastModified", EdmPrimitiveTypeKind.DateTimeOffset);
AddProperty("lastModifiedBy", EdmPrimitiveTypeKind.String);
AddProperty("mimeType", EdmPrimitiveTypeKind.String);
AddProperty("slug", EdmPrimitiveTypeKind.String);
AddProperty("tags", EdmPrimitiveTypeKind.String);
AddProperty("type", EdmPrimitiveTypeKind.String);
var container = new EdmEntityContainer("Squidex", "Container");
container.AddEntitySet("AssetSet", entityType);
var model = new EdmModel();
model.AddElement(container);
model.AddElement(entityType);
return model;
}
}
}