Skip to content

Commit 54470ff

Browse files
authored
Add host parameter to AdaptiveCards.Templating (#8328)
* Add host parameter to AdaptiveCards.Templating * .sln cleanup * Remove net6.0 TFM
1 parent ee62bd3 commit 54470ff

File tree

8 files changed

+395
-63
lines changed

8 files changed

+395
-63
lines changed

source/dotnet/AdaptiveCards.sln

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Rendering.Wpf
1717
EndProject
1818
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Rendering.Wpf.Xceed", "Library\AdaptiveCards.Rendering.Wpf.Xceed\AdaptiveCards.Rendering.Wpf.Xceed.csproj", "{4741ABEC-33B0-424F-B5F1-464EC31AEEBD}"
1919
EndProject
20-
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Sample.Html", "Samples\AdaptiveCards.Sample.Html\AdaptiveCards.Sample.Html.csproj", "{C015DC5F-E523-4828-8E46-118A8242B51A}"
21-
EndProject
2220
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Test", "Test\AdaptiveCards.Test\AdaptiveCards.Test.csproj", "{4DB2C1D1-630A-4445-95F3-4E342ABD9342}"
2321
EndProject
2422
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Sample.ImageRender", "samples\AdaptiveCards.Sample.ImageRender\AdaptiveCards.Sample.ImageRender.csproj", "{BCFC1329-903B-4440-ABE1-160C22A3AE23}"
@@ -31,8 +29,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Templating",
3129
EndProject
3230
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdaptiveCards.Templating.Test", "Test\AdaptiveCards.Templating.Test\AdaptiveCards.Templating.Test.csproj", "{9E7E5953-A5B4-4867-9A06-046A754AFAC1}"
3331
EndProject
34-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A751CC8F-EF13-4EA1-B9FD-611135AD7C09}"
35-
EndProject
3632
Global
3733
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3834
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU

source/dotnet/Library/AdaptiveCards.Templating/AdaptiveCardTemplate.cs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,33 @@ public string Expand(EvaluationContext context, Func<string, object> nullSubstit
9696
return jsonTemplateString;
9797
}
9898

99-
string jsonData = "";
99+
string rootJsonData = "";
100+
if (context?.Root != null)
101+
{
102+
if (context.Root is string root)
103+
{
104+
rootJsonData = root;
105+
}
106+
else
107+
{
108+
rootJsonData = JsonConvert.SerializeObject(context.Root);
109+
}
110+
}
100111

101-
if (context != null && context.Root != null)
112+
string hostJsonData = "";
113+
if (context?.Host != null)
102114
{
103-
if (context.Root is string)
115+
if (context.Host is string host)
104116
{
105-
jsonData = context.Root as string;
117+
hostJsonData = host;
106118
}
107119
else
108120
{
109-
jsonData = JsonConvert.SerializeObject(context.Root);
121+
hostJsonData = JsonConvert.SerializeObject(context.Host);
110122
}
111123
}
112124

113-
AdaptiveCardsTemplateVisitor eval = new AdaptiveCardsTemplateVisitor(nullSubstitutionOption, jsonData);
125+
AdaptiveCardsTemplateVisitor eval = new AdaptiveCardsTemplateVisitor(nullSubstitutionOption, rootJsonData, hostJsonData);
114126
AdaptiveCardsTemplateResult result = eval.Visit(parseTree);
115127

116128
templateExpansionWarnings = eval.getTemplateVisitorWarnings();

source/dotnet/Library/AdaptiveCards.Templating/AdaptiveCards.Templating.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33

4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFrameworks>netstandard2.0</TargetFrameworks>
55
<VersionPrefix>1.0.0</VersionPrefix>
66
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
77
<Authors>Microsoft</Authors>

source/dotnet/Library/AdaptiveCards.Templating/AdaptiveCardsTemplateVisitor.cs

Lines changed: 56 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public sealed class AdaptiveCardsTemplateVisitor : AdaptiveCardsTemplateParserBa
2323
{
2424
private Stack<DataContext> dataContext = new Stack<DataContext>();
2525
private readonly JToken root;
26+
private readonly JToken host;
2627
private readonly Options options;
2728
private ArrayList templateVisitorWarnings;
2829

@@ -33,10 +34,12 @@ private sealed class DataContext
3334
{
3435
public JToken token;
3536
public AdaptiveCardsTemplateSimpleObjectMemory AELMemory;
36-
public bool IsArrayType = false;
37+
public bool IsArrayType;
3738

3839
public JToken RootDataContext;
40+
public JToken HostDataContext;
3941
public const string rootKeyword = "$root";
42+
public const string hostKeyword = "$host";
4043
public const string dataKeyword = "$data";
4144
public const string indexKeyword = "$index";
4245

@@ -45,9 +48,10 @@ private sealed class DataContext
4548
/// </summary>
4649
/// <param name="jtoken">new data to kept as data context</param>
4750
/// <param name="rootDataContext">root data context</param>
48-
public DataContext(JToken jtoken, JToken rootDataContext)
51+
/// <param name="hostDataContext">optional host data context</param>
52+
public DataContext(JToken jtoken, JToken rootDataContext, JToken hostDataContext = null)
4953
{
50-
Init(jtoken, rootDataContext);
54+
Init(jtoken, rootDataContext, hostDataContext);
5155
}
5256

5357
/// <summary>
@@ -56,25 +60,30 @@ public DataContext(JToken jtoken, JToken rootDataContext)
5660
/// <exception cref="JsonException"><c>JToken.Parse(text)</c> can throw JsonException if <paramref name="text"/> is invalid json</exception>
5761
/// <param name="text">json in string</param>
5862
/// <param name="rootDataContext">a root data context</param>
59-
public DataContext(string text, JToken rootDataContext)
63+
/// <param name="hostDataContext">optional host data context</param>
64+
public DataContext(string text, JToken rootDataContext, JToken hostDataContext = null)
6065
{
6166
// disable date parsing handling
62-
var jsonReader = new JsonTextReader(new StringReader(text)) { DateParseHandling = DateParseHandling.None };
63-
var jtoken = JToken.Load(jsonReader);
64-
Init(jtoken, rootDataContext);
67+
using (var jsonReader = new JsonTextReader(new StringReader(text)) { DateParseHandling = DateParseHandling.None })
68+
{
69+
var jtoken = JToken.Load(jsonReader);
70+
Init(jtoken, rootDataContext, hostDataContext);
71+
}
6572
}
6673

6774
/// <summary>
6875
/// Initializer method that takes jtoken and root data context to initialize a data context object
6976
/// </summary>
7077
/// <param name="jtoken">current data context</param>
7178
/// <param name="rootDataContext">root data context</param>
72-
private void Init(JToken jtoken, JToken rootDataContext)
79+
/// <param name="hostDataContext">optional host data context</param>
80+
private void Init(JToken jtoken, JToken rootDataContext, JToken hostDataContext)
7381
{
7482
AELMemory = (jtoken is JObject) ? new AdaptiveCardsTemplateSimpleObjectMemory(jtoken) : new AdaptiveCardsTemplateSimpleObjectMemory(new JObject());
7583

7684
token = jtoken;
7785
RootDataContext = rootDataContext;
86+
HostDataContext = hostDataContext;
7887

7988
if (jtoken is JArray)
8089
{
@@ -83,6 +92,7 @@ private void Init(JToken jtoken, JToken rootDataContext)
8392

8493
AELMemory.SetValue(dataKeyword, token);
8594
AELMemory.SetValue(rootKeyword, rootDataContext);
95+
AELMemory.SetValue(hostKeyword, hostDataContext);
8696
}
8797

8898
/// <summary>
@@ -94,7 +104,7 @@ public DataContext GetDataContextAtIndex(int index)
94104
{
95105
var jarray = token as JArray;
96106
var jtokenAtIndex = jarray[index];
97-
var dataContext = new DataContext(jtokenAtIndex, RootDataContext);
107+
var dataContext = new DataContext(jtokenAtIndex, RootDataContext, HostDataContext);
98108
dataContext.AELMemory.SetValue(indexKeyword, index);
99109
return dataContext;
100110
}
@@ -104,23 +114,41 @@ public DataContext GetDataContextAtIndex(int index)
104114
/// a constructor for AdaptiveCardsTemplateVisitor
105115
/// </summary>
106116
/// <param name="nullSubstitutionOption">it will called upon when AEL finds no suitable functions registered in given AEL expression during evaluation the expression</param>
107-
/// <param name="data">json data in string which will be set as a root data context</param>
108-
public AdaptiveCardsTemplateVisitor(Func<string, object> nullSubstitutionOption, string data = null)
117+
/// <param name="data">json data as string which will be set as a root data context</param>
118+
/// <param name="hostData">json data as string which will be set as the host data context</param>
119+
public AdaptiveCardsTemplateVisitor(Func<string, object> nullSubstitutionOption, string data = null, string hostData = null)
109120
{
110-
if (data?.Length != 0)
121+
if (!String.IsNullOrEmpty(hostData))
122+
{
123+
// parse and save host context
124+
try
125+
{
126+
using (var jsonReader = new JsonTextReader(new StringReader(hostData)) { DateParseHandling = DateParseHandling.None })
127+
{
128+
host = JToken.Load(jsonReader);
129+
}
130+
}
131+
catch (JsonException innerException)
132+
{
133+
throw new AdaptiveTemplateException("Setting host data failed", innerException);
134+
}
135+
}
136+
137+
if (!String.IsNullOrEmpty(data))
111138
{
112139
// set data as root data context
113140
try
114141
{
115-
var jsonReader = new JsonTextReader(new StringReader(data)) { DateParseHandling = DateParseHandling.None };
116-
root = JToken.Load(jsonReader);
117-
PushDataContext(data, root);
142+
using (var jsonReader = new JsonTextReader(new StringReader(data)) { DateParseHandling = DateParseHandling.None })
143+
{
144+
root = JToken.Load(jsonReader);
145+
PushDataContext(data, root);
146+
}
118147
}
119148
catch (JsonException innerException)
120149
{
121150
throw new AdaptiveTemplateException("Setting root data failed with given data context", innerException);
122151
}
123-
124152
}
125153

126154
// if null, set default option
@@ -148,7 +176,7 @@ private DataContext GetCurrentDataContext()
148176
/// <param name="rootDataContext">current root data context</param>
149177
private void PushDataContext(string stringToParse, JToken rootDataContext)
150178
{
151-
dataContext.Push(new DataContext(stringToParse, rootDataContext));
179+
dataContext.Push(new DataContext(stringToParse, rootDataContext, host));
152180
}
153181

154182
/// <summary>
@@ -177,13 +205,13 @@ private void PushTemplatedDataContext(string jpath)
177205
{
178206
if (value is JToken jvalue)
179207
{
180-
dataContext.Push(new DataContext(jvalue, parentDataContext.RootDataContext));
208+
dataContext.Push(new DataContext(jvalue, parentDataContext.RootDataContext, parentDataContext.HostDataContext));
181209

182210
}
183211
else
184212
{
185213
var serializedValue = JsonConvert.SerializeObject(value);
186-
dataContext.Push(new DataContext(serializedValue, parentDataContext.RootDataContext));
214+
dataContext.Push(new DataContext(serializedValue, parentDataContext.RootDataContext, parentDataContext.HostDataContext));
187215
}
188216
}
189217
else
@@ -220,7 +248,7 @@ public ArrayList getTemplateVisitorWarnings()
220248
}
221249

222250
/// <summary>
223-
/// antlr runtime wil call this method when parse tree's context is <see cref="AdaptiveCardsTemplateParser.TemplateDataContext"/>
251+
/// antlr runtime will call this method when parse tree's context is <see cref="AdaptiveCardsTemplateParser.TemplateDataContext"/>
224252
/// <para>It is used in parsing a pair that has $data as key</para>
225253
/// <para>It creates new data context, and set it as current memory scope</para>
226254
/// </summary>
@@ -254,7 +282,7 @@ public override AdaptiveCardsTemplateResult VisitTemplateData([NotNull] Adaptive
254282
var templateLiteral = (templateStrings[0] as AdaptiveCardsTemplateParser.TemplatedStringContext).TEMPLATELITERAL();
255283
try
256284
{
257-
string templateLiteralExpression = templateLiteral.GetText();
285+
string templateLiteralExpression = templateLiteral.GetText();
258286
PushTemplatedDataContext(templateLiteralExpression.Substring(2, templateLiteralExpression.Length - 3));
259287
}
260288
catch (ArgumentNullException)
@@ -268,7 +296,7 @@ public override AdaptiveCardsTemplateResult VisitTemplateData([NotNull] Adaptive
268296
}
269297
}
270298
else
271-
// else clause handles all of the ordinary json values
299+
// else clause handles all of the ordinary json values
272300
{
273301
string childJson = templateDataValueNode.GetText();
274302
try
@@ -535,7 +563,7 @@ public override AdaptiveCardsTemplateResult VisitObj([NotNull] AdaptiveCardsTemp
535563

536564
templateVisitorWarnings.Add($"WARN: Could not evaluate {returnedResult} because it is not an expression or the " +
537565
$"expression is invalid. The $when condition has been set to false by default.");
538-
566+
539567
}
540568
else
541569
{
@@ -579,7 +607,7 @@ public override AdaptiveCardsTemplateResult VisitObj([NotNull] AdaptiveCardsTemp
579607
PopDataContext();
580608
}
581609

582-
// all existing json obj in input and repeated json obj if any have been removed
610+
// all existing json obj in input and repeated json obj if any have been removed
583611
if (removedCounts == repeatsCounts)
584612
{
585613
combinedResult.HasItBeenDropped = true;
@@ -589,7 +617,7 @@ public override AdaptiveCardsTemplateResult VisitObj([NotNull] AdaptiveCardsTemp
589617
}
590618

591619
/// <summary>
592-
/// Visitor method for <c>ITernminalNode</c>
620+
/// Visitor method for <c>ITernminalNode</c>
593621
/// <para>collects token as string and expand template if needed</para>
594622
/// </summary>
595623
/// <param name="node"></param>
@@ -651,7 +679,7 @@ public static string Expand(string unboundString, IMemory data, bool isTemplated
651679
{
652680
exp = Expression.Parse(unboundString.Substring(2, unboundString.Length - 3));
653681
}
654-
// AEL can throw any errors, for example, System.Data.Syntax error will be thrown from AEL's ANTLR
682+
// AEL can throw any errors, for example, System.Data.Syntax error will be thrown from AEL's ANTLR
655683
// when AEL encounters unknown functions.
656684
// We can't possibly know all errors and we simply want to leave the expression as it is when there are any exceptions
657685
#pragma warning disable CA1031 // Do not catch general exception types
@@ -701,9 +729,9 @@ public override AdaptiveCardsTemplateResult VisitTemplateWhen([NotNull] Adaptive
701729
{
702730
throw new ArgumentNullException(nameof(context));
703731
}
704-
// when this node is visited, the children of this node is shown as below:
732+
// when this node is visited, the children of this node is shown as below:
705733
// this node is visited only when parsing was correctly done
706-
// [ '{', '$when', ':', ',', 'expression']
734+
// [ '{', '$when', ':', ',', 'expression']
707735
var result = Visit(context.templateExpression());
708736

709737
if (!result.IsWhen)

source/dotnet/Library/AdaptiveCards.Templating/EvaluationContext.cs

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public sealed class EvaluationContext
1717
/// ""person"": {
1818
/// ""firstName"": ""Hello"",
1919
/// ""lastName"": ""World""
20+
/// }
2021
/// }";
2122
///
2223
/// var context = new EvaluationContext()
@@ -29,22 +30,56 @@ public sealed class EvaluationContext
2930
public object Root
3031
{ get; set; }
3132

33+
/// <summary>
34+
/// Provides Host Data Context
35+
/// </summary>
36+
/// <remarks>
37+
/// Typically this is supplied by the host application providing additional context for template binding. For example, the host might supply language or theming information that the template can use for layout.
38+
/// </remarks>
39+
/// <example>
40+
/// <code>
41+
///
42+
/// string jsonData = @"{
43+
/// ""person"": {
44+
/// ""firstName"": ""Hello"",
45+
/// ""lastName"": ""World""
46+
/// }
47+
/// }";
48+
///
49+
/// string hostData = @"{
50+
/// ""applicationName"": ""Contoso AdaptiveCards Host",
51+
/// ""platform"": ""mobile""
52+
/// }";
53+
///
54+
/// var context = new EvaluationContext()
55+
/// {
56+
/// Root = jsonData,
57+
/// Host = hostData
58+
/// };
59+
///
60+
/// </code>
61+
/// </example>
62+
public object Host
63+
{ get; set; }
64+
3265
/// <summary>
3366
/// default consturctor
3467
/// </summary>
3568
public EvaluationContext()
3669
{
3770
Root = null;
71+
Host = null;
3872
}
3973

4074
/// <summary>
41-
/// constructor for <c>EvaluationContext</c> that takes one argument that will be used for root data context
75+
/// constructor for <c>EvaluationContext</c> that takes one required argument used for root data context and one optional argument supplying host data
4276
/// </summary>
43-
/// <param name="rootData"></param>
44-
public EvaluationContext(object rootData)
77+
/// <param name="rootData">Data to use while binding</param>
78+
/// <param name="hostData">Data supplied by the host for use while binding</param>
79+
public EvaluationContext(object rootData, object hostData = null)
4580
{
4681
Root = rootData;
82+
Host = hostData;
4783
}
48-
4984
}
5085
}

0 commit comments

Comments
 (0)