Skip to content

Commit b95bfd5

Browse files
committed
Reworks tableau code to use tableau's embed visualisation library
1 parent 0955280 commit b95bfd5

File tree

7 files changed

+77
-66
lines changed

7 files changed

+77
-66
lines changed

DigitalLearningSolutions.Data/Extensions/ConfigurationExtensions.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ public static class ConfigurationExtensions
5656
private const string TableauUsername = "Username";
5757
private const string TableauClientName = "ClientName";
5858
private const string TableauSiteUrl = "SiteUrl";
59-
private const string TableauDashboardUrl = "CompetencyDashboardUrl";
59+
private const string TableauWorkbookName = "WorkBookName";
60+
private const string TableauViewName = "ViewName";
61+
private const string TableauSiteName = "SiteName";
62+
private const string TableauAuthApi = "AuthApiPath";
6063
public static string GetAppRootPath(this IConfiguration config)
6164
{
6265
return config[AppRootPathName]!;
@@ -249,9 +252,22 @@ public static string GetTableauSiteUrl(this IConfiguration config)
249252
{
250253
return config[$"{TableauSectionKey}:{TableauSiteUrl}"]!;
251254
}
252-
public static string GetTableauDashboardUrl(this IConfiguration config)
255+
public static string GetTableauAuthApi(this IConfiguration config)
253256
{
254-
return config[$"{TableauSectionKey}:{TableauDashboardUrl}"]!;
257+
return config[$"{TableauSectionKey}:{TableauAuthApi}"]!;
255258
}
259+
public static string GetTableauSiteName(this IConfiguration config)
260+
{
261+
return config[$"{TableauSectionKey}:{TableauSiteName}"]!;
262+
}
263+
public static string GetTableauWorkbookName(this IConfiguration config)
264+
{
265+
return config[$"{TableauSectionKey}:{TableauWorkbookName}"]!;
266+
}
267+
public static string GetTableauViewName(this IConfiguration config)
268+
{
269+
return config[$"{TableauSectionKey}:{TableauViewName}"]!;
270+
}
271+
256272
}
257273
}

DigitalLearningSolutions.Web/Controllers/TrackingSystem/Centre/SelfAssessmentReports/SelfAssessmentReportsController.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
using DigitalLearningSolutions.Web.ViewModels.TrackingSystem.Centre.Reports;
1414
using DigitalLearningSolutions.Web.Helpers.ExternalApis;
1515
using System.Threading.Tasks;
16+
using Microsoft.Extensions.Configuration;
17+
using DigitalLearningSolutions.Data.Extensions;
1618

