Browse Source

All tests fixed.

pull/335/head
Sebastian Stehle 7 years ago
parent
commit
289f2747cf
  1. 4
      extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs
  2. 19
      extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs
  3. 4
      extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs
  4. 9
      extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs
  5. 8
      extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs
  6. 3
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs
  7. 2
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs
  8. 1
      src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj
  9. 13
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs
  10. 11
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs
  11. 9
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs
  12. 7
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs
  13. 7
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs
  14. 11
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs
  15. 4
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  16. 18
      src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs
  17. 19
      src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs
  18. 8
      src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs
  19. 18
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  20. 7
      src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs
  21. 24
      src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs
  22. 7
      src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs
  23. 51
      src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs
  24. 32
      src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs
  25. 89
      src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs
  26. 5
      src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs
  27. 17
      src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs
  28. 4
      src/Squidex.Domain.Apps.Entities/Backup/State/BackupState.cs
  29. 14
      src/Squidex.Domain.Apps.Entities/Backup/State/BackupStateJob.cs
  30. 4
      src/Squidex.Domain.Apps.Entities/Backup/State/RestoreState.cs
  31. 21
      src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs
  32. 13
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs
  33. 2
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs
  34. 4
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs
  35. 4
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs
  36. 4
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs
  37. 6
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs
  38. 4
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs
  39. 41
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs
  40. 25
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs
  41. 6
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs
  42. 8
      src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValue.cs
  43. 18
      src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs
  44. 14
      src/Squidex.Domain.Apps.Entities/DomainObjectState.cs
  45. 8
      src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs
  46. 28
      src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs
  47. 3
      src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs
  48. 9
      src/Squidex.Infrastructure.Redis/RedisPubSub.cs
  49. 10
      src/Squidex.Infrastructure.Redis/RedisSubscription.cs
  50. 6
      src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs
  51. 11
      src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs
  52. 2
      src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs
  53. 6
      src/Squidex.Infrastructure/Json/IJsonSerializer.cs
  54. 57
      src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs
  55. 68
      src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs
  56. 4
      src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs
  57. 13
      src/Squidex.Infrastructure/Json/Objects/JsonValue.cs
  58. 2
      src/Squidex.Infrastructure/Language.cs
  59. 2
      src/Squidex.Infrastructure/Orleans/J{T}.cs
  60. 92
      src/Squidex.Infrastructure/PropertyValue.cs
  61. 2
      src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs
  62. 2
      src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs
  63. 1
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs
  64. 1
      src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs
  65. 1
      src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs
  66. 1
      src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs
  67. 1
      src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs
  68. 4
      src/Squidex/Areas/Api/Controllers/UI/Models/UpdateSettingDto.cs
  69. 7
      src/Squidex/Areas/Api/Controllers/UI/UIController.cs
  70. 8
      src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs
  71. 4
      src/Squidex/Config/Domain/EventPublishersServices.cs
  72. 5
      src/Squidex/Config/Domain/SerializationServices.cs
  73. 3
      src/Squidex/Config/Domain/StoreServices.cs
  74. 2
      src/Squidex/Config/Domain/SubscriptionServices.cs
  75. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs
  76. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs
  77. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs
  78. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs
  79. 12
      tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs
  80. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs
  81. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateJsonSchema/JsonSchemaTests.cs
  82. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs
  83. 2
      tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs
  84. 9
      tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs
  85. 34
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs
  86. 123
      tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs
  87. 161
      tests/Squidex.Domain.Apps.Entities.Tests/Backup/GuidMapperTests.cs
  88. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs
  89. 35
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs
  90. 42
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs
  91. 5
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs
  92. 1
      tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj
  93. 12
      tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs
  94. 6
      tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs
  95. 44
      tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs
  96. 4
      tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs
  97. 8
      tools/Migrate_01/Rebuilder.cs

4
extensions/Squidex.Extensions/Actions/Algolia/AlgoliaActionHandler.cs

@ -56,7 +56,9 @@ namespace Squidex.Extensions.Actions.Algolia
{
ruleDescription = $"Add entry to Algolia index: {action.IndexName}";
ruleJob.Content = ToPayload(contentEvent);
var json = ToJson(contentEvent);
ruleJob.Content = JObject.Parse(json);
ruleJob.Content["objectID"] = contentId;
}

19
extensions/Squidex.Extensions/Actions/Discourse/DiscourseActionHandler.cs

@ -6,10 +6,10 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
@ -32,25 +32,28 @@ namespace Squidex.Extensions.Actions.Discourse
{
var url = $"{action.Url.ToString().TrimEnd('/')}/posts.json?api_key={action.ApiKey}&api_username={action.ApiUsername}";
var json =
new JObject(
new JProperty("raw", Format(action.Text, @event)),
new JProperty("title", Format(action.Title, @event)));
var json = new Dictionary<string, object>
{
["raw"] = Format(action.Text, @event),
["title"] = Format(action.Title, @event)
};
if (action.Topic.HasValue)
{
json.Add(new JProperty("topic_id", action.Topic.Value));
json.Add("topic_id", action.Topic.Value);
}
if (action.Category.HasValue)
{
json.Add(new JProperty("category", action.Category.Value));
json.Add("category", action.Category.Value);
}
var requestBody = ToJson(json);
var ruleJob = new DiscourseJob
{
RequestUrl = url,
RequestBody = json.ToString()
RequestBody = requestBody
};
var description =

4
extensions/Squidex.Extensions/Actions/ElasticSearch/ElasticSearchActionHandler.cs

@ -60,7 +60,9 @@ namespace Squidex.Extensions.Actions.ElasticSearch
{
ruleDescription = $"Upsert to index: {action.IndexName}";
ruleJob.Content = ToPayload(contentEvent);
var json = ToJson(contentEvent);
ruleJob.Content = JObject.Parse(json);
ruleJob.Content["objectID"] = contentId;
}

9
extensions/Squidex.Extensions/Actions/Prerender/PrerenderActionHandler.cs

@ -9,7 +9,6 @@ using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
@ -29,12 +28,10 @@ namespace Squidex.Extensions.Actions.Prerender
{
var url = Format(action.Url, @event);
var request =
new JObject(
new JProperty("prerenderToken", action.Token),
new JProperty("url", url));
var request = new { prerenderToken = action.Token, url };
var requestBody = ToJson(request);
return ($"Recache {url}", new PrerenderJob { RequestBody = request.ToString() });
return ($"Recache {url}", new PrerenderJob { RequestBody = requestBody });
}
protected override async Task<(string Dump, Exception Exception)> ExecuteJobAsync(PrerenderJob job)

8
extensions/Squidex.Extensions/Actions/Slack/SlackActionHandler.cs

@ -9,8 +9,6 @@ using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Infrastructure;
@ -33,14 +31,12 @@ namespace Squidex.Extensions.Actions.Slack
protected override (string Description, SlackJob Data) CreateJob(EnrichedEvent @event, SlackAction action)
{
var body =
new JObject(
new JProperty("text", Format(action.Text, @event)));
var body = new { text = Format(action.Text, @event) };
var ruleJob = new SlackJob
{
RequestUrl = action.WebhookUrl.ToString(),
RequestBody = body.ToString(Formatting.Indented)
RequestBody = ToJson(body)
};
return (Description, ruleJob);

3
src/Squidex.Domain.Apps.Core.Operations/HandleRules/EnrichedEvents/EnrichedEvent.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System;
using System.Runtime.Serialization;
using NodaTime;
using Squidex.Infrastructure;
using Squidex.Shared.Users;
@ -24,8 +25,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
public long Version { get; set; }
[IgnoreDataMember]
public abstract Guid AggregateId { get; }
[IgnoreDataMember]
public IUser User { get; set; }
}
}

2
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleActionHandler.cs

@ -36,7 +36,7 @@ namespace Squidex.Domain.Apps.Core.HandleRules
this.formatter = formatter;
}
protected virtual string ToPayloadJson<T>(T @event)
protected virtual string ToJson<T>(T @event)
{
return formatter.ToPayload(@event);
}

1
src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj

@ -16,7 +16,6 @@
<ItemGroup>
<PackageReference Include="Jint" Version="2.11.58" />
<PackageReference Include="Microsoft.OData.Core" Version="7.5.1" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="NJsonSchema" Version="9.12.2" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" PrivateAssets="all" />

13
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/Extensions.cs

@ -12,6 +12,7 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
@ -22,24 +23,24 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return data.GetReferencedIds(schema).ToList();
}
public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, List<Guid> deletedIds)
public static NamedContentData FromMongoModel(this IdContentData result, Schema schema, List<Guid> deletedIds, IJsonSerializer serializer)
{
return result.ConvertId2Name(schema,
FieldConverters.ForValues(
ValueConverters.DecodeJson(),
ValueConverters.DecodeJson(serializer),
ValueReferencesConverter.CleanReferences(deletedIds)),
FieldConverters.ForNestedId2Name(
ValueConverters.DecodeJson(),
ValueConverters.DecodeJson(serializer),
ValueReferencesConverter.CleanReferences(deletedIds)));
}
public static IdContentData ToMongoModel(this NamedContentData result, Schema schema)
public static IdContentData ToMongoModel(this NamedContentData result, Schema schema, IJsonSerializer serializer)
{
return result.ConvertName2Id(schema,
FieldConverters.ForValues(
ValueConverters.EncodeJson()),
ValueConverters.EncodeJson(serializer)),
FieldConverters.ForNestedName2Id(
ValueConverters.EncodeJson()));
ValueConverters.EncodeJson(serializer)));
}
}
}

11
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentCollection.cs

@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries;
@ -26,10 +27,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
private readonly string collectionName;
public MongoContentCollection(IMongoDatabase database, string collectionName)
protected IJsonSerializer Serializer { get; }
public MongoContentCollection(IMongoDatabase database, IJsonSerializer serializer, string collectionName)
: base(database)
{
this.collectionName = collectionName;
Serializer = serializer;
}
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default(CancellationToken))
@ -64,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
foreach (var entity in contentItems.Result)
{
entity.ParseData(schema.SchemaDef);
entity.ParseData(schema.SchemaDef, Serializer);
}
return ResultList.Create<IContentEntity>(contentCount.Result, contentItems.Result);
@ -96,7 +101,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
foreach (var entity in contentItems.Result)
{
entity.ParseData(schema.SchemaDef);
entity.ParseData(schema.SchemaDef, Serializer);
}
return ResultList.Create<IContentEntity>(contentCount.Result, contentItems.Result);

9
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentDraftCollection.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.State;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States;
@ -26,8 +27,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
internal sealed class MongoContentDraftCollection : MongoContentCollection
{
public MongoContentDraftCollection(IMongoDatabase database)
: base(database, "State_Content_Draft")
public MongoContentDraftCollection(IMongoDatabase database, IJsonSerializer serializer)
: base(database, serializer, "State_Content_Draft")
{
}
@ -88,7 +89,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await Collection.Find(x => x.IndexedSchemaId == schema.Id && x.Id == id && x.IsDeleted != true).Not(x => x.DataText)
.FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef);
contentEntity?.ParseData(schema.SchemaDef, Serializer);
return contentEntity;
}
@ -103,7 +104,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
var schema = await getSchema(contentEntity.IndexedAppId, contentEntity.IndexedSchemaId);
contentEntity.ParseData(schema.SchemaDef);
contentEntity.ParseData(schema.SchemaDef, Serializer);
return (SimpleMapper.Map(contentEntity, new ContentState()), contentEntity.Version);
}

7
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentEntity.cs

@ -14,6 +14,7 @@ using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
@ -124,13 +125,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
get { return dataDraft; }
}
public void ParseData(Schema schema)
public void ParseData(Schema schema, IJsonSerializer serializer)
{
data = DataByIds.FromMongoModel(schema, ReferencedIdsDeleted);
data = DataByIds.FromMongoModel(schema, ReferencedIdsDeleted, serializer);
if (DataDraftByIds != null)
{
dataDraft = DataDraftByIds.FromMongoModel(schema, ReferencedIdsDeleted);
dataDraft = DataDraftByIds.FromMongoModel(schema, ReferencedIdsDeleted, serializer);
}
}
}

7
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentPublishedCollection.cs

@ -13,14 +13,15 @@ using Squidex.Domain.Apps.Core.ConvertContent;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
internal sealed class MongoContentPublishedCollection : MongoContentCollection
{
public MongoContentPublishedCollection(IMongoDatabase database)
: base(database, "State_Content_Published")
public MongoContentPublishedCollection(IMongoDatabase database, IJsonSerializer serializer)
: base(database, serializer, "State_Content_Published")
{
}
@ -42,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
await Collection.Find(x => x.IndexedSchemaId == schema.Id && x.Id == id).Not(x => x.DataText)
.FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef);
contentEntity?.ParseData(schema.SchemaDef, Serializer);
return contentEntity;
}

11
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository.cs

@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Queries;
@ -26,17 +27,21 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{
private readonly IMongoDatabase database;
private readonly IAppProvider appProvider;
private readonly IJsonSerializer serializer;
private readonly MongoContentDraftCollection contentsDraft;
private readonly MongoContentPublishedCollection contentsPublished;
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider)
public MongoContentRepository(IMongoDatabase database, IAppProvider appProvider, IJsonSerializer serializer)
{
Guard.NotNull(appProvider, nameof(appProvider));
Guard.NotNull(serializer, nameof(serializer));
this.appProvider = appProvider;
contentsDraft = new MongoContentDraftCollection(database);
contentsPublished = new MongoContentPublishedCollection(database);
this.serializer = serializer;
contentsDraft = new MongoContentDraftCollection(database, serializer);
contentsPublished = new MongoContentPublishedCollection(database, serializer);
this.database = database;
}

