Skip to content

Commit e7a8aef

Browse files
authored
CSP-compatible template support (#8)
1 parent 9fcad37 commit e7a8aef

File tree

12 files changed

+338
-74
lines changed

12 files changed

+338
-74
lines changed

.github/workflows/create-data-model-pr.yaml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ jobs:
2020
with:
2121
node-version: 14.x
2222

23-
- name: Setup .NET Core 3.1.x
24-
uses: actions/setup-dotnet@v1
23+
- name: Setup .NET 6.0.x
24+
uses: actions/setup-dotnet@v2
2525
with:
26-
dotnet-version: 3.1.x
26+
dotnet-version: 6.0.x
2727

2828
- name: Install OpenActive.DatasetSite.NET.Generator
2929
run: npm install
@@ -43,10 +43,11 @@ jobs:
4343

4444
- name: git stash if material changes have not been made
4545
# When considering whether it's worth making a PR, ignore the following:
46-
# - The dataset-site-template version number updating within the template in DatasetSiteMustacheTemplate.cs (affects one line)
46+
# - The dataset-site-template version number updating within the template in DatasetSiteMustacheTemplate.cs (affects four lines)
47+
# - The dataset-site-template version number updating within the template in README.md (affects three lines)
4748
# - Any updates to the package.json or package-lock.json files in OpenActive.DatasetSite.NET.Generator (not worth creating a PR JUST for these if the new version did not have any implications)
4849
# git stashing if no material changes allows the next step to close the PR if one is already open
49-
run: if [ "$(git diff --numstat | grep -vc '^1\s.*DatasetSiteMustacheTemplate\.cs\|.*package\.json\|.*package-lock\.json$')" -eq "0" ]; then git stash; else echo "Material changes found"; fi
50+
run: if [ "$(git diff --numstat | grep -vc '^4\s.*DatasetSiteMustacheTemplate\.cs\|3\s.*README\.md\|.*package\.json\|.*package-lock\.json$')" -eq "0" ]; then git stash; else echo "Material changes found"; fi
5051
working-directory: ./
5152

5253
- name: Create Pull Request

.github/workflows/nuget-publish.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ jobs:
1818
uses: dotnet/nbgv@master
1919
id: nbgv
2020

21-
- name: Setup .NET Core 3.1.x
22-
uses: actions/setup-dotnet@v1
21+
- name: Setup .NET 6.0.x
22+
uses: actions/setup-dotnet@v2
2323
with:
24-
dotnet-version: 3.1.x
24+
dotnet-version: 6.0.x
2525

2626
- name: Install dependencies
2727
run: dotnet restore

.github/workflows/tests.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ jobs:
1313
uses: actions/checkout@v2
1414
with:
1515
fetch-depth: 0 # avoid shallow clone so nbgv can do its work.
16-
- name: Setup .NET Core 3.1.x
17-
uses: actions/setup-dotnet@v1
16+
- name: Setup .NET 6.0.x
17+
uses: actions/setup-dotnet@v2
1818
with:
19-
dotnet-version: 3.1.x
19+
dotnet-version: 6.0.x
2020
- name: Install dependencies
2121
run: dotnet restore
2222
- name: Build

OpenActive.DatasetSite.NET.Generator/app.js

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,45 @@ var fsExtra = require('fs-extra');
33
var request = require('sync-request');
44
var path = require('path');
55
const { getModels, getEnums, getMetaData } = require('@openactive/data-models');
6-
const { getDatasetSiteTemplateSync } = require('@openactive/dataset-site-template');
6+
const { getDatasetSiteTemplateSync, getStaticAssetsArchiveUrl, getStaticAssetsVersion } = require('@openactive/dataset-site-template');
77

88
const DATA_MODEL_OUTPUT_DIR = "../OpenActive.DatasetSite.NET/metadata/";
9+
const SOLUTION_README_FILE_PATH = "../README.md";
910

1011
removeFiles()
1112
generateDatasetSiteMustacheTemplate();
1213
generateOpportunityTypes();
14+
updateReadme();
1315

1416
function removeFiles() {
1517
// Empty output directories
1618
fsExtra.emptyDirSync(DATA_MODEL_OUTPUT_DIR);
1719
}
1820

1921
function generateDatasetSiteMustacheTemplate (datasetSiteTemplateUrl) {
20-
var template = getDatasetSiteTemplateSync(true);
21-
writeFile('DatasetSiteMustacheTemplate', renderMustacheTemplateFile(template));
22+
var singleFileTemplate = getDatasetSiteTemplateSync(true);
23+
var cspTemplate = getDatasetSiteTemplateSync(false);
24+
writeFile('DatasetSiteMustacheTemplate', renderMustacheTemplateFile(singleFileTemplate, cspTemplate));
2225
}
2326

24-
function renderMustacheTemplateFile(content) {
27+
function updateReadme() {
28+
updateFile(SOLUTION_README_FILE_PATH, x => x.replace(/\[CSP compatible static assets archive[^\]]*\]\([^)]*\.zip\)/g, (match) => {
29+
return `[CSP compatible static assets archive v${getStaticAssetsVersion()}](${getStaticAssetsArchiveUrl()})`;
30+
}));
31+
}
32+
33+
function renderMustacheTemplateFile(singleFileTemplate, cspTemplate) {
2534
return `
2635
namespace OpenActive.DatasetSite.NET
2736
{
2837
public static class DatasetSiteMustacheTemplate
2938
{
30-
public const string Content = @"
31-
${content.replace(/\"/g, '""')}
39+
public const string SingleTemplateFileContent = @"
40+
${singleFileTemplate.replace(/\"/g, '""')}
41+
";
42+
43+
public const string CspCompatibleTemplateFileContent = @"
44+
${cspTemplate.replace(/\"/g, '""')}
3245
";
3346
}
3447
}
@@ -86,11 +99,19 @@ function writeFile(name, content) {
8699
console.log("NAME: " + filename);
87100
console.log(content);
88101

89-
fs.writeFile(DATA_MODEL_OUTPUT_DIR + filename, content, function (err) {
90-
if (err) {
91-
return console.log(err);
92-
}
102+
fs.writeFileSync(DATA_MODEL_OUTPUT_DIR + filename, content);
103+
104+
console.log("FILE SAVED: " + filename);
105+
}
106+
107+
function updateFile(filepath, mutationFn) {
108+
const currentContent = fs.readFileSync(filepath, "utf-8");
109+
const newContent = mutationFn(currentContent);
110+
111+
console.log("NAME: " + filepath);
112+
console.log(newContent);
113+
114+
fs.writeFileSync(filepath, newContent);
93115

94-
console.log("FILE SAVED: " + filename);
95-
});
116+
console.log("FILE SAVED: " + filepath);
96117
}

OpenActive.DatasetSite.NET.Generator/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

OpenActive.DatasetSite.NET.Generator/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"license": "MIT",
1313
"dependencies": {
1414
"@openactive/data-models": "^2.0.285",
15-
"@openactive/dataset-site-template": "^1.0.12",
15+
"@openactive/dataset-site-template": "^1.0.19",
1616
"fs-extra": "^7.0.1",
1717
"sync-request": "^6.0.0"
1818
}

OpenActive.DatasetSite.NET.Tests/OpenActive.DatasetSite.NET.Tests.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

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>net6.0</TargetFramework>
55

66
<IsPackable>false</IsPackable>
77
</PropertyGroup>

OpenActive.DatasetSite.NET.Tests/SimpleImplementation.cs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,45 +15,53 @@ public SimpleImplementation(ITestOutputHelper output)
1515
this.output = output;
1616
}
1717

18-
private string html = "";
19-
20-
[Fact]
21-
public void SettingsContainedInRenderedPage()
18+
// Customer-specific settings for dataset JSON (these should come from a database)
19+
private DatasetSiteGeneratorSettings settings = new DatasetSiteGeneratorSettings
2220
{
23-
// Customer-specific settings for dataset JSON (these should come from a database)
24-
var settings = new DatasetSiteGeneratorSettings
25-
{
26-
OpenDataFeedBaseUrl = "https://customer.example.com/feed".ParseUrlOrNull(),
27-
OpenBookingAPIBaseUrl = "https://customer.example.com/api/openbooking".ParseUrlOrNull(),
28-
OpenBookingAPIAuthenticationAuthorityUrl = "https://auth.acmebooker.example.com".ParseUrlOrNull(),
29-
DatasetSiteUrl = "https://halo-odi.legendonlineservices.co.uk/openactive/".ParseUrlOrNull(),
30-
DatasetDiscussionUrl = "https://github.com/gll-better/opendata".ParseUrlOrNull(),
31-
DatasetDocumentationUrl = "https://docs.acmebooker.example.com/".ParseUrlOrNull(),
32-
DatasetLanguages = new List<string> { "en-GB" },
33-
OrganisationName = "Better",
34-
OrganisationUrl = "https://www.better.org.uk/".ParseUrlOrNull(),
35-
OrganisationLegalEntity = "GLL",
36-
OrganisationPlainTextDescription = "Established in 1993, GLL is the largest UK-based charitable social enterprise delivering leisure, health and community services. Under the consumer facing brand Better, we operate 258 public Sports and Leisure facilities, 88 libraries, 10 children�s centres and 5 adventure playgrounds in partnership with 50 local councils, public agencies and sporting organisations. Better leisure facilities enjoy 46 million visitors a year and have more than 650,000 members.",
37-
OrganisationLogoUrl = "http://data.better.org.uk/images/logo.png".ParseUrlOrNull(),
38-
OrganisationEmail = "info@better.org.uk",
39-
PlatformName = "AcmeBooker",
40-
PlatformUrl = "https://acmebooker.example.com/".ParseUrlOrNull(),
41-
PlatformVersion = "2.0",
42-
BackgroundImageUrl = "https://data.better.org.uk/images/bg.jpg".ParseUrlOrNull(),
43-
DateFirstPublished = new DateTimeOffset(new DateTime(2019, 01, 14))
44-
};
21+
OpenDataFeedBaseUrl = "https://customer.example.com/feed".ParseUrlOrNull(),
22+
OpenBookingAPIBaseUrl = "https://customer.example.com/api/openbooking".ParseUrlOrNull(),
23+
OpenBookingAPIAuthenticationAuthorityUrl = "https://auth.acmebooker.example.com".ParseUrlOrNull(),
24+
DatasetSiteUrl = "https://halo-odi.legendonlineservices.co.uk/openactive/".ParseUrlOrNull(),
25+
DatasetDiscussionUrl = "https://github.com/gll-better/opendata".ParseUrlOrNull(),
26+
DatasetDocumentationUrl = "https://docs.acmebooker.example.com/".ParseUrlOrNull(),
27+
DatasetLanguages = new List<string> { "en-GB" },
28+
OrganisationName = "Better",
29+
OrganisationUrl = "https://www.better.org.uk/".ParseUrlOrNull(),
30+
OrganisationLegalEntity = "GLL",
31+
OrganisationPlainTextDescription = "Established in 1993, GLL is the largest UK-based charitable social enterprise delivering leisure, health and community services. Under the consumer facing brand Better, we operate 258 public Sports and Leisure facilities, 88 libraries, 10 children�s centres and 5 adventure playgrounds in partnership with 50 local councils, public agencies and sporting organisations. Better leisure facilities enjoy 46 million visitors a year and have more than 650,000 members.",
32+
OrganisationLogoUrl = "http://data.better.org.uk/images/logo.png".ParseUrlOrNull(),
33+
OrganisationEmail = "info@better.org.uk",
34+
PlatformName = "AcmeBooker",
35+
PlatformUrl = "https://acmebooker.example.com/".ParseUrlOrNull(),
36+
PlatformVersion = "2.0",
37+
BackgroundImageUrl = "https://data.better.org.uk/images/bg.jpg".ParseUrlOrNull(),
38+
DateFirstPublished = new DateTimeOffset(new DateTime(2019, 01, 14))
39+
};
4540

46-
var supportedFeeds = new List<OpportunityType> {
47-
OpportunityType.SessionSeries,
48-
OpportunityType.ScheduledSession,
49-
OpportunityType.FacilityUse,
50-
OpportunityType.FacilityUseSlot,
51-
OpportunityType.CourseInstance
52-
};
41+
private List<OpportunityType> supportedFeeds = new List<OpportunityType> {
42+
OpportunityType.SessionSeries,
43+
OpportunityType.ScheduledSession,
44+
OpportunityType.FacilityUse,
45+
OpportunityType.FacilityUseSlot,
46+
OpportunityType.CourseInstance
47+
};
5348

49+
[Fact]
50+
public void SettingsContainedInSingleFileTemplateRenderedPage()
51+
{
5452
var html = DatasetSiteGenerator.RenderSimpleDatasetSite(settings, supportedFeeds);
5553
output.WriteLine(html);
5654
Assert.Contains(settings.OrganisationPlainTextDescription, html);
5755
}
56+
57+
[Fact]
58+
public void SettingsContainedInCspCompatibleRenderedPage()
59+
{
60+
var html = DatasetSiteGenerator.RenderSimpleDatasetSite(settings, supportedFeeds, "./example-assets-directory/");
61+
output.WriteLine(html);
62+
// Slash should be removed, which this asserts
63+
Assert.Contains("\"./example-assets-directory/datasetsite.styles", html);
64+
Assert.Contains(settings.OrganisationPlainTextDescription, html);
65+
}
5866
}
5967
}

OpenActive.DatasetSite.NET/DatasetSiteGenerator.cs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ public static class DatasetSiteGenerator
1616
/// </summary>
1717
/// <param name="settings">Configuration settings for the dataset site</param>
1818
/// <param name="supportedFeedTypes">The supplied list auto-generates the metadata associated which each feed using best-practice values.</param>
19+
/// <param name="staticAssetsPathUrl">A relative or absolute URI path of the directory containing the self-hosted static asset files for the CSP-compatible template. If set, the CSP-compatible template will be used.</param>
1920
/// <returns>String containing human readable list</returns>
20-
public static string RenderSimpleDatasetSite(DatasetSiteGeneratorSettings settings, List<OpportunityType> supportedFeedTypes)
21+
public static string RenderSimpleDatasetSite(DatasetSiteGeneratorSettings settings, List<OpportunityType> supportedFeedTypes, string staticAssetsPathUrl = null)
2122
{
2223
// Check input is not null
2324
if (settings == null) throw new ArgumentNullException(nameof(settings));
@@ -38,7 +39,7 @@ public static string RenderSimpleDatasetSite(DatasetSiteGeneratorSettings settin
3839

3940
var dataFeedDescriptions = supportedOpportunityTypes.Select(x => x.ThemeDisplayName).Distinct().ToList();
4041

41-
return RenderSimpleDatasetSiteFromDataDownloads(settings, dataDownloads, dataFeedDescriptions);
42+
return RenderSimpleDatasetSiteFromDataDownloads(settings, dataDownloads, dataFeedDescriptions, staticAssetsPathUrl);
4243
}
4344

4445
/// <summary>
@@ -64,8 +65,9 @@ public static string ToHumanisedList(this List<string> list)
6465
/// <param name="settings">Configuration settings for the dataset site</param>
6566
/// <param name="dataDownloads">A list of DataDownload objects which each describe an available open data feed</param>
6667
/// <param name="dataFeedDescriptions">A list of strings that each describe the dataset</param>
68+
/// <param name="staticAssetsPathUrl">A relative or absolute URI path of the directory containing the self-hosted static asset files for the CSP-compatible template. If set, the CSP-compatible template will be used.</param>
6769
/// <returns>Returns a string corresponding to the compiled HTML</returns>
68-
public static string RenderSimpleDatasetSiteFromDataDownloads(DatasetSiteGeneratorSettings settings, List<DataDownload> dataDownloads, List<string> dataFeedDescriptions)
70+
public static string RenderSimpleDatasetSiteFromDataDownloads(DatasetSiteGeneratorSettings settings, List<DataDownload> dataDownloads, List<string> dataFeedDescriptions, string staticAssetsPathUrl = null)
6971
{
7072
// Check input is not null
7173
if (settings == null) throw new ArgumentNullException(nameof(settings));
@@ -146,26 +148,32 @@ public static string RenderSimpleDatasetSiteFromDataDownloads(DatasetSiteGenerat
146148
LandingPage = settings.OpenBookingAPIRegistrationUrl
147149
}
148150
};
149-
return RenderDatasetSite(dataset);
151+
return RenderDatasetSite(dataset, staticAssetsPathUrl);
150152
}
151153

152154
/// <summary>
153155
/// Returns a string corresponding to the compiled HTML, based on an embedded version of `datasetsite.mustache`, and the provided `dataset`.
154156
/// </summary>
155157
/// <param name="dataset">The an object containing the properties required to render the dataset site</param>
158+
/// <param name="staticAssetsPathUrl">A relative or absolute URI path of the directory containing the self-hosted static asset files for the CSP-compatible template. If set, the CSP-compatible template will be used.</param>
156159
/// <returns>Returns a string corresponding to the compiled HTML</returns>
157-
public static string RenderDatasetSite(Dataset dataset)
160+
public static string RenderDatasetSite(Dataset dataset, string staticAssetsPathUrl = null)
158161
{
159-
return RenderDatasetSiteWithTemplate(dataset, DatasetSiteMustacheTemplate.Content);
162+
var template = staticAssetsPathUrl == null ?
163+
DatasetSiteMustacheTemplate.SingleTemplateFileContent :
164+
DatasetSiteMustacheTemplate.CspCompatibleTemplateFileContent;
165+
166+
return RenderDatasetSiteWithTemplate(dataset, template, staticAssetsPathUrl);
160167
}
161168

162169
/// <summary>
163170
/// Returns a string corresponding to the compiled HTML, based on the supplied content of `datasetsite.mustache`, and the provided `dataset`.
164171
/// </summary>
165172
/// <param name="dataset">The an object containing the properties required to render the dataset site</param>
166173
/// <param name="mustacheTemplate">A string containing the contents of a potentially customised version of datasetsite.mustache</param>
174+
/// <param name="staticAssetsPathUrl">A relative or absolute URI path of the directory containing the self-hosted static asset files for the CSP-compatible template. If set, the CSP-compatible template will be used.</param>
167175
/// <returns>Returns a string corresponding to the compiled HTML</returns>
168-
public static string RenderDatasetSiteWithTemplate(Dataset dataset, string mustacheTemplate)
176+
public static string RenderDatasetSiteWithTemplate(Dataset dataset, string mustacheTemplate, string staticAssetsPathUrl = null)
169177
{
170178
// Check input dataset is not null
171179
if (dataset == null) throw new ArgumentNullException(nameof(dataset));
@@ -180,6 +188,12 @@ public static string RenderDatasetSiteWithTemplate(Dataset dataset, string musta
180188
// within the "jsonld" property at the root of the JSON itself.
181189
jsonObj.Add("jsonld", jsonObj.ToString(Formatting.Indented));
182190

191+
// For the CSP-compatible template, set the "staticAssetsPathUrl" property at the root of the JSON to the relative or absolute
192+
// URL path of the provided directory (containing the CSP static asset files), without a trailing slash (/)
193+
if (staticAssetsPathUrl != null) {
194+
jsonObj.Add("staticAssetsPathUrl", staticAssetsPathUrl.TrimEnd(new[] { '/' }));
195+
}
196+
183197
//Use the resulting JSON with the mustache template to render the dataset site.
184198
var stubble = new StubbleBuilder().Configure(s => s.AddJsonNet()).Build();
185199
return stubble.Render(mustacheTemplate, jsonObj);

OpenActive.DatasetSite.NET/OpenActive.DatasetSite.NET.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1515
<Version>4.2.0</Version>
1616
<Description>.NET library for rendering an OpenActive Dataset Site</Description>
17+
<PackageReadmeFile>README.md</PackageReadmeFile>
1718
</PropertyGroup>
1819

1920
<PropertyGroup>
@@ -25,6 +26,7 @@
2526
</PropertyGroup>
2627

2728
<ItemGroup>
29+
<None Include="..\README.md" Pack="true" PackagePath="\"/>
2830
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.8">
2931
<PrivateAssets>all</PrivateAssets>
3032
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

0 commit comments

Comments
 (0)