Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Sage.Engine.Tests/CompilerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ public void TestAssemblyGeneration(string sourceFile)
}

[Test]
[TestCase("%%=ADD(1,2)=%%", ContentType.Handlebars, "%%=ADD(1,2)=%%")]
[TestCase("%%=ADD(1,2)=%%", ContentType.AMPscript, "3")]
[TestCase("{{#if true}}Hello{{/if}}", ContentType.AMPscript, "")]
[TestCase("{{#if true}}Hello{{/if}}", ContentType.Handlebars, "Hello")]
public void TestRenderer(string input, ContentType type, string expected)
{
var content = new EmbeddedContent(input, "TEST", "TEST", 1, type);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"people": [
{
"name": "Adam"
},
{
"name": "Howard"
}
]
}
6 changes: 6 additions & 0 deletions src/Sage.Engine.Tests/Corpus/Handlebars/eachHelper.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
===========
eachHelper
===========
{{#each people}}<h1>{{name}}</h1>{{/each}}
++++++++++
<h1>Adam</h1><h1>Howard</h1>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"isActiveFalse": false,
"isActiveTrue": true
}
12 changes: 12 additions & 0 deletions src/Sage.Engine.Tests/Corpus/Handlebars/ifHelper.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
===========
If True Block
===========
{{#if isActiveTrue}}hello{{else}}world{{/if}}
++++++++++
hello
===========
If False Block
===========
{{#if isActiveFalse}}hello{{else}}world{{/if}}
++++++++++
world
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"titleTags": "First Template <p> Tags",
"titleNoTags": "First Template No Tags"
}
12 changes: 12 additions & 0 deletions src/Sage.Engine.Tests/Corpus/Handlebars/stringEncoding.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
===========
Escaping
===========
{{titleTags}}
++++++++++
First Template &lt;p&gt; Tags
===========
No Escaping
===========
{{titleNoTags}}
++++++++++
First Template No Tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"isActiveTrue": true,
"isActiveFalse": false
}
11 changes: 11 additions & 0 deletions src/Sage.Engine.Tests/Corpus/Handlebars/unlessHelper.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
===========
Unless Helper False
===========
{{#unless isActiveFalse}}hello{{/unless}}
++++++++++
hello
===========
Unless Helper True
===========
{{#unless isActiveTrue}}hello{{/unless}}
++++++++++
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"primary": {
"name": "Adam"
},
"name": "Howard"
}
6 changes: 6 additions & 0 deletions src/Sage.Engine.Tests/Corpus/Handlebars/withHelper.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
===========
WithHelper Tests
===========
{{#with primary}}{{name}}{{/with}}
++++++++++
Adam
32 changes: 32 additions & 0 deletions src/Sage.Engine.Tests/HandlebarTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2022, salesforce.com, inc.
// All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/Apache-2.0

namespace Sage.Engine.Tests
{
using NUnit.Framework;

/// <summary>
/// Basic tests for the language features and runtime
/// </summary>
public class HandlebarTests : SageTest
{
[Test]
[RuntimeTest("Handlebars")]
public EngineTestResult TestHandlebars(CorpusData test)
{
try
{
var result = TestUtils.GetOutputFromHandlebarTest(_serviceProvider, test);
Assert.That(result.Output, Is.EqualTo(test.Output));
return result;
}
catch (CompileCodeException e)
{
Assert.Fail(e.Message);
return new EngineTestResult("!");
}
}
}
}
30 changes: 30 additions & 0 deletions src/Sage.Engine.Tests/Sage.Engine.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,36 @@
<None Update="Corpus\Function\utility.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\unlessHelper.subscribercontext.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\stringEncoding.subscribercontext.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\ifHelper.subscribercontext.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\eachHelper.subscribercontext.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\eachHelper.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\stringEncoding.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\unlessHelper.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\withHelper.subscribercontext.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\withHelper.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Handlebars\ifHelper.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Corpus\Language\attributes.subscribercontext.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
26 changes: 23 additions & 3 deletions src/Sage.Engine.Tests/TestUtils.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// Copyright (c) 2022, salesforce.com, inc.
// Copyright (c) 2022, salesforce.com, inc.
// All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/Apache-2.0

using Antlr4.Runtime.Tree;
using Microsoft.Extensions.DependencyInjection;
using Sage.Engine.Compiler;
using Sage.Engine.Data;
using Sage.Engine.Parser;
using Sage.Engine.Runtime;

Expand Down Expand Up @@ -42,6 +41,27 @@ private static RuntimeContext GetTestRuntimeContext(IServiceProvider serviceProv
}


/// <summary>
/// Uses Handlebars to compile the content instead of the AMPscript compiler
/// </summary>
public static EngineTestResult GetOutputFromHandlebarTest(IServiceProvider serviceProvider, CorpusData test)
{
CompilationOptions options = new CompilerOptionsBuilder()
.WithContent(new EmbeddedContent(test.Code, test.FileFriendlyName, test.FileFriendlyName, 1, ContentType.Handlebars))
.Build();

try
{
string result = serviceProvider.GetService<ICompiler>()
!.Compile(options, GetTestRuntimeContext(serviceProvider, options, test), test.SubscriberContext);
return new EngineTestResult(result.ReplaceLineEndings("\n").Trim());
}
catch (Exception)
{
return new EngineTestResult("!");
}
}

/// <summary>
/// Executes the engine and gets the expected result from the engine
/// </summary>
Expand Down Expand Up @@ -71,4 +91,4 @@ public static EngineTestResult GetOutputFromTest(IServiceProvider serviceProvide
return new EngineTestResult("!");
}
}
}
}
7 changes: 7 additions & 0 deletions src/Sage.Engine/Content/ContentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public static void AddLocalDiskContentClient(this IServiceCollection services, A
/// </summary>
public static ContentType InferContentTypeFromFilename(string path)
{
string extension = Path.GetExtension(path).ToLowerInvariant();

if (extension == ".hbs")
{
return ContentType.Handlebars;
}

return ContentType.AMPscript;
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/Sage.Engine/Content/IContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ namespace Sage.Engine.Compiler
/// </summary>
public enum ContentType
{
AMPscript
AMPscript,
Handlebars
};

/// <summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Sage.Engine/Content/LocalDiskContentClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ public LocalDiskContentClient(
return new LocalFileContent(id, filePath, 1, ContentType.AMPscript);
}

filePath = Path.Combine(_options.InputDirectory.FullName, $"{id}.hbs");
if (File.Exists(filePath))
{
return new LocalFileContent(id, filePath, 1, ContentType.Handlebars);
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Sage.Engine.Data.DependencyInjection;
using Sage.Engine.Data.Sqlite;
using Sage.Engine.Extensions;
using Sage.Engine.Handlebars;

namespace Sage.Engine.DependencyInjection
{
Expand Down Expand Up @@ -61,6 +62,7 @@ public static void AddSage(
services.AddLocalDiskContentClient();
services.AddScoped<Renderer>();
services.AddScoped<ICompiler, AmpscriptCompiler>();
services.AddScoped<ICompiler, HandlebarsCompiler>();
}
}
}
44 changes: 44 additions & 0 deletions src/Sage.Engine/Handlebars/HandlebarsCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2023, salesforce.com, inc.
// All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/Apache-2.0

using HandlebarsDotNet;
using HandlebarsDotNet.Extension.Json;
using Sage.Engine.Compiler;
using Sage.Engine.Runtime;

namespace Sage.Engine.Handlebars
{
/// <summary>
/// Uses HandlebarsDotNet to compile content to a string.
///
/// Does not support AMPscript embedded in the content.
/// </summary>
internal class HandlebarsCompiler : IHandlebarsCompiler
{
private IHandlebars _handlebar;

public HandlebarsCompiler()
{
_handlebar = HandlebarsDotNet.Handlebars.Create();
_handlebar.Configuration.UseJson();
}

public bool CanCompile(IContent content)
{
return content.ContentType == ContentType.Handlebars;
}

public string Compile(CompilationOptions options, RuntimeContext runtimeContext, SubscriberContext? subscriberContext)
{
var template = _handlebar.Compile(options.Content.GetTextReader());

StringWriter writer = new StringWriter();

template(writer, subscriberContext?.GetRichAttributes());

return writer.ToString();
}
}
}
14 changes: 14 additions & 0 deletions src/Sage.Engine/Handlebars/IHandlebarsCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2023, salesforce.com, inc.
// All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/Apache-2.0

namespace Sage.Engine.Handlebars
{
/// <summary>
/// Renders a piece of Handlebars content to a string.
/// </summary>
public interface IHandlebarsCompiler : ICompiler
{
}
}
20 changes: 4 additions & 16 deletions src/Sage.Engine/Runtime/RuntimeContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,8 @@ public RuntimeContext(
_contentBuilderContentClient = provider.GetRequiredService<IContentBuilderContentClient>();
_dataExtensionClient = provider.GetRequiredService<IDataExtensionClient>();
_dataExtensionClient.ConnectAsync().Wait();

string subscriberContextFile =
Path.Combine(Path.GetDirectoryName(_rootCompilationOptions.Content.Location), "subscriber.json");
if (subscriberContext != null)
{
_subscriberContext = subscriberContext;
}
else if (Path.Exists(subscriberContextFile))
{
_subscriberContext = new SubscriberContext(File.ReadAllText(subscriberContextFile));
}
else
{
_subscriberContext = new SubscriberContext(null);
}

_subscriberContext = subscriberContext ?? new SubscriberContext(null);

_stackFrame.Push(new StackFrame(_rootCompilationOptions.GeneratedMethodName, _rootCompilationOptions.Content));
}
Expand Down Expand Up @@ -229,14 +216,15 @@ public SubscriberContext GetSubscriberContext()

internal string? CompileAndExecuteReferencedCode(CompilationOptions currentOptions)
{
CompileResult compileResult = CSharpCompiler.GenerateAssemblyFromSource(currentOptions);

string poppedContext;

PushContext(currentOptions.GeneratedMethodName, currentOptions.Content);

try
{

CompileResult compileResult = CSharpCompiler.GenerateAssemblyFromSource(currentOptions);
compileResult.Execute(this);
}
finally
Expand Down
Loading
Loading