Skip to content

Commit

Permalink
Features/ai tracker config (#17)
Browse files Browse the repository at this point in the history
* Config loader in JS initial & SPO installer updates

* Code done

* .

* .

* Working

* All working

* Service imports uses MetadataRefreshMinutes
  • Loading branch information
sambetts authored Jul 19, 2024
1 parent 517e5e5 commit 23bae56
Show file tree
Hide file tree
Showing 58 changed files with 4,149 additions and 6,876 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
<Compile Include="Models\AzStorageConnectionInfo.cs" />
<Compile Include="SolutionUninstaller.cs" />
<None Include="SqlExtentions\readme.md" />
<Compile Include="SPO\SiteTrackerInstaller\Models.cs" />
<Compile Include="Utils\AzurePublicCloudEnumerator.cs" />
<Compile Include="Utils\DatabaseUpgrader.cs" />
<Compile Include="Utils\FtpClientFactory.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public async Task RunPostCreatePaaSTasks(WebSiteResource webApp, DatabasePaaSInf
// Install AITracker from downloaded source
var aiTrackerDownload = solutionSources.GetSolutionComponentLocation(SoftwareComponent.AITracker);

var spTasks = new SharePointWebComponentsInstallJob(Config, _logger);
var spTasks = new SharePointWebComponentsInstallJob(Config, _logger, webApp.Data.DefaultHostName);
await spTasks.InstallAITracker(this.Config.SharePointConfig, aiTrackerDownload, appInsights.ConnectionString);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace App.ControlPanel.Engine.InstallerTasks
public class SharePointWebComponentsInstallJob : BaseInstallProcess
{

public SharePointWebComponentsInstallJob(SolutionInstallConfig config, ILogger logger) : base(config, logger)
public SharePointWebComponentsInstallJob(SolutionInstallConfig config, ILogger logger, string defaultHostName) : base(config, logger)
{
}

Expand Down Expand Up @@ -51,7 +51,7 @@ await ExecuteReportFailureAndThrowExceptionIfCritical("Upload SPFx solution",

// Install into sites. Hard-code library name "SPOInsights" for now
var siteInstaller = new SpoSiteListInstaller(_logger);
await siteInstaller.InstallToSites(sharePointInstallConfig.TargetSites, aiTrackerTempFile, appInsightsConnectionString, "SPOInsights");
await siteInstaller.InstallToSites(sharePointInstallConfig.TargetSites, aiTrackerTempFile, appInsightsConnectionString, "SPOInsights", base.Config.AppServiceWebAppName);

_logger.LogInformation("Installed AITracker to target SharePoint sites via CSOM.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using DataUtils;

namespace App.ControlPanel.Engine.SPO
{

public class ModernAppCustomAction
{
public const string DESCRIPTION = "SPO Insights ModernUI AITracker App Customizer";
public const string LOCATION = "ClientSideExtension.ApplicationCustomizer";
public ModernAppCustomAction(string appInsightsConnectionString, string cacheToken)
{
this.ClientSideComponentProperties = "{\"appInsightsConnectionStringHash\": \"" + appInsightsConnectionString.Base64Encode() + "\", \"cacheToken\": \"" + cacheToken + "\"}";
}

public string Name { get; } = "AiTrackerModernApplicationCustomizer";
public string Description { get; } = DESCRIPTION;
public string Title { get; } = "AiTrackerModernApplicationCustomizer";
public Guid ClientSideComponentId { get; } = Guid.Parse("a4e24884-9cfd-41ac-87af-747a47055f25");
public string ClientSideComponentProperties { get; internal set; }
public string Location { get; } = LOCATION;
}
public class ClassicPageCustomAction
{
public const string DESCRIPTION = "SPO Insights AITracker";
public const string LOCATION = "ScriptLink";
public ClassicPageCustomAction(string sourceFileFQDN, string appInsightsConnectionString, string solutionWebsiteBaseUrl)
{
this.ScriptBlock = "var headID = document.getElementsByTagName(\"head\")[0];" +
"var newScript = document.createElement(\"script\");" +
"newScript.type = \"text/javascript\";newScript.src=\"" + sourceFileFQDN +
"\";headID.appendChild(newScript);" +
$"var appInsightsConnectionStringHash = \"'{appInsightsConnectionString.Base64Encode()}'\";" +
$"var insightsWebRootUrlHash = \"'{solutionWebsiteBaseUrl.Base64Encode()}'\";";
}

public string Name { get; set; }
public string Description { get; } = DESCRIPTION;
public string ScriptBlock { get; set; }
public string Location { get; } = LOCATION;
}

public class ListInfo
{
public string ServerRelativeUrl { get; set; }
public bool CreatedNew { get; set; }
public bool EnableMinorVersions { get; set; }
}

public class TrackerInstallConfig
{
public TrackerInstallConfig(string appInsightsConnectionString, string docLibTitle, byte[] aiTrackerContents)
{
if (string.IsNullOrEmpty(appInsightsConnectionString))
{
throw new ArgumentException($"'{nameof(appInsightsConnectionString)}' cannot be null or empty.", nameof(appInsightsConnectionString));
}

if (string.IsNullOrEmpty(docLibTitle))
{
throw new ArgumentException($"'{nameof(docLibTitle)}' cannot be null or empty.", nameof(docLibTitle));
}

AppInsightsConnectionString = appInsightsConnectionString;
DocLibTitle = docLibTitle;
AiTrackerContents = aiTrackerContents ?? throw new ArgumentNullException(nameof(aiTrackerContents));
}

public string AppInsightsConnectionString { get; set; }
public string DocLibTitle { get; set; }
public string AITrackerTitle { get; } = "AITracker.js";

public byte[] AiTrackerContents { get; set; } = null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public SiteAITrackerInstaller(ISiteInstallAdaptor<WEBTYPE> siteInstallAdaptor, I
/// <summary>
/// Install AITracker to site using chosen adaptor
/// </summary>
public async Task InstallWebComponentsToSite(TrackerInstallConfig trackerInstallConfig)
public async Task InstallWebComponentsToSite(TrackerInstallConfig trackerInstallConfig, string solutionWebsiteBaseUrl)
{
if (trackerInstallConfig is null)
{
Expand Down Expand Up @@ -69,7 +69,7 @@ public async Task InstallWebComponentsToSite(TrackerInstallConfig trackerInstall

// Remove old custom actions & add new
await _siteInstallAdaptor.RemoveAITrackerCustomActionFromWeb(_siteInstallAdaptor.RootWeb);
await _siteInstallAdaptor.AddAITrackerCustomActionToWeb(_siteInstallAdaptor.RootWeb, new ClassicPageCustomAction(aiTrackerUrlWithToken, trackerInstallConfig.AppInsightsConnectionString));
await _siteInstallAdaptor.AddAITrackerCustomActionToWeb(_siteInstallAdaptor.RootWeb, new ClassicPageCustomAction(aiTrackerUrlWithToken, trackerInstallConfig.AppInsightsConnectionString, solutionWebsiteBaseUrl));
await _siteInstallAdaptor.RemoveAITrackerCustomActionFromWeb(_siteInstallAdaptor.RootWeb);
await _siteInstallAdaptor.AddModernUIAITrackerCustomActionToWeb(_siteInstallAdaptor.RootWeb, new ModernAppCustomAction(trackerInstallConfig.AppInsightsConnectionString, cacheToken));
foreach (var w in _siteInstallAdaptor.SubWebs)
Expand All @@ -78,7 +78,7 @@ public async Task InstallWebComponentsToSite(TrackerInstallConfig trackerInstall
_logger.LogInformation($"Registering AITracker to on web {webUrl}");

await _siteInstallAdaptor.RemoveAITrackerCustomActionFromWeb(w);
await _siteInstallAdaptor.AddAITrackerCustomActionToWeb(w, new ClassicPageCustomAction(aiTrackerUrlWithToken, trackerInstallConfig.AppInsightsConnectionString));
await _siteInstallAdaptor.AddAITrackerCustomActionToWeb(w, new ClassicPageCustomAction(aiTrackerUrlWithToken, trackerInstallConfig.AppInsightsConnectionString, solutionWebsiteBaseUrl));

await _siteInstallAdaptor.RemoveAITrackerCustomActionFromWeb(w);
await _siteInstallAdaptor.AddModernUIAITrackerCustomActionToWeb(w, new ModernAppCustomAction(trackerInstallConfig.AppInsightsConnectionString, cacheToken));
Expand Down Expand Up @@ -121,77 +121,4 @@ public async Task UninstallWebComponentsFromSite(string docLibTitle)
_logger.LogInformation($"Uninstalled AITracker from all sub-webs");
}
}


#region Models

public class ModernAppCustomAction
{
public const string DESCRIPTION = "SPO Insights ModernUI AITracker App Customizer";
public const string LOCATION = "ClientSideExtension.ApplicationCustomizer";
public ModernAppCustomAction(string appInsightsConnectionString, string cacheToken)
{
this.ClientSideComponentProperties = "{\"appInsightsConnectionStringHash\": \"" + appInsightsConnectionString.Base64Encode() + "\", \"cacheToken\": \"" + cacheToken + "\"}";
}

public string Name { get; } = "AiTrackerModernApplicationCustomizer";
public string Description { get; } = DESCRIPTION;
public string Title { get; } = "AiTrackerModernApplicationCustomizer";
public Guid ClientSideComponentId { get; } = Guid.Parse("a4e24884-9cfd-41ac-87af-747a47055f25");
public string ClientSideComponentProperties { get; internal set; }
public string Location { get; } = LOCATION;
}
public class ClassicPageCustomAction
{
public const string DESCRIPTION = "SPO Insights AITracker";
public const string LOCATION = "ScriptLink";
public ClassicPageCustomAction(string sourceFileFQDN, string appInsightsConnectionString)
{
this.ScriptBlock = "var headID = document.getElementsByTagName(\"head\")[0];" +
"var newScript = document.createElement(\"script\");" +
"newScript.type = \"text/javascript\";newScript.src=\"" + sourceFileFQDN +
"\";headID.appendChild(newScript);" +
$"var appInsightsConnectionStringHash = \"'{appInsightsConnectionString.Base64Encode()}'\"";
}

public string Name { get; set; }
public string Description { get; } = DESCRIPTION;
public string ScriptBlock { get; set; }
public string Location { get; } = LOCATION;
}

public class ListInfo
{
public string ServerRelativeUrl { get; set; }
public bool CreatedNew { get; set; }
public bool EnableMinorVersions { get; set; }
}

public class TrackerInstallConfig
{
public TrackerInstallConfig(string appInsightsConnectionString, string docLibTitle, byte[] aiTrackerContents)
{
if (string.IsNullOrEmpty(appInsightsConnectionString))
{
throw new ArgumentException($"'{nameof(appInsightsConnectionString)}' cannot be null or empty.", nameof(appInsightsConnectionString));
}

if (string.IsNullOrEmpty(docLibTitle))
{
throw new ArgumentException($"'{nameof(docLibTitle)}' cannot be null or empty.", nameof(docLibTitle));
}

AppInsightsConnectionString = appInsightsConnectionString;
DocLibTitle = docLibTitle;
AiTrackerContents = aiTrackerContents ?? throw new ArgumentNullException(nameof(aiTrackerContents));
}

public string AppInsightsConnectionString { get; set; }
public string DocLibTitle { get; set; }
public string AITrackerTitle { get; } = "AITracker.js";

public byte[] AiTrackerContents { get; set; } = null;
}

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public AbstractSiteListInstaller(ILogger logger)
_logger = logger;
}

public async Task InstallToSites(IEnumerable<string> siteUrls, FileInfo aiTrackerFileName, string appInsightsConnectionString, string docLibTitle)
public async Task InstallToSites(IEnumerable<string> siteUrls, FileInfo aiTrackerFileName, string appInsightsConnectionString, string docLibTitle, string solutionWebsiteBaseUrl)
{
if (!aiTrackerFileName.Exists)
throw new ArgumentException($"Can't find AITracker file '{aiTrackerFileName}'", nameof(aiTrackerFileName));
Expand All @@ -29,7 +29,7 @@ public async Task InstallToSites(IEnumerable<string> siteUrls, FileInfo aiTracke
var cfg = new TrackerInstallConfig(appInsightsConnectionString, docLibTitle, fileContents);
var installer = new SiteAITrackerInstaller<WEBTYPE>(adaptor, _logger);

await installer.InstallWebComponentsToSite(cfg);
await installer.InstallWebComponentsToSite(cfg, solutionWebsiteBaseUrl);
}
}
}
Expand Down
40 changes: 20 additions & 20 deletions src/AnalyticsEngine/Common/Entities/Config/AppConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

namespace Common.Entities.Config
{
/// <summary>
/// Config for the entire solution
/// </summary>
public class AppConfig
{
public AppConfig()
Expand Down Expand Up @@ -38,8 +41,6 @@ public AppConfig()
}
this.WebAppURL = ConfigurationManager.AppSettings.Get("WebAppURL");



var ts = TimeSpan.FromDays(1); // default
TimeSpan.TryParse(ConfigurationManager.AppSettings.Get("ChunkSize"), out ts);
this.ChunkSize = ts;
Expand All @@ -50,7 +51,6 @@ public AppConfig()
this.DaysBeforeNowToDownload = daysBeforeNowToDownload;



this.CognitiveEndpoint = ConfigurationManager.AppSettings.Get("CognitiveEndpoint");
this.CognitiveKey = ConfigurationManager.AppSettings.Get("CognitiveKey");

Expand All @@ -60,15 +60,27 @@ public AppConfig()

this.StatsApiSecret = ConfigurationManager.AppSettings.Get("StatsApiSecret");
this.StatsApiUrl = ConfigurationManager.AppSettings.Get("StatsApiUrl");

var metadataRefreshMinutes = ConfigurationManager.AppSettings.Get("MetadataRefreshMinutes");
if (!string.IsNullOrEmpty(metadataRefreshMinutes))
{
int metadataRefreshMinutesInt = 24 * 60; // 24 hours
int.TryParse(metadataRefreshMinutes, out metadataRefreshMinutesInt);
if (metadataRefreshMinutesInt < -1)
{
this.MetadataRefreshMinutes = metadataRefreshMinutesInt;
}
}
}


public string AppInsightsContainerName { get; set; }
public string AppInsightsApiKey { get; set; }
public string AppInsightsAppId { get; set; }

public string AppInsightsConnectionString { get; set; }

public int MetadataRefreshMinutes { get; set; } = 24 * 60; // 24 hours

public string ClientID { get; set; }
public string ClientSecret { get; set; }
public string TenantDomain { get; set; }
Expand All @@ -86,13 +98,8 @@ public AppConfig()
/// <summary>
/// Default {AADInstance}/{TenantGUID} (https://login.microsoftonline.com/0000-000-00000/)
/// </summary>
public string Authority
{
get
{
return this.AADInstance + this.TenantGUID;
}
}
public string Authority => this.AADInstance + this.TenantGUID;


/// <summary>
/// Time-span to query API for in a single request
Expand All @@ -106,7 +113,7 @@ public List<string> ContentTypesToRead
{
get
{
string[] tokens = ContentTypesString.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
var tokens = ContentTypesString.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
return tokens.ToList();
}
}
Expand All @@ -117,20 +124,13 @@ public List<string> ContentTypesToRead
public string CognitiveEndpoint { get; set; }
public string CognitiveKey { get; set; }

public bool IsValidCognitiveConfig
{
get
{
return !(string.IsNullOrEmpty(this.CognitiveEndpoint) || string.IsNullOrEmpty(this.CognitiveKey));
}
}
public bool IsValidCognitiveConfig => !(string.IsNullOrEmpty(this.CognitiveEndpoint) || string.IsNullOrEmpty(this.CognitiveKey));

public ImportTaskSettings ImportJobSettings { get; set; }

public string StatsApiSecret { get; set; } = null;
public string StatsApiUrl { get; set; } = null;

public AppConnectionStrings ConnectionStrings { get; set; } = null;

}
}
14 changes: 14 additions & 0 deletions src/AnalyticsEngine/Common/Entities/Config/ImportConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Newtonsoft.Json;
using System;

namespace Common.Entities.Config
{
public class ImportConfig
{
[JsonProperty(PropertyName = "expiry")]
public DateTime Expiry { get; set; } = DateTime.MinValue;

[JsonProperty(PropertyName = "metadataRefreshMinutes")]
public int MetadataRefreshMinutes { get; set; }
}
}
1 change: 1 addition & 0 deletions src/AnalyticsEngine/Common/Entities/Entities.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
<Compile Include="BaseEntity.cs" />
<Compile Include="Config\AppConfig.cs" />
<Compile Include="Config\AppConnectionStrings.cs" />
<Compile Include="Config\ImportConfig.cs" />
<Compile Include="Entities\AuditLog\CopilotEvents.cs" />
<Compile Include="Entities\OnlineMeeting.cs" />
<Compile Include="Entities\UsageReports\AppPlatformUserActivityLog.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ public static class ClaimsRedisManager
{
private const string TOKEN_KEY_PREFIX = "UserToken_";



public static async Task SaveToken(this CacheConnectionManager connectionManager, ClaimsPrincipal signedInUser, RefreshOAuthToken authToken)
{
await connectionManager.SetString(GetRedisKey(signedInUser), authToken.ToString());
Expand All @@ -28,6 +26,5 @@ static string GetRedisKey(ClaimsPrincipal user)
{
return TOKEN_KEY_PREFIX + user.Identity.Name;
}

}
}
Loading

0 comments on commit 23bae56

Please sign in to comment.