Browse Source

All tests fixed.

pull/335/head
Sebastian Stehle 8 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}"; 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; ruleJob.Content["objectID"] = contentId;
} }

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

@ -6,10 +6,10 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Collections.Generic;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; 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 url = $"{action.Url.ToString().TrimEnd('/')}/posts.json?api_key={action.ApiKey}&api_username={action.ApiUsername}";
var json = var json = new Dictionary<string, object>
new JObject( {
new JProperty("raw", Format(action.Text, @event)), ["raw"] = Format(action.Text, @event),
new JProperty("title", Format(action.Title, @event))); ["title"] = Format(action.Title, @event)
};
if (action.Topic.HasValue) if (action.Topic.HasValue)
{ {
json.Add(new JProperty("topic_id", action.Topic.Value)); json.Add("topic_id", action.Topic.Value);
} }
if (action.Category.HasValue) 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 var ruleJob = new DiscourseJob
{ {
RequestUrl = url, RequestUrl = url,
RequestBody = json.ToString() RequestBody = requestBody
}; };
var description = 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}"; ruleDescription = $"Upsert to index: {action.IndexName}";
ruleJob.Content = ToPayload(contentEvent); var json = ToJson(contentEvent);
ruleJob.Content = JObject.Parse(json);
ruleJob.Content["objectID"] = contentId; ruleJob.Content["objectID"] = contentId;
} }

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

