Skip to content

Commit

Permalink
Add a dev setting to enable the web optimizer for JS and CSS files (#137
Browse files Browse the repository at this point in the history
)
  • Loading branch information
dougwaldron committed Jan 14, 2025
1 parent 4a81de6 commit 42d26da
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 28 deletions.
38 changes: 23 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ This is an ASP.NET web application.

Complete the following tasks when the application is ready for deployment.

* Create server-specific settings and config files and add copies to the "app-config" GitHub repository.
* Create Web Deploy Publish Profiles for each web server using the "Example-Server.pubxml" file as an example.
* Create server-specific `appsettings.json` and `web.config` files and add copies of them to the `app-config` GitHub repository. *Do **NOT** commit server-specific settings files to the main repository.*
* Create Web Deploy Publish Profiles for each web server using the `Example-Server.pubxml` file as an example.
* Configure the following external services as needed:
- [Azure App registration](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) to manage employee authentication. *(Add configuration settings in the "AzureAd" section in a server settings file.)*
When configuring the app in the Azure Portal, add optional claims for "email", "family_name", and "given_name" under "Token configuration".
- [Raygun](https://app.raygun.com/) for crash reporting and performance monitoring. *(Add the API key to the "RaygunSettings" section in a server settings file.)*
- [SonarCloud](https://sonarcloud.io/projects) for code quality and security scanning. *(Update the project key in the "sonarcloud-scan.yml" workflow file and in the badges above.)*
- [Azure App registration](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade) to manage employee authentication. *(Add configuration settings in the `AzureAd` section of the server settings file.)*
When configuring the app in the Azure Portal, add optional claims for `email`, `family_name`, and `given_name` under "Token configuration".
- [Raygun](https://app.raygun.com/) for crash reporting and performance monitoring. *(Add the API key to the `RaygunSettings` section in a server settings file.)*
- [SonarCloud](https://sonarcloud.io/projects) for code quality and security scanning. *(Update the project key in the `sonarcloud-scan.yml` workflow file and in the badges above.)*
- [Better Uptime](https://betterstack.com/better-uptime) for site uptime monitoring. *(No app configuration needed.)*

### Project organization
Expand All @@ -50,12 +50,13 @@ The solution contains the following projects:
* **LocalRepository** — A class library implementing the repositories and data stores using static in-memory test data (for local development).
* **EfRepository** — A class library implementing the repositories and data stores using Entity Framework and a database (as specified by the configured connection strings).
* **WebApp** — The front end web application and/or API.
* **TestData** — A class library containing test data for development and testing.

There are also corresponding unit test projects for each, plus a **TestData** project containing test data for development and testing.
There are also corresponding unit test projects for each (not counting the `TestData` project).

### Development settings

The following settings configure the data stores and authentication for development purposes. To change these settings, add an "appsettings.Development.json" file in the root of the "WebApp" folder with a `DevSettings` section and a top-level setting named `UseDevSettings`. Here's a sample "appsettings.Development.json" file to start out:
The following settings section configures the data stores, authentication, and other settings for development purposes. To work with these settings, add an `appsettings.Development.json` file in the root of the `WebApp` folder with a `DevSettings` section, and make your changes there. Here's a sample `appsettings.Development.json` file to start out:

```json
{
Expand All @@ -68,38 +69,45 @@ The following settings configure the data stores and authentication for developm
"LocalUserIsAuthenticated": true,
"LocalUserIsStaff": true,
"LocalUserIsAdmin": true,
"UseSecurityHeadersInDev": false
"UseSecurityHeadersInDev": false,
"EnableWebOptimizer": false
}
}
```

- *UseDevSettings* — Indicates whether the following Dev settings should be applied.
- *UseInMemoryData*
- When `true`, the "LocalRepository" project is used for repositories and data stores. Data is initially seeded from the "TestData" project.
- When `false`, the "EfRepository" project is used, and a SQL Server database (as specified by the connection string) is created. <small>(If the connection string is missing, then a temporary EF Core in-memory database provider is used. This option is included for convenience and is not recommended.)</small>
- When `true`, the `LocalRepository` project is used for repositories and data stores. Data is initially seeded from the `TestData` project.
- When `false`, the `EfRepository` project is used, and a SQL Server database (as specified by the connection string) is created. <small>(If the connection string is missing, then a temporary EF Core in-memory database provider is used. This option is included for convenience and is not recommended.)</small>
- *UseEfMigrations* — Uses Entity Framework database migrations when `true`. When `false`, the `DeleteAndRebuildDatabase` setting controls how the database is handled. (Only applies if *UseInMemoryData* is `false`.)
- *DeleteAndRebuildDatabase* — When set to `true`, the database is deleted and recreated on each run. When set to `false`, the database is not modified on each run. (Only applies if `UseInMemoryData` and `UseEfMigrations` are both `false`.) If the database does not exist yet, it will not be created if this is set to `false`. The database is seeded with data from the "TestData" project only when `UseEfMigrations` is `false` and `DeleteAndRebuildDatabase` is `true`. Otherwise, the data in the database is not changed.
- *DeleteAndRebuildDatabase* — When set to `true`, the database is deleted and recreated on each run. When set to `false`, the database is not modified on each run. (Only applies if `UseInMemoryData` and `UseEfMigrations` are both `false`.) If the database does not exist yet, it will not be created if this is set to `false`. The database is seeded with data from the `TestData` project only when `UseEfMigrations` is `false` and `DeleteAndRebuildDatabase` is `true`. Otherwise, the data in the database is not changed.
- *UseAzureAd* — If `true`, connects to Azure AD for user authentication. (The app must be registered in the Azure portal, and configuration added to the settings file.) If `false`, authentication is simulated using test user data.
- *LocalUserIsAuthenticated* — Simulates a successful login with a test account when `true`. Simulates a failed login when `false`. (Only applies if *UseAzureAd* is `false`.)
- *LocalUserIsStaff* — Adds the Staff and Site Maintenance Roles to the logged in account when `true` or no roles when `false`. (Applies whether *UserAzureAd* is `true` or `false`.)
- *LocalUserIsAdmin* — Adds all App Roles to the logged in account when `true` or no roles when `false`. (Applies whether *UserAzureAd* is `true` or `false`.) <small>An alternative way to create admin users is to add them to the `SeedAdminUsers` setting as an array of email addresses.</small>
- *UseSecurityHeadersLocally* — Sets whether to include HTTP security headers when running locally in the Development environment.
- *EnableWebOptimizer* — Enables the WebOptimizer middleware for bundling and minification of CSS and JavaScript files. (This is disabled by default because it can interfere with debugging.)

#### Production defaults

When `UseDevSettings` is missing or set to `false` or if the `DevSettings` section is missing, the settings are automatically set to production defaults as follows:
In a production or staging environment (or if `UseDevSettings` is set to `false` or the `DevSettings` section is missing), the settings are automatically set to production defaults as follows:

```csharp
UseDevSettings = false,
UseInMemoryData = false,
UseEfMigrations = true,
DeleteAndRebuildDatabase = false,
UseAzureAd = true,
LocalUserIsAuthenticated = false,
LocalUserIsStaff = false,
LocalUserIsAdmin = false,
UseSecurityHeadersInDev: false
UseSecurityHeadersInDev = false,
EnableWebOptimizer = true,
```

Here's a visualization of how the settings configure data storage at runtime.
#### Data persistence

Here's a visualization of how the settings configure data persistence at runtime.

```mermaid
flowchart LR
Expand Down
21 changes: 10 additions & 11 deletions src/WebApp/Platform/AppConfiguration/BindingsConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,26 @@ public static class BindingsConfiguration
{
public static void BindSettings(WebApplicationBuilder builder)
{
// App settings
// Bind app settings.
builder.Configuration.GetSection(nameof(AppSettings.SupportSettings))
.Bind(AppSettings.SupportSettings);

builder.Configuration.GetSection(nameof(AppSettings.RaygunSettings))
.Bind(AppSettings.RaygunSettings);

// App versioning
var versionSegments = (Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
// Set app version.
var segments = (Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion ?? Assembly.GetEntryAssembly()?.GetName().Version?.ToString() ?? "").Split('+');

AppSettings.SupportSettings.InformationalVersion = versionSegments[0];
if (versionSegments.Length > 0)
{
AppSettings.SupportSettings.InformationalBuild =
versionSegments[1][..Math.Min(7, versionSegments[1].Length)];
}
AppSettings.SupportSettings.InformationalVersion = segments[0];
if (segments.Length > 0)
AppSettings.SupportSettings.InformationalBuild = segments[1][..Math.Min(7, segments[1].Length)];

// Dev settings
// Dev settings should only be used in development environment and when explicitly enabled.
var devConfig = builder.Configuration.GetSection(nameof(AppSettings.DevSettings));
var useDevConfig = devConfig.Exists() &&
var useDevConfig = builder.Environment.IsDevelopment() && devConfig.Exists() &&
Convert.ToBoolean(devConfig[nameof(AppSettings.DevSettings.UseDevSettings)]);

if (useDevConfig)
devConfig.Bind(AppSettings.DevSettings);
else
Expand Down
5 changes: 5 additions & 0 deletions src/WebApp/Platform/Settings/DevSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,10 @@ public record DevSettingsSection
/// Sets whether to include HTTP security headers when running locally in the Development environment.
/// </summary>
public bool UseSecurityHeadersInDev { get; [UsedImplicitly] init; }

/// <summary>
/// Use WebOptimizer to bundle and minify CSS and JS files (`true`).
/// </summary>
public bool EnableWebOptimizer { get; [UsedImplicitly] init; }
}
}
4 changes: 3 additions & 1 deletion src/WebApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@
});

// Configure bundling and minification.
builder.Services.AddWebOptimizer(minifyJavaScript: !isDevelopment);
builder.Services.AddWebOptimizer(
minifyJavaScript: AppSettings.DevSettings.EnableWebOptimizer,
minifyCss: AppSettings.DevSettings.EnableWebOptimizer);

//Add simple cache.
builder.Services.AddMemoryCache();
Expand Down
3 changes: 2 additions & 1 deletion src/WebApp/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"LocalUserIsAuthenticated": true,
"LocalUserIsStaff": true,
"LocalUserIsAdmin": true,
"UseSecurityHeadersInDev": false
"UseSecurityHeadersInDev": false,
"EnableWebOptimizer": false
}
}

0 comments on commit 42d26da

Please sign in to comment.