Skip to content

Commit 6f84176

Browse files
author
Claus
committed
Merge branch 'umco-feature/tuple-connector' into dev
2 parents 3e5e8b9 + 546cae7 commit 6f84176

File tree

5 files changed

+310
-0
lines changed

5 files changed

+310
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This project offers Umbraco Deploy connectors for the following community packag
2121
- [Archetype](https://our.umbraco.org/projects/backoffice-extensions/archetype/)
2222
- [Content List](https://github.com/umco/umbraco-content-list)
2323
- [Property List](https://github.com/umco/umbraco-property-list)
24+
- [Tuple](https://github.com/umco/umbraco-tuple)
2425

2526
---
2627

src/Umbraco.Deploy.Contrib.Connectors/Umbraco.Deploy.Contrib.Connectors.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@
244244
<Compile Include="ValueConnectors\RelatedLinksValueConnector.cs" />
245245
<Compile Include="ValueConnectors\RelatedLinks2ValueConnector.cs" />
246246
<Compile Include="ValueConnectors\ContentListConnector.cs" />
247+
<Compile Include="ValueConnectors\TupleValueConnector.cs" />
247248
<Compile Include="ValueConnectors\VortoValueConnector.cs" />
248249
<Compile Include="ValueConnectors\InnerContentConnector.cs" />
249250
<Compile Include="ValueConnectors\StackedContentConnector.cs" />
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
4+
using Newtonsoft.Json.Linq;
5+
using Umbraco.Core;
6+
using Umbraco.Core.Deploy;
7+
using Umbraco.Core.Logging;
8+
using Umbraco.Core.Models;
9+
using Umbraco.Core.Services;
10+
using Umbraco.Deploy.ValueConnectors;
11+
12+
namespace Umbraco.Deploy.Contrib.Connectors.ValueConnectors
13+
{
14+
public class TupleValueConnector : IValueConnector
15+
{
16+
private readonly IDataTypeService _dataTypeService;
17+
private readonly Lazy<ValueConnectorCollection> _valueConnectorsLazy;
18+
19+
public TupleValueConnector(IDataTypeService dataTypeService, Lazy<ValueConnectorCollection> valueConnectors)
20+
{
21+
Mandate.ParameterNotNull(dataTypeService, nameof(dataTypeService));
22+
Mandate.ParameterNotNull(valueConnectors, nameof(valueConnectors));
23+
24+
_dataTypeService = dataTypeService;
25+
_valueConnectorsLazy = valueConnectors;
26+
}
27+
28+
public IEnumerable<string> PropertyEditorAliases => new[] { "Our.Umbraco.Tuple" };
29+
30+
private ValueConnectorCollection ValueConnectors => _valueConnectorsLazy.Value;
31+
32+
public string GetValue(Property property, ICollection<ArtifactDependency> dependencies)
33+
{
34+
// get the property value
35+
var value = property.Value?.ToString();
36+
if (string.IsNullOrWhiteSpace(value))
37+
return null;
38+
39+
// deserialize it
40+
var items = JsonConvert.DeserializeObject<List<TupleValueItem>>(value);
41+
if (items == null || items.Count == 0)
42+
return null;
43+
44+
// loop through each value
45+
foreach (var item in items)
46+
{
47+
// get the selected data-type (and ensure it exists)
48+
var dataType = _dataTypeService.GetDataTypeDefinitionById(item.DataTypeGuid);
49+
50+
if (dataType == null)
51+
{
52+
LogHelper.Warn<TupleValueConnector>($"Could not resolve the data-type used by the Property List value for: {property.Alias}, with GUID: {item.DataTypeGuid}");
53+
continue;
54+
}
55+
56+
// add the selected data-type as a dependency
57+
dependencies.Add(new ArtifactDependency(dataType.GetUdi(), false, ArtifactDependencyMode.Match));
58+
59+
// make a property-type to use in a mocked Property
60+
// and get the value-connector needed to parse values (outside the loop, as it's the same for all iterations)
61+
var propertyType = new PropertyType(dataType);
62+
var valueConnector = ValueConnectors.Get(propertyType);
63+
64+
item.Value = valueConnector.GetValue(new Property(propertyType, item.Value), dependencies);
65+
}
66+
67+
return JsonConvert.SerializeObject(items);
68+
}
69+
70+
public void SetValue(IContentBase content, string alias, string value)
71+
{
72+
// take the value
73+
if (string.IsNullOrWhiteSpace(value))
74+
return;
75+
76+
// deserialize it
77+
var items = JsonConvert.DeserializeObject<List<TupleValueItem>>(value);
78+
if (items == null || items.Count == 0)
79+
return;
80+
81+
// loop through each value
82+
foreach (var item in items)
83+
{
84+
// get the selected data-type (and ensure it exists)
85+
var dataType = _dataTypeService.GetDataTypeDefinitionById(item.DataTypeGuid);
86+
87+
if (dataType == null)
88+
{
89+
LogHelper.Warn<TupleValueConnector>($"Could not resolve the data-type used by the Tuple item for: {alias}, with GUID: {item.DataTypeGuid}");
90+
continue;
91+
}
92+
93+
// make a property-type to use in a mocked Property
94+
// and get the value-connector needed to parse values (outside the loop, as it's the same for all iterations)
95+
var propertyType = new PropertyType(dataType, "mockTupleAlias");
96+
var valueConnector = ValueConnectors.Get(propertyType);
97+
98+
var mockProperty = new Property(propertyType);
99+
var mockContent = new Content("mockContent", -1, new ContentType(-1), new PropertyCollection(new List<Property> { mockProperty }));
100+
101+
// pass it to its own value-connector
102+
// NOTE: due to how ValueConnector.SetValue() works, we have to pass the mock item
103+
// through to the connector to have it do its work on parsing the value on the item itself.
104+
valueConnector.SetValue(mockContent, mockProperty.Alias, item.Value?.ToString());
105+
106+
// get the value back and assign
107+
item.Value = mockContent.GetValue(mockProperty.Alias);
108+
}
109+
110+
// serialize the JSON values
111+
content.SetValue(alias, JArray.FromObject(items).ToString(Formatting.None));
112+
}
113+
114+
public class TupleValueItem
115+
{
116+
[JsonProperty("key")]
117+
public Guid Key { get; set; }
118+
119+
[JsonProperty("dtd")]
120+
public Guid DataTypeGuid { get; set; }
121+
122+
[JsonProperty("value")]
123+
public object Value { get; set; }
124+
}
125+
}
126+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Moq;
5+
using NUnit.Framework;
6+
using Umbraco.Core;
7+
using Umbraco.Core.Configuration;
8+
using Umbraco.Core.Configuration.UmbracoSettings;
9+
using Umbraco.Core.Deploy;
10+
using Umbraco.Core.Models;
11+
using Umbraco.Core.Services;
12+
using Umbraco.Deploy.Contrib.Connectors.ValueConnectors;
13+
using Umbraco.Deploy.ValueConnectors;
14+
15+
namespace Umbraco.Deploy.Contrib.Tests.Connectors
16+
{
17+
[TestFixture]
18+
public class TupleValueConnectorTests
19+
{
20+
[Test]
21+
public void GetValueTest()
22+
{
23+
var dataTypeService = Mock.Of<IDataTypeService>();
24+
25+
var tupleDataType = new DataTypeDefinition("tupleEditorAlias")
26+
{
27+
Id = 1,
28+
Key = Guid.Parse("3BBFB03B-B6AD-4310-986B-44E9F31A4F1F"),
29+
DatabaseType = DataTypeDatabaseType.Ntext
30+
};
31+
32+
var innerDataType = new DataTypeDefinition("innerEditorAlias")
33+
{
34+
Id = 2,
35+
Key = Guid.Parse("0F6DCC71-2FA7-496B-A858-8D6DDAF37F59"),
36+
DatabaseType = DataTypeDatabaseType.Integer // for true/false
37+
};
38+
39+
var dataTypes = new[] { tupleDataType, innerDataType };
40+
41+
Mock.Get(dataTypeService)
42+
.Setup(x => x.GetDataTypeDefinitionById(It.IsAny<Guid>()))
43+
.Returns<Guid>(id => dataTypes.FirstOrDefault(x => x.Key == id));
44+
45+
var preValues = new Dictionary<int, PreValueCollection>
46+
{
47+
{ 1, new PreValueCollection(new Dictionary<string, PreValue> { { "dataTypes", new PreValue( $"[{{\"key\":\"{Guid.Empty}\",\"dtd\":\"{innerDataType.Key}\"}}]") } }) }
48+
};
49+
50+
Mock.Get(dataTypeService)
51+
.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
52+
.Returns<int>(id => preValues.TryGetValue(id, out var collection) ? collection : null);
53+
54+
ValueConnectorCollection connectors = null;
55+
var defaultConnector = new DefaultValueConnector();
56+
var tupleConnector = new TupleValueConnector(dataTypeService, new Lazy<ValueConnectorCollection>(() => connectors));
57+
connectors = new ValueConnectorCollection(new Dictionary<string, IValueConnector>
58+
{
59+
{ "innerEditorAlias", defaultConnector },
60+
{ "tupleEditorAlias", tupleConnector }
61+
});
62+
63+
var input = $"[{{\"key\":\"{Guid.Empty}\",\"dtd\":\"{innerDataType.Key}\",\"value\":0}}]";
64+
65+
var propertyType = new PropertyType(tupleDataType);
66+
var property = new Property(propertyType, input);
67+
var dependencies = new List<ArtifactDependency>();
68+
var output = tupleConnector.GetValue(property, dependencies);
69+
70+
Console.WriteLine(output);
71+
72+
var expected = $"[{{\"key\":\"{Guid.Empty}\",\"dtd\":\"{innerDataType.Key}\",\"value\":\"i0\"}}]";
73+
Assert.AreEqual(expected, output);
74+
}
75+
76+
[Test]
77+
public void SetValueTest()
78+
{
79+
var dataTypeService = Mock.Of<IDataTypeService>();
80+
81+
var tupleDataType = new DataTypeDefinition("tupleEditorAlias")
82+
{
83+
Id = 1,
84+
Key = Guid.Parse("3BBFB03B-B6AD-4310-986B-44E9F31A4F1F"),
85+
DatabaseType = DataTypeDatabaseType.Ntext
86+
};
87+
88+
var innerDataType = new DataTypeDefinition("innerEditorAlias")
89+
{
90+
Id = 2,
91+
Key = Guid.Parse("0F6DCC71-2FA7-496B-A858-8D6DDAF37F59"),
92+
DatabaseType = DataTypeDatabaseType.Integer // for true/false
93+
};
94+
95+
var dataTypes = new[] { tupleDataType, innerDataType };
96+
97+
Mock.Get(dataTypeService)
98+
.Setup(x => x.GetDataTypeDefinitionById(It.IsAny<Guid>()))
99+
.Returns<Guid>(id => dataTypes.FirstOrDefault(x => x.Key == id));
100+
101+
var preValues = new Dictionary<int, PreValueCollection>
102+
{
103+
{ 1, new PreValueCollection(new Dictionary<string, PreValue> { { "dataTypes", new PreValue( $"[{{\"key\":\"{Guid.Empty}\",\"dtd\":\"{innerDataType.Key}\"}}]") } }) }
104+
};
105+
106+
Mock.Get(dataTypeService)
107+
.Setup(x => x.GetPreValuesCollectionByDataTypeId(It.IsAny<int>()))
108+
.Returns<int>(id => preValues.TryGetValue(id, out var collection) ? collection : null);
109+
110+
ValueConnectorCollection connectors = null;
111+
var defaultConnector = new DefaultValueConnector();
112+
var tupleConnector = new TupleValueConnector(dataTypeService, new Lazy<ValueConnectorCollection>(() => connectors));
113+
connectors = new ValueConnectorCollection(new Dictionary<string, IValueConnector>
114+
{
115+
{ "innerEditorAlias", defaultConnector },
116+
{ "tupleEditorAlias", tupleConnector }
117+
});
118+
119+
var input = $"[{{\"key\":\"{Guid.Empty}\",\"dtd\":\"{innerDataType.Key}\",\"value\":\"i0\"}}]";
120+
121+
UmbracoConfig.For.SetUmbracoSettings(GenerateMockSettings());
122+
123+
var tuplePropertyType = new PropertyType(tupleDataType, "tupleProperty");
124+
var tupleProperty = new Property(tuplePropertyType, null); // value is going to be replaced
125+
var tupleContent = new Content("mockContent", -1, new ContentType(-1), new PropertyCollection(new List<Property> { tupleProperty }));
126+
tupleConnector.SetValue(tupleContent, "tupleProperty", input);
127+
128+
var output = tupleContent.GetValue("tupleProperty");
129+
130+
Assert.IsInstanceOf<string>(output);
131+
132+
Console.WriteLine(output);
133+
134+
var expected = $"[{{\"key\":\"{Guid.Empty}\",\"dtd\":\"{innerDataType.Key}\",\"value\":0}}]";
135+
Assert.AreEqual(expected, output);
136+
}
137+
138+
public static IUmbracoSettingsSection GenerateMockSettings()
139+
{
140+
var settings = new Mock<IUmbracoSettingsSection>();
141+
142+
var content = new Mock<IContentSection>();
143+
var security = new Mock<ISecuritySection>();
144+
var requestHandler = new Mock<IRequestHandlerSection>();
145+
var templates = new Mock<ITemplatesSection>();
146+
var dev = new Mock<IDeveloperSection>();
147+
var logging = new Mock<ILoggingSection>();
148+
var tasks = new Mock<IScheduledTasksSection>();
149+
var distCall = new Mock<IDistributedCallSection>();
150+
var repos = new Mock<IRepositoriesSection>();
151+
var providers = new Mock<IProvidersSection>();
152+
var routing = new Mock<IWebRoutingSection>();
153+
154+
settings.Setup(x => x.Content).Returns(content.Object);
155+
settings.Setup(x => x.Security).Returns(security.Object);
156+
settings.Setup(x => x.RequestHandler).Returns(requestHandler.Object);
157+
settings.Setup(x => x.Templates).Returns(templates.Object);
158+
settings.Setup(x => x.Developer).Returns(dev.Object);
159+
settings.Setup(x => x.Logging).Returns(logging.Object);
160+
settings.Setup(x => x.ScheduledTasks).Returns(tasks.Object);
161+
settings.Setup(x => x.DistributedCall).Returns(distCall.Object);
162+
settings.Setup(x => x.PackageRepositories).Returns(repos.Object);
163+
settings.Setup(x => x.Providers).Returns(providers.Object);
164+
settings.Setup(x => x.WebRouting).Returns(routing.Object);
165+
166+
//Now configure some defaults - the defaults in the config section classes do NOT pertain to the mocked data!!
167+
settings.Setup(x => x.Content.ForceSafeAliases).Returns(true);
168+
//settings.Setup(x => x.Content.ImageAutoFillProperties).Returns(ContentImagingElement.GetDefaultImageAutoFillProperties());
169+
//settings.Setup(x => x.Content.ImageFileTypes).Returns(ContentImagingElement.GetDefaultImageFileTypes());
170+
settings.Setup(x => x.RequestHandler.AddTrailingSlash).Returns(true);
171+
settings.Setup(x => x.RequestHandler.UseDomainPrefixes).Returns(false);
172+
//settings.Setup(x => x.RequestHandler.CharCollection).Returns(RequestHandlerElement.GetDefaultCharReplacements());
173+
settings.Setup(x => x.Content.UmbracoLibraryCacheDuration).Returns(1800);
174+
settings.Setup(x => x.WebRouting.UrlProviderMode).Returns("AutoLegacy");
175+
settings.Setup(x => x.Templates.DefaultRenderingEngine).Returns(RenderingEngine.Mvc);
176+
settings.Setup(x => x.Providers.DefaultBackOfficeUserProvider).Returns("UsersMembershipProvider");
177+
178+
return settings.Object;
179+
}
180+
}
181+
}

src/Umbraco.Deploy.Contrib.Tests/Umbraco.Deploy.Contrib.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@
261261
<Compile Include="Connectors\NestedContentValueConnectorTests.cs" />
262262
<Compile Include="Connectors\UrlPickerValueConnectorTests.cs" />
263263
<Compile Include="Connectors\PropertyListValueConnectorTests.cs" />
264+
<Compile Include="Connectors\TupleValueConnectorTests.cs" />
264265
<Compile Include="Connectors\VortoValueConnectorTests.cs" />
265266
<Compile Include="Properties\AssemblyInfo.cs" />
266267
<Compile Include="TestHelpers\MemoryFileTypeCollection.cs" />

0 commit comments

Comments
 (0)