@ -9,7 +9,6 @@ using System;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
@ -29,12 +28,10 @@ namespace Squidex.Extensions.Actions.Prerender
{ {
var url = Format(action.Url, @event); var url = Format(action.Url, @event);
var request = var request = new { prerenderToken = action.Token, url };
new JObject( var requestBody = ToJson(request);
new JProperty("prerenderToken", action.Token),
new JProperty("url", url));
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) 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.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents; using Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -33,14 +31,12 @@ namespace Squidex.Extensions.Actions.Slack
protected override (string Description, SlackJob Data) CreateJob(EnrichedEvent @event, SlackAction action) protected override (string Description, SlackJob Data) CreateJob(EnrichedEvent @event, SlackAction action)
{ {
var body = var body = new { text = Format(action.Text, @event) };
new JObject(
new JProperty("text", Format(action.Text, @event)));
var ruleJob = new SlackJob var ruleJob = new SlackJob
{ {
RequestUrl = action.WebhookUrl.ToString(), RequestUrl = action.WebhookUrl.ToString(),
RequestBody = body.ToString(Formatting.Indented) RequestBody = ToJson(body)
}; };
return (Description, ruleJob); return (Description, ruleJob);

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

@ -6,6 +6,7 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Runtime.Serialization;
using NodaTime; using NodaTime;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Shared.Users; using Squidex.Shared.Users;
@ -24,8 +25,10 @@ namespace Squidex.Domain.Apps.Core.HandleRules.EnrichedEvents
public long Version { get; set; } public long Version { get; set; }
[IgnoreDataMember]
public abstract Guid AggregateId { get; } public abstract Guid AggregateId { get; }
[IgnoreDataMember]
public IUser User { get; set; } 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; this.formatter = formatter;
} }
protected virtual string ToPayloadJson<T>(T @event) protected virtual string ToJson<T>(T @event)
{ {
return formatter.ToPayload(@event); return formatter.ToPayload(@event);
} }

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

@ -16,7 +16,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Jint" Version="2.11.58" /> <PackageReference Include="Jint" Version="2.11.58" />
<PackageReference Include="Microsoft.OData.Core" Version="7.5.1" /> <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="NJsonSchema" Version="9.12.2" />
<PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" /> <PackageReference Include="RefactoringEssentials" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2" 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.ConvertContent;
using Squidex.Domain.Apps.Core.ExtractReferenceIds; using Squidex.Domain.Apps.Core.ExtractReferenceIds;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
@ -22,24 +23,24 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
return data.GetReferencedIds(schema).ToList(); 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, return result.ConvertId2Name(schema,
FieldConverters.ForValues( FieldConverters.ForValues(
ValueConverters.DecodeJson(), ValueConverters.DecodeJson(serializer),
ValueReferencesConverter.CleanReferences(deletedIds)), ValueReferencesConverter.CleanReferences(deletedIds)),
FieldConverters.ForNestedId2Name( FieldConverters.ForNestedId2Name(
ValueConverters.DecodeJson(), ValueConverters.DecodeJson(serializer),
ValueReferencesConverter.CleanReferences(deletedIds))); 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, return result.ConvertName2Id(schema,
FieldConverters.ForValues( FieldConverters.ForValues(
ValueConverters.EncodeJson()), ValueConverters.EncodeJson(serializer)),
FieldConverters.ForNestedName2Id( 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.MongoDb.Contents.Visitors;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
@ -26,10 +27,14 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
private readonly string collectionName; private readonly string collectionName;
public MongoContentCollection(IMongoDatabase database, string collectionName) protected IJsonSerializer Serializer { get; }
public MongoContentCollection(IMongoDatabase database, IJsonSerializer serializer, string collectionName)
: base(database) : base(database)
{ {
this.collectionName = collectionName; this.collectionName = collectionName;
Serializer = serializer;
} }
protected override async Task SetupCollectionAsync(IMongoCollection<MongoContentEntity> collection, CancellationToken ct = default(CancellationToken)) 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) foreach (var entity in contentItems.Result)
{ {
entity.ParseData(schema.SchemaDef); entity.ParseData(schema.SchemaDef, Serializer);
} }
return ResultList.Create<IContentEntity>(contentCount.Result, contentItems.Result); 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) foreach (var entity in contentItems.Result)
{ {
entity.ParseData(schema.SchemaDef); entity.ParseData(schema.SchemaDef, Serializer);
} }
return ResultList.Create<IContentEntity>(contentCount.Result, contentItems.Result); 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.Contents.State;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
@ -26,8 +27,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
internal sealed class MongoContentDraftCollection : MongoContentCollection internal sealed class MongoContentDraftCollection : MongoContentCollection
{ {
public MongoContentDraftCollection(IMongoDatabase database) public MongoContentDraftCollection(IMongoDatabase database, IJsonSerializer serializer)
: base(database, "State_Content_Draft") : 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) await Collection.Find(x => x.IndexedSchemaId == schema.Id && x.Id == id && x.IsDeleted != true).Not(x => x.DataText)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef); contentEntity?.ParseData(schema.SchemaDef, Serializer);
return contentEntity; return contentEntity;
} }
@ -103,7 +104,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
var schema = await getSchema(contentEntity.IndexedAppId, contentEntity.IndexedSchemaId); 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); 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.Core.Schemas;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
@ -124,13 +125,13 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
get { return dataDraft; } 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) 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.Apps;
using Squidex.Domain.Apps.Entities.Contents; using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
namespace Squidex.Domain.Apps.Entities.MongoDb.Contents namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
internal sealed class MongoContentPublishedCollection : MongoContentCollection internal sealed class MongoContentPublishedCollection : MongoContentCollection
{ {
public MongoContentPublishedCollection(IMongoDatabase database) public MongoContentPublishedCollection(IMongoDatabase database, IJsonSerializer serializer)
: base(database, "State_Content_Published") : 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) await Collection.Find(x => x.IndexedSchemaId == schema.Id && x.Id == id).Not(x => x.DataText)
.FirstOrDefaultAsync(); .FirstOrDefaultAsync();
contentEntity?.ParseData(schema.SchemaDef); contentEntity?.ParseData(schema.SchemaDef, Serializer);
return contentEntity; 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.Contents.Repositories;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Queries; using Squidex.Infrastructure.Queries;
@ -26,17 +27,21 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
{ {
private readonly IMongoDatabase database; private readonly IMongoDatabase database;
private readonly IAppProvider appProvider; private readonly IAppProvider appProvider;
private readonly IJsonSerializer serializer;
private readonly MongoContentDraftCollection contentsDraft; private readonly MongoContentDraftCollection contentsDraft;
private readonly MongoContentPublishedCollection contentsPublished; 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(appProvider, nameof(appProvider));
Guard.NotNull(serializer, nameof(serializer));
this.appProvider = appProvider; this.appProvider = appProvider;
contentsDraft = new MongoContentDraftCollection(database); this.serializer = serializer;
contentsPublished = new MongoContentPublishedCollection(database);
contentsDraft = new MongoContentDraftCollection(database, serializer);
contentsPublished = new MongoContentPublishedCollection(database, serializer);
this.database = database; 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 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; var idDraftData = idData;
if (!ReferenceEquals(value.Data, value.DataDraft)) 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 var content = SimpleMapper.Map(value, new MongoContentEntity

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

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

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

@ -8,7 +8,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Orleans; using Orleans;
using Squidex.Domain.Apps.Entities.Apps.Indexes; using Squidex.Domain.Apps.Entities.Apps.Indexes;
using Squidex.Domain.Apps.Entities.Backup; using Squidex.Domain.Apps.Entities.Backup;
@ -16,6 +15,8 @@ using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Orleans;
using Squidex.Shared.Users; using Squidex.Shared.Users;
@ -27,6 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
private const string SettingsFile = "Settings.json"; private const string SettingsFile = "Settings.json";
private readonly IGrainFactory grainFactory; private readonly IGrainFactory grainFactory;
private readonly IUserResolver userResolver; private readonly IUserResolver userResolver;
private readonly IJsonSerializer serializer;
private readonly IAppsByNameIndex appsByNameIndex; private readonly IAppsByNameIndex appsByNameIndex;
private readonly HashSet<string> contributors = new HashSet<string>(); private readonly HashSet<string> contributors = new HashSet<string>();
private Dictionary<string, string> usersWithEmail = new Dictionary<string, 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 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(grainFactory, nameof(grainFactory));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(userResolver, nameof(userResolver)); Guard.NotNull(userResolver, nameof(userResolver));
this.grainFactory = grainFactory; this.grainFactory = grainFactory;
this.serializer = serializer;
this.userResolver = userResolver; this.userResolver = userResolver;
appsByNameIndex = grainFactory.GetGrain<IAppsByNameIndex>(SingleGrain.Id); appsByNameIndex = grainFactory.GetGrain<IAppsByNameIndex>(SingleGrain.Id);
@ -162,14 +165,14 @@ namespace Squidex.Domain.Apps.Entities.Apps
private async Task ReadUsersAsync(BackupReader reader) 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) private async Task WriteUsersAsync(BackupWriter writer)
{ {
var json = JObject.FromObject(usersWithEmail); var json = usersWithEmail;
await writer.WriteJsonAsync(UsersFile, json); await writer.WriteJsonAsync(UsersFile, json);
} }
@ -183,9 +186,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
private async Task ReadSettingsAsync(BackupReader reader, Guid appId) 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) 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 System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Orleans; using Orleans;
using Squidex.Infrastructure.Json.Objects;
using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Orleans;
namespace Squidex.Domain.Apps.Entities.Apps namespace Squidex.Domain.Apps.Entities.Apps
{ {
public interface IAppUISettingsGrain : IGrainWithGuidKey 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); 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. // 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.Core.Apps;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Apps;
@ -19,28 +19,28 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
[CollectionName("Apps")] [CollectionName("Apps")]
public class AppState : DomainObjectState<AppState>, IAppEntity public class AppState : DomainObjectState<AppState>, IAppEntity
{ {
[JsonProperty] [DataMember]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty] [DataMember]
public Roles Roles { get; set; } = Roles.Empty; public Roles Roles { get; set; } = Roles.Empty;
[JsonProperty] [DataMember]
public AppPlan Plan { get; set; } public AppPlan Plan { get; set; }
[JsonProperty] [DataMember]
public AppClients Clients { get; set; } = AppClients.Empty; public AppClients Clients { get; set; } = AppClients.Empty;
[JsonProperty] [DataMember]
public AppPatterns Patterns { get; set; } = AppPatterns.Empty; public AppPatterns Patterns { get; set; } = AppPatterns.Empty;
[JsonProperty] [DataMember]
public AppContributors Contributors { get; set; } = AppContributors.Empty; public AppContributors Contributors { get; set; } = AppContributors.Empty;
[JsonProperty] [DataMember]
public LanguagesConfig LanguagesConfig { get; set; } = LanguagesConfig.English; public LanguagesConfig LanguagesConfig { get; set; } = LanguagesConfig.English;
[JsonProperty] [DataMember]
public bool IsArchived { get; set; } public bool IsArchived { get; set; }
protected void On(AppCreated @event) protected void On(AppCreated @event)

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

@ -8,7 +8,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Tags; using Squidex.Domain.Apps.Core.Tags;
using Squidex.Domain.Apps.Entities.Assets.Repositories; using Squidex.Domain.Apps.Entities.Assets.Repositories;
using Squidex.Domain.Apps.Entities.Assets.State; 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) 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) private async Task BackupTagsAsync(Guid appId, BackupWriter writer)
{ {
var tags = await tagService.GetExportableTagsAsync(appId, TagGroups.Assets); 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) 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;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json; using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Core.ValidateContent;
using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Assets;
@ -20,37 +20,37 @@ namespace Squidex.Domain.Apps.Entities.Assets.State
{ {
public class AssetState : DomainObjectState<AssetState>, IAssetEntity public class AssetState : DomainObjectState<AssetState>, IAssetEntity
{ {
[JsonProperty] [DataMember]
public NamedId<Guid> AppId { get; set; } public NamedId<Guid> AppId { get; set; }
[JsonProperty] [DataMember]
public string FileName { get; set; } public string FileName { get; set; }
[JsonProperty] [DataMember]
public string MimeType { get; set; } public string MimeType { get; set; }
[JsonProperty] [DataMember]
public long FileVersion { get; set; } public long FileVersion { get; set; }
[JsonProperty] [DataMember]
public long FileSize { get; set; } public long FileSize { get; set; }
[JsonProperty] [DataMember]
public long TotalSize { get; set; } public long TotalSize { get; set; }
[JsonProperty] [DataMember]
public bool IsImage { get; set; } public bool IsImage { get; set; }
[JsonProperty] [DataMember]
public int? PixelWidth { get; set; } public int? PixelWidth { get; set; }
[JsonProperty] [DataMember]
public int? PixelHeight { get; set; } public int? PixelHeight { get; set; }
[JsonProperty] [DataMember]
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
[JsonProperty] [DataMember]
public HashSet<string> Tags { get; set; } public HashSet<string> Tags { get; set; }
Guid IAssetInfo.AssetId 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;
using Squidex.Infrastructure.Assets; using Squidex.Infrastructure.Assets;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
@ -34,6 +35,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IBackupArchiveLocation backupArchiveLocation; private readonly IBackupArchiveLocation backupArchiveLocation;
private readonly IClock clock; private readonly IClock clock;
private readonly IEnumerable<BackupHandler> handlers; private readonly IEnumerable<BackupHandler> handlers;
private readonly IJsonSerializer serializer;
private readonly IEventDataFormatter eventDataFormatter; private readonly IEventDataFormatter eventDataFormatter;
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
private readonly ISemanticLog log; private readonly ISemanticLog log;
@ -51,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
IEnumerable<BackupHandler> handlers, IEnumerable<BackupHandler> handlers,
IJsonSerializer serializer,
ISemanticLog log, ISemanticLog log,
IStore<Guid> store) IStore<Guid> store)
{ {
@ -60,6 +63,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Guard.NotNull(eventStore, nameof(eventStore)); Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter)); Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(handlers, nameof(handlers)); Guard.NotNull(handlers, nameof(handlers));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(store, nameof(store)); Guard.NotNull(store, nameof(store));
Guard.NotNull(log, nameof(log)); Guard.NotNull(log, nameof(log));
@ -69,6 +73,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.eventStore = eventStore; this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter; this.eventDataFormatter = eventDataFormatter;
this.handlers = handlers; this.handlers = handlers;
this.serializer = serializer;
this.store = store; this.store = store;
this.log = log; this.log = log;
} }
@ -139,7 +144,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
{ {
using (var stream = await backupArchiveLocation.OpenStreamAsync(job.Id)) 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 => 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;
using System.IO.Compression; using System.IO.Compression;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Entities.Backup.Helpers; using Squidex.Domain.Apps.Entities.Backup.Helpers;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
namespace Squidex.Domain.Apps.Entities.Backup namespace Squidex.Domain.Apps.Entities.Backup
{ {
public sealed class BackupReader : DisposableObjectBase public sealed class BackupReader : DisposableObjectBase
{ {
private static readonly JsonSerializer Serializer = new JsonSerializer();
private readonly GuidMapper guidMapper = new GuidMapper(); private readonly GuidMapper guidMapper = new GuidMapper();
private readonly ZipArchive archive; private readonly ZipArchive archive;
private readonly IJsonSerializer serializer;
private int readEvents; private int readEvents;
private int readAttachments; private int readAttachments;
@ -36,8 +35,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
get { return readAttachments; } 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); archive = new ZipArchive(stream, ZipArchiveMode.Read, false);
} }
@ -54,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
return guidMapper.OldGuid(newId); return guidMapper.OldGuid(newId);
} }
public async Task<JToken> ReadJsonAttachmentAsync(string name) public Task<T> ReadJsonAttachmentAsync<T>(string name)
{ {
Guard.NotNullOrEmpty(name, nameof(name)); Guard.NotNullOrEmpty(name, nameof(name));
@ -65,24 +68,16 @@ namespace Squidex.Domain.Apps.Entities.Backup
throw new FileNotFoundException("Cannot find attachment.", name); throw new FileNotFoundException("Cannot find attachment.", name);
} }
JToken result; T result;
using (var stream = attachmentEntry.Open()) using (var stream = attachmentEntry.Open())
{ {
using (var textReader = new StreamReader(stream)) result = serializer.Deserialize<T>(stream, null, guidMapper.NewGuidOrValue);
{
using (var jsonReader = new JsonTextReader(textReader))
{
result = await JToken.ReadFromAsync(jsonReader);
guidMapper.NewGuids(result);
}
}
} }
readAttachments++; readAttachments++;
return result; return Task.FromResult(result);
} }
public async Task ReadBlobAsync(string name, Func<Stream, Task> handler) public async Task ReadBlobAsync(string name, Func<Stream, Task> handler)
@ -105,9 +100,10 @@ namespace Squidex.Domain.Apps.Entities.Backup
readAttachments++; 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(handler, nameof(handler));
Guard.NotNull(formatter, nameof(formatter));
Guard.NotNull(streamNameResolver, nameof(streamNameResolver)); Guard.NotNull(streamNameResolver, nameof(streamNameResolver));
while (true) while (true)
@ -121,25 +117,12 @@ namespace Squidex.Domain.Apps.Entities.Backup
using (var stream = eventEntry.Open()) using (var stream = eventEntry.Open())
{ {
using (var textReader = new StreamReader(stream)) var storedEvent = serializer.Deserialize<StoredEvent>(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);
storedEvent = new StoredEvent(streamName, var eventStream = streamNameResolver.WithNewId(storedEvent.StreamName, guidMapper.NewGuidOrNull);
storedEvent.EventPosition, var eventEnvelope = formatter.Parse(storedEvent.Data, true, guidMapper.NewGuidOrValue);
storedEvent.EventStreamNumber,
storedEvent.Data);
await handler(storedEvent); await handler((eventStream, eventEnvelope));
}
}
} }
readEvents++; readEvents++;

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

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

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

