Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Named Profiles for option configuration #162

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
Added ISmidgeProfileStrategy interface and implementations that deter…
…mine which BundleOptions to use for a given request.
gplwhite committed Aug 21, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 439a43c0ecf8c6b7e964fd71a6a0a949608e7da9
14 changes: 14 additions & 0 deletions src/Smidge.Core/DefaultProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Smidge.Options;

namespace Smidge
{

/// <summary>
/// An implementation of ISmidgeProfileStrategy that will always use the Default profile.
/// </summary>
/// <seealso cref="ISmidgeProfileStrategy" />
public class DefaultProfileStrategy : ISmidgeProfileStrategy
{
public string GetCurrentProfileName() => SmidgeOptionsProfile.Default;
}
}
6 changes: 3 additions & 3 deletions src/Smidge.Core/FileProcessors/PreProcessManager.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
@@ -34,7 +34,7 @@ public async Task ProcessAndCacheFileAsync(IWebFile file, BundleOptions bundleOp
if (file == null) throw new ArgumentNullException(nameof(file));
if (file.Pipeline == null) throw new ArgumentNullException($"{nameof(file)}.Pipeline");

await ProcessFile(file, _bundleManager.GetAvailableOrDefaultBundleOptions(bundleOptions, false), bundleContext);
await ProcessFile(file, _bundleManager.GetAvailableOrDefaultBundleOptions(bundleOptions), bundleContext);
}

private async Task ProcessFile(IWebFile file, BundleOptions bundleOptions, BundleContext bundleContext)
@@ -121,4 +121,4 @@ private static void FileModified(WatchedFile file)
file.BundleOptions.FileWatchOptions.Changed(new FileWatchEventArgs(file));
}
}
}
}
33 changes: 33 additions & 0 deletions src/Smidge.Core/HostEnvironmentProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.Extensions.Hosting;
using Smidge.Options;

namespace Smidge
{
/// <summary>
/// An implementation of ISmidgeProfileStrategy that will use the host environment to determine if the Debug profile should be used.
/// </summary>
/// <seealso cref="ISmidgeProfileStrategy" />
public class HostEnvironmentProfileStrategy : ISmidgeProfileStrategy
{
private readonly IHostEnvironment _hostEnvironment;


public HostEnvironmentProfileStrategy(IHostEnvironment hostEnvironment)
{
_hostEnvironment = hostEnvironment;
}


private string _profileName;

public string GetCurrentProfileName() => _profileName ??= GetProfileForEnvironment(_hostEnvironment);


protected virtual string GetProfileForEnvironment(IHostEnvironment hostEnvironment)
{
return hostEnvironment.IsDevelopment()
? SmidgeOptionsProfile.Debug
: SmidgeOptionsProfile.Default;
}
}
}
12 changes: 12 additions & 0 deletions src/Smidge.Core/ISmidgeProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

namespace Smidge
{

/// <summary>
/// An interface that returns the name of an options profile to use for the current request.
/// </summary>
public interface ISmidgeProfileStrategy
{
string GetCurrentProfileName();
}
}
5 changes: 5 additions & 0 deletions src/Smidge.Core/Models/BundleExtensions.cs
Original file line number Diff line number Diff line change
@@ -82,6 +82,11 @@ public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManag
}


public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options)
{
return GetAvailableOrDefaultBundleOptions(bundleMgr, options, SmidgeOptionsProfile.Default);
}

