How to provide non-webpack'd configuration (easy to edit post-build/deploy) to a webpack'd single page application

Tuesday, February 5, 2019 by Nate Bross

SPAs, or single page applications, are all the rage these days. They have their merits, and they are beneficial for many scenarios. One issue that has plagued me repeatedly when working on SPAs is trying to define environment specific variables that are NOT KNOWN at build time. WebPack is great, and scary, and confusing all at the same time, but it packages everything up at once. If you don't know the value at build time, you're out of luck.

While there are dozens of ways to handle this situation, some including separate build and release pipelines with tokens, I opted for a more low tech solution. Let me set the stage before I continue, as I think it helps paint the picture around why I like this solution. My app connects to some web services, and the uri of said services will be different for each deployment. Its important to note, that my SPA and web services are hosted on different domains and setup with CORS configuration. I can't simply use relative paths.

I created a simple config.js file and included it in the head section of my index.html (the entry point for my SPA). It looks like this:

window.api_root_url = "https://runtime-api.example.com";
window.client_id = "my-client-id-for-open-id-connect";

In my SPA code (I'm using typescript) I created a simple globals.ts file which wraps these into type safe constants.

export const apiRootUrl: string = (window as any).api_root_url;
export const clientId: string = (window as any).client_id;

and then whenever I need to reference my api endpoint or my client id I can simply import and use:

import * as globals from '@/globals';
// later on...
console.log(globals.apiRootUrl);

This works well, and provides a single file to edit on the deployed solution. It makes it easy to configure via ftp, ssh, rdp, etc.

When a Difference In Your Environment Make a Difference

Wednesday, January 9, 2019 by Nate Bross

I'm using IdentityServer4 for a couple projects. Its great, and might warrant a post of its own; however, one thing I've been struggling with is loading the jwt signing certificates. I've been using the powershell cmdlet New-SelfSignedCertificate to generate the pfx files and it works great on my local computer.

Running this on a web server has caused some grief, but lead to my learning a bit more about how these things actually work.

This is the final code that works, but I'll walk through how I got to it.

​new X509Certificate2(keyFilePath, keyFilePassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.EphemeralKeySet)​

The first thing is that the certificate file itself can have indicators stating whether to load to User store or Machine store; so I've used MachineKeySet to ensure we override that.

Additionally I'm also specifying EphemeralKeySet, which indicates the private keys should not be persisted anywhere. This is the key part that allows everything to work when running as ApplicationPoolIdentity on a server without administrative privileges.

Without passing the additional Storage Flags, the system uses whats in the certificate file/data and that works locally since I have a user profile and/or administrative rights. By default, IIS does NOT load the user profile which means no environment variables or user certificate store. In order to use MachineKeySet you need administrative privileges (something your web facing accounts should not have) so that's where Ephemeral comes in to play. Nothing is persisted so the admin rights are not needed. There are some incompatibilities with this approach; noted in the sources below, but for jwt signing it works.

For reference and searches, here are the errors I got/overcame.

Using default constructor without flags:

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: The system cannot find the file specified

Specifying only MachineKeySet, while running as ApplicationPoolIdentity:

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Access denied

Relevant sources:

Graphing United States Road/Highway Lane Mileage by State and Type Of Road

Thursday, December 27, 2018 by Nate Bross

There are a few data visualization projects I want to tackle and a good number of them have a cartography component. I knew I had to get started with one and build on it or I'd never get any of them rolling. I ran across a 'Functional System Lane Length' data set from Federal Highway Administration. I figured the table could be cleaned up and presented in a more intuitive way using a map and it would force me to kick start this little prototype.

This is what I came up with, its still a work in progress, though updates will likely come slowly and will be applied to the next data visualization project. This is how I approached it.

Visualization Technology

I had worked with jqvmap on some previous projects, and they have a great sample for color coding a map based on a set of data, so I decided to stick with something I already knew. I also knew I wanted to throw this in a separate page that I could embed on my blog here, so I wanted to make sure it could be completely self contained.

Data Processing

The data was available in an html table form, as well as pdf and excel. I found an online html table to json converter, and used that to build an array of data from the table. This got me close to what I wanted, but it left a bad taste in my mouth indexing into a multi dimensional array to pull out data every time I wanted to change data sets. This is what it looked like before any conversion:

[
    ["INTERSTATE","OTHER FREEWAYS AND EXPRESSWAYS","OTHER PRINCIPAL ARTERIAL","MINOR ARTERIAL","MAJOR COLLECTOR","MINOR COLLECTOR (2)","LOCAL (2)","TOTAL","INTERSTATE","OTHER FREEWAYS AND EXPRESSWAYS","OTHER PRINCIPAL ARTERIAL","MINOR ARTERIAL","MAJOR COLLECTOR","MINOR COLLECTOR","LOCAL (2)","TOTAL","",""  ],
    ["Alabama","2,414","-","6,045","8,398","24,615","12,441","94,725","148,638","2,189","138","4,905","6,070","7,547","372","41,481","62,701","211,339"  ],
    ["Alaska","2,057","-","1,612","867","2,743","2,862","15,294","25,434","303","-","503","475","510","472","3,898","6,162","31,597"  ],
    ["Arizona","3,714","70","3,417","2,691","8,552","3,789","61,350","83,584","1,462","1,552","3,771","9,278","4,352","456","40,505","61,375","144,959"  ],
    ["Arkansas","1,752","288","4,996","6,360","23,780","13,656","122,548","173,378","1,469","424","2,208","4,364","4,504","499","23,685","37,154","210,532"  ],
    ["California","5,254","1,518","8,253","12,647","24,037","15,080","82,233","149,022","9,671","9,283","25,149","30,758","27,593","644","142,263","245,361","394,383"  ],
]

Clearly, something needed done, so I wrote a small processing utility and JSON.stringify'd it:

var ddt = [];
for (var i = 0; i < states.length; i++) {
    var abvr = StateAbvrFromName(states[i][0]); // since data shows state name and jqvmap uses state code, il, ca, etc
    ddt[ddt.length] = {
        'State': { 'Name' : states[i][0], 'Code': abvr },
        'Rural': {
            'Interstate': parseInt(states[i][1].replace(/,/g, ""), 10),
            'Other_Freeways_Expressways': parseInt(states[i][2].replace(/,/g, ""), 10),
            'Other_Principal_Arterial': parseInt(states[i][3].replace(/,/g, ""), 10),
            'Minor_Arterial': parseInt(states[i][4].replace(/,/g, ""), 10),
            'Major_Collector':parseInt(states[i][5].replace(/,/g, ""), 10),
            'Minor_Collector_2': parseInt(states[i][6].replace(/,/g, ""), 10),
            'Local_2': parseInt(states[i][7].replace(/,/g, ""), 10),
            'Total': parseInt(states[i][8].replace(/,/g, ""), 10),
        },
        'Urban': {
            'Interstate': parseInt(states[i][9].replace(/,/g, ""), 10),
            'Other_Freeways_Expressways': parseInt(states[i][10].replace(/,/g, ""), 10),
            'Other_Principal_Arterial': parseInt(states[i][11].replace(/,/g, ""), 10),
            'Minor_Arterial': parseInt(states[i][12].replace(/,/g, ""), 10),
            'Major_Collector': parseInt(states[i][13].replace(/,/g, ""), 10),
            'Minor_Collector_2': parseInt(states[i][14].replace(/,/g, ""), 10),
            'Local_2': parseInt(states[i][15].replace(/,/g, ""), 10),
            'Total': parseInt(states[i][16].replace(/,/g, ""), 10),
        },
        'Total' : parseInt(states[i][17].replace(/,/g, ""), 10),
    };
}

Throwing It All Together

Going in to this little project, I knew that I wanted to run without any server side code, I wanted users to be able to minipulate the data to change the map, and I knew I wanted it to be as light weight as possible. I didn't want any node modules or components or any 'build' time tooling to be required to make this work.

I opted to use some jQuery event handling to glue everything together, and I'm actually happy with how it worked out.

 

 

My Great Grandma's Buttermilk Pancakes

Saturday, November 17, 2018 by Nate Bross

The ingredients:

  • 2 cups flour
  • 2 tsp baking powder
  • 1 tsp salt
  • 1/2 tsp baking soda
  • 3 Tbs sugar
  • 2 eggs, separated
  • 2 cups buttermilk (or more)
  • 1/4 cup melted butter

Sift flour, measure and re-sift with other dry ingredients. Combine beaten yolks with buttermilk and add to dry ingredients. Add melted butter. Beat egg whites until stiff and fold into mixture. Bake on hot waffle iron or make into pancakes.

Introducting my latest side project: FMData

Wednesday, September 5, 2018 by Nate Bross

I switched jobs in March of this year, that is a story for a different time, but the important thing is that it reintegrated me with some technology I had not used in quite some time: FileMaker. FileMaker is a database system that provides UI and basic scripting capabilities all in a single package. Users use the FileMaker client to access data stored in FileMaker databases, utilizing layouts and scripts in the database. Its an all in one system.

As a web developer with a lot of C# code under my belt, I wanted to connect to data in FileMaker from the outside. Historically the way to do this was through their XML Publishing Engine, I picked up an existing open source project and modified it to suit my needs. Ten years ago, this was a great solution and it worked well. It still does, but as things change so must we. In FileMaker 17 the Data API was introduced. It uses JSON and is RESTful. A new package was needed. Enter my side project: FMData. I built this as a learning exercise, but quickly realized it could be useful to other developers.

The library provides coverage for the FileMaker 17 Data API, and I've just released v2.1, cleaning up a handful of bugs and improving coverage of the underlying FileMaker API. I still don't have full coverage, but I'm inching towards it. I have tons of ideas for features and enhancements, so be sure to keep an eye on the project. If you find it useful let me know. If you find a bug, open an issue. If you have a feature idea, open an issue on github and consider making a pull request.

The package is available on Nuget, and getting some data is really this simple:

var client = new FileMakerRestClient("server", "fileName", "user", "pass"); // without .fmp12
var toFind = new Model { Name = "someName" };
var results = await client.FindAsync(toFind);
// results = IEnumerable<Model> matching with Name field matching "someName" as a FileMaker Findrequest.

That's all there is to it, and there's more examples on the project site: https://fmdata.io/

 View More Posts