@ -7,112 +7,39 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using Squidex.Infrastructure; using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Backup namespace Squidex.Domain.Apps.Entities.Backup
{ {
public sealed class GuidMapper internal sealed class GuidMapper
{ {
private static readonly int GuidLength = Guid.Empty.ToString().Length; 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> oldToNewGuid = new Dictionary<Guid, Guid>();
private readonly Dictionary<Guid, Guid> newToOldGuid = 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) public Guid OldGuid(Guid newGuid)
{ {
return newToOldGuid.GetOrDefault(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; return null;
} }
public JToken NewGuids(JToken jToken) public string NewGuidOrValue(string value)
{
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)
{ {
foreach (var jProperty in jObject.Properties()) if (TryGenerateNewGuidString(value, out var result) || TryGenerateNewNamedId(value, out result))
{ {
var newValue = NewGuidsCore(jProperty.Value); return result;
if (!ReferenceEquals(newValue, jProperty.Value))
{
jProperty.Value = newValue;
}
if (TryConvertString(jProperty.Name, out var newKey))
{
mappings.Add((jObject, newKey, jProperty.Name));
}
} }
}
private bool TryConvertString(string value, out string result) return value;
{
return TryGenerateNewGuidString(value, out result) || TryGenerateNewNamedId(value, out result);
} }
private bool TryGenerateNewGuidString(string value, out string result) 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.IO;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure.Json;
namespace Squidex.Domain.Apps.Entities.Backup.Helpers 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; Stream stream = null;
@ -47,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Backup.Helpers
{ {
stream = await backupArchiveLocation.OpenStreamAsync(id); stream = await backupArchiveLocation.OpenStreamAsync(id);
return new BackupReader(stream); return new BackupReader(serializer, stream);
} }
catch (IOException) 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;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
@ -31,6 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
private readonly IClock clock; private readonly IClock clock;
private readonly ICommandBus commandBus; private readonly ICommandBus commandBus;
private readonly IEnumerable<BackupHandler> handlers; private readonly IEnumerable<BackupHandler> handlers;
private readonly IJsonSerializer serializer;
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter; private readonly IEventDataFormatter eventDataFormatter;
private readonly ISemanticLog log; private readonly ISemanticLog log;
@ -51,6 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
IEventStore eventStore, IEventStore eventStore,
IEventDataFormatter eventDataFormatter, IEventDataFormatter eventDataFormatter,
IEnumerable<BackupHandler> handlers, IEnumerable<BackupHandler> handlers,
IJsonSerializer serializer,
ISemanticLog log, ISemanticLog log,
IStreamNameResolver streamNameResolver, IStreamNameResolver streamNameResolver,
IStore<string> store) IStore<string> store)
@ -61,6 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
Guard.NotNull(eventStore, nameof(eventStore)); Guard.NotNull(eventStore, nameof(eventStore));
Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter)); Guard.NotNull(eventDataFormatter, nameof(eventDataFormatter));
Guard.NotNull(handlers, nameof(handlers)); Guard.NotNull(handlers, nameof(handlers));
Guard.NotNull(serializer, nameof(serializer));
Guard.NotNull(store, nameof(store)); Guard.NotNull(store, nameof(store));
Guard.NotNull(streamNameResolver, nameof(streamNameResolver)); Guard.NotNull(streamNameResolver, nameof(streamNameResolver));
Guard.NotNull(log, nameof(log)); Guard.NotNull(log, nameof(log));
@ -71,6 +75,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
this.eventStore = eventStore; this.eventStore = eventStore;
this.eventDataFormatter = eventDataFormatter; this.eventDataFormatter = eventDataFormatter;
this.handlers = handlers; this.handlers = handlers;
this.serializer = serializer;
this.store = store; this.store = store;
this.streamNameResolver = streamNameResolver; this.streamNameResolver = streamNameResolver;
this.log = log; this.log = log;
@ -161,7 +166,7 @@ namespace Squidex.Domain.Apps.Entities.Backup
await DownloadAsync(); await DownloadAsync();
} }
using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id)) using (var reader = await backupArchiveLocation.OpenArchiveAsync(CurrentJob.Id, serializer))
{ {
using (Profiler.Trace("ReadEvents")) using (Profiler.Trace("ReadEvents"))
{ {
@ -273,17 +278,15 @@ namespace Squidex.Domain.Apps.Entities.Backup
private async Task ReadEventsAsync(BackupReader reader) 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.Stream, storedEvent.Event);
await HandleEventAsync(reader, storedEvent, @event);
}); });
Log("Reading events completed."); 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) 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 eventData = eventDataFormatter.ToEventData(@event, @event.Headers.CommitId());
var eventCommit = new List<EventData> { eventData }; 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); 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 System.Collections.Generic;
using Newtonsoft.Json; using System.Runtime.Serialization;
namespace Squidex.Domain.Apps.Entities.Backup.State namespace Squidex.Domain.Apps.Entities.Backup.State
{ {
public sealed class BackupState public sealed class BackupState
{ {
[JsonProperty] [DataMember]
public List<BackupStateJob> Jobs { get; } = new List<BackupStateJob>(); 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 System;
using Newtonsoft.Json; using System.Runtime.Serialization;
using NodaTime; using NodaTime;
namespace Squidex.Domain.Apps.Entities.Backup.State namespace Squidex.Domain.Apps.Entities.Backup.State
{ {
public sealed class BackupStateJob : IBackupJob public sealed class BackupStateJob : IBackupJob
{ {
[JsonProperty] [DataMember]
public Guid Id { get; set; } public Guid Id { get; set; }
[JsonProperty] [DataMember]
public Instant Started { get; set; } public Instant Started { get; set; }
[JsonProperty] [DataMember]
public Instant? Stopped { get; set; } public Instant? Stopped { get; set; }
[JsonProperty] [DataMember]
public int HandledEvents { get; set; } public int HandledEvents { get; set; }
[JsonProperty] [DataMember]
public int HandledAssets { get; set; } public int HandledAssets { get; set; }
[JsonProperty] [DataMember]
public JobStatus Status { get; set; } 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Newtonsoft.Json; using System.Runtime.Serialization;
namespace Squidex.Domain.Apps.Entities.Backup.State namespace Squidex.Domain.Apps.Entities.Backup.State
{ {
public class RestoreState public class RestoreState
{ {
[JsonProperty] [DataMember]
public RestoreStateJob Job { get; set; } public RestoreStateJob Job { get; set; }
} }
} }

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

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

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

@ -8,8 +8,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Entities.Assets; using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
public sealed class GraphQLExecutionContext : QueryExecutionContext public sealed class GraphQLExecutionContext : QueryExecutionContext
@ -25,29 +26,29 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
UrlGenerator = urlGenerator; UrlGenerator = urlGenerator;
} }
public Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(JToken value) public Task<IReadOnlyList<IAssetEntity>> GetReferencedAssetsAsync(IJsonValue value)
{ {
var ids = ParseIds(value); var ids = ParseIds(value);
return GetReferencedAssetsAsync(ids); 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); var ids = ParseIds(value);
return GetReferencedContentsAsync(schemaId, ids); return GetReferencedContentsAsync(schemaId, ids);
} }
private static ICollection<Guid> ParseIds(JToken value) private static ICollection<Guid> ParseIds(IJsonValue value)
{ {
try try
{ {
var result = new List<Guid>(); 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())); 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 Guid = new GuidGraphType2();
public static readonly IGraphType Date = new DateTimeGraphType(); public static readonly IGraphType Date = new InstantGraphType();
public static readonly IGraphType Json = new JsonGraphType(); 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", Name = "created",
ResolvedType = AllTypes.NonNullDate, 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." 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", Name = "lastModified",
ResolvedType = AllTypes.NonNullDate, 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." 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 System.Linq;
using GraphQL.Resolvers; using GraphQL.Resolvers;
using GraphQL.Types; using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types 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."; 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); 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", Name = "created",
ResolvedType = AllTypes.NonNullDate, 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." 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", Name = "lastModified",
ResolvedType = AllTypes.NonNullDate, 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." 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 System.Linq;
using GraphQL.Resolvers; using GraphQL.Resolvers;
using GraphQL.Types; using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types 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) 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 => 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); return fieldInfo.Resolver(value, c);
} }

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

@ -7,13 +7,13 @@
using System; using System;
using GraphQL.Types; using GraphQL.Types;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types 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)> 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.Language.AST;
using GraphQL.Types; using GraphQL.Types;
using Newtonsoft.Json.Linq; using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils 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) 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) 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 GraphQL.Language.AST;
using Newtonsoft.Json.Linq; using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Domain.Apps.Entities.Contents.GraphQL.Types.Utils 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; Value = value;
} }
protected override bool Equals(ValueNode<JObject> node) protected override bool Equals(ValueNode<IJsonValue> node)
{ {
return false; return false;
} }

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

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

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

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

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

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

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

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

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Globalization;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events namespace Squidex.Domain.Apps.Events
@ -15,7 +14,7 @@ namespace Squidex.Domain.Apps.Events
{ {
public static Guid AppId(this EnvelopeHeaders headers) 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 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.Collections.Concurrent;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
using StackExchange.Redis; using StackExchange.Redis;
@ -19,11 +20,13 @@ namespace Squidex.Infrastructure
{ {
private readonly ConcurrentDictionary<string, object> subscriptions = new ConcurrentDictionary<string, object>(); private readonly ConcurrentDictionary<string, object> subscriptions = new ConcurrentDictionary<string, object>();
private readonly Lazy<IConnectionMultiplexer> redisClient; private readonly Lazy<IConnectionMultiplexer> redisClient;
private readonly IJsonSerializer serializer;
private readonly Lazy<ISubscriber> redisSubscriber; private readonly Lazy<ISubscriber> redisSubscriber;
private readonly ISemanticLog log; 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(redis, nameof(redis));
Guard.NotNull(log, nameof(log)); Guard.NotNull(log, nameof(log));
@ -31,6 +34,8 @@ namespace Squidex.Infrastructure
redisClient = redis; redisClient = redis;
redisSubscriber = new Lazy<ISubscriber>(() => redis.Value.GetSubscriber()); redisSubscriber = new Lazy<ISubscriber>(() => redis.Value.GetSubscriber());
this.serializer = serializer;
} }
public Task InitializeAsync(CancellationToken ct = default(CancellationToken)) public Task InitializeAsync(CancellationToken ct = default(CancellationToken))
@ -61,7 +66,7 @@ namespace Squidex.Infrastructure
{ {
var typeName = typeof(T).FullName; 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;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using Newtonsoft.Json; using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Log;
using StackExchange.Redis; using StackExchange.Redis;
@ -20,6 +20,7 @@ namespace Squidex.Infrastructure
private readonly Guid selfId = Guid.NewGuid(); private readonly Guid selfId = Guid.NewGuid();
private readonly Subject<T> subject = new Subject<T>(); private readonly Subject<T> subject = new Subject<T>();
private readonly ISubscriber subscriber; private readonly ISubscriber subscriber;
private readonly IJsonSerializer serializer;
private readonly ISemanticLog log; private readonly ISemanticLog log;
private readonly string channelName; private readonly string channelName;
@ -30,10 +31,11 @@ namespace Squidex.Infrastructure
public Guid Sender; 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.log = log;
this.serializer = serializer;
this.subscriber = subscriber; this.subscriber = subscriber;
this.subscriber.Subscribe(channelName, (channel, value) => HandleMessage(value)); this.subscriber.Subscribe(channelName, (channel, value) => HandleMessage(value));
@ -46,7 +48,7 @@ namespace Squidex.Infrastructure
{ {
var senderId = notifySelf ? Guid.Empty : selfId; 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); subscriber.Publish(channelName, envelope);
} }
@ -68,7 +70,7 @@ namespace Squidex.Infrastructure
return; return;
} }
var envelope = JsonConvert.DeserializeObject<Envelope>(value); var envelope = serializer.Deserialize<Envelope>(value);
if (envelope.Sender != selfId) if (envelope.Sender != selfId)
{ {

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

@ -25,12 +25,12 @@ namespace Squidex.Infrastructure.EventSourcing
this.serializer = serializer; 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 eventType = typeNameRegistry.GetType(eventData.Type);
var eventHeaders = serializer.Deserialize<EnvelopeHeaders>(eventData.Metadata); var eventHeaders = serializer.Deserialize<EnvelopeHeaders>(eventData.Metadata, null, stringConverter);
var eventContent = serializer.Deserialize<IEvent>(eventData.Payload, eventType); var eventContent = serializer.Deserialize<IEvent>(eventData.Payload, eventType, stringConverter);
if (migrate && eventContent is IMigratedEvent migratedEvent) if (migrate && eventContent is IMigratedEvent migratedEvent)
{ {

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Globalization;
using NodaTime; using NodaTime;
namespace Squidex.Infrastructure.EventSourcing namespace Squidex.Infrastructure.EventSourcing
@ -27,7 +26,7 @@ namespace Squidex.Infrastructure.EventSourcing
public static long EventStreamNumber(this EnvelopeHeaders headers) 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 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) 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 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) 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 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) 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 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) 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 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 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); 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 public interface IJsonSerializer
{ {
string Serialize<T>(T value); string Serialize<T>(T value, bool intented = false);
void Serialize<T>(T value, Stream stream); 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 Newtonsoft.Json;
using Squidex.Infrastructure.Json.Objects; using Squidex.Infrastructure.Json.Objects;
#pragma warning disable RECS0018 // Comparison of floating point numbers with equality operator
namespace Squidex.Infrastructure.Json.Newtonsoft namespace Squidex.Infrastructure.Json.Newtonsoft
{ {
public sealed class JsonValueConverter : JsonConverter 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) public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{ {
return ReadJson(reader); return ReadJson(reader);
@ -125,36 +122,46 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
writer.WriteValue(s.Value); writer.WriteValue(s.Value);
break; break;
case JsonScalar<double> s: case JsonScalar<double> s:
writer.WriteValue(s.Value);
break; if (s.Value % 1 == 0)
case JsonArray array: {
writer.WriteValue((long)s.Value);
}
else
{ {
writer.WriteStartArray(); writer.WriteValue(s.Value);
}
foreach (var item in array) break;
{ case JsonArray array:
WriteJson(writer, item); writer.WriteStartArray();
}
writer.WriteEndArray(); foreach (var item in array)
break; {
WriteJson(writer, item);
} }
case JsonObject obj: writer.WriteEndArray();
{ break;
writer.WriteStartObject();
foreach (var kvp in obj) case JsonObject obj:
{ writer.WriteStartObject();
writer.WritePropertyName(kvp.Key);
WriteJson(writer, kvp.Value); foreach (var kvp in obj)
} {
writer.WritePropertyName(kvp.Key);
writer.WriteEndObject(); WriteJson(writer, kvp.Value);
break;
} }
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 JsonSerializerSettings settings;
private readonly JsonSerializer serializer; 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) public NewtonsoftJsonSerializer(JsonSerializerSettings settings)
{ {
Guard.NotNull(settings, nameof(settings)); Guard.NotNull(settings, nameof(settings));
@ -25,36 +51,50 @@ namespace Squidex.Infrastructure.Json.Newtonsoft
serializer = JsonSerializer.Create(settings); 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 JsonConvert.SerializeObject(value, intented ? Formatting.Indented : Formatting.None, settings);
return (T)JsonConvert.DeserializeObject(value, actualType, 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); 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 else
{ {

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

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

2
src/Squidex.Infrastructure/Language.cs

@ -18,7 +18,7 @@ namespace Squidex.Infrastructure
private static Language AddLanguage(string iso2Code, string englishName) 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) public static Language GetLanguage(string iso2Code)

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

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

92
src/Squidex.Infrastructure/PropertyValue.cs

@ -18,24 +18,24 @@ namespace Squidex.Infrastructure
{ {
private readonly object rawValue; private readonly object rawValue;
private static readonly Dictionary<Type, Func<PropertyValue, CultureInfo, object>> Parsers = private static readonly Dictionary<Type, Func<PropertyValue, object>> Parsers =
new Dictionary<Type, Func<PropertyValue, CultureInfo, object>> new Dictionary<Type, Func<PropertyValue, object>>
{ {
{ typeof(string), (p, c) => p.ToString() }, { typeof(string), p => p.ToString() },
{ typeof(bool), (p, c) => p.ToBoolean(c) }, { typeof(bool), p => p.ToBoolean() },
{ typeof(bool?), (p, c) => p.ToNullableBoolean(c) }, { typeof(bool?), p => p.ToNullableBoolean() },
{ typeof(float), (p, c) => p.ToSingle(c) }, { typeof(float), p => p.ToSingle() },
{ typeof(float?), (p, c) => p.ToNullableSingle(c) }, { typeof(float?), p => p.ToNullableSingle() },
{ typeof(double), (p, c) => p.ToDouble(c) }, { typeof(double), p => p.ToDouble() },
{ typeof(double?), (p, c) => p.ToNullableDouble(c) }, { typeof(double?), p => p.ToNullableDouble() },
{ typeof(int), (p, c) => p.ToInt32(c) }, { typeof(int), p => p.ToInt32() },
{ typeof(int?), (p, c) => p.ToNullableInt32(c) }, { typeof(int?), p => p.ToNullableInt32() },
{ typeof(long), (p, c) => p.ToInt64(c) }, { typeof(long), p => p.ToInt64() },
{ typeof(long?), (p, c) => p.ToNullableInt64(c) }, { typeof(long?), p => p.ToNullableInt64() },
{ typeof(Instant), (p, c) => p.ToInstant(c) }, { typeof(Instant), p => p.ToInstant() },
{ typeof(Instant?), (p, c) => p.ToNullableInstant(c) }, { typeof(Instant?), p => p.ToNullableInstant() },
{ typeof(Guid), (p, c) => p.ToGuid(c) }, { typeof(Guid), p => p.ToGuid() },
{ typeof(Guid?), (p, c) => p.ToNullableGuid(c) } { typeof(Guid?), p => p.ToNullableGuid() }
}; };
public object RawValue public object RawValue
@ -62,7 +62,7 @@ namespace Squidex.Infrastructure
return false; return false;
} }
result = parser(this, CultureInfo.InvariantCulture); result = parser(this);
return true; return true;
} }
@ -72,74 +72,74 @@ namespace Squidex.Infrastructure
return rawValue?.ToString(); 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 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) 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 => 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 => 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>() var settings = new SwaggerSettings<SwaggerGeneratorSettings>()
.AddAssetODataParams() .AddAssetODataParams()

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

@ -8,7 +8,6 @@
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services; using Squidex.Domain.Apps.Entities.Apps.Services;
using Squidex.Infrastructure.Commands; 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.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Entities.Apps; using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.Services; 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 System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;

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

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

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
namespace Squidex.Areas.Api.Controllers.Apps.Models 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. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Newtonsoft.Json.Linq; using Squidex.Infrastructure.Json.Objects;
namespace Squidex.Areas.Api.Controllers.UI.Models namespace Squidex.Areas.Api.Controllers.UI.Models
{ {
@ -14,6 +14,6 @@ namespace Squidex.Areas.Api.Controllers.UI.Models
/// <summary> /// <summary>
/// The value for the setting. /// The value for the setting.
/// </summary> /// </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(); var result = await grainFactory.GetGrain<IAppUISettingsGrain>(AppId).GetAsync();
result.Value["mapType"] = uiOptions.Map?.Type ?? "OSM"; result.Value.Add("mapType", uiOptions.Map?.Type ?? "OSM");
result.Value["mapKey"] = uiOptions.Map?.GoogleMaps?.Key; result.Value.Add("mapKey", uiOptions.Map?.GoogleMaps?.Key);
result.Value["supportTwitterAction"] = twitterOptions.IsConfigured();
result.Value.Add("supportTwitterAction", twitterOptions.IsConfigured());
return Ok(result.Value); 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) 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 userManager = services.GetRequiredService<UserManager<IdentityUser>>();
var userFactory = services.GetService<IUserFactory>(); var userFactory = services.GetRequiredService<IUserFactory>();
var log = services.GetService<ISemanticLog>(); var log = services.GetRequiredService<ISemanticLog>();
if (options.IsAdminConfigured()) if (options.IsAdminConfigured())
{ {

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

@ -8,10 +8,10 @@
using System; using System;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.CQRS.Events;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
namespace Squidex.Config.Domain namespace Squidex.Config.Domain
{ {
@ -54,7 +54,7 @@ namespace Squidex.Config.Domain
if (enabled) 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>(); .As<IEventConsumer>();
} }
} }

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

@ -20,7 +20,7 @@ using Squidex.Domain.Apps.Events;
using Squidex.Extensions.Actions; using Squidex.Extensions.Actions;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Config.Domain namespace Squidex.Config.Domain
{ {
@ -49,6 +49,7 @@ namespace Squidex.Config.Domain
new AppPatternsConverter(), new AppPatternsConverter(),
new ClaimsPrincipalConverter(), new ClaimsPrincipalConverter(),
new InstantConverter(), new InstantConverter(),
new JsonValueConverter(),
new LanguageConverter(), new LanguageConverter(),
new LanguagesConfigConverter(), new LanguagesConfigConverter(),
new NamedGuidIdConverter(), new NamedGuidIdConverter(),
@ -86,6 +87,8 @@ namespace Squidex.Config.Domain
services.AddSingleton(DefaultJsonSerializer); services.AddSingleton(DefaultJsonSerializer);
services.AddSingleton(TypeNameRegistry); services.AddSingleton(TypeNameRegistry);
services.AddSingleton(new NewtonsoftJsonSerializer(DefaultJsonSettings));
return services; 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;
using Squidex.Infrastructure.Diagnostics; using Squidex.Infrastructure.Diagnostics;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Migrations; using Squidex.Infrastructure.Migrations;
using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
@ -88,7 +89,7 @@ namespace Squidex.Config.Domain
.As<IAssetRepository>() .As<IAssetRepository>()
.As<ISnapshotStore<AssetState, Guid>>(); .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<IContentRepository>()
.As<ISnapshotStore<ContentState, Guid>>() .As<ISnapshotStore<ContentState, Guid>>()
.As<IEventConsumer>(); .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) 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>() services.AddSingletonAs<ConfigAppPlansProvider>()
.As<IAppPlansProvider>(); .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))] [MemberData(nameof(Triggers))]
public void Should_freeze_triggers(RuleTrigger trigger) 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))] [MemberData(nameof(FieldProperties))]
public void Should_freeze_field_properties(FieldProperties action) 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] [Fact]
public void Should_serialize_and_deserialize_schema() public void Should_serialize_and_deserialize_schema()
{ {
var schemaSource = TestData.MixedSchema(); var schemaSource = TestUtils.MixedSchema();
var schemaTarget = schemaSource.SerializeAndDeserialize(); var schemaTarget = schemaSource.SerializeAndDeserialize();
schemaTarget.Should().BeEquivalentTo(schemaSource); 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() new ContentFieldData()
.AddValue("iv", JsonValue.Object()); .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 = var expected =
new ContentFieldData() 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 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); Assert.Equal(JsonValue.Create("e30="), result);
} }
@ -33,7 +33,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var source = JsonValue.Null; var source = JsonValue.Null;
var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, jsonField); var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Same(source, result); Assert.Same(source, result);
} }
@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var source = JsonValue.Create("NO-JSON"); 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); Assert.Same(source, result);
} }
@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var source = JsonValue.Create("e30="); 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); Assert.Equal(JsonValue.Object(), result);
} }
@ -63,7 +63,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var source = JsonValue.Null; var source = JsonValue.Null;
var result = ValueConverters.DecodeJson(TestData.DefaultSerializer)(source, jsonField); var result = ValueConverters.DecodeJson(TestUtils.DefaultSerializer)(source, jsonField);
Assert.Same(source, result); Assert.Same(source, result);
} }
@ -73,7 +73,7 @@ namespace Squidex.Domain.Apps.Core.Operations.ConvertContent
{ {
var source = JsonValue.Null; var source = JsonValue.Null;
var result = ValueConverters.EncodeJson(TestData.DefaultSerializer)(source, stringField); var result = ValueConverters.EncodeJson(TestUtils.DefaultSerializer)(source, stringField);
Assert.Same(source, result); 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 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); 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 public class JsonSchemaTests
{ {
private readonly Schema schema = TestData.MixedSchema(); private readonly Schema schema = TestUtils.MixedSchema();
[Fact] [Fact]
public void Should_build_json_schema() 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) A.CallTo(() => user.Claims)
.Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") }); .Returns(new List<Claim> { new Claim(SquidexClaimTypes.DisplayName, "me") });
sut = new RuleEventFormatter(TestData.DefaultSerializer, urlGenerator); sut = new RuleEventFormatter(TestUtils.DefaultSerializer, urlGenerator);
} }
[Fact] [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) A.CallTo(() => ruleTriggerHandler.TriggerType)
.Returns(typeof(ContentChangedTrigger)); .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] [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.Domain.Apps.Core.Schemas.Json;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.Collections;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Newtonsoft; using Squidex.Infrastructure.Json.Newtonsoft;
using Xunit; using Xunit;
namespace Squidex.Domain.Apps.Core namespace Squidex.Domain.Apps.Core
{ {
public static class TestData public static class TestUtils
{ {
public static readonly IJsonSerializer DefaultSerializer = CreateSerializer(); public static readonly IJsonSerializer DefaultSerializer = CreateSerializer();
private static IJsonSerializer CreateSerializer() public static IJsonSerializer CreateSerializer(TypeNameHandling typeNameHandling = TypeNameHandling.Auto)
{ {
var typeNameRegistry = new TypeNameRegistry(); var typeNameRegistry = new TypeNameRegistry();
@ -46,13 +47,15 @@ namespace Squidex.Domain.Apps.Core
new NamedGuidIdConverter(), new NamedGuidIdConverter(),
new NamedLongIdConverter(), new NamedLongIdConverter(),
new NamedStringIdConverter(), new NamedStringIdConverter(),
new PropertiesBagConverter<EnvelopeHeaders>(),
new PropertiesBagConverter<PropertiesBag>(),
new RefTokenConverter(), new RefTokenConverter(),
new RolesConverter(), new RolesConverter(),
new RuleConverter(), new RuleConverter(),
new SchemaConverter(), new SchemaConverter(),
new StringEnumConverter()), new StringEnumConverter()),
TypeNameHandling = TypeNameHandling.Auto TypeNameHandling = typeNameHandling
}; };
return new NewtonsoftJsonSerializer(serializerSettings); return new NewtonsoftJsonSerializer(serializerSettings);

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

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

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

@ -8,11 +8,12 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using FluentAssertions; using Squidex.Domain.Apps.Core;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks; using Squidex.Infrastructure.Tasks;
using Xunit; using Xunit;
@ -22,9 +23,26 @@ namespace Squidex.Domain.Apps.Entities.Backup
public class BackupReaderWriterTests public class BackupReaderWriterTests
{ {
private readonly IStreamNameResolver streamNameResolver = A.Fake<IStreamNameResolver>(); 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() public BackupReaderWriterTests()
{ {
typeNameRegistry.Map(typeof(MyEvent));
formatter = new DefaultEventDataFormatter(typeNameRegistry, serializer);
A.CallTo(() => streamNameResolver.WithNewId(A<string>.Ignored, A<Func<string, string>>.Ignored)) A.CallTo(() => streamNameResolver.WithNewId(A<string>.Ignored, A<Func<string, string>>.Ignored))
.ReturnsLazily(new Func<string, Func<string, string>, string>((stream, idGenerator) => stream + "^2")); .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 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); var eventStored = new StoredEvent("S", "1", 2, eventData);
if (i % 17 == 0) var index = @event.Event.Headers["Index"].ToInt32();
{
var localI = i;
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; 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); writer.WriteEvent(eventStored);
sourceEvents.Add(eventStored);
} }
} }
stream.Position = 0; 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; 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 = for (var i = 0; i < targetEvents.Count; i++)
sourceEvents.Select(x => {
new StoredEvent(streamNameResolver.WithNewId(x.StreamName, null), var lhs = targetEvents[i].Event.To<MyEvent>();
x.EventPosition, var rhs = sourceEvents[i].Event.To<MyEvent>();
x.EventStreamNumber,
x.Data)).ToList();
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() new NamedContentData()
.AddField("my-field1", .AddField("my-field1",
new ContentFieldData() new ContentFieldData()
.AddValue(null)) .AddValue("iv", null))
.AddField("my-field2", .AddField("my-field2",
new ContentFieldData() new ContentFieldData()
.AddValue(1)); .AddValue("iv", 1));
private readonly NamedContentData data = private readonly NamedContentData data =
new NamedContentData() new NamedContentData()
.AddField("my-field1", .AddField("my-field1",

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

@ -8,7 +8,6 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Newtonsoft.Json.Linq;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Xunit; using Xunit;
@ -77,9 +76,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
id = asset.Id, id = asset.Id,
version = 1, version = 1,
created = asset.Created.ToDateTimeUtc(), created = asset.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = asset.LastModified.ToDateTimeUtc(), lastModified = asset.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "subject:user2",
url = $"assets/{asset.Id}", url = $"assets/{asset.Id}",
thumbnailUrl = $"assets/{asset.Id}?width=100", thumbnailUrl = $"assets/{asset.Id}?width=100",
@ -147,9 +146,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
id = asset.Id, id = asset.Id,
version = 1, version = 1,
created = asset.Created.ToDateTimeUtc(), created = asset.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = asset.LastModified.ToDateTimeUtc(), lastModified = asset.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "subject:user2",
url = $"assets/{asset.Id}", url = $"assets/{asset.Id}",
thumbnailUrl = $"assets/{asset.Id}?width=100", thumbnailUrl = $"assets/{asset.Id}?width=100",
@ -211,9 +210,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
id = asset.Id, id = asset.Id,
version = 1, version = 1,
created = asset.Created.ToDateTimeUtc(), created = asset.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = asset.LastModified.ToDateTimeUtc(), lastModified = asset.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "subject:user2",
url = $"assets/{asset.Id}", url = $"assets/{asset.Id}",
thumbnailUrl = $"assets/{asset.Id}?width=100", thumbnailUrl = $"assets/{asset.Id}?width=100",
@ -298,9 +297,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
id = content.Id, id = content.Id,
version = 1, version = 1,
created = content.Created.ToDateTimeUtc(), created = content.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = content.LastModified.ToDateTimeUtc(), lastModified = content.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "subject:user2",
status = "DRAFT", status = "DRAFT",
url = $"contents/my-schema/{content.Id}", url = $"contents/my-schema/{content.Id}",
@ -320,7 +319,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}, },
myDatetime = new myDatetime = new
{ {
iv = content.LastModified.ToDateTimeUtc() iv = content.LastModified
}, },
myJson = new myJson = new
{ {
@ -440,9 +439,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
id = content.Id, id = content.Id,
version = 1, version = 1,
created = content.Created.ToDateTimeUtc(), created = content.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = content.LastModified.ToDateTimeUtc(), lastModified = content.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "subject:user2",
status = "DRAFT", status = "DRAFT",
url = $"contents/my-schema/{content.Id}", url = $"contents/my-schema/{content.Id}",
@ -462,7 +461,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}, },
myDatetime = new myDatetime = new
{ {
iv = content.LastModified.ToDateTimeUtc() iv = content.LastModified
}, },
myJson = new myJson = new
{ {
@ -560,9 +559,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
{ {
id = content.Id, id = content.Id,
version = 1, version = 1,
created = content.Created.ToDateTimeUtc(), created = content.Created,
createdBy = "subject:user1", createdBy = "subject:user1",
lastModified = content.LastModified.ToDateTimeUtc(), lastModified = content.LastModified,
lastModifiedBy = "subject:user2", lastModifiedBy = "subject:user2",
status = "DRAFT", status = "DRAFT",
url = $"contents/my-schema/{content.Id}", url = $"contents/my-schema/{content.Id}",
@ -582,7 +581,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
}, },
myDatetime = new myDatetime = new
{ {
iv = content.LastModified.ToDateTimeUtc() iv = content.LastModified
}, },
myJson = new myJson = new
{ {
@ -819,9 +818,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
var result = await sut.QueryAsync(context, new GraphQLQuery { Query = query }); 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() 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.Caching.Memory;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NodaTime.Extensions; using NodaTime.Extensions;
using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Apps; 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.Contents.TestData;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.Json.Objects;
using Xunit; using Xunit;
#pragma warning disable SA1311 // Static readonly fields must begin with upper-case letter #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 IContentQueryService contentQuery = A.Fake<IContentQueryService>();
protected readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>(); protected readonly IAssetQueryService assetQuery = A.Fake<IAssetQueryService>();
protected readonly ISchemaEntity schema = A.Fake<ISchemaEntity>(); 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 IMemoryCache cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
protected readonly IAppProvider appProvider = A.Fake<IAppProvider>(); protected readonly IAppProvider appProvider = A.Fake<IAppProvider>();
protected readonly IAppEntity app = A.Dummy<IAppEntity>(); protected readonly IAppEntity app = A.Dummy<IAppEntity>();
@ -106,40 +108,40 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
.AddValue("de", "value")) .AddValue("de", "value"))
.AddField("my-assets", .AddField("my-assets",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", JToken.FromObject(new[] { assetId }))) .AddValue("iv", JsonValue.Array(assetId.ToString())))
.AddField("my-number", .AddField("my-number",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", 1)) .AddValue("iv", 1.0))
.AddField("my-boolean", .AddField("my-boolean",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", true)) .AddValue("iv", true))
.AddField("my-datetime", .AddField("my-datetime",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", now.ToDateTimeUtc())) .AddValue("iv", now))
.AddField("my-tags", .AddField("my-tags",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", JToken.FromObject(new[] { "tag1", "tag2" }))) .AddValue("iv", JsonValue.Array("tag1", "tag2")))
.AddField("my-references", .AddField("my-references",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", JToken.FromObject(new[] { refId }))) .AddValue("iv", JsonValue.Array(refId.ToString())))
.AddField("my-geolocation", .AddField("my-geolocation",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", JToken.FromObject(new { latitude = 10, longitude = 20 }))) .AddValue("iv", JsonValue.Object().Add("latitude", 10).Add("longitude", 20)))
.AddField("my-json", .AddField("my-json",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", JToken.FromObject(new { value = 1 }))) .AddValue("iv", JsonValue.Object().Add("value", 1)))
.AddField("my-localized", .AddField("my-localized",
new ContentFieldData() new ContentFieldData()
.AddValue("de-DE", "de-DE")) .AddValue("de-DE", "de-DE"))
.AddField("my-array", .AddField("my-array",
new ContentFieldData() new ContentFieldData()
.AddValue("iv", new JArray( .AddValue("iv", JsonValue.Array(
new JObject( JsonValue.Object()
new JProperty("nested-boolean", true), .Add("nested-boolean", true)
new JProperty("nested-number", 1)), .Add("nested-number", 1),
new JObject( JsonValue.Object()
new JProperty("nested-boolean", false), .Add("nested-boolean", false)
new JProperty("nested-number", 2))))); .Add("nested-number", 2))));
var content = new ContentEntity var content = new ContentEntity
{ {
@ -179,22 +181,22 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL
return asset; 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) if (checkErrors && result.HasErrors)
{ {
throw new InvalidOperationException(Serialize(result)); throw new InvalidOperationException(Serialize(result));
} }
var resultJson = JsonConvert.SerializeObject(result.Response, Formatting.Indented); var resultJson = serializer.Serialize(result.Response, true);
var expectJson = JsonConvert.SerializeObject(expected, Formatting.Indented); var expectJson = serializer.Serialize(expected, true);
Assert.Equal(expectJson, resultJson); 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;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Newtonsoft.Json.Linq;
using NodaTime; using NodaTime;
using Squidex.Domain.Apps.Core.HandleRules; using Squidex.Domain.Apps.Core.HandleRules;
using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Core.Rules;
@ -49,7 +48,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
[InlineData(4, 0, RuleResult.Failed, RuleJobResult.Failed)] [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) 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 actionName = "MyAction";
var @event = CreateEvent(calls, actionName, actionData); var @event = CreateEvent(calls, actionName, actionData);
@ -73,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
.MustHaveHappened(); .MustHaveHappened();
} }
private IRuleEventEntity CreateEvent(int numCalls, string actionName, JObject actionData) private IRuleEventEntity CreateEvent(int numCalls, string actionName, string actionData)
{ {
var @event = A.Fake<IRuleEventEntity>(); 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.MongoDb\Squidex.Infrastructure.MongoDb.csproj" />
<ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" /> <ProjectReference Include="..\..\src\Squidex.Infrastructure\Squidex.Infrastructure.csproj" />
<ProjectReference Include="..\..\src\Squidex.Domain.Apps.Entities\Squidex.Domain.Apps.Entities.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>
<ItemGroup> <ItemGroup>
<PackageReference Include="FakeItEasy" Version="4.9.1" /> <PackageReference Include="FakeItEasy" Version="4.9.1" />

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

@ -6,7 +6,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Globalization;
using NodaTime; using NodaTime;
using Xunit; using Xunit;
@ -15,7 +14,6 @@ namespace Squidex.Infrastructure.EventSourcing
public class EnvelopeExtensionsTests public class EnvelopeExtensionsTests
{ {
private readonly Envelope<string> sut = new Envelope<string>(string.Empty); private readonly Envelope<string> sut = new Envelope<string>(string.Empty);
private readonly CultureInfo culture = CultureInfo.InvariantCulture;
[Fact] [Fact]
public void Should_set_and_get_timestamp() public void Should_set_and_get_timestamp()
@ -25,7 +23,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetTimestamp(timestamp); sut.SetTimestamp(timestamp);
Assert.Equal(timestamp, sut.Headers.Timestamp()); Assert.Equal(timestamp, sut.Headers.Timestamp());
Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant(culture)); Assert.Equal(timestamp, sut.Headers["Timestamp"].ToInstant());
} }
[Fact] [Fact]
@ -36,7 +34,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetCommitId(commitId); sut.SetCommitId(commitId);
Assert.Equal(commitId, sut.Headers.CommitId()); Assert.Equal(commitId, sut.Headers.CommitId());
Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid(culture)); Assert.Equal(commitId, sut.Headers["CommitId"].ToGuid());
} }
[Fact] [Fact]
@ -47,7 +45,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetEventId(commitId); sut.SetEventId(commitId);
Assert.Equal(commitId, sut.Headers.EventId()); Assert.Equal(commitId, sut.Headers.EventId());
Assert.Equal(commitId, sut.Headers["EventId"].ToGuid(culture)); Assert.Equal(commitId, sut.Headers["EventId"].ToGuid());
} }
[Fact] [Fact]
@ -58,7 +56,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetAggregateId(commitId); sut.SetAggregateId(commitId);
Assert.Equal(commitId, sut.Headers.AggregateId()); Assert.Equal(commitId, sut.Headers.AggregateId());
Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid(culture)); Assert.Equal(commitId, sut.Headers["AggregateId"].ToGuid());
} }
[Fact] [Fact]
@ -80,7 +78,7 @@ namespace Squidex.Infrastructure.EventSourcing
sut.SetEventStreamNumber(eventStreamNumber); sut.SetEventStreamNumber(eventStreamNumber);
Assert.Equal(eventStreamNumber, sut.Headers.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)) A.CallTo(() => persistence.WriteSnapshotAsync(A<EventConsumerState>.Ignored))
.Invokes(new Action<EventConsumerState>(s => state = s)); .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( sut = new MyEventConsumerGrain(
x => eventConsumer, x => eventConsumer,
@ -192,7 +192,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
[Fact] [Fact]
public async Task Should_ignore_old_events() public async Task Should_ignore_old_events()
{ {
A.CallTo(() => formatter.Parse(eventData, true)) A.CallTo(() => formatter.Parse(eventData, true, null))
.Throws(new TypeNameNotFoundException()); .Throws(new TypeNameNotFoundException());
var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData); var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData);
@ -326,7 +326,7 @@ namespace Squidex.Infrastructure.EventSourcing.Grains
{ {
var ex = new InvalidOperationException(); var ex = new InvalidOperationException();
A.CallTo(() => formatter.Parse(eventData, true)) A.CallTo(() => formatter.Parse(eventData, true, null))
.Throws(ex); .Throws(ex);
var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData); var @event = new StoredEvent("Stream", Guid.NewGuid().ToString(), 123, eventData);

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

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

8
tools/Migrate_01/Rebuilder.cs

@ -25,6 +25,7 @@ using Squidex.Infrastructure;
using Squidex.Infrastructure.Caching; using Squidex.Infrastructure.Caching;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Json;
using Squidex.Infrastructure.States; using Squidex.Infrastructure.States;
namespace Migrate_01 namespace Migrate_01
@ -33,18 +34,21 @@ namespace Migrate_01
{ {
private readonly FieldRegistry fieldRegistry; private readonly FieldRegistry fieldRegistry;
private readonly ILocalCache localCache; private readonly ILocalCache localCache;
private readonly IJsonSerializer serializer;
private readonly IStore<Guid> store; private readonly IStore<Guid> store;
private readonly IEventStore eventStore; private readonly IEventStore eventStore;
public Rebuilder( public Rebuilder(
FieldRegistry fieldRegistry, FieldRegistry fieldRegistry,
ILocalCache localCache, ILocalCache localCache,
IJsonSerializer serializer,
IStore<Guid> store, IStore<Guid> store,
IEventStore eventStore) IEventStore eventStore)
{ {
this.fieldRegistry = fieldRegistry; this.fieldRegistry = fieldRegistry;
this.eventStore = eventStore; this.eventStore = eventStore;
this.localCache = localCache; this.localCache = localCache;
this.serializer = serializer;
this.store = store; this.store = store;
} }
@ -104,7 +108,9 @@ namespace Migrate_01
await eventStore.QueryAsync(async storedEvent => 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)) if (handledIds.Add(id))
{ {

Loading…
Cancel
Save