4
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -38,12 +38,12 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
var schema = await GetSchemaAsync(value.AppId.Id, value.SchemaId.Id);
var idData = value.Data.ToMongoModel(schema.SchemaDef);
var idData = value.Data.ToMongoModel(schema.SchemaDef, serializer);
var idDraftData = idData;
if (!ReferenceEquals(value.Data, value.DataDraft))
{
idDraftData = value.DataDraft?.ToMongoModel(schema.SchemaDef);
idDraftData = value.DataDraft?.ToMongoModel(schema.SchemaDef, serializer);
}
var content = SimpleMapper.Map(value, new MongoContentEntity

18
src/Squidex.Domain.Apps.Entities/Apps/AppUISettingsGrain.cs

@ -8,8 +8,8 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
[CollectionName("UISettings")]
public sealed class State
{
public JObject Settings { get; set; } = new JObject();
public JsonObject Settings { get; set; } = JsonValue.Object();
}
public AppUISettingsGrain(IStore<Guid> store)
@ -41,19 +41,19 @@ namespace Squidex.Domain.Apps.Entities.Apps
return persistence.ReadAsync();
}
public Task<J<JObject>> GetAsync()
public Task<J<JsonObject>> GetAsync()
{
return Task.FromResult(state.Settings.AsJ());
}
public Task SetAsync(J<JObject> settings)
public Task SetAsync(J<JsonObject> settings)
{
state.Settings = settings;
return persistence.WriteSnapshotAsync(state);
}
public Task SetAsync(string path, J<JToken> value)
public Task SetAsync(string path, J<IJsonValue> value)
{
var container = GetContainer(path, out var key);
@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
throw new InvalidOperationException("Path does not lead to an object.");
}
container[key] = value;
container[key] = value.Value;
return persistence.WriteSnapshotAsync(state);
}
@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
return persistence.WriteSnapshotAsync(state);
}
private JObject GetContainer(string path, out string key)
private JsonObject GetContainer(string path, out string key)
{
Guard.NotNullOrEmpty(path, nameof(path));
@ -95,12 +95,12 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
if (!current.TryGetValue(segment, out var temp))
{
temp = new JObject();
temp = JsonValue.Object();
current[segment] = temp;
}
if (temp is JObject next)
if (temp is JsonObject next)
{
current = next;
}

19
src/Squidex.Domain.Apps.Entities/Apps/BackupApps.cs

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Orleans;
using Squidex.Domain.Apps.Entities.Apps.Indexes;
using Squidex.Domain.Apps.Entities.Backup;
@ -16,6 +15,8 @@ using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Orleans;
using Squidex.Shared.Users;
@ -27,6 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
private const string SettingsFile = "Settings.json";
private readonly IGrainFactory grainFactory;
private readonly IUserResolver userResolver;
private readonly IJsonSerializer serializer;
private readonly IAppsByNameIndex appsByNameIndex;
private readonly HashSet<string> contributors = new HashSet<string>();
private Dictionary<string, string> usersWithEmail = new Dictionary<string, string>();
@ -36,13 +38,14 @@ namespace Squidex.Domain.Apps.Entities.Apps
public override string Name { get; } = "Apps";
public BackupApps(IGrainFactory grainFactory, IUserResolver userResolver)
public BackupApps(IGrainFactory grainFactory, IUserResolver userResolver, IJsonSerializer serializer)
{
Guard.NotNull(grainFactory, nameof(grainFactory));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(userResolver, nameof(userResolver));
this.grainFactory = grainFactory;
this.serializer = serializer;
this.userResolver = userResolver;
appsByNameIndex = grainFactory.GetGrain<IAppsByNameIndex>(SingleGrain.Id);
@ -162,14 +165,14 @@ namespace Squidex.Domain.Apps.Entities.Apps
private async Task ReadUsersAsync(BackupReader reader)
{
var json = await reader.ReadJsonAttachmentAsync(UsersFile);
var json = await reader.ReadJsonAttachmentAsync<Dictionary<string, string>>(UsersFile);
usersWithEmail = json.ToObject<Dictionary<string, string>>();
usersWithEmail = json;
}
private async Task WriteUsersAsync(BackupWriter writer)
{
var json = JObject.FromObject(usersWithEmail);
var json = usersWithEmail;
await writer.WriteJsonAsync(UsersFile, json);
}
@ -183,9 +186,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
private async Task ReadSettingsAsync(BackupReader reader, Guid appId)
{
var json = await reader.ReadJsonAttachmentAsync(SettingsFile);
var json = await reader.ReadJsonAttachmentAsync<JsonObject>(SettingsFile);
await grainFactory.GetGrain<IAppUISettingsGrain>(appId).SetAsync((JObject)json);
await grainFactory.GetGrain<IAppUISettingsGrain>(appId).SetAsync(json);
}
public override async Task CompleteRestoreAsync(Guid appId, BackupReader reader)

8
src/Squidex.Domain.Apps.Entities/Apps/IAppUISettingsGrain.cs

@ -6,19 +6,19 @@
// ==========================================================================
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Orleans;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Entities.Apps
{
public interface IAppUISettingsGrain : IGrainWithGuidKey
{
Task<J<JObject>> GetAsync();
Task<J<JsonObject>> GetAsync();
Task SetAsync(string path, J<JToken> value);
Task SetAsync(string path, J<IJsonValue> value);
Task SetAsync(J<JObject> settings);
Task SetAsync(J<JsonObject> settings);
Task RemoveAsync(string path);
}

18
src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
@ -19,28 +19,28 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
[CollectionName("Apps")]
public class AppState : DomainObjectState<AppState>, IAppEntity
{
[JsonProperty]
[DataMember]
public string Name { get; set; }
[JsonProperty]
[DataMember]
public Roles Roles { get; set; } = Roles.Empty;
[JsonProperty]
[DataMember]
public AppPlan Plan { get; set; }
[JsonProperty]
[DataMember]
public AppClients Clients { get; set; } = AppClients.Empty;
[JsonProperty]
[DataMember]
public AppPatterns Patterns { get; set; } = AppPatterns.Empty;
[JsonProperty]
[DataMember]
public AppContributors Contributors { get; set; } = AppContributors.Empty;
[JsonProperty]
[DataMember]
public LanguagesConfig LanguagesConfig { get; set; } = LanguagesConfig.English;
[JsonProperty]
[DataMember]
public bool IsArchived { get; set; }
protected void On(AppCreated @event)

7
src/Squidex.Domain.Apps.Entities/Assets/BackupAssets.cs

@ -8,7 +8,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Assets.State;
@ -89,16 +88,16 @@ namespace Squidex.Domain.Apps.Entities.Assets
private async Task RestoreTagsAsync(Guid appId, BackupReader reader)
{
var tags = await reader.ReadJsonAttachmentAsync(TagsFile);
var tags = await reader.ReadJsonAttachmentAsync<TagSet>(TagsFile);
await tagService.RebuildTagsAsync(appId, TagGroups.Assets, tags.ToObject<TagSet>());
await tagService.RebuildTagsAsync(appId, TagGroups.Assets, tags);
}
private async Task BackupTagsAsync(Guid appId, BackupWriter writer)
{
var tags = await tagService.GetExportableTagsAsync(appId, TagGroups.Assets);
await writer.WriteJsonAsync(TagsFile, JObject.FromObject(tags));
await writer.WriteJsonAsync(TagsFile, tags);
}
private Task WriteAssetAsync(Guid assetId, long fileVersion, BackupWriter writer)

24
src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs

@ -7,7 +7,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
@ -20,37 +20,37 @@ namespace Squidex.Domain.Apps.Entities.Assets.State
{
public class AssetState : DomainObjectState<AssetState>, IAssetEntity
{
[JsonProperty]
[DataMember]
public NamedId<Guid> AppId { get; set; }
[JsonProperty]
[DataMember]
public string FileName { get; set; }
[JsonProperty]
[DataMember]
public string MimeType { get; set; }
[JsonProperty]
[DataMember]
public long FileVersion { get; set; }
[JsonProperty]
[DataMember]
public long FileSize { get; set; }
[JsonProperty]
[DataMember]
public long TotalSize { get; set; }
[JsonProperty]
[DataMember]
public bool IsImage { get; set; }
[JsonProperty]
[DataMember]
public int? PixelWidth { get; set; }
[JsonProperty]
[DataMember]
public int? PixelHeight { get; set; }
[JsonProperty]
[DataMember]
public bool IsDeleted { get; set; }
[JsonProperty]
[DataMember]
public HashSet<string> Tags { get; set; }
Guid IAssetInfo.AssetId

7
src/Squidex.Domain.Apps.Entities/Backup/BackupGrain.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
@ -34,6 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IBackupArchiveLocation backupArchiveLocation;
private readonly IClock clock;
private readonly IEnumerable<BackupHandler> handlers;
private readonly IJsonSerializer serializer;
private readonly IEventDataFormatter eventDataFormatter;
private readonly IEventStore eventStore;
private readonly ISemanticLog log;
@ -51,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
IEnumerable<BackupHandler> handlers,
IJsonSerializer serializer,
ISemanticLog log,
IStore<Guid> store)
{
@ -60,6 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(handlers, nameof(handlers));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(store, nameof(store));
Guard.NotNull(log, nameof(log));
@ -69,6 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter;
this.handlers = handlers;
this.serializer = serializer;
this.store = store;
this.log = log;
}
@ -139,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id))
{
using (var writer = new BackupWriter(stream, true))
using (var writer = new BackupWriter(serializer, stream, true))
{
await eventStore.QueryAsync(async storedEvent =>
{

51
src/Squidex.Domain.Apps.Entities/Backup/BackupReader.cs

@ -9,20 +9,19 @@ using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Entities.Backup.Helpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Backup
{
public sealed class BackupReader : DisposableObjectBase
{
private static readonly JsonSerializer Serializer = new JsonSerializer();
private readonly GuidMapper guidMapper = new GuidMapper();
private readonly ZipArchive archive;
private readonly IJsonSerializer serializer;
private int readEvents;
private int readAttachments;
@ -36,8 +35,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
get { return readAttachments; }
}
public BackupReader(Stream stream)
public BackupReader(IJsonSerializer serializer, Stream stream)
{
Guard.NotNull(serializer, nameof(serializer));
this.serializer = serializer;
archive = new ZipArchive(stream, ZipArchiveMode.Read, false);
}
@ -54,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
return guidMapper.OldGuid(newId);
}
public async Task<JToken> ReadJsonAttachmentAsync(string name)
public Task<T> ReadJsonAttachmentAsync<T>(string name)
{
Guard.NotNullOrEmpty(name, nameof(name));
@ -65,24 +68,16 @@ namespace Squidex.Domain.Apps.Entities.Backup
throw new FileNotFoundException("Cannot find attachment.", name);
}
JToken result;
T result;
using (var stream = attachmentEntry.Open())
{
using (var textReader = new StreamReader(stream))
{
using (var jsonReader = new JsonTextReader(textReader))
{
result = await JToken.ReadFromAsync(jsonReader);
guidMapper.NewGuids(result);
}
}
result = serializer.Deserialize<T>(stream, null, guidMapper.NewGuidOrValue);
}
readAttachments++;
return result;
return Task.FromResult(result);
}
public async Task ReadBlobAsync(string name, Func<Stream, Task> handler)
@ -105,9 +100,10 @@ namespace Squidex.Domain.Apps.Entities.Backup
readAttachments++;
}
public async Task ReadEventsAsync(IStreamNameResolver streamNameResolver, Func<StoredEvent, Task> handler)
public async Task ReadEventsAsync(IStreamNameResolver streamNameResolver, IEventDataFormatter formatter, Func<(string Stream, Envelope<IEvent> Event), Task> handler)
{
Guard.NotNull(handler, nameof(handler));
Guard.NotNull(formatter, nameof(formatter));
Guard.NotNull(streamNameResolver, nameof(streamNameResolver));
while (true)
@ -121,25 +117,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
using (var stream = eventEntry.Open())
{
using (var textReader = new StreamReader(stream))
{
using (var jsonReader = new JsonTextReader(textReader))
{
var storedEvent = Serializer.Deserialize<StoredEvent>(jsonReader);
storedEvent.Data.Payload = guidMapper.NewGuids(storedEvent.Data.Payload);
storedEvent.Data.Metadata = guidMapper.NewGuids(storedEvent.Data.Metadata);
var streamName = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidString);
var storedEvent = serializer.Deserialize<StoredEvent>(stream);
storedEvent = new StoredEvent(streamName,
storedEvent.EventPosition,
storedEvent.EventStreamNumber,
storedEvent.Data);
var eventStream = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidOrNull);
var eventEnvelope = formatter.Parse(storedEvent.Data, true, guidMapper.NewGuidOrValue);
await handler(storedEvent);
}
}
await handler((eventStream, eventEnvelope));
}
readEvents++;

32
src/Squidex.Domain.Apps.Entities/Backup/BackupWriter.cs

@ -9,18 +9,18 @@ using System;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Entities.Backup.Helpers;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Tasks;
namespace Squidex.Domain.Apps.Entities.Backup
{
public sealed class BackupWriter : DisposableObjectBase
{
private static readonly JsonSerializer Serializer = new JsonSerializer();
private readonly ZipArchive archive;
private readonly IJsonSerializer serializer;
private int writtenEvents;
private int writtenAttachments;
@ -34,8 +34,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
get { return writtenAttachments; }
}
public BackupWriter(Stream stream, bool keepOpen = false)
public BackupWriter(IJsonSerializer serializer, Stream stream, bool keepOpen = false)
{
Guard.NotNull(serializer, nameof(serializer));
this.serializer = serializer;
archive = new ZipArchive(stream, ZipArchiveMode.Create, keepOpen);
}
@ -47,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
}
}
public async Task WriteJsonAsync(string name, JToken value)
public Task WriteJsonAsync(string name, object value)
{
Guard.NotNullOrEmpty(name, nameof(name));
@ -55,16 +59,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
using (var stream = attachmentEntry.Open())
{
using (var textWriter = new StreamWriter(stream))
{
using (var jsonWriter = new JsonTextWriter(textWriter))
{
await value.WriteToAsync(jsonWriter);
}
}
serializer.Serialize(value, stream);
}
writtenAttachments++;
return TaskHelper.Done;
}
public async Task WriteBlobAsync(string name, Func<Stream, Task> handler)
@ -90,13 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
using (var stream = eventEntry.Open())
{
using (var textWriter = new StreamWriter(stream))
{
using (var jsonWriter = new JsonTextWriter(textWriter))
{
Serializer.Serialize(jsonWriter, storedEvent);
}
}
serializer.Serialize(storedEvent, stream);
}
writtenEvents++;

89
src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs

@ -7,112 +7,39 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Backup
{
public sealed class GuidMapper
internal sealed class GuidMapper
{
private static readonly int GuidLength = Guid.Empty.ToString().Length;
private readonly List<(JObject Source, string NewKey, string OldKey)> mappings = new List<(JObject Source, string NewKey, string OldKey)>();
private readonly Dictionary<Guid, Guid> oldToNewGuid = new Dictionary<Guid, Guid>();
private readonly Dictionary<Guid, Guid> newToOldGuid = new Dictionary<Guid, Guid>();
public Guid NewGuid(Guid oldGuid)
{
return oldToNewGuid.GetOrDefault(oldGuid);
}
public Guid OldGuid(Guid newGuid)
{
return newToOldGuid.GetOrDefault(newGuid);
}
public string NewGuidString(string key)
public string NewGuidOrNull(string value)
{
if (Guid.TryParse(key, out var guid))
if (TryGenerateNewGuidString(value, out var result) || TryGenerateNewNamedId(value, out result))
{
return GenerateNewGuid(guid).ToString();
return result;
}
return null;
}
public JToken NewGuids(JToken jToken)
{
var result = NewGuidsCore(jToken);
if (mappings.Count > 0)
{
foreach (var mapping in mappings)
{
if (mapping.Source.TryGetValue(mapping.OldKey, out var value))
{
mapping.Source.Remove(mapping.OldKey);
mapping.Source[mapping.NewKey] = value;
}
}
mappings.Clear();
}
return result;
}
private JToken NewGuidsCore(JToken jToken)
{
switch (jToken.Type)
{
case JTokenType.String:
if (TryConvertString(jToken.ToString(), out var result))
{
return result;
}
break;
case JTokenType.Guid:
return GenerateNewGuid((Guid)jToken);
case JTokenType.Object:
NewGuidsCore((JObject)jToken);
break;
case JTokenType.Array:
NewGuidsCore((JArray)jToken);
break;
}
return jToken;
}
private void NewGuidsCore(JArray jArray)
{
for (var i = 0; i < jArray.Count; i++)
{
jArray[i] = NewGuidsCore(jArray[i]);
}
}
private void NewGuidsCore(JObject jObject)
public string NewGuidOrValue(string value)
{
foreach (var jProperty in jObject.Properties())
if (TryGenerateNewGuidString(value, out var result) || TryGenerateNewNamedId(value, out result))
{
var newValue = NewGuidsCore(jProperty.Value);
if (!ReferenceEquals(newValue, jProperty.Value))
{
jProperty.Value = newValue;
}
if (TryConvertString(jProperty.Name, out var newKey))
{
mappings.Add((jObject, newKey, jProperty.Name));
}
return result;
}
}
private bool TryConvertString(string value, out string result)
{
return TryGenerateNewGuidString(value, out result) || TryGenerateNewNamedId(value, out result);
return value;
}
private bool TryGenerateNewGuidString(string value, out string result)

5
src/Squidex.Domain.Apps.Entities/Backup/Helpers/Downloader.cs

@ -9,6 +9,7 @@ using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.Backup.Helpers
{
@ -39,7 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
}
}
public static async Task<BackupReader> OpenArchiveAsync(this IBackupArchiveLocation backupArchiveLocation, Guid id)
public static async Task<BackupReader> OpenArchiveAsync(this IBackupArchiveLocation backupArchiveLocation, Guid id, IJsonSerializer serializer)
{
Stream stream = null;
@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
{
stream = await backupArchiveLocation.OpenStreamAsync(id);
return new BackupReader(stream);
return new BackupReader(serializer, stream);
}
catch (IOException)
{

17
src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs

@ -18,6 +18,7 @@ using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
@ -31,6 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IClock clock;
private readonly ICommandBus commandBus;
private readonly IEnumerable<BackupHandler> handlers;
private readonly IJsonSerializer serializer;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
private readonly ISemanticLog log;
@ -51,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
IEnumerable<BackupHandler> handlers,
IJsonSerializer serializer,
ISemanticLog log,
IStreamNameResolver streamNameResolver,
IStore<string> store)
@ -61,6 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(handlers, nameof(handlers));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(store, nameof(store));
Guard.NotNull(streamNameResolver, nameof(streamNameResolver));
Guard.NotNull(log, nameof(log));
@ -71,6 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter;
this.handlers = handlers;
this.serializer = serializer;
this.store = store;
this.streamNameResolver = streamNameResolver;
this.log = log;
@ -161,7 +166,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
await DownloadAsync();
}
using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id))
using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id, serializer))
{
using (Profiler.Trace("ReadEvents"))
{
@ -273,17 +278,15 @@ namespace Squidex.Domain.Apps.Entities.Backup
private async Task ReadEventsAsync(BackupReader reader)
{
await reader.ReadEventsAsync(streamNameResolver, async storedEvent =>
await reader.ReadEventsAsync(streamNameResolver, eventDataFormatter, async storedEvent =>
{
var @event = eventDataFormatter.Parse(storedEvent.Data);
await HandleEventAsync(reader, storedEvent, @event);
await HandleEventAsync(reader, storedEvent.Stream, storedEvent.Event);
});
Log("Reading events completed.");
}
private async Task HandleEventAsync(BackupReader reader, StoredEvent storedEvent, Envelope<IEvent> @event)
private async Task HandleEventAsync(BackupReader reader, string stream, Envelope<IEvent> @event)
{
if (@event.Payload is SquidexEvent squidexEvent)
{
@ -316,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
var eventData = eventDataFormatter.ToEventData(@event, @event.Headers.CommitId());
var eventCommit = new List<EventData> { eventData };
await eventStore.AppendAsync(Guid.NewGuid(), storedEvent.StreamName, eventCommit);
await eventStore.AppendAsync(Guid.NewGuid(), stream, eventCommit);
Log($"Read {reader.ReadEvents} events and {reader.ReadAttachments} attachments.", true);
}

4
src/Squidex.Domain.Apps.Entities/Backup/State/BackupState.cs

@ -6,13 +6,13 @@
// ==========================================================================
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
namespace Squidex.Domain.Apps.Entities.Backup.State
{
public sealed class BackupState
{
[JsonProperty]
[DataMember]
public List<BackupStateJob> Jobs { get; } = new List<BackupStateJob>();
}
}

14
src/Squidex.Domain.Apps.Entities/Backup/State/BackupStateJob.cs

@ -6,29 +6,29 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using NodaTime;
namespace Squidex.Domain.Apps.Entities.Backup.State
{
public sealed class BackupStateJob : IBackupJob
{
[JsonProperty]
[DataMember]
public Guid Id { get; set; }
[JsonProperty]
[DataMember]
public Instant Started { get; set; }
[JsonProperty]
[DataMember]
public Instant? Stopped { get; set; }
[JsonProperty]
[DataMember]
public int HandledEvents { get; set; }
[JsonProperty]
[DataMember]
public int HandledAssets { get; set; }
[JsonProperty]
[DataMember]
public JobStatus Status { get; set; }
}
}

4
src/Squidex.Domain.Apps.Entities/Backup/State/RestoreState.cs

@ -5,13 +5,13 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json;
using System.Runtime.Serialization;
namespace Squidex.Domain.Apps.Entities.Backup.State
{
public class RestoreState
{
[JsonProperty]
[DataMember]
public RestoreStateJob Job { get; set; }
}
}

21
src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs

@ -7,38 +7,39 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using NodaTime;
namespace Squidex.Domain.Apps.Entities.Backup.State
{
[DataContract]
public sealed class RestoreStateJob : IRestoreJob
{
[JsonProperty]
[DataMember]
public string AppName { get; set; }
[JsonProperty]
[DataMember]
public Guid Id { get; set; }
[JsonProperty]
[DataMember]
public Guid AppId { get; set; }
[JsonProperty]
[DataMember]
public Uri Url { get; set; }
[JsonProperty]
[DataMember]
public string NewAppName { get; set; }
[JsonProperty]
[DataMember]
public Instant Started { get; set; }
[JsonProperty]
[DataMember]
public Instant? Stopped { get; set; }
[JsonProperty]
[DataMember]
public List<string> Log { get; set; } = new List<string>();
[JsonProperty]
[DataMember]
public JobStatus Status { get; set; }
}
}

13
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLExecutionContext.cs

@ -8,8 +8,9 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
public sealed class GraphQLExecutionContext : QueryExecutionContext
@ -25,29 +26,29 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
UrlGenerator = urlGenerator;
}
public Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(JToken value)
public Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(IJsonValue value)
{
var ids = ParseIds(value);
return GetReferencedAssetsAsync(ids);
}
public Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(Guid schemaId, JToken value)
public Task<IReadOnlyList<IContentEntity>> GetReferencedContentsAsync(Guid schemaId, IJsonValue value)
{
var ids = ParseIds(value);
return GetReferencedContentsAsync(schemaId, ids);
}
private static ICollection<Guid> ParseIds(JToken value)
private static ICollection<Guid> ParseIds(IJsonValue value)
{
try
{
var result = new List<Guid>();
if (value is JArray)
if (value is JsonArray array)
{
foreach (var id in value)
foreach (var id in array)
{
result.Add(Guid.Parse(id.ToString()));
}

2
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AllTypes.cs

@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
public static readonly IGraphType Guid = new GuidGraphType2();
public static readonly IGraphType Date = new DateTimeGraphType();
public static readonly IGraphType Date = new InstantGraphType();
public static readonly IGraphType Json = new JsonGraphType();

4
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/AssetGraphType.cs

@ -39,7 +39,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "created",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created.ToDateTimeUtc()),
Resolver = Resolve(x => x.Created),
Description = "The date and time when the asset has been created."
});
@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModified",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified.ToDateTimeUtc()),
Resolver = Resolve(x => x.LastModified),
Description = "The date and time when the asset has been modified last."
});

4
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs

@ -9,10 +9,10 @@ using System.Collections.Generic;
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
@ -68,7 +68,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
fieldGraphType.Description = $"The structure of the {fieldName} field of the {schemaName} content type.";
var fieldResolver = new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, JToken>>(c =>
var fieldResolver = new FuncFieldResolver<NamedContentData, IReadOnlyDictionary<string, IJsonValue>>(c =>
{
return c.Source.GetOrDefault(field.Name);
});

4
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentGraphType.cs

@ -42,7 +42,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "created",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.Created.ToDateTimeUtc()),
Resolver = Resolve(x => x.Created),
Description = $"The date and time when the {schemaName} content has been created."
});
@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
Name = "lastModified",
ResolvedType = AllTypes.NonNullDate,
Resolver = Resolve(x => x.LastModified.ToDateTimeUtc()),
Resolver = Resolve(x => x.LastModified),
Description = $"The date and time when the {schemaName} content has been modified last."
});

