diff --git a/.gitignore b/.gitignore index b8374ec8..3238b5d3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ *.user *.vs *.log +*.received.txt .vs .git diff --git a/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationService.cs b/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationService.cs index 7b4b82d9..18cdb862 100644 --- a/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationService.cs +++ b/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationService.cs @@ -18,7 +18,7 @@ public sealed class ConfigurationService : IConfigurationService private sealed class ConfigurationModel { - public Dictionary Apps { get; } = new Dictionary(); + public Dictionary Apps { get; } = []; public string? CurrentApp { get; set; } } diff --git a/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationStore.cs b/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationStore.cs index 7fd37819..fccdfd25 100644 --- a/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationStore.cs +++ b/cli/Squidex.CLI/Squidex.CLI.Core/Configuration/ConfigurationStore.cs @@ -12,7 +12,10 @@ namespace Squidex.CLI.Configuration; public sealed class ConfigurationStore : IConfigurationStore { - private readonly JsonSerializer jsonSerializer = new JsonSerializer(); + private readonly JsonSerializer jsonSerializer = new JsonSerializer + { + Formatting = Formatting.Indented, + }; private DirectoryInfo workingDirectory; public DirectoryInfo WorkingDirectory diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/ContentQueryTests.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/ContentQueryTests.cs index c5635795..ef4b7741 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/ContentQueryTests.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/ContentQueryTests.cs @@ -11,10 +11,12 @@ namespace Squidex.ClientLibrary.Tests; public class ContentQueryTests { + private readonly SquidexOptions options = new SquidexOptions(); + [Fact] public void Should_create_query_for_empty_query() { - var query = new ContentQuery().ToQuery(true); + var query = new ContentQuery().ToQuery(true, options); Assert.Empty(query); } @@ -22,7 +24,7 @@ public void Should_create_query_for_empty_query() [Fact] public void Should_create_query_for_top() { - var query = new ContentQuery { Top = 10 }.ToQuery(true); + var query = new ContentQuery { Top = 10 }.ToQuery(true, options); Assert.Equal("?$top=10", query); } @@ -30,7 +32,7 @@ public void Should_create_query_for_top() [Fact] public void Should_create_query_for_skip() { - var query = new ContentQuery { Skip = 10 }.ToQuery(true); + var query = new ContentQuery { Skip = 10 }.ToQuery(true, options); Assert.Equal("?$skip=10", query); } @@ -38,7 +40,7 @@ public void Should_create_query_for_skip() [Fact] public void Should_create_query_for_skip_and_top() { - var query = new ContentQuery { Skip = 20, Top = 10 }.ToQuery(true); + var query = new ContentQuery { Skip = 20, Top = 10 }.ToQuery(true, options); Assert.Equal("?$skip=20&$top=10", query); } @@ -46,7 +48,7 @@ public void Should_create_query_for_skip_and_top() [Fact] public void Should_create_query_for_filter() { - var query = new ContentQuery { Filter = "my-filter" }.ToQuery(true); + var query = new ContentQuery { Filter = "my-filter" }.ToQuery(true, options); Assert.Equal("?$filter=my-filter", query); } @@ -54,7 +56,7 @@ public void Should_create_query_for_filter() [Fact] public void Should_create_query_for_search() { - var query = new ContentQuery { Search = "my-search" }.ToQuery(true); + var query = new ContentQuery { Search = "my-search" }.ToQuery(true, options); Assert.Equal("?$search=\"my-search\"", query); } @@ -62,7 +64,7 @@ public void Should_create_query_for_search() [Fact] public void Should_create_query_for_search_and_filter() { - var query = new ContentQuery { Search = "my-search", Filter = "my-filter" }.ToQuery(true); + var query = new ContentQuery { Search = "my-search", Filter = "my-filter" }.ToQuery(true, options); Assert.Equal("?$search=\"my-search\"&$filter=my-filter", query); } diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/EnrichedEvents/EnrichedEventsTests.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/EnrichedEvents/EnrichedEventsTests.cs index 08dc6bd5..19638c85 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/EnrichedEvents/EnrichedEventsTests.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/EnrichedEvents/EnrichedEventsTests.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Microsoft.Extensions.Options; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Squidex.ClientLibrary.EnrichedEvents; @@ -12,8 +13,12 @@ namespace Squidex.ClientLibrary.Tests.EnrichedEvents; +[UsesVerify] public class EnrichedEventsTests { + + private readonly SquidexOptions options = new SquidexOptions(); + public class SchemaData { [JsonConverter(typeof(InvariantConverter))] @@ -24,143 +29,107 @@ public class Schema : Content { } - private const string JsonEnrichedContentEvent = @"{ - 'type': 'SchemaUpdated', - 'payload': { - '$type': 'EnrichedContentEvent', - 'id': '062b936f-7496-4f87-bd4f-ba7bbb63c751', - 'actor': 'subject:601c2cbafa4e669f214c0438', - 'appId': '3e2df825-86a9-43cb-8eb7-97d5a5bd4eea,testapp', - 'created': '2021-01-01T00:00:00Z', - 'createdBy': 'subject:601c2cbafa4e669f214c0438', - 'lastModified': '2021-01-01T00:01:00Z', - 'lastModifiedBy': 'subject:601c2cbafa4e669f214c0438', - 'name': 'SchemaUpdated', - 'partition': -792991992, - 'schemaId': '062b936f-7496-4f87-bd4f-ba7bbb63c751,schema', - 'status': 'Published', - 'timestamp': '2021-01-01T00:01:00Z', - 'type': 'Updated', - 'version': 3, - 'data': { - 'testField': { - 'iv': 'test2' - } - }, - 'dataOld': { - 'testField': { - 'iv': 'test' - } + private static readonly string JsonEnrichedContentEvent = @"{ + 'type': 'SchemaUpdated', + 'payload': { + '$type': 'EnrichedContentEvent', + 'id': '062b936f-7496-4f87-bd4f-ba7bbb63c751', + 'actor': 'subject:601c2cbafa4e669f214c0438', + 'appId': '3e2df825-86a9-43cb-8eb7-97d5a5bd4eea,testapp', + 'created': '2021-01-01T00:00:00Z', + 'createdBy': 'subject:601c2cbafa4e669f214c0438', + 'lastModified': '2021-01-01T00:01:00Z', + 'lastModifiedBy': 'subject:601c2cbafa4e669f214c0438', + 'name': 'SchemaUpdated', + 'partition': -792991992, + 'schemaId': '062b936f-7496-4f87-bd4f-ba7bbb63c751,schema', + 'status': 'Published', + 'timestamp': '2021-01-01T00:01:00Z', + 'type': 'Updated', + 'version': 3, + 'data': { + 'testField': { + 'iv': 'test2' } }, - 'timestamp': '2021-01-01T00:01:00Z' - }"; + 'dataOld': { + 'testField': { + 'iv': 'test' + } + } + }, + 'timestamp': '2021-01-01T00:01:00Z' + }"; [Fact] - public void Should_deserialize_EnrichedContentEvent() + public async void Should_deserialize_EnrichedContentEvent() { - var envelope = EnrichedEventEnvelope.FromJson(JsonEnrichedContentEvent); + var envelope = EnrichedEventEnvelope.FromJson(JsonEnrichedContentEvent, options); Assert.True(envelope.Payload is EnrichedContentEvent); - var contentEvent = (EnrichedContentEvent)envelope.Payload; - - Assert.Equal("SchemaUpdated", contentEvent.Name); - Assert.Equal("testapp", contentEvent.App.Name); - Assert.Equal("601c2cbafa4e669f214c0438", contentEvent.Actor.Id); - Assert.Equal(typeof(JObject), contentEvent.Data.GetType()); - - switch (contentEvent.Schema.Name) - { - case "schema": - var res = contentEvent.ToTyped(); - - Assert.Equal(typeof(EnrichedContentEvent), res.GetType()); - Assert.Equal(typeof(SchemaData), res.Data.GetType()); - - Assert.Equal("test2", res.Data.TestField); - Assert.Equal("test", res.DataOld.TestField); - break; - default: throw new Exception("Unknown schema"); - } + await Verify(envelope.Payload); } public static string JsonEnrichedCommentEvent { get; } = @"{ - 'type': 'UserMentioned', - 'payload': { - '$type': 'EnrichedCommentEvent', - 'actor': 'subject:601c2cbafa4e669f214c0438', - 'appId': '3e2df825-86a9-43cb-8eb7-97d5a5bd4eea,testapp', - 'name': 'UserMentioned', - 'partition': -1730311374, - 'text': '@user@test.com testmessage', - 'timestamp': '2021-01-01T00:00:00Z', - 'url': '/app/testapp/content/schema/0e5955e3-cd2a-49f2-92ba-303acf4dd192/comments', - 'version': 0 - }, - 'timestamp': '2021-01-01T00:00:00Z' - }"; + 'type': 'UserMentioned', + 'payload': { + '$type': 'EnrichedCommentEvent', + 'actor': 'subject:601c2cbafa4e669f214c0438', + 'appId': '3e2df825-86a9-43cb-8eb7-97d5a5bd4eea,testapp', + 'name': 'UserMentioned', + 'partition': -1730311374, + 'text': '@user@test.com testmessage', + 'timestamp': '2021-01-01T00:00:00Z', + 'url': '/app/testapp/content/schema/0e5955e3-cd2a-49f2-92ba-303acf4dd192/comments', + 'version': 0 + }, + 'timestamp': '2021-01-01T00:00:00Z' + }"; [Fact] - public void Should_deserialize_EnrichedCommentEvent() + public async Task Should_deserialize_EnrichedCommentEvent() { - var envelope = EnrichedEventEnvelope.FromJson(JsonEnrichedCommentEvent); + var envelope = EnrichedEventEnvelope.FromJson(JsonEnrichedCommentEvent, options); Assert.True(envelope.Payload is EnrichedCommentEvent); - var commentEvent = (EnrichedCommentEvent)envelope.Payload; - - Assert.Equal("@user@test.com testmessage", commentEvent.Text); - Assert.Equal("UserMentioned", commentEvent.Name); - Assert.Equal("testapp", commentEvent.App.Name); - Assert.Equal("601c2cbafa4e669f214c0438", commentEvent.Actor.Id); + await Verify(envelope.Payload); } public static string JsonEnrichedAssetEvent { get; } = @"{ - 'type': 'AssetCreatedFromSnapshot', - 'payload': { - '$type': 'EnrichedAssetEvent', - 'id': 'c5dc4403-713d-4ebd-9a8f-a17efdba924e', - 'actor': 'subject:6025a698a825d86becf541fe', - 'appId': '3e2df825-86a9-43cb-8eb7-97d5a5bd4eea,testapp', - 'assetType': 'Unknown', - 'created': '2021-01-01T00:00:00Z', - 'createdBy': 'subject:6025a698a825d86becf541fe', - 'fileName': 'name.pdf', - 'fileSize': 447021, - 'fileVersion': 0, - 'isImage': false, - 'lastModified': '2021-01-01T00:00:00Z', - 'lastModifiedBy': 'subject:6025a698a825d86becf541fe', - 'mimeType': 'application/pdf', - 'name': 'AssetCreatedFromSnapshot', - 'partition': -755061617, - 'timestamp': '1970-01-01T00:00:00Z', - 'type': 'Created', - 'version': 1 - }, - 'timestamp': '1970-01-01T00:00:00Z' - }"; + 'type': 'AssetCreatedFromSnapshot', + 'payload': { + '$type': 'EnrichedAssetEvent', + 'id': 'c5dc4403-713d-4ebd-9a8f-a17efdba924e', + 'actor': 'subject:6025a698a825d86becf541fe', + 'appId': '3e2df825-86a9-43cb-8eb7-97d5a5bd4eea,testapp', + 'assetType': 'Unknown', + 'created': '2021-01-01T00:00:00Z', + 'createdBy': 'subject:6025a698a825d86becf541fe', + 'fileName': 'name.pdf', + 'fileSize': 447021, + 'fileVersion': 0, + 'isImage': false, + 'lastModified': '2021-01-01T00:00:00Z', + 'lastModifiedBy': 'subject:6025a698a825d86becf541fe', + 'mimeType': 'application/pdf', + 'name': 'AssetCreatedFromSnapshot', + 'partition': -755061617, + 'timestamp': '1970-01-01T00:00:00Z', + 'type': 'Created', + 'version': 1 + }, + 'timestamp': '1970-01-01T00:00:00Z' + }"; [Fact] - public void Should_deserialize_EnrichedAssetEvent() + public async Task Should_deserialize_EnrichedAssetEvent() { - var envelope = EnrichedEventEnvelope.FromJson(JsonEnrichedAssetEvent); + var envelope = EnrichedEventEnvelope.FromJson(JsonEnrichedAssetEvent, options); Assert.True(envelope.Payload is EnrichedAssetEvent); - var assetEvent = (EnrichedAssetEvent)envelope.Payload; - - Assert.Equal("6025a698a825d86becf541fe", assetEvent.Actor.Id); - Assert.Equal("6025a698a825d86becf541fe", assetEvent.CreatedBy.Id); - Assert.Equal("6025a698a825d86becf541fe", assetEvent.LastModifiedBy.Id); - Assert.Equal("application/pdf", assetEvent.MimeType); - Assert.Equal("AssetCreatedFromSnapshot", assetEvent.Name); - Assert.Equal("c5dc4403-713d-4ebd-9a8f-a17efdba924e", assetEvent.Id); - Assert.Equal("name.pdf", assetEvent.FileName); - Assert.Equal("subject", assetEvent.CreatedBy.Type); - Assert.Equal("subject", assetEvent.LastModifiedBy.Type); - Assert.Equal("testapp", assetEvent.App.Name); - Assert.Equal(447021, assetEvent.FileSize); + await Verify(envelope.Payload); } } diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SerializationTests.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SerializationTests.cs index 5fd9ca0d..10ed945a 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SerializationTests.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SerializationTests.cs @@ -14,6 +14,8 @@ namespace Squidex.ClientLibrary.Tests; public class SerializationTests { + private readonly SquidexOptions options = new SquidexOptions(); + public sealed record MyClass { [JsonConverter(typeof(InvariantConverter))] @@ -47,7 +49,7 @@ public void Should_serialize_datetime_to_iso8601() Value = utcTime }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("2012-11-10T09:08:07Z", serialized, StringComparison.Ordinal); } @@ -62,7 +64,7 @@ public void Should_serialize_local_datetime_to_iso8601_utc() Value = utcTime.ToLocalTime() }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("2012-11-10T09:08:07Z", serialized, StringComparison.Ordinal); } @@ -77,7 +79,7 @@ public void Should_serialize_datetimeoffset_to_iso8601() Value = utcTime }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("2012-11-10T09:08:07Z", serialized, StringComparison.Ordinal); } @@ -92,7 +94,7 @@ public void Should_serialize_local_datetimeoffset_to_iso8601_utc() Value = utcTime.ToLocalTime() }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("2012-11-10T09:08:07Z", serialized, StringComparison.Ordinal); } @@ -107,7 +109,7 @@ public void Should_serialize_local_datetime_to_iso8601_utc_with_ms() Value = utcTime.ToLocalTime() }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("2010-12-29T13:32:27Z", serialized, StringComparison.Ordinal); } @@ -120,7 +122,7 @@ public void Should_serialize_false() DoNotScript = false }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"doNotScript\": false", serialized, StringComparison.Ordinal); } @@ -133,7 +135,7 @@ public void Should_serialize_type() Type = BulkUpdateType.ChangeStatus }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"type\": \"ChangeStatus\"", serialized, StringComparison.Ordinal); } @@ -146,7 +148,7 @@ public void Should_serialize_invariant() Value = "hello" }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"iv\": \"hello\"", serialized, StringComparison.Ordinal); } @@ -159,7 +161,7 @@ public void Should_serialize_invariant_jsonnull() Value = "hello" }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"iv\": \"hello\"", serialized, StringComparison.Ordinal); } @@ -172,7 +174,7 @@ public void Should_serialize_write_invariant_jsonull() Value = "hello" }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"iv\": \"hello\"", serialized, StringComparison.Ordinal); } @@ -188,7 +190,7 @@ public void Should_serialize_localized_jsonnull() } }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"en\": \"hello\"", serialized, StringComparison.Ordinal); } @@ -201,7 +203,7 @@ public void Should_serialize_dynamic_properties_with_original_casing() ["Property1"] = new JObject() }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"Property1\": {}", serialized, StringComparison.Ordinal); } @@ -214,7 +216,7 @@ public void Should_serialize_with_camel_case() Value = "hello" }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"value\": \"hello\"", serialized, StringComparison.Ordinal); } @@ -227,7 +229,7 @@ public void Should_serialize_with_pascal_case() Value = "hello" }; - var serialized = source.ToJson(); + var serialized = source.ToJson(options); Assert.Contains("\"Value\": \"hello\"", serialized, StringComparison.Ordinal); } @@ -237,7 +239,7 @@ public void Should_deserialize_invariant() { var json = "{ 'value': { 'iv': 'hello'} }"; - var result = json.FromJson>(); + var result = json.FromJson>(options); Assert.Equal("hello", result?.Value); } @@ -247,7 +249,7 @@ public void Should_deserialize_invariant_null_value() { var json = "{ 'value': null }"; - var result = json.FromJson>(); + var result = json.FromJson>(options); Assert.Null(result?.Value); } @@ -257,7 +259,7 @@ public void Should_deserialize_invariant_empty_value() { var json = "{ 'value': {} }"; - var result = json.FromJson>(); + var result = json.FromJson>(options); Assert.Null(result?.Value); } @@ -267,7 +269,7 @@ public void Should_deserialize_invariant_jsonnull() { var json = "{ 'value': { 'iv': 'hello'} }"; - var result = json.FromJson>>(); + var result = json.FromJson>>(options); Assert.Equal("hello", result?.Value.Value); } @@ -277,7 +279,7 @@ public void Should_deserialize_invariant_jsonnull_ull_value() { var json = "{ 'value': null }"; - var result = json.FromJson>>(); + var result = json.FromJson>>(options); Assert.Null(result?.Value.Value); } @@ -287,7 +289,7 @@ public void Should_deserialize_invariant_jsonnull_empty_value() { var json = "{ 'value': {} }"; - var result = json.FromJson>>(); + var result = json.FromJson>>(options); Assert.Null(result?.Value.Value); } @@ -297,7 +299,7 @@ public void Should_deserialize_localized_jsonnull() { var json = "{ 'value': { 'en': 'hello'} }"; - var result = json.FromJson>>>(); + var result = json.FromJson>>>(options); Assert.Equal("hello", result?.Value["en"].Value); } diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/Squidex.ClientLibrary.Tests.csproj b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/Squidex.ClientLibrary.Tests.csproj index 122e7a68..7263ea5c 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/Squidex.ClientLibrary.Tests.csproj +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/Squidex.ClientLibrary.Tests.csproj @@ -23,6 +23,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SquidexExceptionTests.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SquidexExceptionTests.cs deleted file mode 100644 index 1ba724a3..00000000 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary.Tests/SquidexExceptionTests.cs +++ /dev/null @@ -1,65 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using System.Runtime.Serialization.Formatters.Binary; -using Xunit; - -namespace Squidex.ClientLibrary.Tests; - -public class SquidexExceptionTests -{ - [Fact] - public void Should_serialize_simple_exception() - { - var source = new SquidexException("Message"); - - var serialized = SerializeAndDeserialize(source); - - Assert.Equal("Message", serialized.Message); - } - - [Fact] - public void Should_serialize_with_status_code() - { - var source = new SquidexException("Message", 404); - - var serialized = SerializeAndDeserialize(source); - - Assert.Equal(404, serialized.StatusCode); - } - - [Fact] - public void Should_serialize_with_details() - { - var details = new ErrorDto - { - Message = "My Error" - }; - - var source = new SquidexException("Message", 0, details); - - var serialized = SerializeAndDeserialize(source); - - Assert.Equal(details.Message, serialized.Result?.Message); - } - - private static T SerializeAndDeserialize(T source) where T : notnull - { - var formatter = new BinaryFormatter(); - - using (var stream = new MemoryStream()) - { -#pragma warning disable SYSLIB0011 // Type or member is obsolete - formatter.Serialize(stream, source); - - stream.Position = 0; - - return (T)formatter.Deserialize(stream); -#pragma warning restore SYSLIB0011 // Type or member is obsolete - } - } -} diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetQueryDto.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetQueryDto.cs index 034485c0..4ed1b419 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetQueryDto.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetQueryDto.cs @@ -91,9 +91,9 @@ public sealed class AssetQuery /// public string? ParentId { get; set; } - internal string? ToQueryJson() + internal string? ToQueryJson(SquidexOptions options) { - return Query?.ToJson(); + return Query?.ToJson(options); } internal string? ToIdString() diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetsClient.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetsClient.cs index 5c021c93..fa6581b8 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetsClient.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/AssetsClient.cs @@ -122,7 +122,7 @@ public async Task UploadAssetAsync(FileParameter file, AssetUploadOptions option public Task GetAssetsAsync(AssetQuery? query = null, CancellationToken cancellationToken = default) { - return GetAssetsAsync(query?.ParentId, query?.ToIdString(), query?.ToQueryJson(), query?.Top, query?.Skip, query?.OrderBy, query?.Filter, null, null, cancellationToken); + return GetAssetsAsync(query?.ParentId, query?.ToIdString(), query?.ToQueryJson(_options), query?.Top, query?.Skip, query?.OrderBy, query?.Filter, null, null, cancellationToken); } /// diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Configuration/Authenticator.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Configuration/Authenticator.cs index e15e374c..b88edb5b 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Configuration/Authenticator.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Configuration/Authenticator.cs @@ -62,13 +62,19 @@ public async Task GetBearerTokenAsync(string appName, ThrowFromPreviousAttempt(clientId, clientSecret); - var httpRequest = Authenticator.BuildRequest(clientId, clientSecret); + var httpRequest = BuildRequest(clientId, clientSecret); using (var response = await httpClient.SendAsync(httpRequest, ct)) { if (!response.IsSuccessStatusCode) { - var exception = new SecurityException($"Failed to retrieve access token for client '{options.ClientId}', got HTTP {response.StatusCode}."); +#if NET5_0_OR_GREATER + var errorString = await response.Content.ReadAsStringAsync(ct); +#else + var errorString = await response.Content.ReadAsStringAsync(); +#endif + + var exception = new SecurityException($"Failed to retrieve access token for client '{options.ClientId}', got HTTP {response.StatusCode} and error {errorString}."); StorePreviousAttempt(clientId, clientSecret, exception); throw exception; diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentQuery.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentQuery.cs index cb3523d7..747d86c9 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentQuery.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentQuery.cs @@ -95,7 +95,7 @@ public class ContentQuery /// public int Random { get; set; } - internal string ToQuery(bool supportsSearch) + internal string ToQuery(bool supportsSearch, SquidexOptions options) { var queries = new List(); @@ -121,7 +121,7 @@ internal string ToQuery(bool supportsSearch) if (JsonQuery != null) { - queries.Add($"q={JsonQuery.ToJson()}"); + queries.Add($"q={JsonQuery.ToJson(options)}"); } if (Ids != null && Ids.Count > 0) diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsClient.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsClient.cs index 142cffae..96208df2 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsClient.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsClient.cs @@ -127,7 +127,7 @@ public async Task StreamAllAsync(Func callback, int skip = 0, Que stringReader.Read(); } - var contentItem = stringReader.FromJson(); + var contentItem = stringReader.FromJson(Options); await callback(contentItem); } @@ -140,7 +140,7 @@ public async Task StreamAllAsync(Func callback, int skip = 0, Que public Task> GetAsync(ContentQuery? query = null, QueryContext? context = null, CancellationToken ct = default) { - var q = query?.ToQuery(true) ?? string.Empty; + var q = query?.ToQuery(true, Options) ?? string.Empty; return RequestJsonAsync>(HttpMethod.Get, BuildUrl(q, true, context), null, context, ct); } @@ -178,7 +178,7 @@ public Task> GetReferencingAsync(string id, Conte { Guard.NotNullOrEmpty(id, nameof(id)); - var q = query?.ToQuery(true) ?? string.Empty; + var q = query?.ToQuery(true, Options) ?? string.Empty; return RequestJsonAsync>(HttpMethod.Get, BuildUrl($"{id}/referencing{q}", true, context), null, context, ct); } @@ -198,7 +198,7 @@ public Task> GetReferencesAsync(string id, Conten { Guard.NotNullOrEmpty(id, nameof(id)); - var q = query?.ToQuery(true) ?? string.Empty; + var q = query?.ToQuery(true, Options) ?? string.Empty; return RequestJsonAsync>(HttpMethod.Get, BuildUrl($"{id}/references{q}", true, context), null, context, ct); } @@ -209,7 +209,7 @@ public Task CreateAsync(TData data, ContentCreateOptions options = defa { Guard.NotNull(data, nameof(data)); - return RequestJsonAsync(HttpMethod.Post, BuildUrl($"?publish={options.Publish}&id={options.Id ?? string.Empty}", false), data.ToContent(), context, ct); + return RequestJsonAsync(HttpMethod.Post, BuildUrl($"?publish={options.Publish}&id={options.Id ?? string.Empty}", false), data.ToContent(Options), context, ct); } /// @@ -237,7 +237,7 @@ public Task ChangeStatusAsync(string id, ChangeStatus request, QueryCon Guard.NotNull(id, nameof(id)); Guard.NotNull(request, nameof(request)); - return RequestJsonAsync(HttpMethod.Put, BuildUrl($"{id}/status", false), request.ToContent(), context, ct); + return RequestJsonAsync(HttpMethod.Put, BuildUrl($"{id}/status", false), request.ToContent(Options), context, ct); } /// @@ -256,7 +256,7 @@ public Task PatchAsync(string id, TPatch patch, ContentPatchOpt Guard.NotNullOrEmpty(id, nameof(id)); Guard.NotNull(patch, nameof(patch)); - return RequestJsonAsync(HttpMethodEx.Patch, BuildUrl($"{id}/", false), patch.ToContent(), context, ct); + return RequestJsonAsync(HttpMethodEx.Patch, BuildUrl($"{id}/", false), patch.ToContent(Options), context, ct); } /// @@ -275,7 +275,7 @@ public Task UpsertAsync(string id, TData data, ContentUpsertOptions opt Guard.NotNullOrEmpty(id, nameof(id)); Guard.NotNull(data, nameof(data)); - return RequestJsonAsync(HttpMethod.Post, BuildUrl($"{id}?publish={options.Publish}&patch={options.Patch}", false), data.ToContent(), context, ct); + return RequestJsonAsync(HttpMethod.Post, BuildUrl($"{id}?publish={options.Publish}&patch={options.Patch}", false), data.ToContent(Options), context, ct); } /// @@ -294,7 +294,7 @@ public async Task UpdateAsync(string id, TData data, ContentUpdateOptio Guard.NotNullOrEmpty(id, nameof(id)); Guard.NotNull(data, nameof(data)); - return await RequestJsonAsync(HttpMethod.Put, BuildUrl($"{id}", false), data.ToContent(), context, ct); + return await RequestJsonAsync(HttpMethod.Put, BuildUrl($"{id}", false), data.ToContent(Options), context, ct); } /// @@ -348,7 +348,7 @@ public Task> BulkUpdateAsync(BulkUpdate update, { Guard.NotNull(update, nameof(update)); - return RequestJsonAsync>(HttpMethod.Post, BuildUrl("bulk", false), update.ToContent(), null, ct); + return RequestJsonAsync>(HttpMethod.Post, BuildUrl("bulk", false), update.ToContent(Options), null, ct); } private string BuildUrl(string path, bool query, QueryContext? context = null) diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsSharedClient.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsSharedClient.cs index 337d9623..755b4ef3 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsSharedClient.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ContentsSharedClient.cs @@ -37,7 +37,7 @@ public async Task>> GraphQlAsync[]>(HttpMethod.Post, BuildUrl("graphql/batch", false, context), requests.ToContent(), context, ct); + var response = await RequestJsonAsync[]>(HttpMethod.Post, BuildUrl("graphql/batch", false, context), requests.ToContent(Options), context, ct); return response; } @@ -66,7 +66,7 @@ public async Task GraphQlAsync(object request, QueryContex { Guard.NotNull(request, nameof(request)); - var response = await RequestJsonAsync>(HttpMethod.Post, BuildUrl("graphql", false, context), request.ToContent(), context, ct); + var response = await RequestJsonAsync>(HttpMethod.Post, BuildUrl("graphql", false, context), request.ToContent(Options), context, ct); if (response.Errors?.Length > 0) { @@ -94,7 +94,7 @@ public Task> BulkUpdateAsync(BulkUpdate update, { Guard.NotNull(update, nameof(update)); - return RequestJsonAsync>(HttpMethod.Post, BuildUrl("bulk", false), update.ToContent(), null, ct); + return RequestJsonAsync>(HttpMethod.Post, BuildUrl("bulk", false), update.ToContent(Options), null, ct); } private string BuildUrl(string path, bool query, QueryContext? context = null) diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/EnrichedEvents/EnrichedEventEnvelope.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/EnrichedEvents/EnrichedEventEnvelope.cs index b77db7bc..f49a7c50 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/EnrichedEvents/EnrichedEventEnvelope.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/EnrichedEvents/EnrichedEventEnvelope.cs @@ -33,11 +33,12 @@ public class EnrichedEventEnvelope /// Utils to deserialize an Envelope. /// /// The string to be deserialized. + /// The Squidex options. /// /// The enriched event. /// - public static EnrichedEventEnvelope FromJson(string json) + public static EnrichedEventEnvelope FromJson(string json, SquidexOptions options) { - return json.FromJsonWithTypes(); + return json.FromJsonWithTypes(options); } } diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ExtendableRulesClient.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ExtendableRulesClient.cs index 32efc90d..f85d88f4 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ExtendableRulesClient.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/ExtendableRulesClient.cs @@ -40,7 +40,7 @@ public Task CreateRuleAsync(CreateExtendableRuleDto request, { Guard.NotNull(request, nameof(request)); - return RequestJsonAsync(HttpMethod.Post, BuildUrl(), request.ToContent(), null, ct); + return RequestJsonAsync(HttpMethod.Post, BuildUrl(), request.ToContent(Options), null, ct); } /// @@ -50,7 +50,7 @@ public Task UpdateRuleAsync(string id, UpdateExtendableRuleDt Guard.NotNullOrEmpty(id, nameof(id)); Guard.NotNull(request, nameof(request)); - return RequestJsonAsync(HttpMethod.Put, BuildUrl($"{id}"), request.ToContent(), null, ct); + return RequestJsonAsync(HttpMethod.Put, BuildUrl($"{id}"), request.ToContent(Options), null, ct); } /// diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/SquidexOptions.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/SquidexOptions.cs index 925c68af..3a8b9f8f 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/SquidexOptions.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/SquidexOptions.cs @@ -6,6 +6,7 @@ // ========================================================================== using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Squidex.ClientLibrary.Configuration; using Squidex.ClientLibrary.Utils; @@ -29,6 +30,17 @@ public class SquidexOptions : OptionsBase private TimeSpan? timeout; private TimeSpan tokenRetryTime = TimeSpan.FromHours(1); + /// + /// Initializes a new instance of the class. + /// + public SquidexOptions() + { + SerializerSettings.ContractResolver = new JsonNullContractResolver(); + + SerializerSettings.Converters.Add(new StringEnumConverter()); + SerializerSettings.Converters.Add(new UTCIsoDateTimeConverter()); + } + /// /// Gets or sets the URL to the Squidex installation. /// diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/HttpClientExtensions.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/HttpClientExtensions.cs index e3d5be57..67e51da9 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/HttpClientExtensions.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/HttpClientExtensions.cs @@ -8,7 +8,6 @@ using System.Net.Http.Headers; using System.Text; using Newtonsoft.Json; -using Newtonsoft.Json.Converters; using Squidex.ClientLibrary.EnrichedEvents; namespace Squidex.ClientLibrary.Utils; @@ -18,48 +17,19 @@ namespace Squidex.ClientLibrary.Utils; /// public static class HttpClientExtensions { - private static readonly JsonSerializerSettings SerializerSettings; - private static readonly JsonSerializerSettings SerializerSettingsWithTypes; - private static readonly JsonSerializer Serializer; - - static HttpClientExtensions() - { - SerializerSettings = new JsonSerializerSettings().SetupSquidex(); - SerializerSettingsWithTypes = new JsonSerializerSettings().SetupSquidex(); - SerializerSettingsWithTypes.SerializationBinder = new EnrichedEventSerializationBinder(); - SerializerSettingsWithTypes.TypeNameHandling = TypeNameHandling.Auto; - - Serializer = JsonSerializer.CreateDefault(SerializerSettings); - } - - /// - /// Converts the serializer settings to deal with squidex data. - /// - /// The JSON settings. - /// - /// The settings. - /// - public static JsonSerializerSettings SetupSquidex(this JsonSerializerSettings settings) - { - settings.ContractResolver = new JsonNullContractResolver(); - - settings.Converters.Add(new StringEnumConverter()); - settings.Converters.Add(new UTCIsoDateTimeConverter()); - - return settings; - } - /// /// Converts a value to a instance which contains a JSON body. /// /// The type of the value. /// The value to convert. + /// The options. /// /// The created instance. /// - public static HttpContent ToContent(this T value) + /// is null. + public static HttpContent ToContent(this T value, SquidexOptions options) { - var json = value.ToJson(); + var json = value.ToJson(options); var content = new StringContent(json, Encoding.UTF8, "application/json"); @@ -94,12 +64,16 @@ public static HttpContent ToContent(this Stream stream, string name, string mime /// /// The type of the value. /// The value to convert. + /// The options. /// /// The JSON string. /// - public static string ToJson(this T value) + /// is null. + public static string ToJson(this T value, SquidexOptions options) { - var json = JsonConvert.SerializeObject(value, Formatting.Indented, SerializerSettings); + Guard.NotNull(options, nameof(options)); + + var json = JsonConvert.SerializeObject(value, Formatting.Indented, options.SerializerSettings); return json; } @@ -109,12 +83,16 @@ public static string ToJson(this T value) /// /// The type of the value. /// The JSON string. + /// The options. /// /// The deserialized value. /// - public static T FromJson(this string value) + /// is null. + public static T FromJson(this string value, SquidexOptions options) { - var json = JsonConvert.DeserializeObject(value, SerializerSettings)!; + Guard.NotNull(options, nameof(options)); + + var json = JsonConvert.DeserializeObject(value, options.SerializerSettings)!; return json; } @@ -124,14 +102,18 @@ public static T FromJson(this string value) /// /// The type of the value. /// The JSON string. + /// The options. /// /// The deserialized value. /// - public static T FromJson(this StringReader value) + /// is null. + public static T FromJson(this StringReader value, SquidexOptions options) { + Guard.NotNull(options, nameof(options)); + using var jsonReader = new JsonTextReader(value); - var jsonSerializer = JsonSerializer.CreateDefault(SerializerSettings); + var jsonSerializer = JsonSerializer.CreateDefault(options.SerializerSettings); return jsonSerializer.Deserialize(jsonReader)!; } @@ -141,12 +123,23 @@ public static T FromJson(this StringReader value) /// /// The type of the value. /// The JSON string. + /// The options. /// /// The deserialized value. /// - public static T FromJsonWithTypes(this string value) + /// is null. + public static T FromJsonWithTypes(this string value, SquidexOptions options) { - var json = JsonConvert.DeserializeObject(value, SerializerSettingsWithTypes)!; + Guard.NotNull(options, nameof(options)); + + var serializerSettings = new JsonSerializerSettings(options.SerializerSettings) + { + SerializationBinder = new EnrichedEventSerializationBinder(), + TypeNameHandling = TypeNameHandling.Auto, + TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple + }; + + var json = JsonConvert.DeserializeObject(value, serializerSettings)!; return json; } @@ -156,11 +149,17 @@ public static T FromJsonWithTypes(this string value) /// /// The type of the value. /// The content to read from. + /// The options. /// /// The deserialized value. /// - public static async Task ReadAsJsonAsync(this HttpContent content) + /// is null. + public static async Task ReadAsJsonAsync(this HttpContent content, SquidexOptions options) { + Guard.NotNull(options, nameof(options)); + + var serializer = JsonSerializer.CreateDefault(options.SerializerSettings); + #if NET5_0_OR_GREATER await using (var stream = await content.ReadAsStreamAsync()) { @@ -168,7 +167,7 @@ public static T FromJsonWithTypes(this string value) { using (var jsonReader = new JsonTextReader(reader)) { - return Serializer.Deserialize(jsonReader); + return serializer.Deserialize(jsonReader); } } } @@ -179,7 +178,7 @@ public static T FromJsonWithTypes(this string value) { using (var jsonReader = new JsonTextReader(reader)) { - return Serializer.Deserialize(jsonReader); + return serializer.Deserialize(jsonReader); } } } diff --git a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/SquidexClientBase.cs b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/SquidexClientBase.cs index 84bb304f..5454f58b 100644 --- a/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/SquidexClientBase.cs +++ b/csharp/Squidex.ClientLibrary/Squidex.ClientLibrary/Utils/SquidexClientBase.cs @@ -73,7 +73,7 @@ protected internal async Task RequestJsonAsync(HttpMethod method, string p { await EnsureResponseIsValidAsync(response); - return (await response.Content.ReadAsJsonAsync())!; + return (await response.Content.ReadAsJsonAsync(Options))!; } } } @@ -94,7 +94,7 @@ protected internal HttpRequestMessage BuildRequest(HttpMethod method, string pat return request; } - protected internal static async Task EnsureResponseIsValidAsync(HttpResponseMessage response) + protected internal async Task EnsureResponseIsValidAsync(HttpResponseMessage response) { if (!response.IsSuccessStatusCode) { @@ -122,7 +122,7 @@ protected internal static async Task EnsureResponseIsValidAsync(HttpResponseMess { try { - details = message.FromJson(); + details = message.FromJson(Options); } catch {