1719
[FeatureGate(FeatureFlags.RefactoredTrackingSystem)]
1820
[Authorize(Policy = CustomPolicies.UserCentreAdmin)]
@@ -24,16 +26,24 @@ public class SelfAssessmentReportsController : Controller
2426
private readonly ISelfAssessmentReportService selfAssessmentReportService;
2527
private readonly ITableauConnectionHelperService tableauConnectionHelper;
2628
private readonly IClockUtility clockUtility;
27-
29+
private readonly string tableauUrl;
30+
private readonly string tableauSiteName;
31+
private readonly string workbookName;
32+
private readonly string viewName;
2833
public SelfAssessmentReportsController(
2934
ISelfAssessmentReportService selfAssessmentReportService,
3035
ITableauConnectionHelperService tableauConnectionHelper,
31-
IClockUtility clockUtility
36+
IClockUtility clockUtility,
37+
IConfiguration config
3238
)
3339
{
3440
this.selfAssessmentReportService = selfAssessmentReportService;
3541
this.tableauConnectionHelper = tableauConnectionHelper;
3642
this.clockUtility = clockUtility;
43+
tableauUrl = config.GetTableauSiteUrl();
44+
tableauSiteName = config.GetTableauSiteName();
45+
workbookName = config.GetTableauWorkbookName();
46+
viewName = config.GetTableauViewName();
3747
}
3848
public IActionResult Index()
3949
{
@@ -69,13 +79,18 @@ public IActionResult DownloadSelfAssessmentReport(int selfAssessmentId)
6979
);
7080
}
7181
[HttpGet]
72-
[Route("LaunchTableauDashboards")]
73-
public async Task<IActionResult> LaunchTableauDashboards()
82+
[Route("TableauCompetencyDashboard")]
83+
public IActionResult TableauCompetencyDashboard()
7484
{
7585
var userEmail = User.GetUserPrimaryEmail();
7686
var jwt = tableauConnectionHelper.GetTableauJwt(userEmail);
77-
var url = await tableauConnectionHelper.AuthenticateUserAsync(jwt);
78-
return RedirectToPage(url);
87+
ViewBag.SiteName = tableauSiteName;
88+
ViewBag.TableauServerUrl = tableauUrl;
89+
ViewBag.WorkbookName = workbookName;
90+
ViewBag.ViewName = viewName;
91+
ViewBag.JwtToken = jwt;
92+
93+
return View();
7994
}
8095
}
8196
}
Lines changed: 7 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,33 @@
11
namespace DigitalLearningSolutions.Web.Helpers.ExternalApis
22
{
33
using Microsoft.IdentityModel.Tokens;
4-
using System.Collections.Generic;
54
using System.IdentityModel.Tokens.Jwt;
6-
using System.Security.Claims;
75
using System.Text;
86
using System;
97
using Microsoft.Extensions.Configuration;
108
using DigitalLearningSolutions.Data.Extensions;
11-
using System.Net.Http.Headers;
12-
using System.Net.Http;
13-
using System.Threading.Tasks;
149

1510
public interface ITableauConnectionHelperService
1611
{
1712
string GetTableauJwt(string email);
18-
Task<string> AuthenticateUserAsync(string jwtToken);
1913
}
2014
public class TableauConnectionHelper : ITableauConnectionHelperService
2115
{
22-
private readonly string connectedAppClientName;
2316
private readonly string connectedAppSecretKey;
2417
private readonly string connectedAppSecretId;
2518
private readonly string connectedAppClientId;
26-
private readonly string tableauUrl;
27-
private readonly string dashboardUrl;
2819
private readonly string user;
2920
public TableauConnectionHelper(IConfiguration config)
3021
{
31-
connectedAppClientName = config.GetTableauClientName();
3222
connectedAppClientId = config.GetTableauClientId();
3323
connectedAppSecretId = config.GetTableauClientSecretId();
3424
connectedAppSecretKey = config.GetTableauClientSecret();
35-
tableauUrl = config.GetTableauSiteUrl();
36-
dashboardUrl = config.GetTableauDashboardUrl();
3725
user = config.GetTableauUser();
3826
}
3927
public string GetTableauJwt(string email)
4028
{
4129
var key = Encoding.UTF8.GetBytes(connectedAppSecretKey);
4230
var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256);
43-
44-
var claims = new List<Claim>
45-
{
46-
new Claim(JwtRegisteredClaimNames.Sub, user),
47-
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
48-
new Claim("users.primaryemail", email),
49-
};
50-
5131
var header = new JwtHeader(signingCredentials)
5232
{
5333
{ "kid", connectedAppSecretId },
@@ -56,41 +36,18 @@ public string GetTableauJwt(string email)
5636

5737
var payload = new JwtPayload
5838
{
59-
{ "iss", connectedAppClientId },
60-
{ "aud", "tableau" },
61-
{ "exp", new DateTimeOffset(DateTime.UtcNow.AddMinutes(5)).ToUnixTimeSeconds() },
62-
{ "sub", user },
63-
{ "scp", new[] { "tableau:content:read" } }
39+
{ "jti", Guid.NewGuid().ToString()},
40+
{ "iss", connectedAppClientId },
41+
{ "aud", "tableau" },
42+
{ "exp", new DateTimeOffset(DateTime.UtcNow.AddMinutes(5)).ToUnixTimeSeconds() },
43+
{ "sub", user },
44+
{ "scp", new[] { "tableau:content:read" } },
45+
{ "users.primaryemail", email}
6446
};
65-
6647
var token = new JwtSecurityToken(header, payload);
6748
var tokenHandler = new JwtSecurityTokenHandler();
6849
var tokenString = tokenHandler.WriteToken(token);
6950
return tokenString;
7051
}
71-
72-
public async Task<string> AuthenticateUserAsync(string jwtToken)
73-
{
74-
using (var client = new HttpClient())
75-
{
76-
client.BaseAddress = new Uri(tableauUrl);
77-
client.DefaultRequestHeaders.Accept.Clear();
78-
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
79-
80-
var requestContent = new StringContent($"{{ \"token\": \"{jwtToken}\" }}", Encoding.UTF8, "application/json");
81-
82-
HttpResponseMessage response = await client.PostAsync("/api/3.8/auth/signin", requestContent);
83-
84-
if (response.IsSuccessStatusCode)
85-
{
86-
string responseBody = await response.Content.ReadAsStringAsync();
87-
return dashboardUrl;
88-
}
89-
else
90-
{
91-
throw new Exception("Failed to authenticate with Tableau Server: " + response.ReasonPhrase);
92-
}
93-
}
94-
}
9552
}
9653
}

DigitalLearningSolutions.Web/Startup.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,12 +585,13 @@ private static void RegisterWebServiceFilters(IServiceCollection services)
585585