public static BundleOptions GetAvailableOrDefaultBundleOptions(this IBundleManager bundleMgr, BundleOptions options, string profileName)
{
return options != null
53 changes: 36 additions & 17 deletions src/Smidge/SmidgeHelper.cs
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ namespace Smidge
/// </summary>
public class SmidgeHelper : ISmidgeRequire
{
private readonly ISmidgeProfileStrategy _profileStrategy;
private readonly DynamicallyRegisteredWebFiles _dynamicallyRegisteredWebFiles;
private readonly IPreProcessManager _preProcessManager;
private readonly ISmidgeFileSystem _fileSystem;
@@ -35,6 +36,7 @@ public class SmidgeHelper : ISmidgeRequire
/// <summary>
/// Constructor
/// </summary>
/// <param name="profileStrategy"></param>
/// <param name="fileSetGenerator"></param>
/// <param name="dynamicallyRegisteredWebFiles"></param>
/// <param name="preProcessManager"></param>
@@ -47,6 +49,7 @@ public class SmidgeHelper : ISmidgeRequire
/// <param name="httpContextAccessor"></param>
/// <param name="cacheBusterResolver"></param>
public SmidgeHelper(
ISmidgeProfileStrategy profileStrategy,
IBundleFileSetGenerator fileSetGenerator,
DynamicallyRegisteredWebFiles dynamicallyRegisteredWebFiles,
IPreProcessManager preProcessManager,
@@ -59,6 +62,7 @@ public SmidgeHelper(
IHttpContextAccessor httpContextAccessor,
CacheBusterResolver cacheBusterResolver)
{
_profileStrategy = profileStrategy ?? throw new ArgumentNullException(nameof(profileStrategy));
_fileSetGenerator = fileSetGenerator ?? throw new ArgumentNullException(nameof(fileSetGenerator));
_processorFactory = processorFactory ?? throw new ArgumentNullException(nameof(processorFactory));
_urlManager = urlManager ?? throw new ArgumentNullException(nameof(urlManager));
@@ -72,7 +76,7 @@ public SmidgeHelper(
_fileBatcher = new FileBatcher(_fileSystem, _requestHelper, hasher);
}

public async Task<HtmlString> JsHereAsync(string bundleName, bool debug = false)
public async Task<HtmlString> JsHereAsync(string bundleName, bool? debug = null)
{
var urls = await GenerateJsUrlsAsync(bundleName, debug);
var result = new StringBuilder();
@@ -84,7 +88,7 @@ public async Task<HtmlString> JsHereAsync(string bundleName, bool debug = false)
return new HtmlString(result.ToString());
}

public async Task<HtmlString> CssHereAsync(string bundleName, bool debug = false)
public async Task<HtmlString> CssHereAsync(string bundleName, bool? debug = null)
{
var urls = await GenerateCssUrlsAsync(bundleName, debug);
var result = new StringBuilder();
@@ -104,7 +108,7 @@ public async Task<HtmlString> CssHereAsync(string bundleName, bool debug = false
/// TODO: Once the tags are rendered the collection on the context is cleared. Therefore if this method is called multiple times it will
/// render anything that has been registered as 'pending' but has not been rendered.
/// </remarks>
public async Task<HtmlString> JsHereAsync(PreProcessPipeline pipeline = null, bool debug = false)
public async Task<HtmlString> JsHereAsync(PreProcessPipeline pipeline = null, bool? debug = null)
{
var result = new StringBuilder();
var urls = await GenerateJsUrlsAsync(pipeline, debug);
@@ -123,7 +127,7 @@ public async Task<HtmlString> JsHereAsync(PreProcessPipeline pipeline = null, bo
/// TODO: Once the tags are rendered the collection on the context is cleared. Therefore if this method is called multiple times it will
/// render anything that has been registered as 'pending' but has not been rendered.
/// </remarks>
public async Task<HtmlString> CssHereAsync(PreProcessPipeline pipeline = null, bool debug = false)
public async Task<HtmlString> CssHereAsync(PreProcessPipeline pipeline = null, bool? debug = null)
{
var result = new StringBuilder();
var urls = await GenerateCssUrlsAsync(pipeline, debug);
@@ -138,12 +142,12 @@ public async Task<HtmlString> CssHereAsync(PreProcessPipeline pipeline = null, b
/// Generates the list of URLs to render based on what is dynamically registered
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<string>> GenerateJsUrlsAsync(PreProcessPipeline pipeline = null, bool debug = false)
public async Task<IEnumerable<string>> GenerateJsUrlsAsync(PreProcessPipeline pipeline = null, bool? debug = null)
{
return await GenerateUrlsAsync(_dynamicallyRegisteredWebFiles.JavaScriptFiles, WebFileType.Js, pipeline, debug);
}

public Task<IEnumerable<string>> GenerateJsUrlsAsync(string bundleName, bool debug = false)
public Task<IEnumerable<string>> GenerateJsUrlsAsync(string bundleName, bool? debug = null)
{
return Task.FromResult(GenerateBundleUrlsAsync(bundleName, ".js", debug));
}
@@ -152,12 +156,12 @@ public Task<IEnumerable<string>> GenerateJsUrlsAsync(string bundleName, bool deb
/// Generates the list of URLs to render based on what is dynamically registered
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<string>> GenerateCssUrlsAsync(PreProcessPipeline pipeline = null, bool debug = false)
public async Task<IEnumerable<string>> GenerateCssUrlsAsync(PreProcessPipeline pipeline = null, bool? debug = null)
{
return await GenerateUrlsAsync(_dynamicallyRegisteredWebFiles.CssFiles, WebFileType.Css, pipeline, debug);
}

public Task<IEnumerable<string>> GenerateCssUrlsAsync(string bundleName, bool debug = false)
public Task<IEnumerable<string>> GenerateCssUrlsAsync(string bundleName, bool? debug = null)
{
return Task.FromResult(GenerateBundleUrlsAsync(bundleName, ".css", debug));
}
@@ -169,7 +173,7 @@ public Task<IEnumerable<string>> GenerateCssUrlsAsync(string bundleName, bool de
/// <param name="fileExt"></param>
/// <param name="debug"></param>
/// <returns></returns>
private IEnumerable<string> GenerateBundleUrlsAsync(string bundleName, string fileExt, bool debug)
private IEnumerable<string> GenerateBundleUrlsAsync(string bundleName, string fileExt, bool? debug = null)
{
//TODO: We should cache this, but problem is how do we do that with file watchers enabled? We'd still have to lookup the bundleOptions
// or maybe we just cache when file watchers are not enabled - probably the way to do it
@@ -185,7 +189,16 @@ private IEnumerable<string> GenerateBundleUrlsAsync(string bundleName, string fi

var result = new List<string>();

var profileName = debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default;
string profileName;
if (debug != null)
{
// Backwards compatibility - use the Debug parameter to choose the profile to use
profileName = debug.Value ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default;
}
else
{
profileName = _profileStrategy.GetCurrentProfileName();
}

//get the bundle options from the bundle if they have been set otherwise with the defaults
var bundleOptions = bundle.GetBundleOptions(_bundleManager, profileName);
@@ -221,17 +234,23 @@ private IEnumerable<string> GenerateBundleUrlsAsync(string bundleName, string fi
/// <param name="pipeline"></param>
/// <param name="debug"></param>
/// <returns></returns>
private async Task<IEnumerable<string>> GenerateUrlsAsync(
IEnumerable<IWebFile> files,
WebFileType fileType,
PreProcessPipeline pipeline = null,
bool debug = false)
private async Task<IEnumerable<string>> GenerateUrlsAsync(IEnumerable<IWebFile> files, WebFileType fileType, PreProcessPipeline pipeline = null, bool? debug = null)
{
var result = new List<string>();

var orderedFiles = _fileSetGenerator.GetOrderedFileSet(files, pipeline ?? _processorFactory.CreateDefault(fileType));

var profileName = debug ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default;
string profileName;
if (debug != null)
{
// Backwards compatibility - use the Debug parameter to choose the profile to use
profileName = debug.Value ? SmidgeOptionsProfile.Debug : SmidgeOptionsProfile.Default;
}
else
{
profileName = _profileStrategy.GetCurrentProfileName();
}

var bundleOptions = _bundleManager.GetDefaultBundleOptions(profileName);

var cacheBuster = _cacheBusterResolver.GetCacheBuster(bundleOptions.GetCacheBusterType());
@@ -278,7 +297,7 @@ private async Task<IEnumerable<string>> GenerateUrlsAsync(
//need to process/minify these files - need to use their original paths of course
foreach (var file in batch.Select(x => x.Original))
{
await _preProcessManager.ProcessAndCacheFileAsync(file, null, bundleContext);
await _preProcessManager.ProcessAndCacheFileAsync(file, bundleOptions, bundleContext);
}
}
}
3 changes: 2 additions & 1 deletion src/Smidge/SmidgeStartup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
@@ -34,6 +34,7 @@ public static IServiceCollection AddSmidge(this IServiceCollection services, ICo
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();

services.AddSingleton<ISmidgeProfileStrategy, DefaultProfileStrategy>();
services.AddTransient<IConfigureOptions<SmidgeOptions>, SmidgeOptionsSetup>();

services.AddSingleton<IPreProcessManager, PreProcessManager>();
29 changes: 29 additions & 0 deletions test/Smidge.Tests/Helpers/FakeProfileStrategy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Smidge.Options;

namespace Smidge.Tests.Helpers
{
public class FakeProfileStrategy : ISmidgeProfileStrategy
{
public static readonly ISmidgeProfileStrategy DebugProfileStrategy = new FakeProfileStrategy(SmidgeOptionsProfile.Debug);
public static readonly ISmidgeProfileStrategy DefaultProfileStrategy = new FakeProfileStrategy(SmidgeOptionsProfile.Default);


public FakeProfileStrategy()
{
ProfileName = SmidgeOptionsProfile.Default;
}

public FakeProfileStrategy(string profileName)
{
ProfileName = profileName;
}


public string ProfileName { get; set; }


public string GetCurrentProfileName() => ProfileName;


}
}
13 changes: 10 additions & 3 deletions test/Smidge.Tests/SmidgeHelperTests.cs
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
using Smidge.Hashing;
using Smidge.FileProcessors;
using Smidge.Options;
using Smidge.Tests.Helpers;
using Xunit;

namespace Smidge.Tests
@@ -47,6 +48,7 @@ public SmidgeHelperTests()

_dynamicallyRegisteredWebFiles = new DynamicallyRegisteredWebFiles();
_fileSystemHelper = new SmidgeFileSystem(_fileProvider, _fileProviderFilter, _cacheProvider, Mock.Of<IWebsiteInfo>());

_smidgeOptions = new Mock<IOptions<SmidgeOptions>>();
_smidgeOptions.Setup(opt => opt.Value).Returns(new SmidgeOptions
{
@@ -69,6 +71,7 @@ public SmidgeHelperTests()
public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found()
{
var sut = new SmidgeHelper(
FakeProfileStrategy.DefaultProfileStrategy,
_fileSetGenerator,
_dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper,
_hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper,
@@ -77,14 +80,15 @@ public async Task JsHereAsync_Returns_Empty_String_Result_When_No_Files_Found()

_bundleManager.CreateJs("empty", Array.Empty<string>());

var result = (await sut.JsHereAsync("empty", false)).ToString();
var result = (await sut.JsHereAsync("empty")).ToString();
Assert.Equal(string.Empty, result);
}

[Fact]
public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception()
{
var sut = new SmidgeHelper(
FakeProfileStrategy.DebugProfileStrategy,
_fileSetGenerator,
_dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper,
_hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper,
@@ -93,7 +97,7 @@ public async Task Generate_Css_Urls_For_Non_Existent_Bundle_Throws_Exception()

var exception = await Assert.ThrowsAsync<BundleNotFoundException>
(
async () => await sut.GenerateCssUrlsAsync("DoesntExist", true)
async () => await sut.GenerateCssUrlsAsync("DoesntExist")

);

@@ -105,6 +109,7 @@ public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception()
{

var sut = new SmidgeHelper(
FakeProfileStrategy.DebugProfileStrategy,
_fileSetGenerator,
_dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper,
_hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper,
@@ -113,7 +118,7 @@ public async Task Generate_Js_Urls_For_Non_Existent_Bundle_Throws_Exception()

var exception = await Assert.ThrowsAsync<BundleNotFoundException>
(
async () => await sut.GenerateJsUrlsAsync("DoesntExist", true)
async () => await sut.GenerateJsUrlsAsync("DoesntExist")
);


@@ -124,6 +129,7 @@ public async Task CssHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exceptio
{

var sut = new SmidgeHelper(
FakeProfileStrategy.DebugProfileStrategy,
_fileSetGenerator,
_dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper,
_hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper,
@@ -148,6 +154,7 @@ public async Task JsHere_HtmlString_For_Non_Existent_Css_Bundle_Throws_Exception
{

var sut = new SmidgeHelper(
FakeProfileStrategy.DebugProfileStrategy,
_fileSetGenerator,
_dynamicallyRegisteredWebFiles, _preProcessManager, _fileSystemHelper,
_hasher, _bundleManager, _processorFactory, _urlManager, _requestHelper,