6
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/NestedGraphType.cs

@ -8,14 +8,14 @@
using System.Linq;
using GraphQL.Resolvers;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public sealed class NestedGraphType : ObjectGraphType<JObject>
public sealed class NestedGraphType : ObjectGraphType<JsonObject>
{
public NestedGraphType(IGraphModel model, ISchemaEntity schema, IArrayField field)
{
@ -34,7 +34,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
var resolver = new FuncFieldResolver<object>(c =>
{
if (((JObject)c.Source).TryGetValue(nestedField.Name, out var value))
if (((JsonObject)c.Source).TryGetValue(nestedField.Name, out var value))
{
return fieldInfo.Resolver(value, c);
}

4
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/QueryGraphTypeVisitor.cs

@ -7,13 +7,13 @@
using System;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types
{
public delegate object ValueResolver(JToken value, ResolveFieldContext context);
public delegate object ValueResolver(IJsonValue value, ResolveFieldContext context);
public sealed class QueryGraphTypeVisitor : IFieldVisitor<(IGraphType ResolveType, ValueResolver Resolver)>
{

41
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantGraphType.cs

@ -0,0 +1,41 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Language.AST;
using GraphQL.Types;
using NodaTime.Text;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class InstantGraphType : DateGraphType
{
public override object Serialize(object value)
{
return ParseValue(value);
}
public override object ParseValue(object value)
{
return InstantPattern.General.Parse(value.ToString()).Value;
}
public override object ParseLiteral(IValue value)
{
if (value is InstantValue timeValue)
{
return ParseValue(timeValue.Value);
}
if (value is StringValue stringValue)
{
return ParseValue(stringValue.Value);
}
return null;
}
}
}

25
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/InstantValue.cs

@ -0,0 +1,25 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using GraphQL.Language.AST;
using NodaTime;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class InstantValue : ValueNode<Instant>
{
public InstantValue(Instant value)
{
Value = value;
}
protected override bool Equals(ValueNode<Instant> node)
{
return Value.Equals(node.Value);
}
}
}

6
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonConverter.cs

@ -7,7 +7,7 @@
using GraphQL.Language.AST;
using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
@ -21,12 +21,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
public IValue Convert(object value, IGraphType type)
{
return new JsonValue(value as JObject);
return new JsonValue(value as JsonObject);
}
public bool Matches(object value, IGraphType type)
{
return value is JObject;
return value is JsonObject;
}
}
}

8
src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/Utils/JsonValue.cs

@ -6,18 +6,18 @@
// ==========================================================================
using GraphQL.Language.AST;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils
{
public sealed class JsonValue : ValueNode<JObject>
public sealed class JsonValue : ValueNode<IJsonValue>
{
public JsonValue(JObject value)
public JsonValue(IJsonValue value)
{
Value = value;
}
protected override bool Equals(ValueNode<JObject> node)
protected override bool Equals(ValueNode<IJsonValue> node)
{
return false;
}

18
src/Squidex.Domain.Apps.Entities/Contents/State/ContentState.cs

@ -6,7 +6,7 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Contents;
@ -19,28 +19,28 @@ namespace Squidex.Domain.Apps.Entities.Contents.State
{
public class ContentState : DomainObjectState<ContentState>, IContentEntity
{
[JsonProperty]
[DataMember]
public NamedId<Guid> AppId { get; set; }
[JsonProperty]
[DataMember]
public NamedId<Guid> SchemaId { get; set; }
[JsonProperty]
[DataMember]
public NamedContentData Data { get; set; }
[JsonProperty]
[DataMember]
public NamedContentData DataDraft { get; set; }
[JsonProperty]
[DataMember]
public ScheduleJob ScheduleJob { get; set; }
[JsonProperty]
[DataMember]
public bool IsPending { get; set; }
[JsonProperty]
[DataMember]
public bool IsDeleted { get; set; }
[JsonProperty]
[DataMember]
public Status Status { get; set; }
protected void On(ContentCreated @event)

14
src/Squidex.Domain.Apps.Entities/DomainObjectState.cs

@ -6,7 +6,7 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using NodaTime;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands;
@ -24,22 +24,22 @@ namespace Squidex.Domain.Apps.Entities
IUpdateableEntityWithLastModifiedBy
where T : Cloneable
{
[JsonProperty]
[DataMember]
public Guid Id { get; set; }
[JsonProperty]
[DataMember]
public RefToken CreatedBy { get; set; }
[JsonProperty]
[DataMember]
public RefToken LastModifiedBy { get; set; }
[JsonProperty]
[DataMember]
public Instant Created { get; set; }
[JsonProperty]
[DataMember]
public Instant LastModified { get; set; }
[JsonProperty]
[DataMember]
public long Version { get; set; } = EtagVersion.Empty;
public T Clone()

8
src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs

@ -6,7 +6,7 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Rules;
@ -20,13 +20,13 @@ namespace Squidex.Domain.Apps.Entities.Rules.State
[CollectionName("Rules")]
public class RuleState : DomainObjectState<RuleState>, IRuleEntity
{
[JsonProperty]
[DataMember]
public NamedId<Guid> AppId { get; set; }
[JsonProperty]
[DataMember]
public Rule RuleDef { get; set; }
[JsonProperty]
[DataMember]
public bool IsDeleted { get; set; }
protected void On(RuleCreated @event)

28
src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs

@ -6,7 +6,7 @@
// ==========================================================================
using System;
using Newtonsoft.Json;
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Events;
@ -22,43 +22,43 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
[CollectionName("Schemas")]
public class SchemaState : DomainObjectState<SchemaState>, ISchemaEntity
{
[JsonProperty]
[DataMember]
public NamedId<Guid> AppId { get; set; }
[JsonProperty]
[DataMember]
public string Name { get; set; }
[JsonProperty]
[DataMember]
public string Category { get; set; }
[JsonProperty]
[DataMember]
public int TotalFields { get; set; }
[JsonProperty]
[DataMember]
public bool IsDeleted { get; set; }
[JsonProperty]
[DataMember]
public bool IsSingleton { get; set; }
[JsonProperty]
[DataMember]
public string ScriptQuery { get; set; }
[JsonProperty]
[DataMember]
public string ScriptCreate { get; set; }
[JsonProperty]
[DataMember]
public string ScriptUpdate { get; set; }
[JsonProperty]
[DataMember]
public string ScriptDelete { get; set; }
[JsonProperty]
[DataMember]
public string ScriptChange { get; set; }
[JsonProperty]
[DataMember]
public Schema SchemaDef { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public bool IsPublished
{
get { return SchemaDef.IsPublished; }

3
src/Squidex.Domain.Apps.Events/SquidexHeaderExtensions.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Globalization;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events
@ -15,7 +14,7 @@ namespace Squidex.Domain.Apps.Events
{
public static Guid AppId(this EnvelopeHeaders headers)
{
return headers[SquidexHeaders.AppId].ToGuid(CultureInfo.InvariantCulture);
return headers[SquidexHeaders.AppId].ToGuid();
}
public static Envelope<T> SetAppId<T>(this Envelope<T> envelope, Guid value) where T : class

9
src/Squidex.Infrastructure.Redis/RedisPubSub.cs

@ -9,6 +9,7 @@ using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Tasks;
using StackExchange.Redis;
@ -19,11 +20,13 @@ namespace Squidex.Infrastructure
{
private readonly ConcurrentDictionary<string, object> subscriptions = new ConcurrentDictionary<string, object>();
private readonly Lazy<IConnectionMultiplexer> redisClient;
private readonly IJsonSerializer serializer;
private readonly Lazy<ISubscriber> redisSubscriber;
private readonly ISemanticLog log;
public RedisPubSub(Lazy<IConnectionMultiplexer> redis, ISemanticLog log)
public RedisPubSub(Lazy<IConnectionMultiplexer> redis, IJsonSerializer serializer, ISemanticLog log)
{
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(redis, nameof(redis));
Guard.NotNull(log, nameof(log));
@ -31,6 +34,8 @@ namespace Squidex.Infrastructure
redisClient = redis;
redisSubscriber = new Lazy<ISubscriber>(() => redis.Value.GetSubscriber());
this.serializer = serializer;
}
public Task InitializeAsync(CancellationToken ct = default(CancellationToken))
@ -61,7 +66,7 @@ namespace Squidex.Infrastructure
{
var typeName = typeof(T).FullName;
return (RedisSubscription<T>)subscriptions.GetOrAdd(typeName, this, (k, c) => new RedisSubscription<T>(c.redisSubscriber.Value, k, c.log));
return (RedisSubscription<T>)subscriptions.GetOrAdd(typeName, this, (k, c) => new RedisSubscription<T>(c.redisSubscriber.Value, serializer, k, c.log));
}
}
}

10
src/Squidex.Infrastructure.Redis/RedisSubscription.cs

@ -7,7 +7,7 @@
using System;
using System.Reactive.Subjects;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log;
using StackExchange.Redis;
@ -20,6 +20,7 @@ namespace Squidex.Infrastructure
private readonly Guid selfId = Guid.NewGuid();
private readonly Subject<T> subject = new Subject<T>();
private readonly ISubscriber subscriber;
private readonly IJsonSerializer serializer;
private readonly ISemanticLog log;
private readonly string channelName;
@ -30,10 +31,11 @@ namespace Squidex.Infrastructure
public Guid Sender;
}
public RedisSubscription(ISubscriber subscriber, string channelName, ISemanticLog log)
public RedisSubscription(ISubscriber subscriber, IJsonSerializer serializer, string channelName, ISemanticLog log)
{
this.log = log;
this.serializer = serializer;
this.subscriber = subscriber;
this.subscriber.Subscribe(channelName, (channel, value) => HandleMessage(value));
@ -46,7 +48,7 @@ namespace Squidex.Infrastructure
{
var senderId = notifySelf ? Guid.Empty : selfId;
var envelope = JsonConvert.SerializeObject(new Envelope { Sender = senderId, Payload = (T)value });
var envelope = serializer.Serialize(new Envelope { Sender = senderId, Payload = (T)value });
subscriber.Publish(channelName, envelope);
}
@ -68,7 +70,7 @@ namespace Squidex.Infrastructure
return;
}
var envelope = JsonConvert.DeserializeObject<Envelope>(value);
var envelope = serializer.Deserialize<Envelope>(value);
if (envelope.Sender != selfId)
{

6
src/Squidex.Infrastructure/EventSourcing/DefaultEventDataFormatter.cs

@ -25,12 +25,12 @@ namespace Squidex.Infrastructure.EventSourcing
this.serializer = serializer;
}
public Envelope<IEvent> Parse(EventData eventData, bool migrate = true)
public Envelope<IEvent> Parse(EventData eventData, bool migrate = true, Func<string, string> stringConverter = null)
{
var eventType = typeNameRegistry.GetType(eventData.Type);
var eventHeaders = serializer.Deserialize<EnvelopeHeaders>(eventData.Metadata);
var eventContent = serializer.Deserialize<IEvent>(eventData.Payload, eventType);
var eventHeaders = serializer.Deserialize<EnvelopeHeaders>(eventData.Metadata, null, stringConverter);
var eventContent = serializer.Deserialize<IEvent>(eventData.Payload, eventType, stringConverter);
if (migrate && eventContent is IMigratedEvent migratedEvent)
{

11
src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Globalization;
using NodaTime;
namespace Squidex.Infrastructure.EventSourcing
@ -27,7 +26,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static long EventStreamNumber(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.EventStreamNumber].ToInt64(CultureInfo.InvariantCulture);
return headers[CommonHeaders.EventStreamNumber].ToInt64();
}
public static Envelope<T> SetEventStreamNumber<T>(this Envelope<T> envelope, long value) where T : class
@ -39,7 +38,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static Guid CommitId(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.CommitId].ToGuid(CultureInfo.InvariantCulture);
return headers[CommonHeaders.CommitId].ToGuid();
}
public static Envelope<T> SetCommitId<T>(this Envelope<T> envelope, Guid value) where T : class
@ -51,7 +50,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static Guid AggregateId(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.AggregateId].ToGuid(CultureInfo.InvariantCulture);
return headers[CommonHeaders.AggregateId].ToGuid();
}
public static Envelope<T> SetAggregateId<T>(this Envelope<T> envelope, Guid value) where T : class
@ -63,7 +62,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static Guid EventId(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.EventId].ToGuid(CultureInfo.InvariantCulture);
return headers[CommonHeaders.EventId].ToGuid();
}
public static Envelope<T> SetEventId<T>(this Envelope<T> envelope, Guid value) where T : class
@ -75,7 +74,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static Instant Timestamp(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.Timestamp].ToInstant(CultureInfo.InvariantCulture);
return headers[CommonHeaders.Timestamp].ToInstant();
}
public static Envelope<T> SetTimestamp<T>(this Envelope<T> envelope, Instant value) where T : class

2
src/Squidex.Infrastructure/EventSourcing/IEventDataFormatter.cs

@ -11,7 +11,7 @@ namespace Squidex.Infrastructure.EventSourcing
{
public interface IEventDataFormatter
{
Envelope<IEvent> Parse(EventData eventData, bool migrate = true);
Envelope<IEvent> Parse(EventData eventData, bool migrate = true, Func<string, string> stringConverter = null);
EventData ToEventData(Envelope<IEvent> envelope, Guid commitId, bool migrate = true);
}

6
src/Squidex.Infrastructure/Json/IJsonSerializer.cs

@ -12,12 +12,12 @@ namespace Squidex.Infrastructure.Json
{
public interface IJsonSerializer
{
string Serialize<T>(T value);
string Serialize<T>(T value, bool intented = false);
void Serialize<T>(T value, Stream stream);
T Deserialize<T>(string value, Type actualType = null);
T Deserialize<T>(string value, Type actualType = null, Func<string, string> stringConverter = null);
T Deserialize<T>(Stream stream, Type actualType = null);
T Deserialize<T>(Stream stream, Type actualType = null, Func<string, string> stringConverter = null);
}
}

57
src/Squidex.Infrastructure/Json/Newtonsoft/JsonValueConverter.cs

@ -10,15 +10,12 @@ using System.Globalization;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Objects;
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
namespace Squidex.Infrastructure.Json.Newtonsoft
{
public sealed class JsonValueConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(IJsonValue).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return ReadJson(reader);
@ -125,36 +122,46 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
writer.WriteValue(s.Value);
break;
case JsonScalar<double> s:
writer.WriteValue(s.Value);
break;
case JsonArray array:
if (s.Value % 1 == 0)
{
writer.WriteValue((long)s.Value);
}
else
{
writer.WriteStartArray();
writer.WriteValue(s.Value);
}
foreach (var item in array)
{
WriteJson(writer, item);
}
break;
case JsonArray array:
writer.WriteStartArray();
writer.WriteEndArray();
break;
foreach (var item in array)
{
WriteJson(writer, item);
}
case JsonObject obj:
{
writer.WriteStartObject();
writer.WriteEndArray();
break;
foreach (var kvp in obj)
{
writer.WritePropertyName(kvp.Key);
case JsonObject obj:
writer.WriteStartObject();
WriteJson(writer, kvp.Value);
}
foreach (var kvp in obj)
{
writer.WritePropertyName(kvp.Key);
writer.WriteEndObject();
break;
WriteJson(writer, kvp.Value);
}
writer.WriteEndObject();
break;
}
}
public override bool CanConvert(Type objectType)
{
return typeof(IJsonValue).IsAssignableFrom(objectType);
}
}
}

68
src/Squidex.Infrastructure/Json/Newtonsoft/NewtonsoftJsonSerializer.cs

@ -16,6 +16,32 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
private readonly JsonSerializerSettings settings;
private readonly JsonSerializer serializer;
private sealed class CustomReader : JsonTextReader
{
private readonly Func<string, string> stringConverter;
public override object Value
{
get
{
var value = base.Value;
if (value is string s)
{
return stringConverter(s);
}
return value;
}
}
public CustomReader(TextReader reader, Func<string, string> stringConverter)
: base(reader)
{
this.stringConverter = stringConverter;
}
}
public NewtonsoftJsonSerializer(JsonSerializerSettings settings)
{
Guard.NotNull(settings, nameof(settings));
@ -25,36 +51,50 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
serializer = JsonSerializer.Create(settings);
}
public T Deserialize<T>(string value, Type actualType = null)
public string Serialize<T>(T value, bool intented)
{
actualType = actualType ?? typeof(T);
return (T)JsonConvert.DeserializeObject(value, actualType, settings);
return JsonConvert.SerializeObject(value, intented ? Formatting.Indented : Formatting.None, settings);
}
public T Deserialize<T>(Stream stream, Type actualType = null)
public void Serialize<T>(T value, Stream stream)
{
using (var streamReader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
actualType = actualType ?? typeof(T);
serializer.Serialize(writer, value);
return (T)serializer.Deserialize(streamReader, actualType);
writer.Flush();
}
}
public string Serialize<T>(T value)
public T Deserialize<T>(string value, Type actualType = null, Func<string, string> stringConverter = null)
{
return JsonConvert.SerializeObject(value, settings);
using (var textReader = new StringReader(value))
{
actualType = actualType ?? typeof(T);
using (var reader = GetReader(stringConverter, textReader))
{
return (T)serializer.Deserialize(reader, actualType);
}
}
}
public void Serialize<T>(T value, Stream stream)
public T Deserialize<T>(Stream stream, Type actualType = null, Func<string, string> stringConverter = null)
{
using (var writer = new StreamWriter(stream))
using (var textReader = new StreamReader(stream))
{
serializer.Serialize(writer, value);
actualType = actualType ?? typeof(T);
writer.Flush();
using (var reader = GetReader(stringConverter, textReader))
{
return (T)serializer.Deserialize(reader, actualType);
}
}
}
private static JsonTextReader GetReader(Func<string, string> stringConverter, TextReader textReader)
{
return stringConverter != null ? new CustomReader(textReader, stringConverter) : new JsonTextReader(textReader);
}
}
}

4
src/Squidex.Infrastructure/Json/Newtonsoft/PropertiesBagConverter.cs

@ -22,9 +22,9 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
{
writer.WritePropertyName(kvp.Key);
if (kvp.Value.RawValue is Instant)
if (kvp.Value.RawValue is Instant instant)
{
writer.WriteValue(kvp.Value.ToString());
writer.WriteValue(instant.ToString());
}
else
{

13
src/Squidex.Infrastructure/Json/Objects/JsonValue.cs

@ -6,6 +6,7 @@
// ==========================================================================
using System;
using NodaTime;
namespace Squidex.Infrastructure.Json.Objects
{
@ -59,6 +60,8 @@ namespace Squidex.Infrastructure.Json.Objects
return Create(i);
case long l:
return Create(l);
case Instant i:
return Create(i);
}
throw new ArgumentException("Invalid json type");
@ -76,6 +79,16 @@ namespace Squidex.Infrastructure.Json.Objects
return new JsonScalar<double>(JsonValueType.Number, value);
}
public static IJsonValue Create(Instant? value)
{
if (value == null)
{
return Null;
}
return Create(value.Value.ToString());
}
public static IJsonValue Create(double? value)
{
if (value == null)

2
src/Squidex.Infrastructure/Language.cs

@ -18,7 +18,7 @@ namespace Squidex.Infrastructure
private static Language AddLanguage(string iso2Code, string englishName)
{
return AllLanguagesField.GetOrAdd(iso2Code, code => new Language(code, englishName));
return AllLanguagesField.GetOrAdd(iso2Code, englishName, (c, n) => new Language(c, n));
}
public static Language GetLanguage(string iso2Code)

2
src/Squidex.Infrastructure/Orleans/J{T}.cs

@ -82,7 +82,7 @@ namespace Squidex.Infrastructure.Orleans
{
try
{
return context?.ServiceProvider?.GetService<IJsonSerializer>() ?? J.DefaultSerializer;
return context?.ServiceProvider?.GetRequiredService<IJsonSerializer>() ?? J.DefaultSerializer;
}
catch
{

92
src/Squidex.Infrastructure/PropertyValue.cs

@ -18,24 +18,24 @@ namespace Squidex.Infrastructure
{
private readonly object rawValue;
private static readonly Dictionary<Type, Func<PropertyValue, CultureInfo, object>> Parsers =
new Dictionary<Type, Func<PropertyValue, CultureInfo, object>>
private static readonly Dictionary<Type, Func<PropertyValue, object>> Parsers =
new Dictionary<Type, Func<PropertyValue, object>>
{
{ typeof(string), (p, c) => p.ToString() },
{ typeof(bool), (p, c) => p.ToBoolean(c) },
{ typeof(bool?), (p, c) => p.ToNullableBoolean(c) },
{ typeof(float), (p, c) => p.ToSingle(c) },
{ typeof(float?), (p, c) => p.ToNullableSingle(c) },
{ typeof(double), (p, c) => p.ToDouble(c) },
{ typeof(double?), (p, c) => p.ToNullableDouble(c) },
{ typeof(int), (p, c) => p.ToInt32(c) },
{ typeof(int?), (p, c) => p.ToNullableInt32(c) },
{ typeof(long), (p, c) => p.ToInt64(c) },
{ typeof(long?), (p, c) => p.ToNullableInt64(c) },
{ typeof(Instant), (p, c) => p.ToInstant(c) },
{ typeof(Instant?), (p, c) => p.ToNullableInstant(c) },
{ typeof(Guid), (p, c) => p.ToGuid(c) },
{ typeof(Guid?), (p, c) => p.ToNullableGuid(c) }
{ typeof(string), p => p.ToString() },
{ typeof(bool), p => p.ToBoolean() },
{ typeof(bool?), p => p.ToNullableBoolean() },
{ typeof(float), p => p.ToSingle() },
{ typeof(float?), p => p.ToNullableSingle() },
{ typeof(double), p => p.ToDouble() },
{ typeof(double?), p => p.ToNullableDouble() },
{ typeof(int), p => p.ToInt32() },
{ typeof(int?), p => p.ToNullableInt32() },
{ typeof(long), p => p.ToInt64() },
{ typeof(long?), p => p.ToNullableInt64() },
{ typeof(Instant), p => p.ToInstant() },
{ typeof(Instant?), p => p.ToNullableInstant() },
{ typeof(Guid), p => p.ToGuid() },
{ typeof(Guid?), p => p.ToNullableGuid() }
};
public object RawValue
@ -62,7 +62,7 @@ namespace Squidex.Infrastructure
return false;
}
result = parser(this, CultureInfo.InvariantCulture);
result = parser(this);
return true;
}
@ -72,74 +72,74 @@ namespace Squidex.Infrastructure
return rawValue?.ToString();
}
public bool ToBoolean(CultureInfo culture)
public bool ToBoolean()
{
return ToOrParseValue(culture, ParseBoolean);
return ToOrParseValue(CultureInfo.InvariantCulture, ParseBoolean);
}
public bool? ToNullableBoolean(CultureInfo culture)
public bool? ToNullableBoolean()
{
return ToNullableOrParseValue(culture, ParseBoolean);
return ToNullableOrParseValue(CultureInfo.InvariantCulture, ParseBoolean);
}
public float ToSingle(CultureInfo culture)
public float ToSingle()
{
return ToOrParseValue(culture, x => float.Parse(x, culture));
return ToOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture));
}
public float? ToNullableSingle(CultureInfo culture)
public float? ToNullableSingle()
{
return ToNullableOrParseValue(culture, x => float.Parse(x, culture));
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => float.Parse(x, CultureInfo.InvariantCulture));
}
public double ToDouble(CultureInfo culture)
public double ToDouble()
{
return ToOrParseValue(culture, x => double.Parse(x, culture));
return ToOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture));
}
public double? ToNullableDouble(CultureInfo culture)
public double? ToNullableDouble()
{
return ToNullableOrParseValue(culture, x => double.Parse(x, culture));
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => double.Parse(x, CultureInfo.InvariantCulture));
}
public int ToInt32(CultureInfo culture)
public int ToInt32()
{
return ToOrParseValue(culture, x => int.Parse(x, culture));
return ToOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture));
}
public int? ToNullableInt32(CultureInfo culture)
public int? ToNullableInt32()
{
return ToNullableOrParseValue(culture, x => int.Parse(x, culture));
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => int.Parse(x, CultureInfo.InvariantCulture));
}
public long ToInt64(CultureInfo culture)
public long ToInt64()
{
return ToOrParseValue(culture, x => long.Parse(x, culture));
return ToOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture));
}
public long? ToNullableInt64(CultureInfo culture)
public long? ToNullableInt64()
{
return ToNullableOrParseValue(culture, x => long.Parse(x, culture));
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => long.Parse(x, CultureInfo.InvariantCulture));
}
public Instant ToInstant(CultureInfo culture)
public Instant ToInstant()
{
return ToOrParseValue(culture, x => InstantPattern.General.Parse(x).Value);
return ToOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value);
}
public Instant? ToNullableInstant(CultureInfo culture)
public Instant? ToNullableInstant()
{
return ToNullableOrParseValue(culture, x => InstantPattern.General.Parse(x).Value);
return ToNullableOrParseValue(CultureInfo.InvariantCulture, x => InstantPattern.General.Parse(x).Value);
}
public Guid ToGuid(CultureInfo culture)
public Guid ToGuid()
{
return ToOrParseValue(culture, Guid.Parse);
return ToOrParseValue(CultureInfo.InvariantCulture, Guid.Parse);
}
public Guid? ToNullableGuid(CultureInfo culture)
public Guid? ToNullableGuid()
{
return ToNullableOrParseValue(culture, Guid.Parse);
return ToNullableOrParseValue(CultureInfo.InvariantCulture, Guid.Parse);
}
private T? ToNullableOrParseValue<T>(IFormatProvider culture, Func<string, T> parser) where T : struct