586586
public void Configure(IApplicationBuilder app, IMigrationRunner migrationRunner, IFeatureManager featureManager)
587587
{
588+
var tableauServerUrl = config.GetTableauSiteUrl();
588589
app.UseMiddleware<DLSIPRateLimitMiddleware>();
589590
app.Use(async (context, next) =>
590591
{
591592
context.Response.Headers.Add("content-security-policy",
592593
"default-src 'self'; " +
593-
"script-src 'self' 'unsafe-hashes' 'sha256-oywvD6W6okwID679n4cvPJtWLowSS70Pz87v1ryS0DU=' 'sha256-kbHtQyYDQKz4SWMQ8OHVol3EC0t3tHEJFPCSwNG9NxQ' 'sha256-YoDy5WvNzQHMq2kYTFhDYiGnEgPrvAY5Il6eUu/P4xY=' 'sha256-/n13APBYdqlQW71ZpWflMB/QoXNSUKDxZk1rgZc+Jz8=' https://script.hotjar.com https://www.google-analytics.com https://static.hotjar.com https://www.googletagmanager.com https://cdnjs.cloudflare.com 'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU=' 'sha256-VQKp2qxuvQmMpqE/U/ASQ0ZQ0pIDvC3dgQPPCqDlvBo=';" +
594+
$"script-src 'self' 'nonce-random772362' https://script.hotjar.com https://www.google-analytics.com https://static.hotjar.com https://www.googletagmanager.com https://cdnjs.cloudflare.com {tableauServerUrl} 'unsafe-hashes' 'sha256-oywvD6W6okwID679n4cvPJtWLowSS70Pz87v1ryS0DU=' 'sha256-kbHtQyYDQKz4SWMQ8OHVol3EC0t3tHEJFPCSwNG9NxQ' 'sha256-YoDy5WvNzQHMq2kYTFhDYiGnEgPrvAY5Il6eUu/P4xY=' 'sha256-/n13APBYdqlQW71ZpWflMB/QoXNSUKDxZk1rgZc+Jz8=' 'sha256-+6WnXIl4mbFTCARd8N3COQmT3bJJmo32N8q8ZSQAIcU=' 'sha256-VQKp2qxuvQmMpqE/U/ASQ0ZQ0pIDvC3dgQPPCqDlvBo=';" +
594595
"style-src 'self' 'unsafe-inline' https://use.fontawesome.com; " +
595596
"font-src https://script.hotjar.com https://assets.nhs.uk/; " +
596597
"connect-src 'self' http: ws:; " +

DigitalLearningSolutions.Web/Views/TrackingSystem/Centre/SelfAssessmentReports/Index.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
</ul>
4444
@if (Model.SelfAssessmentSelects.Any())
4545
{
46-
<a asp-controller="SelfAssessmentReports" asp-action="LaunchTableauDashboards">@($"View Tableau Competency Dashboards")</a>
46+
<a asp-controller="SelfAssessmentReports" asp-action="TableauCompetencyDashboard">@($"View Tableau Competency Dashboards")</a>
4747
}
4848

4949
</div>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@{
2+
var tableauServerUrl = ViewBag.TableauServerUrl;
3+
var workbookName = ViewBag.WorkbookName;
4+
var viewName = ViewBag.ViewName;
5+
var jwtToken = ViewBag.JwtToken;
6+
var siteName = ViewBag.SiteName;
7+
}
8+
9+
<!DOCTYPE html>
10+
<html lang="en">
11+
<head>
12+
<meta charset="UTF-8">
13+
<title>Tableau Embedded View</title>
14+
@* We are not using Yarn/npm for the Tableau JS becaue of errors during installation relating to a missing dependency *@
15+
<script type="module" src="@tableauServerUrl/javascripts/api/tableau.embedding.3.latest.min.js"></script>
16+
</head>
17+
<body>
18+
<tableau-viz id="tableauViz"
19+
src="@tableauServerUrl/#/site/@siteName/views/@workbookName/@viewName" token="@jwtToken">
20+
</tableau-viz>
21+
</body>
22+
</html>

DigitalLearningSolutions.Web/appsettings.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@
8080
},
8181
"TableauDashboards": {
8282
"SiteUrl": "https://tabuat.data.england.nhs.uk",
83+
"SiteName": "monitor",
84+
"AuthApiPath": "/api/3.21/auth/signin",
85+
"WorkBookName": "DLSIdentifiableDataNHSEUAT",
86+
"ViewName": "Cover",
8387
"CompetencyDashboardUrl": "https://tabuat.data.england.nhs.uk/#/site/monitor/views/DLSIdentifiableDataNHSEUAT/Cover",
84-
//"CompetencyDashboardUrl": "https://tabuat.data.england.nhs.uk/#/workbooks/7866/views",
85-
//"CompetencyDashboardUrl": "https://tabuat.data.england.nhs.uk/#/workbooks/7839/views",
86-
//"Username": "SVC_default_TEL",
87-
"Username": "[email protected]",
88-
"Password": "",
88+
"Username": "svc-tel-dls",
8989
"ClientName": "tel_dls",
9090
"ClientId": "a7906ce3-e0c9-403e-a169-8eb78d858f8a",
9191
"ClientSecretId": "38b99058-a806-4ee2-bf7a-ad1933a81ae5",

0 commit comments

Comments
 (0)