Skip to content

Commit 596d3c4

Browse files
authored
Merge pull request #10 from adobe/TNT-40079
TNT-40079 [ODD] Implement integration tests for decision provider
2 parents ecae3a2 + f7876f0 commit 596d3c4

File tree

56 files changed

+11330
-74
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+11330
-74
lines changed

Source/Adobe.Target.Client/Model/OnDevice/OnDeviceDecisioningRule.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ namespace Adobe.Target.Client.Model.OnDevice
1212
{
1313
using System.Collections.Generic;
1414
using Newtonsoft.Json;
15+
using Newtonsoft.Json.Linq;
1516

1617
internal sealed class OnDeviceDecisioningRule
1718
{
1819
[JsonConstructor]
19-
internal OnDeviceDecisioningRule(string ruleKey, string activityId, IReadOnlyList<string> propertyTokens, object condition, IReadOnlyDictionary<string, object> consequence, IReadOnlyDictionary<string, object> meta)
20+
internal OnDeviceDecisioningRule(string ruleKey, string activityId, IReadOnlyList<string> propertyTokens, JToken condition, IReadOnlyDictionary<string, object> consequence, IReadOnlyDictionary<string, object> meta)
2021
{
2122
this.RuleKey = ruleKey;
2223
this.ActivityId = activityId;
@@ -32,7 +33,7 @@ internal OnDeviceDecisioningRule(string ruleKey, string activityId, IReadOnlyLis
3233

3334
internal IReadOnlyList<string> PropertyTokens { get; }
3435

35-
internal object Condition { get; }
36+
internal JToken Condition { get; }
3637

3738
internal IReadOnlyDictionary<string, object> Consequence { get; }
3839

Source/Adobe.Target.Client/OnDevice/CampaignMacroReplacer.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ namespace Adobe.Target.Client.OnDevice
1717
using Adobe.Target.Client.Model.OnDevice;
1818
using Adobe.Target.Delivery.Model;
1919
using Newtonsoft.Json.Linq;
20+
using Action = Adobe.Target.Delivery.Model.Action;
2021

2122
internal sealed class CampaignMacroReplacer
2223
{
23-
private const string Content = "content";
2424
private const string Name = "name";
2525
private const string Index = "index";
2626
private const string MacroPatternRegexString = @"\$\{([a-zA-Z0-9_.]*?)\}";
@@ -84,6 +84,11 @@ internal IList<Option> GetOptions()
8484

8585
private static string SanitizedMacroKey(string macroKey)
8686
{
87+
if (macroKey == "mbox.name")
88+
{
89+
macroKey = "location.name";
90+
}
91+
8792
foreach (var replacement in MacroNameReplacements)
8893
{
8994
macroKey = macroKey.Replace(replacement.Key, replacement.Value);
@@ -103,15 +108,15 @@ private static string SanitizedMacroKey(string macroKey)
103108
return string.Join(".", keySegments);
104109
}
105110

106-
private bool GetActionsContent(object actionsObject, out IList<IDictionary<string, object>> result)
111+
private bool GetActionsContent(object actionsObject, out IList<Action> result)
107112
{
108113
if (actionsObject is not JArray actionsContent)
109114
{
110115
result = null;
111116
return false;
112117
}
113118

114-
var actions = actionsContent.ToObject<IList<IDictionary<string, object>>>();
119+
var actions = actionsContent.ToObject<IList<Action>>();
115120
if (actions == null)
116121
{
117122
result = null;
@@ -120,9 +125,9 @@ private bool GetActionsContent(object actionsObject, out IList<IDictionary<strin
120125

121126
foreach (var action in actions)
122127
{
123-
if (action[Content] is string actionContent)
128+
if (action.Content is string actionContent)
124129
{
125-
action[Content] = this.AddCampaignMacroValues(actionContent);
130+
action.Content = this.AddCampaignMacroValues(actionContent);
126131
}
127132
}
128133

@@ -134,8 +139,9 @@ private string AddCampaignMacroValues(string content)
134139
{
135140
return MacroPatternRegex.Replace(content, match =>
136141
{
137-
var macroKey = SanitizedMacroKey(match.Value);
138-
return this.GetMacroValue(macroKey, "${" + match.Value + "}");
142+
var matchValue = match.Groups[1].Value;
143+
var macroKey = SanitizedMacroKey(matchValue);
144+
return this.GetMacroValue(macroKey, "${" + matchValue + "}");
139145
});
140146
}
141147

Source/Adobe.Target.Client/OnDevice/ClusterLocator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ internal sealed class ClusterLocator
2323
{
2424
private volatile string? locationHint;
2525

26-
internal ClusterLocator(TargetClientConfig clientConfig, TargetService targetService)
26+
internal ClusterLocator(TargetClientConfig clientConfig, ITargetService targetService)
2727
{
2828
if (clientConfig.DecisioningMethod == DecisioningMethod.ServerSide)
2929
{
@@ -38,12 +38,12 @@ internal ClusterLocator(TargetClientConfig clientConfig, TargetService targetSer
3838
return this.locationHint;
3939
}
4040

41-
private void FetchLocation(TargetService targetService)
41+
private void FetchLocation(ITargetService targetService)
4242
{
4343
_ = this.FetchLocationAsync(targetService);
4444
}
4545

46-
private async Task FetchLocationAsync(TargetService targetService)
46+
private async Task FetchLocationAsync(ITargetService targetService)
4747
{
4848
var request = new TargetDeliveryRequest.Builder()
4949
.SetContext(new Context(ChannelType.Web))

Source/Adobe.Target.Client/OnDevice/Collator/GeoParamsCollator.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Adobe.Target.Client.OnDevice.Collator
1212
{
1313
using System.Collections.Generic;
14+
using Adobe.Target.Client.Extension;
1415
using Adobe.Target.Client.Model;
1516
using Adobe.Target.Delivery.Model;
1617

@@ -22,13 +23,23 @@ internal sealed class GeoParamsCollator : IParamsCollator
2223
internal const string GeoRegion = "region";
2324
internal const string GeoCountry = "country";
2425

26+
internal static readonly IDictionary<string, object> DefaultGeoParams = new Dictionary<string, object>
27+
{
28+
{ GeoLatitude, 0f },
29+
{ GeoLongitude, 0f },
30+
{ GeoCity, string.Empty },
31+
{ GeoRegion, string.Empty },
32+
{ GeoCountry, string.Empty },
33+
};
34+
2535
public Dictionary<string, object> CollateParams(TargetDeliveryRequest deliveryRequest = default, RequestDetails requestDetails = default)
2636
{
2737
var result = new Dictionary<string, object>();
2838
var geo = deliveryRequest?.DeliveryRequest?.Context?.Geo;
2939
if (geo == null)
3040
{
31-
return GetBlankGeoParams(result);
41+
result.AddAll(DefaultGeoParams);
42+
return result;
3243
}
3344

3445
result.Add(GeoLatitude, geo.Latitude);
@@ -39,16 +50,5 @@ public Dictionary<string, object> CollateParams(TargetDeliveryRequest deliveryRe
3950

4051
return result;
4152
}
42-
43-
private static Dictionary<string, object> GetBlankGeoParams(Dictionary<string, object> result)
44-
{
45-
result.Add(GeoLatitude, 0f);
46-
result.Add(GeoLongitude, 0f);
47-
result.Add(GeoCity, string.Empty);
48-
result.Add(GeoRegion, string.Empty);
49-
result.Add(GeoCountry, string.Empty);
50-
51-
return result;
52-
}
5353
}
5454
}

Source/Adobe.Target.Client/OnDevice/DecisioningDetailsExecutor.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace Adobe.Target.Client.OnDevice
1515
using System.Linq;
1616
using Adobe.Target.Client.Model;
1717
using Adobe.Target.Client.Model.OnDevice;
18+
using Adobe.Target.Client.Util;
1819
using Adobe.Target.Delivery.Model;
1920
using Newtonsoft.Json.Linq;
2021

@@ -77,7 +78,7 @@ public void ExecuteDetails(
7778

7879
if (details.Match(
7980
_ => false,
80-
mboxRequest => true,
81+
_ => true,
8182
_ => false))
8283
{
8384
break;
@@ -196,6 +197,7 @@ private static bool HandleMboxRequest(
196197
option.EventToken = null;
197198
return option;
198199
})
200+
.Where(option => option.Type != null || option.Content != null)
199201
.ToList(),
200202
};
201203
executeResponse.Mboxes ??= new List<MboxResponse>();
@@ -235,6 +237,11 @@ private static bool HandlePageLoad(
235237
option.EventToken = null;
236238
}
237239

240+
if (option.Type == null && option.Content == null && option.EventToken == null)
241+
{
242+
continue;
243+
}
244+
238245
pageLoad.Options ??= new List<Option>();
239246
pageLoad.Options.Add(option);
240247
}
@@ -259,7 +266,8 @@ private static Notification CreateNotification(RequestDetailsUnion details, List
259266
{
260267
var id = Guid.NewGuid().ToString();
261268
var impressionId = Guid.NewGuid().ToString();
262-
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
269+
var dateTime = TimeProvider.Current.UtcNow;
270+
var timestamp = new DateTimeOffset(dateTime).ToUnixTimeMilliseconds();
263271
var tokens = options == null ? new List<string>() : options.Select(option => option.EventToken).ToList();
264272
var notification = new Notification(id: id, type: MetricType.Display, impressionId: impressionId, timestamp: timestamp, tokens: tokens);
265273
return details.Match(
@@ -280,7 +288,7 @@ private static Notification CreateNotification(RequestDetailsUnion details, List
280288

281289
private static List<Metric> GetMetrics(object metricsObject)
282290
{
283-
return metricsObject is not JObject metricsJObject ? null : metricsJObject.ToObject<List<Metric>>();
291+
return metricsObject is not JArray metricsArray || metricsArray.Count == 0 ? null : metricsArray.ToObject<List<Metric>>();
284292
}
285293

286294
private static void UnhandledResponse(RequestDetailsUnion details, PrefetchResponse prefetchResponse, ExecuteResponse executeResponse)
@@ -330,11 +338,16 @@ private static object UnhandledPageLoadResponse(PrefetchResponse prefetchRespons
330338

331339
private static bool PropertyTokenMismatch(IReadOnlyList<string> rulePropertyTokens, string propertyToken)
332340
{
333-
if (string.IsNullOrEmpty(propertyToken) || rulePropertyTokens == null || rulePropertyTokens.Count == 0)
341+
if (rulePropertyTokens == null || rulePropertyTokens.Count == 0)
334342
{
335343
return false;
336344
}
337345

346+
if (string.IsNullOrEmpty(propertyToken))
347+
{
348+
return true;
349+
}
350+
338351
return !rulePropertyTokens.Contains(propertyToken);
339352
}
340353

Source/Adobe.Target.Client/OnDevice/DecisioningRuleExecutor.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ public IDictionary<string, object> ExecuteRule(
4646
{
4747
localContext.Remove(Allocation);
4848
localContext.Add(Allocation, this.ComputeAllocation(visitorId, rule));
49-
var matched = this.EvaluateRule((JObject)rule.Condition, localContext.ToExpandoObject());
49+
50+
var matched = this.EvaluateRule(rule.Condition, localContext.ToExpandoObject());
5051
if (!matched)
5152
{
5253
return null;
@@ -67,7 +68,7 @@ private static IDictionary<string, object> GetGeoResponseTokens(IDictionary<stri
6768
{
6869
var tokenKey = GetGeoTokenKey(geo);
6970

70-
if (responseTokenKeys.Contains(tokenKey))
71+
if (responseTokenKeys.Contains(tokenKey) && geo.Value != GeoParamsCollator.DefaultGeoParams[geo.Key])
7172
{
7273
result.Add(tokenKey, geo.Value);
7374
}

Source/Adobe.Target.Client/OnDevice/GeoClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Adobe.Target.Client.OnDevice
1515
using Adobe.Target.Delivery.Model;
1616
using RestSharp;
1717

18-
internal sealed class GeoClient
18+
internal sealed class GeoClient : IGeoClient
1919
{
2020
private const string GeoPath = "/v1/geo";
2121
private const string GeoIpHeader = "x-forwarded-for";
@@ -36,7 +36,7 @@ internal GeoClient(TargetClientConfig clientConfig)
3636
};
3737
}
3838

39-
internal async Task<Geo> LookupGeoAsync(Geo geo)
39+
public async Task<Geo> LookupGeoAsync(Geo geo)
4040
{
4141
if (!this.ValidateGeo(geo))
4242
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace Adobe.Target.Client.OnDevice
2+
{
3+
using System.Threading.Tasks;
4+
using Adobe.Target.Delivery.Model;
5+
6+
/// <summary>
7+
/// Geo Client
8+
/// </summary>
9+
public interface IGeoClient
10+
{
11+
/// <summary>
12+
/// Look up Geo data
13+
/// </summary>
14+
/// <param name="geo">Geo with IP address</param>
15+
/// <returns>Geo with location data</returns>
16+
Task<Geo> LookupGeoAsync(Geo geo);
17+
}
18+
}

Source/Adobe.Target.Client/OnDevice/RuleLoader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ internal RuleLoader(TargetClientConfig clientConfig)
6161
this.timerStartDelayMs = clientConfig.OnDeviceDecisioningPollingIntSecs * 1000;
6262
this.logger = TargetClient.Logger;
6363
this.SetLocalRules();
64+
if (clientConfig.LocalArtifactOnly)
65+
{
66+
return;
67+
}
68+
6469
this.ScheduleTimer(0);
6570
}
6671

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2021 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
* Unless required by applicable law or agreed to in writing, software distributed under
7+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
8+
* OF ANY KIND, either express or implied. See the License for the specific language
9+
* governing permissions and limitations under the License.
10+
*/
11+
namespace Adobe.Target.Client.Service
12+
{
13+
using System.Threading.Tasks;
14+
using Adobe.Target.Client.Model;
15+
16+
/// <summary>
17+
/// Target Service
18+
/// </summary>
19+
public interface ITargetService
20+
{
21+
/// <summary>
22+
/// Execute sync Delivery request
23+
/// </summary>
24+
/// <param name="request">Target Delivery request</param>
25+
/// <returns>Target Delivery Response</returns>
26+
TargetDeliveryResponse ExecuteRequest(TargetDeliveryRequest request);
27+
28+
/// <summary>
29+
/// Execute async Delivery request
30+
/// </summary>
31+
/// <param name="request">Target Delivery request</param>
32+
/// <returns>Target Delivery Response</returns>
33+
Task<TargetDeliveryResponse> ExecuteRequestAsync(TargetDeliveryRequest request);
34+
}
35+
}

0 commit comments

Comments
 (0)