2
src/Squidex/Areas/Api/Config/Swagger/SwaggerExtensions.cs

@ -17,7 +17,7 @@ namespace Squidex.Areas.Api.Config.Swagger
{
public static void UseMySwagger(this IApplicationBuilder app)
{
var urlOptions = app.ApplicationServices.GetService<IOptions<MyUrlsOptions>>().Value;
var urlOptions = app.ApplicationServices.GetRequiredService<IOptions<MyUrlsOptions>>().Value;
app.UseSwaggerWithApiExplorer(settings =>
{

2
src/Squidex/Areas/Api/Config/Swagger/SwaggerServices.cs

@ -28,7 +28,7 @@ namespace Squidex.Areas.Api.Config.Swagger
{
services.AddSingleton(typeof(SwaggerSettings<SwaggerGeneratorSettings>), s =>
{
var urlOptions = s.GetService<IOptions<MyUrlsOptions>>().Value;
var urlOptions = s.GetRequiredService<IOptions<MyUrlsOptions>>().Value;
var settings = new SwaggerSettings<SwaggerGeneratorSettings>()
.AddAssetODataParams()

1
src/Squidex/Areas/Api/Controllers/Apps/Models/AppCreatedDto.cs

@ -8,7 +8,6 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure.Commands;

1
src/Squidex/Areas/Api/Controllers/Apps/Models/AppDto.cs

@ -9,7 +9,6 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Newtonsoft.Json;
using NodaTime;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services;

1
src/Squidex/Areas/Api/Controllers/Apps/Models/AssignContributorDto.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection;

1
src/Squidex/Areas/Api/Controllers/Apps/Models/ClientDto.cs

@ -7,7 +7,6 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection;

1
src/Squidex/Areas/Api/Controllers/Apps/Models/ContributorDto.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace Squidex.Areas.Api.Controllers.Apps.Models
{

4
src/Squidex/Areas/Api/Controllers/UI/Models/UpdateSettingDto.cs

@ -5,7 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Areas.Api.Controllers.UI.Models
{
@ -14,6 +14,6 @@ namespace Squidex.Areas.Api.Controllers.UI.Models
/// <summary>
/// The value for the setting.
/// </summary>
public JToken Value { get; set; }
public IJsonValue Value { get; set; }
}
}

7
src/Squidex/Areas/Api/Controllers/UI/UIController.cs

@ -55,9 +55,10 @@ namespace Squidex.Areas.Api.Controllers.UI
{
var result = await grainFactory.GetGrain<IAppUISettingsGrain>(AppId).GetAsync();
result.Value["mapType"] = uiOptions.Map?.Type ?? "OSM";
result.Value["mapKey"] = uiOptions.Map?.GoogleMaps?.Key;
result.Value["supportTwitterAction"] = twitterOptions.IsConfigured();
result.Value.Add("mapType", uiOptions.Map?.Type ?? "OSM");
result.Value.Add("mapKey", uiOptions.Map?.GoogleMaps?.Key);
result.Value.Add("supportTwitterAction", twitterOptions.IsConfigured());
return Ok(result.Value);
}

8
src/Squidex/Areas/IdentityServer/Config/IdentityServerExtensions.cs

@ -31,12 +31,12 @@ namespace Squidex.Areas.IdentityServer.Config
public static IServiceProvider UseMyAdmin(this IServiceProvider services)
{
var options = services.GetService<IOptions<MyIdentityOptions>>().Value;
var options = services.GetRequiredService<IOptions<MyIdentityOptions>>().Value;
var userManager = services.GetService<UserManager<IdentityUser>>();
var userFactory = services.GetService<IUserFactory>();
var userManager = services.GetRequiredService<UserManager<IdentityUser>>();
var userFactory = services.GetRequiredService<IUserFactory>();
var log = services.GetService<ISemanticLog>();
var log = services.GetRequiredService<ISemanticLog>();
if (options.IsAdminConfigured())
{

4
src/Squidex/Config/Domain/EventPublishersServices.cs

@ -8,10 +8,10 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
namespace Squidex.Config.Domain
{
@ -54,7 +54,7 @@ namespace Squidex.Config.Domain
if (enabled)
{
services.AddSingletonAs(c => new RabbitMqEventConsumer(c.GetRequiredService<JsonSerializerSettings>(), name, publisherConfig, exchange, eventsFilter))
services.AddSingletonAs(c => new RabbitMqEventConsumer(c.GetRequiredService<IJsonSerializer>(), name, publisherConfig, exchange, eventsFilter))
.As<IEventConsumer>();
}
}

5
src/Squidex/Config/Domain/SerializationServices.cs

@ -20,7 +20,7 @@ using Squidex.Domain.Apps.Events;
using Squidex.Extensions.Actions;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Config.Domain
{
@ -49,6 +49,7 @@ namespace Squidex.Config.Domain
new AppPatternsConverter(),
new ClaimsPrincipalConverter(),
new InstantConverter(),
new JsonValueConverter(),
new LanguageConverter(),
new LanguagesConfigConverter(),
new NamedGuidIdConverter(),
@ -86,6 +87,8 @@ namespace Squidex.Config.Domain
services.AddSingleton(DefaultJsonSerializer);
services.AddSingleton(TypeNameRegistry);
services.AddSingleton(new NewtonsoftJsonSerializer(DefaultJsonSettings));
return services;
}

3
src/Squidex/Config/Domain/StoreServices.cs

@ -29,6 +29,7 @@ using Squidex.Domain.Users.MongoDb.Infrastructure;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Diagnostics;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States;
@ -88,7 +89,7 @@ namespace Squidex.Config.Domain
.As<IAssetRepository>()
.As<ISnapshotStore<AssetState, Guid>>();
services.AddSingletonAs(c => new MongoContentRepository(mongoContentDatabase, c.GetService<IAppProvider>()))
services.AddSingletonAs(c => new MongoContentRepository(mongoContentDatabase, c.GetRequiredService<IAppProvider>(), c.GetRequiredService<IJsonSerializer>()))
.As<IContentRepository>()
.As<ISnapshotStore<ContentState, Guid>>()
.As<IEventConsumer>();

2
src/Squidex/Config/Domain/SubscriptionServices.cs

@ -19,7 +19,7 @@ namespace Squidex.Config.Domain
{
public static void AddMySubscriptionServices(this IServiceCollection services, IConfiguration config)
{
services.AddSingletonAs(c => c.GetService<IOptions<MyUsageOptions>>()?.Value?.Plans.OrEmpty());
services.AddSingletonAs(c => c.GetRequiredService<IOptions<MyUsageOptions>>()?.Value?.Plans.OrEmpty());
services.AddSingletonAs<ConfigAppPlansProvider>()
.As<IAppPlansProvider>();

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Rules/RuleTests.cs

@ -128,7 +128,7 @@ namespace Squidex.Domain.Apps.Core.Model.Rules
[MemberData(nameof(Triggers))]
public void Should_freeze_triggers(RuleTrigger trigger)
{
TestData.TestFreeze(trigger);
TestUtils.TestFreeze(trigger);
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaFieldTests.cs

@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
[MemberData(nameof(FieldProperties))]
public void Should_freeze_field_properties(FieldProperties action)
{
TestData.TestFreeze(action);
TestUtils.TestFreeze(action);
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Schemas/SchemaTests.cs

@ -278,7 +278,7 @@ namespace Squidex.Domain.Apps.Core.Model.Schemas
[Fact]
public void Should_serialize_and_deserialize_schema()
{
var schemaSource = TestData.MixedSchema();
var schemaSource = TestUtils.MixedSchema();
var schemaTarget = schemaSource.SerializeAndDeserialize();
schemaTarget.Should().BeEquivalentTo(schemaSource);

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/FieldConvertersTests.cs

@ -58,7 +58,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
new ContentFieldData()
.AddValue("iv", JsonValue.Object());
var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(TestData.DefaultSerializer))(input, jsonField);
var actual = FieldConverters.ForValues(ValueConverters.EncodeJson(TestUtils.DefaultSerializer))(input, jsonField);
var expected =
new ContentFieldData()

12
tests/Squidex.Domain.Apps.Core.Tests/Operations/ConvertContent/ValueConvertersTests.cs

@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
var source = JsonValue.Object();
var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField);
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Equal(JsonValue.Create("e30="), result);
}
@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
var source = JsonValue.Null;
var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField);
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Same(source, result);
}
@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
var source = JsonValue.Create("NO-JSON");
var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField);
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField);
Assert.Same(source, result);
}
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
var source = JsonValue.Create("e30=");
var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField);
var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Equal(JsonValue.Object(), result);
}
@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
var source = JsonValue.Null;
var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField);
var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Same(source, result);
}
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{
var source = JsonValue.Null;
var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField);
var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField);
Assert.Same(source, result);
}

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateEdmSchema/EdmTests.cs

