mirror of https://github.com/Squidex/squidex.git
Browse Source
# Conflicts: # src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs # src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs # src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cspull/311/head
19 changed files with 560 additions and 114 deletions
@ -1,41 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.IO; |
|
||||
using Newtonsoft.Json; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Backup |
|
||||
{ |
|
||||
public static class BackupSerializer |
|
||||
{ |
|
||||
private static readonly JsonSerializer JsonSerializer = JsonSerializer.CreateDefault(); |
|
||||
|
|
||||
public static void SerializeAsJson<T>(this Stream stream, T value) |
|
||||
{ |
|
||||
using (var writer = new StreamWriter(stream)) |
|
||||
{ |
|
||||
JsonSerializer.Serialize(writer, value); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static T DeserializeAsJson<T>(this Stream stream) |
|
||||
{ |
|
||||
using (var reader = new StreamReader(stream)) |
|
||||
{ |
|
||||
return (T)JsonSerializer.Deserialize(reader, typeof(T)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public static void DeserializeAsJson<T>(this Stream stream, T result) |
|
||||
{ |
|
||||
using (var reader = new StreamReader(stream)) |
|
||||
{ |
|
||||
JsonSerializer.Populate(reader, result); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,170 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Newtonsoft.Json.Linq; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Backup |
||||
|
{ |
||||
|
public sealed class GuidMapper |
||||
|
{ |
||||
|
private static readonly int GuidLength = Guid.Empty.ToString().Length; |
||||
|
private readonly List<(JObject Source, string NewKey, string OldKey)> mappings = new List<(JObject Source, string NewKey, string OldKey)>(); |
||||
|
private readonly Dictionary<Guid, Guid> oldToNewGuid = new Dictionary<Guid, Guid>(); |
||||
|
private readonly Dictionary<Guid, Guid> newToOldGuid = new Dictionary<Guid, Guid>(); |
||||
|
|
||||
|
public Guid NewGuid(Guid oldGuid) |
||||
|
{ |
||||
|
return oldToNewGuid.GetOrDefault(oldGuid); |
||||
|
} |
||||
|
|
||||
|
public Guid OldGuid(Guid newGuid) |
||||
|
{ |
||||
|
return newToOldGuid.GetOrDefault(newGuid); |
||||
|
} |
||||
|
|
||||
|
public string NewGuidString(string key) |
||||
|
{ |
||||
|
if (Guid.TryParse(key, out var guid)) |
||||
|
{ |
||||
|
return GenerateNewGuid(guid).ToString(); |
||||
|
} |
||||
|
|
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
public JToken NewGuids(JToken jToken) |
||||
|
{ |
||||
|
var result = NewGuidsCore(jToken); |
||||
|
|
||||
|
if (mappings.Count > 0) |
||||
|
{ |
||||
|
foreach (var mapping in mappings) |
||||
|
{ |
||||
|
if (mapping.Source.TryGetValue(mapping.OldKey, out var value)) |
||||
|
{ |
||||
|
mapping.Source.Remove(mapping.OldKey); |
||||
|
mapping.Source[mapping.NewKey] = value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
mappings.Clear(); |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private JToken NewGuidsCore(JToken jToken) |
||||
|
{ |
||||
|
switch (jToken.Type) |
||||
|
{ |
||||
|
case JTokenType.String: |
||||
|
if (TryConvertString(jToken.ToString(), out var result)) |
||||
|
{ |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
break; |
||||
|
case JTokenType.Guid: |
||||
|
return GenerateNewGuid((Guid)jToken); |
||||
|
case JTokenType.Object: |
||||
|
NewGuidsCore((JObject)jToken); |
||||
|
break; |
||||
|
case JTokenType.Array: |
||||
|
NewGuidsCore((JArray)jToken); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return jToken; |
||||
|
} |
||||
|
|
||||
|
private void NewGuidsCore(JArray jArray) |
||||
|
{ |
||||
|
for (var i = 0; i < jArray.Count; i++) |
||||
|
{ |
||||
|
jArray[i] = NewGuidsCore(jArray[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void NewGuidsCore(JObject jObject) |
||||
|
{ |
||||
|
foreach (var jProperty in jObject.Properties()) |
||||
|
{ |
||||
|
var newValue = NewGuidsCore(jProperty.Value); |
||||
|
|
||||
|
if (!ReferenceEquals(newValue, jProperty.Value)) |
||||
|
{ |
||||
|
jProperty.Value = newValue; |
||||
|
} |
||||
|
|
||||
|
if (TryConvertString(jProperty.Name, out var newKey)) |
||||
|
{ |
||||
|
mappings.Add((jObject, newKey, jProperty.Name)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool TryConvertString(string value, out string result) |
||||
|
{ |
||||
|
return TryGenerateNewGuidString(value, out result) || TryGenerateNewNamedId(value, out result); |
||||
|
} |
||||
|
|
||||
|
private bool TryGenerateNewGuidString(string value, out string result) |
||||
|
{ |
||||
|
result = null; |
||||
|
|
||||
|
if (value.Length == GuidLength) |
||||
|
{ |
||||
|
if (Guid.TryParse(value, out var guid)) |
||||
|
{ |
||||
|
var newGuid = GenerateNewGuid(guid); |
||||
|
|
||||
|
result = newGuid.ToString(); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private bool TryGenerateNewNamedId(string value, out string result) |
||||
|
{ |
||||
|
result = null; |
||||
|
|
||||
|
if (value.Length > GuidLength && value[GuidLength] == ',') |
||||
|
{ |
||||
|
if (Guid.TryParse(value.Substring(0, GuidLength), out var guid)) |
||||
|
{ |
||||
|
var newGuid = GenerateNewGuid(guid); |
||||
|
|
||||
|
result = newGuid + value.Substring(GuidLength); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private Guid GenerateNewGuid(Guid oldGuid) |
||||
|
{ |
||||
|
return oldToNewGuid.GetOrAdd(oldGuid, GuidGenerator); |
||||
|
} |
||||
|
|
||||
|
private Guid GuidGenerator(Guid oldGuid) |
||||
|
{ |
||||
|
var newGuid = Guid.NewGuid(); |
||||
|
|
||||
|
newToOldGuid[newGuid] = oldGuid; |
||||
|
|
||||
|
return newGuid; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,17 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System.Threading.Tasks; |
|
||||
using Orleans; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities |
|
||||
{ |
|
||||
public interface ICleanableAppGrain : IGrainWithGuidKey |
|
||||
{ |
|
||||
Task ClearAsync(); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,161 @@ |
|||||
|
// ==========================================================================
|
||||
|
// 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")); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue