Skip to content

Commit b454f27

Browse files
authored
Merge pull request #290 from Microsoft/dm/callbackkey
Added support for runtime expressions embedded in string literals
2 parents 5d92b29 + 0338e60 commit b454f27

File tree

3 files changed

+132
-0
lines changed

3 files changed

+132
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Text.RegularExpressions;
9+
using System.Threading.Tasks;
10+
11+
namespace Microsoft.OpenApi.Expressions
12+
{
13+
/// <summary>
14+
/// String literal with embedded expressions
15+
/// </summary>
16+
public class CompositeExpression : RuntimeExpression
17+
{
18+
private readonly string template;
19+
private Regex expressionPattern = new Regex("{(?<exp>[^}]+)");
20+
/// <summary>
21+
/// Expressions embedded into string literal
22+
/// </summary>
23+
public List<RuntimeExpression> ContainedExpressions = new List<RuntimeExpression>();
24+
25+
/// <summary>
26+
/// Create a composite expression from a string literal with an embedded expression
27+
/// </summary>
28+
/// <param name="expression"></param>
29+
public CompositeExpression(string expression)
30+
{
31+
template = expression;
32+
33+
// Extract subexpressions and convert to RuntimeExpressions
34+
var matches = expressionPattern.Matches(expression);
35+
36+
foreach (var item in matches.Cast<Match>())
37+
{
38+
var value = item.Groups["exp"].Captures.Cast<Capture>().First().Value;
39+
ContainedExpressions.Add(RuntimeExpression.Build(value));
40+
}
41+
}
42+
43+
/// <summary>
44+
/// Return original string literal with embedded expression
45+
/// </summary>
46+
public override string Expression => template;
47+
}
48+
}

src/Microsoft.OpenApi/Expressions/RuntimeExpression.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ public static RuntimeExpression Build(string expression)
3434
throw Error.ArgumentNullOrWhiteSpace(nameof(expression));
3535
}
3636

37+
if (expression.Contains("{$"))
38+
{
39+
return new CompositeExpression(expression);
40+
}
41+
3742
if (!expression.StartsWith(Prefix))
3843
{
3944
throw new OpenApiException(string.Format(SRResource.RuntimeExpressionMustBeginWithDollar, expression));

test/Microsoft.OpenApi.Tests/Expressions/RuntimeExpressionTests.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.OpenApi.Expressions;
66
using Microsoft.OpenApi.Properties;
77
using System;
8+
using System.Linq;
89
using Xunit;
910

1011
namespace Microsoft.OpenApi.Tests.Writers
@@ -171,5 +172,83 @@ public void CompareRuntimeExpressionWorks(string expression)
171172
Assert.NotSame(runtimeExpression1, runtimeExpression2);
172173
Assert.Equal(runtimeExpression1, runtimeExpression2);
173174
}
175+
176+
177+
[Fact]
178+
public void CompositeRuntimeExpressionContainsExpression()
179+
{
180+
// Arrange
181+
string expression = "This is a composite expression {$url} yay";
182+
183+
// Act
184+
var runtimeExpression = RuntimeExpression.Build(expression);
185+
186+
// Assert
187+
Assert.NotNull(runtimeExpression);
188+
var response = Assert.IsType<CompositeExpression>(runtimeExpression);
189+
Assert.Equal(expression, response.Expression);
190+
191+
var compositeExpression = runtimeExpression as CompositeExpression;
192+
Assert.Single(compositeExpression.ContainedExpressions);
193+
194+
}
195+
196+
[Fact]
197+
public void CompositeRuntimeExpressionContainsMultipleExpressions()
198+
{
199+
// Arrange
200+
string expression = "This is a composite expression {$url} yay and {$request.header.foo}";
201+
202+
// Act
203+
var runtimeExpression = RuntimeExpression.Build(expression);
204+
205+
// Assert
206+
Assert.NotNull(runtimeExpression);
207+
var response = Assert.IsType<CompositeExpression>(runtimeExpression);
208+
Assert.Equal(expression, response.Expression);
209+
210+
var compositeExpression = runtimeExpression as CompositeExpression;
211+
Assert.Equal(2,compositeExpression.ContainedExpressions.Count);
212+
213+
Assert.IsType<UrlExpression>(compositeExpression.ContainedExpressions.First());
214+
Assert.IsType<RequestExpression>(compositeExpression.ContainedExpressions.Last());
215+
}
216+
217+
218+
219+
[Fact]
220+
public void CompositeRuntimeExpressionForWebHook()
221+
{
222+
// Arrange
223+
string expression = "http://notificationServer.com?transactionId={$request.body#/id}&email={$request.body#/email}";
224+
225+
// Act
226+
var runtimeExpression = RuntimeExpression.Build(expression);
227+
228+
// Assert
229+
Assert.NotNull(runtimeExpression);
230+
var response = Assert.IsType<CompositeExpression>(runtimeExpression);
231+
Assert.Equal(expression, response.Expression);
232+
233+
var compositeExpression = runtimeExpression as CompositeExpression;
234+
Assert.Equal(2, compositeExpression.ContainedExpressions.Count);
235+
236+
Assert.IsType<RequestExpression>(compositeExpression.ContainedExpressions.First());
237+
Assert.IsType<RequestExpression>(compositeExpression.ContainedExpressions.Last());
238+
}
239+
240+
[Theory]
241+
[InlineData("This is a composite expression yay and {} and {$sddsd}")]
242+
[InlineData("This is a composite expression {url} yay and {} and {$url}")]
243+
public void CompositeRuntimeExpressionContainsInvalidExpressions(string expression)
244+
{
245+
// Arrange
246+
247+
// Act
248+
Action test = () => RuntimeExpression.Build(expression);
249+
250+
// Assert
251+
Assert.Throws<OpenApiException>(test);
252+
}
174253
}
175254
}

0 commit comments

Comments
 (0)