@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Core.Operations.GenerateEdmSchema
{
var languagesConfig = LanguagesConfig.Build(Language.DE, Language.EN);
var edmModel = TestData.MixedSchema().BuildEdmType(languagesConfig.ToResolver(), x => x);
var edmModel = TestUtils.MixedSchema().BuildEdmType(languagesConfig.ToResolver(), x => x);
Assert.NotNull(edmModel);
}

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/GenerateJsonSchema/JsonSchemaTests.cs

@ -16,7 +16,7 @@ namespace Squidex.Domain.Apps.Core.Operations.GenerateJsonSchema
{
public class JsonSchemaTests
{
private readonly Schema schema = TestData.MixedSchema();
private readonly Schema schema = TestUtils.MixedSchema();
[Fact]
public void Should_build_json_schema()

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs

@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => user.Claims)
.Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") });
sut = new RuleEventFormatter(TestData.DefaultSerializer, urlGenerator);
sut = new RuleEventFormatter(TestUtils.DefaultSerializer, urlGenerator);
}
[Fact]

2
tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs

@ -79,7 +79,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules
A.CallTo(() => ruleTriggerHandler.TriggerType)
.Returns(typeof(ContentChangedTrigger));
sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, TestData.DefaultSerializer, clock, typeNameRegistry);
sut = new RuleService(new[] { ruleTriggerHandler }, new[] { ruleActionHandler }, eventEnricher, TestUtils.DefaultSerializer, clock, typeNameRegistry);
}
[Fact]

