mirror of https://github.com/Squidex/squidex.git
Browse Source
* Simplified data usage. * Code simplified. * Fixed tests. * Migration fixed.pull/627/head
committed by
GitHub
127 changed files with 815 additions and 1359 deletions
@ -0,0 +1,17 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Contents |
|||
{ |
|||
public enum GeoJsonParseResult |
|||
{ |
|||
Success, |
|||
InvalidLatitude, |
|||
InvalidLongitude, |
|||
InvalidValue |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using GeoJSON.Net; |
|||
using GeoJSON.Net.Geometry; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
using Squidex.Infrastructure.ObjectPool; |
|||
using Squidex.Infrastructure.Validation; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.Contents |
|||
{ |
|||
public static class GeoJsonValue |
|||
{ |
|||
public static GeoJsonParseResult TryParse(IJsonValue value, IJsonSerializer serializer, out GeoJSONObject geoJSON) |
|||
{ |
|||
Guard.NotNull(serializer, nameof(serializer)); |
|||
Guard.NotNull(value, nameof(value)); |
|||
|
|||
geoJSON = null!; |
|||
|
|||
if (value is JsonObject geoObject) |
|||
{ |
|||
try |
|||
{ |
|||
var stream = DefaultPools.MemoryStream.Get(); |
|||
|
|||
try |
|||
{ |
|||
serializer.Serialize(value, stream, true); |
|||
|
|||
stream.Position = 0; |
|||
|
|||
geoJSON = serializer.Deserialize<GeoJSONObject>(stream, null, leaveOpen: true); |
|||
|
|||
return GeoJsonParseResult.Success; |
|||
} |
|||
finally |
|||
{ |
|||
DefaultPools.MemoryStream.Return(stream); |
|||
} |
|||
} |
|||
catch |
|||
{ |
|||
if (geoObject.TryGetValue<JsonNumber>("latitude", out var lat)) |
|||
{ |
|||
if (!lat.Value.IsBetween(-90, 90)) |
|||
{ |
|||
return GeoJsonParseResult.InvalidLatitude; |
|||
} |
|||
} |
|||
|
|||
if (geoObject.TryGetValue<JsonNumber>("longitude", out var lon)) |
|||
{ |
|||
if (!lon.Value.IsBetween(-180, 180)) |
|||
{ |
|||
return GeoJsonParseResult.InvalidLongitude; |
|||
} |
|||
} |
|||
|
|||
geoJSON = new Point(new Position(lat!.Value, lon!.Value)); |
|||
|
|||
return GeoJsonParseResult.Success; |
|||
} |
|||
} |
|||
|
|||
return GeoJsonParseResult.InvalidValue; |
|||
} |
|||
} |
|||
} |
|||
@ -1,57 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Squidex.Infrastructure; |
|||
|
|||
#pragma warning disable CA1067 // Override Object.Equals(object) when implementing IEquatable<T>
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Contents |
|||
{ |
|||
public sealed class IdContentData : ContentData<long>, IEquatable<IdContentData> |
|||
{ |
|||
public IdContentData() |
|||
: base(EqualityComparer<long>.Default) |
|||
{ |
|||
} |
|||
|
|||
public IdContentData(int capacity) |
|||
: base(capacity, EqualityComparer<long>.Default) |
|||
{ |
|||
} |
|||
|
|||
public static IdContentData Merge(params IdContentData[] contents) |
|||
{ |
|||
return MergeTo(new IdContentData(), contents); |
|||
} |
|||
|
|||
public IdContentData MergeInto(IdContentData target) |
|||
{ |
|||
return Merge(target, this); |
|||
} |
|||
|
|||
public IdContentData ToCleaned() |
|||
{ |
|||
return Clean(this, new IdContentData()); |
|||
} |
|||
|
|||
public IdContentData AddField(long id, ContentFieldData? data) |
|||
{ |
|||
Guard.GreaterThan(id, 0, nameof(id)); |
|||
|
|||
this[id] = data; |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public bool Equals(IdContentData? other) |
|||
{ |
|||
return base.Equals(other); |
|||
} |
|||
} |
|||
} |
|||
@ -1,79 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using Squidex.Infrastructure; |
|||
|
|||
#pragma warning disable CA1067 // Override Object.Equals(object) when implementing IEquatable<T>
|
|||
|
|||
namespace Squidex.Domain.Apps.Core.Contents |
|||
{ |
|||
public sealed class NamedContentData : ContentData<string>, IEquatable<NamedContentData> |
|||
{ |
|||
public NamedContentData() |
|||
: base(StringComparer.Ordinal) |
|||
{ |
|||
} |
|||
|
|||
public NamedContentData(NamedContentData source) |
|||
: base(source, StringComparer.Ordinal) |
|||
{ |
|||
} |
|||
|
|||
public NamedContentData(int capacity) |
|||
: base(capacity, StringComparer.Ordinal) |
|||
{ |
|||
} |
|||
|
|||
public static NamedContentData Merge(params NamedContentData[] contents) |
|||
{ |
|||
return MergeTo(new NamedContentData(), contents); |
|||
} |
|||
|
|||
public NamedContentData MergeInto(NamedContentData target) |
|||
{ |
|||
return Merge(target, this); |
|||
} |
|||
|
|||
public NamedContentData ToCleaned() |
|||
{ |
|||
return Clean(this, new NamedContentData()); |
|||
} |
|||
|
|||
public NamedContentData AddField(string name, ContentFieldData? data) |
|||
{ |
|||
Guard.NotNullOrEmpty(name, nameof(name)); |
|||
|
|||
this[name] = data; |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public NamedContentData Clone() |
|||
{ |
|||
var clone = new NamedContentData(Count); |
|||
|
|||
foreach (var (key, value) in this) |
|||
{ |
|||
clone[key] = value?.Clone()!; |
|||
} |
|||
|
|||
return clone; |
|||
} |
|||
|
|||
public bool Equals(NamedContentData? other) |
|||
{ |
|||
return base.Equals(other); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return $"{{{string.Join(", ", this.Select(x => $"\"{x.Key}\":{x.Value}"))}}}";
|
|||
} |
|||
} |
|||
} |
|||
@ -1,53 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.Collections.Generic; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ConvertContent |
|||
{ |
|||
public abstract class FieldIdentifier |
|||
{ |
|||
public static readonly FieldIdentifier ByName = new FieldByName(); |
|||
public static readonly FieldIdentifier ById = new FieldById(); |
|||
|
|||
public abstract IField? GetField(IArrayField arrayField, string key); |
|||
|
|||
public abstract string GetStringKey(IField field); |
|||
|
|||
private sealed class FieldByName : FieldIdentifier |
|||
{ |
|||
public override IField? GetField(IArrayField arrayField, string key) |
|||
{ |
|||
return arrayField.FieldsByName.GetValueOrDefault(key); |
|||
} |
|||
|
|||
public override string GetStringKey(IField field) |
|||
{ |
|||
return field.Name; |
|||
} |
|||
} |
|||
|
|||
private sealed class FieldById : FieldIdentifier |
|||
{ |
|||
public override IField? GetField(IArrayField arrayField, string key) |
|||
{ |
|||
if (long.TryParse(key, out var id)) |
|||
{ |
|||
return arrayField.FieldsById.GetValueOrDefault(id); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
public override string GetStringKey(IField field) |
|||
{ |
|||
return field.Id.ToString(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using NodaTime.Text; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Json; |
|||
using Squidex.Infrastructure.Json.Objects; |
|||
|
|||
namespace Squidex.Domain.Apps.Core.ValidateContent |
|||
{ |
|||
public sealed class JsonValueValidator : IFieldVisitor<bool, JsonValueValidator.Args> |
|||
{ |
|||
private static readonly JsonValueValidator Instance = new JsonValueValidator(); |
|||
|
|||
public readonly struct Args |
|||
{ |
|||
public readonly IJsonValue Value; |
|||
public readonly IJsonSerializer JsonSerializer; |
|||
|
|||
public Args(IJsonValue value, IJsonSerializer jsonSerializer) |
|||
{ |
|||
Value = value; |
|||
|
|||
JsonSerializer = jsonSerializer; |
|||
} |
|||
} |
|||
|
|||
private JsonValueValidator() |
|||
{ |
|||
} |
|||
|
|||
public static bool IsValid(IField field, IJsonValue value, IJsonSerializer jsonSerializer) |
|||
{ |
|||
Guard.NotNull(field, nameof(field)); |
|||
Guard.NotNull(value, nameof(value)); |
|||
|
|||
var args = new Args(value, jsonSerializer); |
|||
|
|||
return field.Accept(Instance, args); |
|||
} |
|||
|
|||
public bool Visit(IArrayField field, Args args) |
|||
{ |
|||
return IsValidObjectList(args.Value); |
|||
} |
|||
|
|||
public bool Visit(IField<AssetsFieldProperties> field, Args args) |
|||
{ |
|||
return IsValidStringList(args.Value); |
|||
} |
|||
|
|||
public bool Visit(IField<ReferencesFieldProperties> field, Args args) |
|||
{ |
|||
return IsValidStringList(args.Value); |
|||
} |
|||
|
|||
public bool Visit(IField<TagsFieldProperties> field, Args args) |
|||
{ |
|||
return IsValidStringList(args.Value); |
|||
} |
|||
|
|||
public bool Visit(IField<BooleanFieldProperties> field, Args args) |
|||
{ |
|||
return args.Value is JsonBoolean; |
|||
} |
|||
|
|||
public bool Visit(IField<NumberFieldProperties> field, Args args) |
|||
{ |
|||
return args.Value is JsonNumber; |
|||
} |
|||
|
|||
public bool Visit(IField<StringFieldProperties> field, Args args) |
|||
{ |
|||
return args.Value is JsonString; |
|||
} |
|||
|
|||
public bool Visit(IField<UIFieldProperties> field, Args args) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
public bool Visit(IField<DateTimeFieldProperties> field, Args args) |
|||
{ |
|||
if (args.Value.Type == JsonValueType.String) |
|||
{ |
|||
var parseResult = InstantPattern.General.Parse(args.Value.ToString()); |
|||
|
|||
return parseResult.Success; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public bool Visit(IField<GeolocationFieldProperties> field, Args args) |
|||
{ |
|||
var result = GeoJsonValue.TryParse(args.Value, args.JsonSerializer, out _); |
|||
|
|||
return result == GeoJsonParseResult.Success; |
|||
} |
|||
|
|||
public bool Visit(IField<JsonFieldProperties> field, Args args) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
private static bool IsValidStringList(IJsonValue value) |
|||
{ |
|||
if (value is JsonArray array) |
|||
{ |
|||
foreach (var item in array) |
|||
{ |
|||
if (item is not JsonString) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static bool IsValidObjectList(IJsonValue value) |
|||
{ |
|||
if (value is JsonArray array) |
|||
{ |
|||
foreach (var item in array) |
|||
{ |
|||
if (item is not JsonObject) |
|||
{ |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
} |
|||
@ -1,32 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using MongoDB.Bson.Serialization; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Assets |
|||
{ |
|||
internal static class Fields |
|||
{ |
|||
private static readonly Lazy<string> AssetIdField = new Lazy<string>(GetAssetIdField); |
|||
private static readonly Lazy<string> AssetFolderIdField = new Lazy<string>(GetAssetFolderIdField); |
|||
|
|||
public static string AssetId => AssetIdField.Value; |
|||
|
|||
public static string AssetFolderId => AssetFolderIdField.Value; |
|||
|
|||
private static string GetAssetIdField() |
|||
{ |
|||
return BsonClassMap.LookupClassMap(typeof(MongoAssetEntity)).GetMemberMap(nameof(MongoAssetEntity.Id)).ElementName; |
|||
} |
|||
|
|||
private static string GetAssetFolderIdField() |
|||
{ |
|||
return BsonClassMap.LookupClassMap(typeof(MongoAssetFolderEntity)).GetMemberMap(nameof(MongoAssetFolderEntity.Id)).ElementName; |
|||
} |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.ConvertContent; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Infrastructure.Json; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents.Operations |
|||
{ |
|||
public sealed class DataConverter |
|||
{ |
|||
private readonly FieldConverter[] decodeJsonConverters; |
|||
private readonly FieldConverter[] encodeJsonConverters; |
|||
|
|||
public DataConverter(IJsonSerializer serializer) |
|||
{ |
|||
var decoder = ValueConverters.DecodeJson(serializer); |
|||
|
|||
decodeJsonConverters = new[] |
|||
{ |
|||
FieldConverters.ForValues(decoder, ValueConverters.ForNested(decoder)) |
|||
}; |
|||
|
|||
var encoder = ValueConverters.EncodeJson(serializer); |
|||
|
|||
encodeJsonConverters = new[] |
|||
{ |
|||
FieldConverters.ForValues(encoder, ValueConverters.ForNested(encoder)) |
|||
}; |
|||
} |
|||
|
|||
public NamedContentData FromMongoModel(IdContentData result, Schema schema) |
|||
{ |
|||
return result.ConvertId2Name(schema, decodeJsonConverters); |
|||
} |
|||
|
|||
public IdContentData ToMongoModel(NamedContentData result, Schema schema) |
|||
{ |
|||
return result.ConvertName2IdCloned(schema, encodeJsonConverters); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.IO; |
|||
using System.Text; |
|||
using Microsoft.Extensions.ObjectPool; |
|||
|
|||
namespace Squidex.Infrastructure.ObjectPool |
|||
{ |
|||
public static class DefaultPools |
|||
{ |
|||
public static readonly ObjectPool<MemoryStream> MemoryStream = |
|||
new DefaultObjectPool<MemoryStream>(new MemoryStreamPooledObjectPolicy()); |
|||
|
|||
public static readonly ObjectPool<StringBuilder> StringBuilder = |
|||
new DefaultObjectPool<StringBuilder>(new StringBuilderPooledObjectPolicy()); |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System.IO; |
|||
using Microsoft.Extensions.ObjectPool; |
|||
|
|||
namespace Squidex.Infrastructure.ObjectPool |
|||
{ |
|||
public sealed class MemoryStreamPooledObjectPolicy : PooledObjectPolicy<MemoryStream> |
|||
{ |
|||
public int InitialCapacity { get; set; } = 100; |
|||
|
|||
public int MaximumRetainedCapacity { get; set; } = 4 * 1024; |
|||
|
|||
public override MemoryStream Create() |
|||
{ |
|||
return new MemoryStream(InitialCapacity); |
|||
} |
|||
|
|||
public override bool Return(MemoryStream obj) |
|||
{ |
|||
if (obj.Capacity > MaximumRetainedCapacity) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
obj.Position = 0; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue