fix udi leaking in the management api (#15684)
* [WIP] Stop Udi leaking on ConterPicker * Refined Udi conversion for contentPicker Cleaned up base construcor usage to move away from the obsoleted one. * Fixed Udi lieaking in MNTP * Stopped Udi bleeding for MultiUrlPicker * Remove unused assignment * Resolved namespace issue * Use correct configuration value for MNTP udi parsing * Turn helper auto props into local helper function to avoid unnecesary serialization * Remove Newtonsoft.Json from Multi URL picker * Fixed MNTP configuration serialization * Changed MNTP editor data from csv guid to EditorEntityReference[] * Added remarks for the MNTP editor conversion logic * Reworked MNTPPropertyEditor Unittests changed intent of one fixed bug because of 1 rework * Update OpenApi.json --------- Co-authored-by: Sven Geusens <sge@umbraco.dk> Co-authored-by: Elitsa <elm@umbraco.dk> Co-authored-by: kjac <kja@umbraco.dk>
This commit is contained in:
@@ -22664,8 +22664,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
}
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/security/forgot-password/reset": {
|
||||
@@ -22969,16 +22977,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/server/configuration": {
|
||||
@@ -31027,16 +31027,8 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/user/set-user-groups": {
|
||||
@@ -31230,6 +31222,62 @@
|
||||
}
|
||||
},
|
||||
"/umbraco/management/api/v1/webhook": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Webhook"
|
||||
],
|
||||
"operationId": "GetWebhook",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "skip",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "take",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"default": 100
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Success",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PagedWebhookResponseModel"
|
||||
}
|
||||
},
|
||||
"text/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PagedWebhookResponseModel"
|
||||
}
|
||||
},
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PagedWebhookResponseModel"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "The resource is protected and requires an authentication token"
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"Backoffice User": [ ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"tags": [
|
||||
"Webhook"
|
||||
@@ -34061,12 +34109,27 @@
|
||||
},
|
||||
"DatatypeConfigurationResponseModel": {
|
||||
"required": [
|
||||
"canBeChanged"
|
||||
"canBeChanged",
|
||||
"documentListViewId",
|
||||
"mediaListViewId",
|
||||
"memberListViewId"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"canBeChanged": {
|
||||
"$ref": "#/components/schemas/DataTypeChangeModeModel"
|
||||
},
|
||||
"documentListViewId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"mediaListViewId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"memberListViewId": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -38115,6 +38178,30 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PagedWebhookResponseModel": {
|
||||
"required": [
|
||||
"items",
|
||||
"total"
|
||||
],
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"total": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/WebhookResponseModel"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"PartialViewFolderResponseModel": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
|
||||
@@ -23,9 +23,19 @@ public class LinkDisplay
|
||||
[DataMember(Name = "trashed")]
|
||||
public bool Trashed { get; set; }
|
||||
|
||||
[DataMember(Name = "udi")]
|
||||
public GuidUdi? Udi { get; set; }
|
||||
[DataMember(Name = "type")]
|
||||
public string? Type { get; set; }
|
||||
|
||||
[DataMember(Name = "unique")]
|
||||
public Guid? Unique { get; set; }
|
||||
|
||||
[DataMember(Name = "url")]
|
||||
public string? Url { get; set; }
|
||||
|
||||
public static class Types
|
||||
{
|
||||
public const string Document = "document";
|
||||
public const string Media = "media";
|
||||
public const string External = "external";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
@@ -35,6 +39,7 @@ public class ContentPickerPropertyEditor : DataEditor
|
||||
|
||||
internal class ContentPickerPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
|
||||
public ContentPickerPropertyValueEditor(
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
@@ -58,5 +63,40 @@ public class ContentPickerPropertyEditor : DataEditor
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
}
|
||||
}
|
||||
|
||||
// starting in v14 the passed in value is always a guid, we store it as a document Udi string. Else it's an invalid value
|
||||
public override object? FromEditor(ContentPropertyData editorValue, object? currentValue) =>
|
||||
editorValue.Value is not null
|
||||
&& Guid.TryParse(editorValue.Value as string, out Guid guidValue)
|
||||
? GuidUdi.Create(Constants.UdiEntityType.Document, guidValue).ToString()
|
||||
: null;
|
||||
|
||||
public override object? ToEditor(IProperty property, string? culture = null, string? segment = null)
|
||||
{
|
||||
// since our storage type is a string, we can expect the base to return a string
|
||||
var stringValue = base.ToEditor(property, culture, segment) as string;
|
||||
|
||||
if (stringValue.IsNullOrWhiteSpace())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// this string can actually be an Int value from old versions => convert to it's guid counterpart
|
||||
if (int.TryParse(stringValue, out var oldInt))
|
||||
{
|
||||
// todo: This is a temporary code path that should be removed ASAP
|
||||
Attempt<Guid> conversionAttempt = StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>()
|
||||
.GetKeyForId(oldInt, UmbracoObjectTypes.Document);
|
||||
return conversionAttempt.Success ? conversionAttempt.Result : null;
|
||||
}
|
||||
|
||||
// if its not an old value, it should be a udi
|
||||
if (UdiParser.TryParse(stringValue, out GuidUdi? guidUdi) is false)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return guidUdi.Guid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
/// <summary>
|
||||
@@ -5,6 +7,7 @@ namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
/// </summary>
|
||||
public class MultiNodePickerConfiguration : IIgnoreUserStartNodesConfig
|
||||
{
|
||||
[JsonPropertyName("startNode")]
|
||||
[ConfigurationField("startNode")]
|
||||
public MultiNodePickerConfigurationTreeSource? TreeSource { get; set; }
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
@@ -8,15 +9,18 @@ namespace Umbraco.Cms.Core.PropertyEditors;
|
||||
[DataContract]
|
||||
public class MultiNodePickerConfigurationTreeSource
|
||||
{
|
||||
[JsonPropertyName("type")]
|
||||
[DataMember(Name = "type")]
|
||||
public string? ObjectType { get; set; }
|
||||
|
||||
[JsonPropertyName("query")]
|
||||
[DataMember(Name = "query")]
|
||||
public string? StartNodeQuery { get; set; }
|
||||
|
||||
[DataMember(Name = "dynamicRoot")]
|
||||
public DynamicRoot? DynamicRoot { get; set; }
|
||||
|
||||
[JsonPropertyName("id")]
|
||||
[DataMember(Name = "id")]
|
||||
public Udi? StartNodeId { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
@@ -31,8 +32,16 @@ public class MultiNodeTreePickerPropertyEditor : DataEditor
|
||||
protected override IDataValueEditor CreateValueEditor() =>
|
||||
DataValueEditorFactory.Create<MultiNodeTreePickerPropertyValueEditor>(Attribute!);
|
||||
|
||||
/// <remarks>
|
||||
/// At first glance, the fromEditor and toEditor methods might seem strange.
|
||||
/// This is because we wanted to stop the leaking of UDI's to the frontend while not having to do database migrations
|
||||
/// so we opted to, for now, translate the udi string in the database into a structured format unique to the client
|
||||
/// This way, for now, no migration is needed and no changes outside of the editor logic needs to be touched to stop the leaking.
|
||||
/// </remarks>
|
||||
public class MultiNodeTreePickerPropertyValueEditor : DataValueEditor, IDataValueReference
|
||||
{
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
|
||||
public MultiNodeTreePickerPropertyValueEditor(
|
||||
IShortStringHelper shortStringHelper,
|
||||
IJsonSerializer jsonSerializer,
|
||||
@@ -40,6 +49,7 @@ public class MultiNodeTreePickerPropertyEditor : DataEditor
|
||||
DataEditorAttribute attribute)
|
||||
: base(shortStringHelper, jsonSerializer, ioHelper, attribute)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
}
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object? value)
|
||||
@@ -56,23 +66,41 @@ public class MultiNodeTreePickerPropertyEditor : DataEditor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override object? FromEditor(ContentPropertyData editorValue, object? currentValue)
|
||||
=> editorValue.Value is JsonArray jsonArray
|
||||
? EntityReferencesToUdis(_jsonSerializer.Deserialize<IEnumerable<EditorEntityReference>>(jsonArray.ToJsonString()) ?? Enumerable.Empty<EditorEntityReference>())
|
||||
: null;
|
||||
|
||||
public override object? ToEditor(IProperty property, string? culture = null, string? segment = null)
|
||||
{
|
||||
var value = property.GetValue(culture, segment);
|
||||
return value is string stringValue
|
||||
? ParseValidUdis(stringValue.Split(Constants.CharArrays.Comma))
|
||||
: null;
|
||||
? UdisToEntityReferences(stringValue.Split(Constants.CharArrays.Comma)).ToArray()
|
||||
: null;
|
||||
}
|
||||
|
||||
public override object? FromEditor(ContentPropertyData editorValue, object? currentValue)
|
||||
=> editorValue.Value is IEnumerable<string> stringValues
|
||||
? string.Join(",", ParseValidUdis(stringValues))
|
||||
: null;
|
||||
private IEnumerable<EditorEntityReference> UdisToEntityReferences(IEnumerable<string> stringUdis)
|
||||
{
|
||||
foreach (var stringUdi in stringUdis)
|
||||
{
|
||||
if (UdiParser.TryParse(stringUdi, out GuidUdi? guidUdi) is false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
private string[] ParseValidUdis(IEnumerable<string> stringValues)
|
||||
=> stringValues
|
||||
.Select(s => UdiParser.TryParse(s, out Udi? udi) && udi is GuidUdi guidUdi ? guidUdi.ToString() : null)
|
||||
.WhereNotNull()
|
||||
.ToArray();
|
||||
yield return new EditorEntityReference() { Type = guidUdi.EntityType, Unique = guidUdi.Guid };
|
||||
}
|
||||
}
|
||||
|
||||
private string EntityReferencesToUdis(IEnumerable<EditorEntityReference> nodeReferences)
|
||||
=> string.Join(",", nodeReferences.Select(entityReference => Udi.Create(entityReference.Type, entityReference.Unique).ToString()));
|
||||
|
||||
public class EditorEntityReference
|
||||
{
|
||||
public required string Type { get; set; }
|
||||
|
||||
public required Guid Unique { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,9 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference
|
||||
Trashed = trashed,
|
||||
Published = published,
|
||||
QueryString = dto.QueryString,
|
||||
Udi = udi,
|
||||
Type = dto.Udi is null ? LinkDisplay.Types.External
|
||||
: dto.Udi.EntityType,
|
||||
Unique = dto.Udi?.Guid,
|
||||
Url = url ?? string.Empty,
|
||||
});
|
||||
}
|
||||
@@ -169,8 +171,8 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference
|
||||
Name = link.Name,
|
||||
QueryString = link.QueryString,
|
||||
Target = link.Target,
|
||||
Udi = link.Udi,
|
||||
Url = link.Udi == null ? link.Url : null, // only save the URL for external links
|
||||
Udi = TypeIsUdiBased(link) ? new GuidUdi(link.Type!, link.Unique!.Value) : null,
|
||||
Url = TypeIsExternal(link) ? link.Url : null, // only save the URL for external links
|
||||
}));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -181,6 +183,14 @@ public class MultiUrlPickerValueEditor : DataValueEditor, IDataValueReference
|
||||
return base.FromEditor(editorValue, currentValue);
|
||||
}
|
||||
|
||||
private static bool TypeIsExternal(LinkDisplay link) =>
|
||||
link.Type is not null && link.Type.Equals(LinkDisplay.Types.External, StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
private static bool TypeIsUdiBased(LinkDisplay link) =>
|
||||
link.Type is not null && link.Unique is not null &&
|
||||
(link.Type.Equals(LinkDisplay.Types.Document, StringComparison.InvariantCultureIgnoreCase)
|
||||
|| link.Type.Equals(LinkDisplay.Types.Media, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
[DataContract]
|
||||
public class LinkDto
|
||||
{
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using Moq;
|
||||
using System.Text.Json.Nodes;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Org.BouncyCastle.Asn1.X500;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors;
|
||||
|
||||
@@ -67,9 +69,11 @@ public class MultiNodeTreePickerTests
|
||||
[Test]
|
||||
public void Can_Parse_Single_Value_From_Editor()
|
||||
{
|
||||
var value = new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString();
|
||||
var fromEditor = FromEditor(new[] { value }) as string;
|
||||
Assert.AreEqual(value, fromEditor);
|
||||
var value = new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid());
|
||||
var editorValue = $"[{{\"type\" :\"{value.EntityType}\",\"unique\":\"{value.Guid}\"}}]";
|
||||
var fromEditor =
|
||||
FromEditor(JsonNode.Parse(editorValue), jsonSerializer: new SystemTextJsonSerializer()) as string;
|
||||
Assert.AreEqual(value.ToString(), fromEditor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -77,51 +81,65 @@ public class MultiNodeTreePickerTests
|
||||
{
|
||||
var values = new[]
|
||||
{
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString()
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid())
|
||||
};
|
||||
|
||||
var fromEditor = FromEditor(values) as string;
|
||||
Assert.AreEqual(string.Join(",", values), fromEditor);
|
||||
var editorValue =
|
||||
$"[{{\"type\" :\"{values[0].EntityType}\",\"unique\":\"{values[0].Guid}\"}},{{\"type\" :\"{values[1].EntityType}\",\"unique\":\"{values[1].Guid}\"}},{{\"type\" :\"{values[2].EntityType}\",\"unique\":\"{values[2].Guid}\"}}]";
|
||||
|
||||
var fromEditor = FromEditor(JsonNode.Parse(editorValue), jsonSerializer: new SystemTextJsonSerializer()) as string;
|
||||
Assert.AreEqual(string.Join(",", values.Select(v => v.ToString())), fromEditor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Parse_Different_Entity_Types_From_Editor()
|
||||
{
|
||||
var values = new[]
|
||||
var expectedValues = new[]
|
||||
{
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Member, Guid.NewGuid()).ToString()
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Media, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Member, Guid.NewGuid())
|
||||
};
|
||||
|
||||
var fromEditor = FromEditor(values) as string;
|
||||
Assert.AreEqual(string.Join(",", values), fromEditor);
|
||||
var editorValue =
|
||||
$"[{{\"type\" :\"{expectedValues[0].EntityType}\",\"unique\":\"{expectedValues[0].Guid}\"}},{{\"type\" :\"{expectedValues[1].EntityType}\",\"unique\":\"{expectedValues[1].Guid}\"}},{{\"type\" :\"{expectedValues[2].EntityType}\",\"unique\":\"{expectedValues[2].Guid}\"}}]";
|
||||
|
||||
var fromEditor = FromEditor(JsonNode.Parse(editorValue), jsonSerializer: new SystemTextJsonSerializer()) as string;
|
||||
Assert.AreEqual(string.Join(",", expectedValues.Select(v => v.ToString())), fromEditor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Skip_Invalid_Values_From_Editor()
|
||||
public void From_Editor_Throws_Error_On_Invalid_Json()
|
||||
{
|
||||
var values = new[]
|
||||
{
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
"Invalid Value",
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString()
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid())
|
||||
};
|
||||
|
||||
var fromEditor = FromEditor(values) as string;
|
||||
Assert.AreEqual(string.Join(",", values.First(), values.Last()), fromEditor);
|
||||
var editorValue =
|
||||
$"[{{\"type\" :\"{values[0].EntityType}\",\"unique\":\"{values[0].Guid}\"}},{{\"invalidProperty\" :\"nonsenseValue\",\"otherWeirdProperty\":\"definitelyNotAGuid\"}},{{\"type\" :\"{values[1].EntityType}\",\"unique\":\"{values[1].Guid}\"}}]";
|
||||
|
||||
Assert.Catch<System.Text.Json.JsonException>(() =>
|
||||
FromEditor(JsonNode.Parse(editorValue), jsonSerializer: new SystemTextJsonSerializer()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Parse_Single_Value_To_Editor()
|
||||
{
|
||||
var value = new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString();
|
||||
var toEditor = ToEditor(value) as IEnumerable<string>;
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(1, toEditor.Count());
|
||||
Assert.AreEqual(value, toEditor.First());
|
||||
var value = new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid());
|
||||
var toEditor = ToEditor(value.ToString()) as IEnumerable<MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor.EditorEntityReference>;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(1, toEditor.Count());
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(value).Type, toEditor.First().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(value).Unique, toEditor.First().Unique);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -129,16 +147,23 @@ public class MultiNodeTreePickerTests
|
||||
{
|
||||
var values = new[]
|
||||
{
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString()
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid())
|
||||
};
|
||||
var toEditor = ToEditor(string.Join(",", values)) as IEnumerable<string>;
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(3, toEditor.Count());
|
||||
Assert.AreEqual(values[0], toEditor.First());
|
||||
Assert.AreEqual(values[1], toEditor.Skip(1).First());
|
||||
Assert.AreEqual(values[2], toEditor.Last());
|
||||
var toEditor = ToEditor(string.Join(",", values.Select(v => v.ToString()))) as IEnumerable<MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor.EditorEntityReference>;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(3, toEditor.Count());
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[0]).Type, toEditor.First().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[0]).Unique, toEditor.First().Unique);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[1]).Type, toEditor.Skip(1).First().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[1]).Unique, toEditor.Skip(1).First().Unique);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[2]).Type, toEditor.Last().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[2]).Unique, toEditor.Last().Unique);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -146,32 +171,46 @@ public class MultiNodeTreePickerTests
|
||||
{
|
||||
var values = new[]
|
||||
{
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(),
|
||||
new GuidUdi(Constants.UdiEntityType.Member, Guid.NewGuid()).ToString()
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Media, Guid.NewGuid()),
|
||||
new GuidUdi(Constants.UdiEntityType.Member, Guid.NewGuid())
|
||||
};
|
||||
var toEditor = ToEditor(string.Join(",", values)) as IEnumerable<string>;
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(3, toEditor.Count());
|
||||
Assert.AreEqual(values[0], toEditor.First());
|
||||
Assert.AreEqual(values[1], toEditor.Skip(1).First());
|
||||
Assert.AreEqual(values[2], toEditor.Last());
|
||||
var toEditor = ToEditor(string.Join(",", values.Select(v => v.ToString()))) as IEnumerable<MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor.EditorEntityReference>;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(3, toEditor.Count());
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[0]).Type, toEditor.First().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[0]).Unique, toEditor.First().Unique);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[1]).Type, toEditor.Skip(1).First().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[1]).Unique, toEditor.Skip(1).First().Unique);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[2]).Type, toEditor.Last().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(values[2]).Unique, toEditor.Last().Unique);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Skip_Invalid_Values_To_Editor()
|
||||
{
|
||||
var firstGuid = new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid());
|
||||
var secondGuid = new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid());
|
||||
var values = new[]
|
||||
{
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString(),
|
||||
firstGuid.ToString(),
|
||||
"Invalid Value",
|
||||
new GuidUdi(Constants.UdiEntityType.Document, Guid.NewGuid()).ToString()
|
||||
secondGuid.ToString(),
|
||||
};
|
||||
var toEditor = ToEditor(string.Join(",", values)) as IEnumerable<string>;
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(2, toEditor.Count());
|
||||
Assert.AreEqual(values[0], toEditor.First());
|
||||
Assert.AreEqual(values[2], toEditor.Last());
|
||||
var toEditor = ToEditor(string.Join(",", values)) as IEnumerable<MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor.EditorEntityReference>;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.IsNotNull(toEditor);
|
||||
Assert.AreEqual(2, toEditor.Count());
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(firstGuid).Type, toEditor.First().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(firstGuid).Unique, toEditor.First().Unique);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(secondGuid).Type, toEditor.Last().Type);
|
||||
Assert.AreEqual(EditorEntityReferenceFromUdi(secondGuid).Unique, toEditor.Last().Unique);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -188,26 +227,36 @@ public class MultiNodeTreePickerTests
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
private static object? FromEditor(object? value, int max = 0)
|
||||
=> CreateValueEditor().FromEditor(new ContentPropertyData(value, new MultipleTextStringConfiguration { Max = max }), null);
|
||||
private static object? FromEditor(object? value, int max = 0, IJsonSerializer? jsonSerializer = null)
|
||||
=> CreateValueEditor(jsonSerializer)
|
||||
.FromEditor(new ContentPropertyData(value, new MultipleTextStringConfiguration { Max = max }), null);
|
||||
|
||||
private static object? ToEditor(object? value)
|
||||
private static object? ToEditor(object? value, IJsonSerializer? jsonSerializer = null)
|
||||
{
|
||||
var property = new Mock<IProperty>();
|
||||
property
|
||||
.Setup(p => p.GetValue(It.IsAny<string?>(), It.IsAny<string?>(), It.IsAny<bool>()))
|
||||
.Returns(value);
|
||||
|
||||
return CreateValueEditor().ToEditor(property.Object);
|
||||
return CreateValueEditor(jsonSerializer).ToEditor(property.Object);
|
||||
}
|
||||
|
||||
private static MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor CreateValueEditor()
|
||||
private static MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor CreateValueEditor(
|
||||
IJsonSerializer? jsonSerializer = null)
|
||||
{
|
||||
var valueEditor = new MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor(
|
||||
Mock.Of<IShortStringHelper>(),
|
||||
Mock.Of<IJsonSerializer>(),
|
||||
jsonSerializer ?? Mock.Of<IJsonSerializer>(),
|
||||
Mock.Of<IIOHelper>(),
|
||||
new DataEditorAttribute("alias"));
|
||||
return valueEditor;
|
||||
}
|
||||
|
||||
private static MultiNodeTreePickerPropertyEditor.MultiNodeTreePickerPropertyValueEditor.EditorEntityReference
|
||||
EditorEntityReferenceFromUdi(GuidUdi udi) =>
|
||||
new()
|
||||
{
|
||||
Type = udi.EntityType,
|
||||
Unique = udi.Guid,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user