9
tests/Squidex.Domain.Apps.Core.Tests/TestData.cs → tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs

@ -16,17 +16,18 @@ using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Core.Schemas.Json;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
using Xunit;
namespace Squidex.Domain.Apps.Core
{
public static class TestData
public static class TestUtils
{
public static readonly IJsonSerializer DefaultSerializer = CreateSerializer();
private static IJsonSerializer CreateSerializer()
public static IJsonSerializer CreateSerializer(TypeNameHandling typeNameHandling = TypeNameHandling.Auto)
{
var typeNameRegistry = new TypeNameRegistry();
@ -46,13 +47,15 @@ namespace Squidex.Domain.Apps.Core
new NamedGuidIdConverter(),
new NamedLongIdConverter(),
new NamedStringIdConverter(),
new PropertiesBagConverter<EnvelopeHeaders>(),
new PropertiesBagConverter<PropertiesBag>(),
new RefTokenConverter(),
new RolesConverter(),
new RuleConverter(),
new SchemaConverter(),
new StringEnumConverter()),
TypeNameHandling = TypeNameHandling.Auto
TypeNameHandling = typeNameHandling
};
return new NewtonsoftJsonSerializer(serializerSettings);

34
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppUISettingsGrainTests.cs

@ -8,7 +8,7 @@
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States;
using Xunit;
@ -33,13 +33,12 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Should_set_setting()
{
await sut.SetAsync(new JObject(new JProperty("key", 15)).AsJ());
await sut.SetAsync(JsonValue.Object().Add("key", 15).AsJ());
var actual = await sut.GetAsync();
var expected =
new JObject(
new JProperty("key", 15));
JsonValue.Object().Add("key", 15);
Assert.Equal(expected.ToString(), actual.Value.ToString());
}
@ -47,13 +46,12 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Should_set_root_value()
{
await sut.SetAsync("key", ((JToken)123).AsJ());
await sut.SetAsync("key", JsonValue.Create(123).AsJ());
var actual = await sut.GetAsync();
var expected =
new JObject(
new JProperty("key", 123));
JsonValue.Object().Add("key", 123);
Assert.Equal(expected.ToString(), actual.Value.ToString());
}
@ -61,12 +59,12 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Should_remove_root_value()
{
await sut.SetAsync("key", ((JToken)123).AsJ());
await sut.SetAsync("key", JsonValue.Create(123).AsJ());
await sut.RemoveAsync("key");
var actual = await sut.GetAsync();
var expected = new JObject();
var expected = JsonValue.Object();
Assert.Equal(expected.ToString(), actual.Value.ToString());
}
@ -74,15 +72,13 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Should_set_nested_value()
{
await sut.SetAsync("root.nested", ((JToken)123).AsJ());
await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ());
var actual = await sut.GetAsync();
var expected =
new JObject(
new JProperty("root",
new JObject(
new JProperty("nested", 123))));
JsonValue.Object().Add("root",
JsonValue.Object().Add("nested", 123));
Assert.Equal(expected.ToString(), actual.Value.ToString());
}
@ -90,14 +86,14 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Should_remove_nested_value()
{
await sut.SetAsync("root.nested", ((JToken)123).AsJ());
await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ());
await sut.RemoveAsync("root.nested");
var actual = await sut.GetAsync();
var expected =
new JObject(
new JProperty("root", new JObject()));
JsonValue.Object().Add("root",
JsonValue.Object());
Assert.Equal(expected.ToString(), actual.Value.ToString());
}
@ -105,9 +101,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Should_throw_exception_if_nested_not_an_object()
{
await sut.SetAsync("root.nested", ((JToken)123).AsJ());
await sut.SetAsync("root.nested", JsonValue.Create(123).AsJ());
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.SetAsync("root.nested.value", ((JToken)123).AsJ()));
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.SetAsync("root.nested.value", JsonValue.Create(123).AsJ()));
}
[Fact]

123
tests/Squidex.Domain.Apps.Entities.Tests/Backup/BackupReaderWriterTests.cs

@ -8,11 +8,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FakeItEasy;
using FluentAssertions;
using Squidex.Domain.Apps.Core;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
using Xunit;
@ -22,9 +23,26 @@ namespace Squidex.Domain.Apps.Entities.Backup
public class BackupReaderWriterTests
{
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>();
private readonly IJsonSerializer serializer = TestUtils.DefaultSerializer;
private readonly IEventDataFormatter formatter;
private readonly TypeNameRegistry typeNameRegistry = new TypeNameRegistry();
[TypeName(nameof(MyEvent))]
public sealed class MyEvent : IEvent
{
public Guid GuidRaw { get; set; }
public NamedId<Guid> GuidNamed { get; set; }
public Dictionary<Guid, string> Values { get; set; }
}
public BackupReaderWriterTests()
{
typeNameRegistry.Map(typeof(MyEvent));
formatter = new DefaultEventDataFormatter(typeNameRegistry, serializer);
A.CallTo(() => streamNameResolver.WithNewId(A<string>.Ignored, A<Func<string, string>>.Ignored))
.ReturnsLazily(new Func<string, Func<string, string>, string>((stream, idGenerator) => stream + "^2"));
}
@ -34,77 +52,112 @@ namespace Squidex.Domain.Apps.Entities.Backup
{
var stream = new MemoryStream();
var sourceEvents = new List<StoredEvent>();
var random = new Random();
var randomGuids = new List<Guid>();
for (var i = 0; i < 100; i++)
{
randomGuids.Add(Guid.NewGuid());
}
Guid RandomGuid()
{
return randomGuids[random.Next(randomGuids.Count)];
}
var sourceEvents = new List<(string Stream, Envelope<IEvent> Event)>();
using (var writer = new BackupWriter(stream, true))
for (var i = 0; i < 200; i++)
{
for (var i = 0; i < 1000; i++)
var @event = new MyEvent
{
var eventData = new EventData { Type = i.ToString(), Metadata = i, Payload = i };
GuidNamed = new NamedId<Guid>(RandomGuid(), $"name{i}"),
GuidRaw = RandomGuid(),
Values = new Dictionary<Guid, string>
{
[RandomGuid()] = $"name{i}_1",
[RandomGuid()] = $"name{i}_1",
}
};
var envelope = Envelope.Create<IEvent>(@event);
envelope.Headers.Set(RandomGuid().ToString(), i);
envelope.Headers.Set("Id", RandomGuid());
envelope.Headers.Set("Index", i);
sourceEvents.Add(($"My-{RandomGuid()}", envelope));
}
using (var writer = new BackupWriter(serializer, stream, true))
{
foreach (var @event in sourceEvents)
{
var eventData = formatter.ToEventData(@event.Event, Guid.NewGuid(), true);
var eventStored = new StoredEvent("S", "1", 2, eventData);
if (i % 17 == 0)
{
var localI = i;
var index = @event.Event.Headers["Index"].ToInt32();
await writer.WriteBlobAsync(eventData.Type, innerStream =>
if (index % 17 == 0)
{
await writer.WriteBlobAsync(index.ToString(), innerStream =>
{
innerStream.WriteByte((byte)localI);
innerStream.WriteByte((byte)index);
return TaskHelper.Done;
});
}
else if (i % 37 == 0)
else if (index % 37 == 0)
{
await writer.WriteJsonAsync(eventData.Type, $"JSON_{i}");
await writer.WriteJsonAsync(index.ToString(), $"JSON_{index}");
}
writer.WriteEvent(eventStored);
sourceEvents.Add(eventStored);
}
}
stream.Position = 0;
var readEvents = new List<StoredEvent>();
var targetEvents = new List<(string Stream, Envelope<IEvent> Event)>();
using (var reader = new BackupReader(stream))
using (var reader = new BackupReader(serializer, stream))
{
await reader.ReadEventsAsync(streamNameResolver, async @event =>
await reader.ReadEventsAsync(streamNameResolver, formatter, async @event =>
{
var i = int.Parse(@event.Data.Type);
var index = @event.Event.Headers["Index"].ToInt32();
if (i % 17 == 0)
if (index % 17 == 0)
{
await reader.ReadBlobAsync(@event.Data.Type, innerStream =>
await reader.ReadBlobAsync(index.ToString(), innerStream =>
{
var b = innerStream.ReadByte();
var byteRead = innerStream.ReadByte();
Assert.Equal((byte)i, b);
Assert.Equal((byte)index, byteRead);
return TaskHelper.Done;
});
}
else if (i % 37 == 0)
else if (index % 37 == 0)
{
var j = await reader.ReadJsonAttachmentAsync(@event.Data.Type);
var json = await reader.ReadJsonAttachmentAsync<string>(index.ToString());
Assert.Equal($"JSON_{i}", j.ToString());
Assert.Equal($"JSON_{index}", json);
}
readEvents.Add(@event);
targetEvents.Add(@event);
});
}
var sourceEventsWithNewStreamName =
sourceEvents.Select(x =>
new StoredEvent(streamNameResolver.WithNewId(x.StreamName, null),
x.EventPosition,
x.EventStreamNumber,
x.Data)).ToList();
for (var i = 0; i < targetEvents.Count; i++)
{
var lhs = targetEvents[i].Event.To<MyEvent>();
var rhs = sourceEvents[i].Event.To<MyEvent>();
readEvents.Should().BeEquivalentTo(sourceEventsWithNewStreamName);
Assert.Equal(rhs.Payload.GuidRaw, reader.OldGuid(lhs.Payload.GuidRaw));
Assert.Equal(rhs.Payload.GuidNamed.Id, reader.OldGuid(lhs.Payload.GuidNamed.Id));
Assert.Equal(rhs.Headers["Id"].ToGuid(), reader.OldGuid(lhs.Headers["Id"].ToGuid()));
}
}
}
}
}

161
tests/Squidex.Domain.Apps.Entities.Tests/Backup/GuidMapperTests.cs

@ -1,161 +0,0 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Newtonsoft.Json.Linq;
using Xunit;
namespace Squidex.Domain.Apps.Entities.Backup
{
public class GuidMapperTests
{
private readonly Guid id1 = Guid.NewGuid();
private readonly Guid id2 = Guid.NewGuid();
private readonly GuidMapper map = new GuidMapper();
[Fact]
public void Should_map_guid_string_if_valid()
{
var result = map.NewGuidString(id1.ToString());
Assert.Equal(map.NewGuid(id1).ToString(), result);
}
[Fact]
public void Should_return_null_if_mapping_invalid_guid_string()
{
var result = map.NewGuidString("invalid");
Assert.Null(result);
}
[Fact]
public void Should_return_null_if_mapping_null_guid_string()
{
var result = map.NewGuidString(null);
Assert.Null(result);
}
[Fact]
public void Should_map_guid()
{
var result = map.NewGuids(id1);
Assert.Equal(map.NewGuid(id1), result.Value<Guid>());
}
[Fact]
public void Should_return_old_guid()
{
var newGuid = map.NewGuids(id1).Value<Guid>();
Assert.Equal(id1, map.OldGuid(newGuid));
}
[Fact]
public void Should_map_guid_string()
{
var result = map.NewGuids(id1.ToString());
Assert.Equal(map.NewGuid(id1).ToString(), result.Value<string>());
}
[Fact]
public void Should_map_named_id()
{
var result = map.NewGuids($"{id1},name");
Assert.Equal($"{map.NewGuid(id1)},name", result.Value<string>());
}
[Fact]
public void Should_map_array_with_guid()
{
var obj =
new JObject(
new JProperty("k",
new JArray(id1, id1, id2)));
map.NewGuids(obj);
Assert.Equal(map.NewGuid(id1), obj["k"][0].Value<Guid>());
Assert.Equal(map.NewGuid(id1), obj["k"][1].Value<Guid>());
Assert.Equal(map.NewGuid(id2), obj["k"][2].Value<Guid>());
}
[Fact]
public void Should_map_objects_with_guid_keys()
{
var obj =
new JObject(
new JProperty("k",
new JObject(
new JProperty(id1.ToString(), id1),
new JProperty(id2.ToString(), id2))));
map.NewGuids(obj);
Assert.Equal(map.NewGuid(id1), obj["k"].Value<Guid>(map.NewGuid(id1).ToString()));
Assert.Equal(map.NewGuid(id2), obj["k"].Value<Guid>(map.NewGuid(id2).ToString()));
}
[Fact]
public void Should_map_objects_with_guid()
{
var obj =
new JObject(
new JProperty("k",
new JObject(
new JProperty("v1", id1),
new JProperty("v2", id1),
new JProperty("v3", id2))));
map.NewGuids(obj);
Assert.Equal(map.NewGuid(id1), obj["k"].Value<Guid>("v1"));
Assert.Equal(map.NewGuid(id1), obj["k"].Value<Guid>("v2"));
Assert.Equal(map.NewGuid(id2), obj["k"].Value<Guid>("v3"));
}
[Fact]
public void Should_map_objects_with_guid_string()
{
var obj =
new JObject(
new JProperty("k",
new JObject(
new JProperty("v1", id1.ToString()),
new JProperty("v2", id1.ToString()),
new JProperty("v3", id2.ToString()))));
map.NewGuids(obj);
Assert.Equal(map.NewGuid(id1).ToString(), obj["k"].Value<string>("v1"));
Assert.Equal(map.NewGuid(id1).ToString(), obj["k"].Value<string>("v2"));
Assert.Equal(map.NewGuid(id2).ToString(), obj["k"].Value<string>("v3"));
}
[Fact]
public void Should_map_objects_with_named_id()
{
var obj =
new JObject(
new JProperty("k",
new JObject(
new JProperty("v1", $"{id1},v1"),
new JProperty("v2", $"{id1},v2"),
new JProperty("v3", $"{id2},v3"))));
map.NewGuids(obj);
Assert.Equal($"{map.NewGuid(id1).ToString()},v1", obj["k"].Value<string>("v1"));
Assert.Equal($"{map.NewGuid(id1).ToString()},v2", obj["k"].Value<string>("v2"));
Assert.Equal($"{map.NewGuid(id2).ToString()},v3", obj["k"].Value<string>("v3"));
}
}
}

4
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs

@ -41,10 +41,10 @@ namespace Squidex.Domain.Apps.Entities.Contents
new NamedContentData()
.AddField("my-field1",
new ContentFieldData()
.AddValue(null))
.AddValue("iv", null))
.AddField("my-field2",
new ContentFieldData()
.AddValue(1));
.AddValue("iv", 1));
private readonly NamedContentData data =
new NamedContentData()
.AddField("my-field1",

35
tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLQueriesTests.cs

@ -8,7 +8,6 @@
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
using Xunit;
@ -77,9 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
id = asset.Id,
version = 1,
created = asset.Created.ToDateTimeUtc(),
created = asset.Created,
createdBy = "subject:user1",
lastModified = asset.LastModified.ToDateTimeUtc(),
lastModified = asset.LastModified,
lastModifiedBy = "subject:user2",
url = $"assets/{asset.Id}",
thumbnailUrl = $"assets/{asset.Id}?width=100",
@ -147,9 +146,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
id = asset.Id,
version = 1,
created = asset.Created.ToDateTimeUtc(),
created = asset.Created,
createdBy = "subject:user1",
lastModified = asset.LastModified.ToDateTimeUtc(),
lastModified = asset.LastModified,
lastModifiedBy = "subject:user2",
url = $"assets/{asset.Id}",
thumbnailUrl = $"assets/{asset.Id}?width=100",
@ -211,9 +210,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
id = asset.Id,
version = 1,
created = asset.Created.ToDateTimeUtc(),
created = asset.Created,
createdBy = "subject:user1",
lastModified = asset.LastModified.ToDateTimeUtc(),
lastModified = asset.LastModified,
lastModifiedBy = "subject:user2",
url = $"assets/{asset.Id}",
thumbnailUrl = $"assets/{asset.Id}?width=100",
@ -298,9 +297,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
id = content.Id,
version = 1,
created = content.Created.ToDateTimeUtc(),
created = content.Created,
createdBy = "subject:user1",
lastModified = content.LastModified.ToDateTimeUtc(),
lastModified = content.LastModified,
lastModifiedBy = "subject:user2",
status = "DRAFT",
url = $"contents/my-schema/{content.Id}",
@ -320,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
},
myDatetime = new
{
iv = content.LastModified.ToDateTimeUtc()
iv = content.LastModified
},
myJson = new
{
@ -440,9 +439,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
id = content.Id,
version = 1,
created = content.Created.ToDateTimeUtc(),
created = content.Created,
createdBy = "subject:user1",
lastModified = content.LastModified.ToDateTimeUtc(),
lastModified = content.LastModified,
lastModifiedBy = "subject:user2",
status = "DRAFT",
url = $"contents/my-schema/{content.Id}",
@ -462,7 +461,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
},
myDatetime = new
{
iv = content.LastModified.ToDateTimeUtc()
iv = content.LastModified
},
myJson = new
{
@ -560,9 +559,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{
id = content.Id,
version = 1,
created = content.Created.ToDateTimeUtc(),
created = content.Created,
createdBy = "subject:user1",
lastModified = content.LastModified.ToDateTimeUtc(),
lastModified = content.LastModified,
lastModifiedBy = "subject:user2",
status = "DRAFT",
url = $"contents/my-schema/{content.Id}",
@ -582,7 +581,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
},
myDatetime = new
{
iv = content.LastModified.ToDateTimeUtc()
iv = content.LastModified
},
myJson = new
{
@ -819,9 +818,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query });
var json = JToken.FromObject(result);
var json = serializer.Serialize(result);
Assert.Null(json["data"]);
Assert.Contains("\"data\":null", json);
}
private QueryContext MatchsAssetContext()

42
tests/Squidex.Domain.Apps.Entities.Tests/Contents/GraphQL/GraphQLTestBase.cs

@ -12,7 +12,6 @@ using FakeItEasy;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NodaTime.Extensions;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps;
@ -23,6 +22,8 @@ using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents.TestData;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Xunit;
#pragma warning disable SA1311 // Static readonly fields must begin with upper-case letter
@ -39,6 +40,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
protected readonly IContentQueryService contentQuery = A.Fake<IContentQueryService>();
protected readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>();
protected readonly ISchemaEntity schema = A.Fake<ISchemaEntity>();
protected readonly IJsonSerializer serializer = TestUtils.CreateSerializer(TypeNameHandling.None);
protected readonly IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
protected readonly IAppProvider appProvider = A.Fake<IAppProvider>();
protected readonly IAppEntity app = A.Dummy<IAppEntity>();
@ -106,40 +108,40 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
.AddValue("de", "value"))
.AddField("my-assets",
new ContentFieldData()
.AddValue("iv", JToken.FromObject(new[] { assetId })))
.AddValue("iv", JsonValue.Array(assetId.ToString())))
.AddField("my-number",
new ContentFieldData()
.AddValue("iv", 1))
.AddValue("iv", 1.0))
.AddField("my-boolean",
new ContentFieldData()
.AddValue("iv", true))
.AddField("my-datetime",
new ContentFieldData()
.AddValue("iv", now.ToDateTimeUtc()))
.AddValue("iv", now))
.AddField("my-tags",
new ContentFieldData()
.AddValue("iv", JToken.FromObject(new[] { "tag1", "tag2" })))
.AddValue("iv", JsonValue.Array("tag1", "tag2")))
.AddField("my-references",
new ContentFieldData()
.AddValue("iv", JToken.FromObject(new[] { refId })))
.AddValue("iv", JsonValue.Array(refId.ToString())))
.AddField("my-geolocation",
new ContentFieldData()
.AddValue("iv", JToken.FromObject(new { latitude = 10, longitude = 20 })))
.AddValue("iv", JsonValue.Object().Add("latitude", 10).Add("longitude", 20)))
.AddField("my-json",
new ContentFieldData()
.AddValue("iv", JToken.FromObject(new { value = 1 })))
.AddValue("iv", JsonValue.Object().Add("value", 1)))
.AddField("my-localized",
new ContentFieldData()
.AddValue("de-DE", "de-DE"))
.AddField("my-array",
new ContentFieldData()
.AddValue("iv", new JArray(
new JObject(
new JProperty("nested-boolean", true),
new JProperty("nested-number", 1)),
new JObject(
new JProperty("nested-boolean", false),
new JProperty("nested-number", 2)))));
.AddValue("iv", JsonValue.Array(
JsonValue.Object()
.Add("nested-boolean", true)
.Add("nested-number", 1),
JsonValue.Object()
.Add("nested-boolean", false)
.Add("nested-number", 2))));
var content = new ContentEntity
{
@ -179,22 +181,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return asset;
}
protected static void AssertResult(object expected, (bool HasErrors, object Response) result, bool checkErrors = true)
protected void AssertResult(object expected, (bool HasErrors, object Response) result, bool checkErrors = true)
{
if (checkErrors && result.HasErrors)
{
throw new InvalidOperationException(Serialize(result));
}
var resultJson = JsonConvert.SerializeObject(result.Response, Formatting.Indented);
var expectJson = JsonConvert.SerializeObject(expected, Formatting.Indented);
var resultJson = serializer.Serialize(result.Response, true);
var expectJson = serializer.Serialize(expected, true);
Assert.Equal(expectJson, resultJson);
}
private static string Serialize((bool HasErrors, object Response) result)
private string Serialize((bool HasErrors, object Response) result)
{
return JsonConvert.SerializeObject(result);
return serializer.Serialize(result);
}
}
}

5
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDequeuerTests.cs

@ -8,7 +8,6 @@
using System;
using System.Threading.Tasks;
using FakeItEasy;
using Newtonsoft.Json.Linq;
using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules;
@ -49,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
[InlineData(4, 0, RuleResult.Failed, RuleJobResult.Failed)]
public async Task Should_set_next_attempt_based_on_num_calls(int calls, int minutes, RuleResult result, RuleJobResult jobResult)
{
var actionData = new JObject();
var actionData = "{}";
var actionName = "MyAction";
var @event = CreateEvent(calls, actionName, actionData);
@ -73,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
.MustHaveHappened();
}
private IRuleEventEntity CreateEvent(int numCalls, string actionName, JObject actionData)
private IRuleEventEntity CreateEvent(int numCalls, string actionName, string actionData)
{
var @event = A.Fake<IRuleEventEntity>();

1
tests/Squidex.Domain.Apps.Entities.Tests/Squidex.Domain.Apps.Entities.Tests.csproj

@ -18,6 +18,7 @@
<ProjectReference Include="..\..\src\Squidex.Infrastructure.MongoDb\Squidex.Infrastructure.MongoDb.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.csproj" />
<ProjectReference Include="..\Squidex.Domain.Apps.Core.Tests\Squidex.Domain.Apps.Core.Tests.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FakeItEasy" Version="4.9.1" />

12
tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Globalization;
using NodaTime;
using Xunit;
@ -15,7 +14,6 @@ namespace Squidex.Infrastructure.EventSourcing
public class EnvelopeExtensionsTests
{
private readonly Envelope<string> sut = new Envelope<string>(string.Empty);
private readonly CultureInfo culture = CultureInfo.InvariantCulture;
[Fact]
public void Should_set_and_get_timestamp()
@ -25,7 +23,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetTimestamp(timestamp);
Assert.Equal(timestamp, sut.Headers.Timestamp());
Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant(culture));
Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant());
}
[Fact]
@ -36,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetCommitId(commitId);
Assert.Equal(commitId, sut.Headers.CommitId());
Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid(culture));
Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid());
}
[Fact]
@ -47,7 +45,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetEventId(commitId);
Assert.Equal(commitId, sut.Headers.EventId());
Assert.Equal(commitId, sut.Headers["EventId"].ToGuid(culture));
Assert.Equal(commitId, sut.Headers["EventId"].ToGuid());
}
[Fact]
@ -58,7 +56,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetAggregateId(commitId);
Assert.Equal(commitId, sut.Headers.AggregateId());
Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid(culture));
Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid());
}
[Fact]
@ -80,7 +78,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetEventStreamNumber(eventStreamNumber);
Assert.Equal(eventStreamNumber, sut.Headers.EventStreamNumber());
Assert.Equal(eventStreamNumber, sut.Headers["EventStreamNumber"].ToInt64(culture));
Assert.Equal(eventStreamNumber, sut.Headers["EventStreamNumber"].ToInt64());
}
}
}

6
tests/Squidex.Infrastructure.Tests/EventSourcing/Grains/EventConsumerGrainTests.cs

@ -79,7 +79,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
A.CallTo(() => persistence.WriteSnapshotAsync(A<EventConsumerState>.Ignored))
.Invokes(new Action<EventConsumerState>(s => state = s));
A.CallTo(() => formatter.Parse(eventData, true)).Returns(envelope);
A.CallTo(() => formatter.Parse(eventData, true, null)).Returns(envelope);
sut = new MyEventConsumerGrain(
x => eventConsumer,
@ -192,7 +192,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
[Fact]
public async Task Should_ignore_old_events()
{
A.CallTo(() => formatter.Parse(eventData, true))
A.CallTo(() => formatter.Parse(eventData, true, null))
.Throws(new TypeNameNotFoundException());
var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData);
@ -326,7 +326,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{
var ex = new InvalidOperationException();
A.CallTo(() => formatter.Parse(eventData, true))
A.CallTo(() => formatter.Parse(eventData, true, null))
.Throws(ex);
var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData);

44
tests/Squidex.Infrastructure.Tests/PropertiesBagTests.cs

@ -1,12 +1,11 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// Copyright () Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Globalization;
using System.Linq;
using Microsoft.CSharp.RuntimeBinder;
using NodaTime;
@ -17,7 +16,6 @@ namespace Squidex.Infrastructure
{
public class PropertiesBagTests
{
private readonly CultureInfo c = CultureInfo.InvariantCulture;
private readonly PropertiesBag bag = new PropertiesBag();
private readonly dynamic dynamicBag;
@ -52,7 +50,7 @@ namespace Squidex.Infrastructure
Assert.Equal(kvp.Value.RawValue, bag[kvp.Key].RawValue);
}
Assert.Equal(bag["Key5"].ToGuid(c), output["Key5"].ToGuid(c));
Assert.Equal(bag["Key5"].ToGuid(), output["Key5"].ToGuid());
}
[Fact]
@ -84,7 +82,7 @@ namespace Squidex.Infrastructure
Assert.True(bag.Contains("NewKey"));
Assert.Equal(1, bag.Count);
Assert.Equal(123, bag["NewKey"].ToInt64(c));
Assert.Equal(123, bag["NewKey"].ToInt64());
Assert.False(bag.Contains("OldKey"));
}
@ -172,7 +170,7 @@ namespace Squidex.Infrastructure
{
bag.Set("Key", "abc");
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt64(CultureInfo.InvariantCulture));
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt64());
}
[Fact]
@ -212,7 +210,7 @@ namespace Squidex.Infrastructure
{
bag.Set("Key", long.MaxValue);
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt32(c));
Assert.Throws<InvalidCastException>(() => bag["Key"].ToInt32());
}
[Fact]
@ -236,7 +234,7 @@ namespace Squidex.Infrastructure
{
bag.Set("Key", double.MaxValue);
Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle(c));
Assert.Equal(float.PositiveInfinity, bag["Key"].ToSingle());
}
[Fact]
@ -340,7 +338,7 @@ namespace Squidex.Infrastructure
{
bag.Set("Key", SystemClock.Instance.GetCurrentInstant());
Assert.Throws<InvalidCastException>(() => bag["Key"].ToGuid(CultureInfo.InvariantCulture));
Assert.Throws<InvalidCastException>(() => bag["Key"].ToGuid());
}
private void AssertNumber()
@ -366,8 +364,8 @@ namespace Squidex.Infrastructure
private void AssertBoolean(bool expected)
{
Assert.Equal(expected, bag["Key"].ToBoolean(c));
Assert.Equal(expected, bag["Key"].ToNullableBoolean(c));
Assert.Equal(expected, bag["Key"].ToBoolean());
Assert.Equal(expected, bag["Key"].ToNullableBoolean());
Assert.Equal(expected, (bool)dynamicBag.Key);
Assert.Equal(expected, (bool?)dynamicBag.Key);
@ -375,8 +373,8 @@ namespace Squidex.Infrastructure
private void AssertInstant(Instant expected)
{
Assert.Equal(expected, bag["Key"].ToInstant(c));
Assert.Equal(expected, bag["Key"].ToNullableInstant(c).Value);
Assert.Equal(expected, bag["Key"].ToInstant());
Assert.Equal(expected, bag["Key"].ToNullableInstant().Value);
Assert.Equal(expected, (Instant)dynamicBag.Key);
Assert.Equal(expected, (Instant?)dynamicBag.Key);
@ -384,8 +382,8 @@ namespace Squidex.Infrastructure
private void AssertGuid(Guid expected)
{
Assert.Equal(expected, bag["Key"].ToGuid(c));
Assert.Equal(expected, bag["Key"].ToNullableGuid(c));
Assert.Equal(expected, bag["Key"].ToGuid());
Assert.Equal(expected, bag["Key"].ToNullableGuid());
Assert.Equal(expected, (Guid)dynamicBag.Key);
Assert.Equal(expected, (Guid?)dynamicBag.Key);
@ -393,8 +391,8 @@ namespace Squidex.Infrastructure
private void AssertDouble(double expected)
{
Assert.Equal(expected, bag["Key"].ToDouble(c));
Assert.Equal(expected, bag["Key"].ToNullableDouble(c));
Assert.Equal(expected, bag["Key"].ToDouble());
Assert.Equal(expected, bag["Key"].ToNullableDouble());
Assert.Equal(expected, (double)dynamicBag.Key);
Assert.Equal(expected, (double?)dynamicBag.Key);
@ -402,8 +400,8 @@ namespace Squidex.Infrastructure
private void AssertSingle(float expected)
{
Assert.Equal(expected, bag["Key"].ToSingle(c));
Assert.Equal(expected, bag["Key"].ToNullableSingle(c));
Assert.Equal(expected, bag["Key"].ToSingle());
Assert.Equal(expected, bag["Key"].ToNullableSingle());
Assert.Equal(expected, (float)dynamicBag.Key);
Assert.Equal(expected, (float?)dynamicBag.Key);
@ -411,8 +409,8 @@ namespace Squidex.Infrastructure
private void AssertInt32(long expected)
{
Assert.Equal(expected, bag["Key"].ToInt64(c));
Assert.Equal(expected, bag["Key"].ToNullableInt64(c));
Assert.Equal(expected, bag["Key"].ToInt64());
Assert.Equal(expected, bag["Key"].ToNullableInt64());
Assert.Equal(expected, (long)dynamicBag.Key);
Assert.Equal(expected, (long?)dynamicBag.Key);
@ -420,8 +418,8 @@ namespace Squidex.Infrastructure
private void AssertInt64(int expected)
{
Assert.Equal(expected, bag["Key"].ToInt64(c));
Assert.Equal(expected, bag["Key"].ToNullableInt64(c));
Assert.Equal(expected, bag["Key"].ToInt64());
Assert.Equal(expected, bag["Key"].ToNullableInt64());
Assert.Equal(expected, (int)dynamicBag.Key);
Assert.Equal(expected, (int?)dynamicBag.Key);

4
tests/Squidex.Infrastructure.Tests/States/PersistenceEventSourcingTests.cs

@ -61,7 +61,7 @@ namespace Squidex.Infrastructure.States
A.CallTo(() => eventStore.QueryAsync(key, 0))
.Returns(new List<StoredEvent> { storedEvent });
A.CallTo(() => eventDataFormatter.Parse(storedEvent.Data, true))
A.CallTo(() => eventDataFormatter.Parse(storedEvent.Data, true, null))
.Throws(new TypeNameNotFoundException());
var persistedEvents = new List<IEvent>();
@ -256,7 +256,7 @@ namespace Squidex.Infrastructure.States
eventsStored.Add(eventStored);
A.CallTo(() => eventDataFormatter.Parse(eventData, true))
A.CallTo(() => eventDataFormatter.Parse(eventData, true, null))
.Returns(new Envelope<IEvent>(@event));
i++;

8
tools/Migrate_01/Rebuilder.cs

@ -25,6 +25,7 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.States;
namespace Migrate_01
@ -33,18 +34,21 @@ namespace Migrate_01
{
private readonly FieldRegistry fieldRegistry;
private readonly ILocalCache localCache;
private readonly IJsonSerializer serializer;
private readonly IStore<Guid> store;
private readonly IEventStore eventStore;
public Rebuilder(
FieldRegistry fieldRegistry,
ILocalCache localCache,
IJsonSerializer serializer,
IStore<Guid> store,
IEventStore eventStore)
{
this.fieldRegistry = fieldRegistry;
this.eventStore = eventStore;
this.localCache = localCache;
this.serializer = serializer;
this.store = store;
}
@ -104,7 +108,9 @@ namespace Migrate_01
await eventStore.QueryAsync(async storedEvent =>
{
var id = Guid.Parse(storedEvent.Data.Metadata.Value<string>(CommonHeaders.AggregateId));
var headers = serializer.Deserialize<EnvelopeHeaders>(storedEvent.Data.Metadata);
var id = headers.AggregateId();
if (handledIds.Add(id))
{

Loading…
Cancel
Save