Creating an Angular and Azure Function API App with Azure Static Web App

Recently, Azure released Azure Static Web Apps, which looks like a way to host static web sites easily. Some of the perks I see immediately are:

  • Works well with SPA technologies (Angular, React, Vue)
  • Serve an API using Azure Functions
  • Automatic integration with GitHub and GitHub Actions to deploy immediately
  • Currently costs nothing (while this is in preview)


To get started, you’ll need:

  • Angular CLI
  • Azure Functions Core Tools
  • An Azure account

Create a GitHub Repo with Angular and Azure Function Apps

First, create a repo in your GitHub account, and clone that repo to your local PC.

Now create an Angular app with the CLI:

ng new NAME --directory app

Next, create an Azure Functions API (currently, there is a limitation that only allows for use of Javascript as the runtime):

func init NAME --javascript
mv NAME api
cd api
func new --name TestFunction --language dotnet --template HttpTrigger

Commit the changes made above, and then let’s move onto creating the Azure Static Web App.

Creating Azure Static Web App

Next, create an Azure Static Web App in your Azure account. When doing this, do the following:

  • Sign in to your GitHub account and select the correct repository and branch.
  • For build details, use the following information (replacing azure-static-web-app-poc with):
    • App location: app
    • Api location: api
    • App artifact location: dist/APPNAME

Automatic Deployment

After creating the Static Web App, a GitHub Workflows file will be created and committed to your repo. In turn, your skeleton application should be built and deployed automatically.

With the deployment completed, you can view the deployed UI and API by checking the URL of the Static Web App in the Azure portal:

  • UI – check the URL provided.
  • API – check the URL, plus /api/TestFunction

Further Reading

Microsoft’s Guide on Static Web Apps:

Adding Settings to a Plugin in NopCommerce (pre-4.00)

Once you’re started creating a plugin for NopCommerce, you’ll likely want to add the ability to configure settings inside the plugin for reference later.

When adding this capability, we’re going to work on trying to make this as immutable as possible, to follow functional programming as best we can, just because it makes things a little cleaner and puts all of the conversion between the configuration model and settings object.

First, create an ISettings implementation in the root of the plugin.

public class MyPluginSettings : ISettings
    public string MySetting { get; private set; }

We use private set here to make sure we initialize the settings object from a configuration model (when saving) and disallow the ability to change the settings object – making it immutable.

Next, create the configuration model to be used on the configuration page – this will usually have the same values as the Settings object above:

public class ConfigurationModel {
    public string Name

Next, create the base plugin controller, which will hold the implementation for the configure page:

public class YourPluginController : BasePluginController
    public ActionResult Configure()
        // implementation..

    public ActionResult Configure(ConfigurationModel model)
        // implementation..

Finally, create the View.

Book Notes – Why Zebras Don’t Get Ulcers

Pretty interesting read that goes into the details on how stress affects us as we’re generally in situations that generate chronic stress (as opposed to wildlife, that deals with acute stress). The big takeaways:

  • Stress-related sickness comes from the basic fact that we have our stress-response (response to stressor to return to homeostatis) turned on continually, as opposed to in the appropriate fight-or-flight situations.
  • Provides an aversion to Hans Seyle’s 3 part of stress, General Adaptation Principle:
    • Alarm – stressor, initial reaction, flight or flight
    • Resistance – stress-response, attempted recovery to homeostatis
    • Exhaustion – the point where the stress-response becomes more harmful than the stressor (as opposed to the stressor causing exhaustion)
  • The opposite of love is not hate – it’s indifference.
  • Having less social relationships is correlated to shorter lifespan and worse health.
  • Type-A personalities generally associated with higher levels of stress-related disease.
  • Addiction:
    • Turning point of addiction – when issue comes not from good feeling of drug, but bad feeling from it’s absence.
    • “No such thing as an ex-addict, just an addict not in the context that triggers use”
  • Subset of healthy population in old age traits:
    • No smoking
    • Minimal alcohol use
    • Lots of exercise
    • Normal body weight
    • No depression
    • Stable and happy marriage
    • Mature, resilient coping style (extroversion, social connectedness, low neuroticism)

Building Blocks of Physiological Stressors

  • Having outlets for frustration
  • Social support
  • Predictability
  • Control (belief of control, not necessarily actual control)
  • A perception of things worsening

Effect of Poverty on Stress

Poverty in general provides an environment of high stress and plenty of issues. Includes:

  • Lack of capability to think ahead (constantly putting out fires).
  • Lack of outlets (unable to get away due to lack of funds/resources)
  • Experiencing poverty early in life makes one sensitive to stressors (even if they rise above poverty)

Recording HTTP Request Body with Java, Spring Boot and Application Insights

Building off of my previous post about integrating App Insights into Spring Boot, I also wanted to record the request body in each trace sent to Azure. This is especially useful when looking up failures, since you’ll be able to see the request body used that caused the failure.

Important Note Regarding Privacy

Before getting started, something to consider is the issue of privacy – by activating this, you’ll be storing request body information into Azure, which can be an issue if you’re dealing with sensitive information.

If that’s the case, you should be sure to process the body extracted from this inplementation and remove the sensitive information in the payload before adding it to the request telemetry.

Bypassing the HttpServletRequest issue

Java servlets do not allow the ability to read a response multiple times – if you try to do so by reading getReader() multiple times, you’ll get an IllegalStateException. To fix this, we’ll create a custom implementation of HttpServletRequest that will cache the request provided, allowing us to read the request body, and then passing this down further the Spring Boot chain.

Create the CachedBodyHttpServletRequest class:

package com.example.demo;


import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.springframework.util.StreamUtils;

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {
    private byte[] cachedBody;

    public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);

    public ServletInputStream getInputStream() throws IOException {
        return new CachedBodyServletInputStream(this.cachedBody);

    public BufferedReader getReader() throws IOException {
        // Create a reader from cachedContent
        // and return it
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
        return new BufferedReader(new InputStreamReader(byteArrayInputStream));

    public String getBody() throws IOException {
        return getReader().lines().collect(Collectors.joining(System.lineSeparator()));

Next, create the CachedBodyServletInputStream class:

package com.example.demo;


import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;

public class CachedBodyServletInputStream extends ServletInputStream {
    private InputStream cachedBodyInputStream;

    public CachedBodyServletInputStream(byte[] cachedBody) {
        this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);

    public boolean isFinished() {
        try {
            return cachedBodyInputStream.available() == 0;
        } catch (IOException e) {
            // TODO Auto-generated catch block
        return false;

    public boolean isReady() {
        return true;

    public void setReadListener(ReadListener readListener) {
        throw new UnsupportedOperationException();

    public int read() throws IOException {

Adding CachedBodyHttpServletRequest to Spring Boot Filter

To use this, you’ll create a filter that activates before processing a request, which will add the request body to the request telemetry when defined as a POST or PUT method.

Create the CachedHttpServletRequestFilter class:

package com.example.demo;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Order(value = Ordered.HIGHEST_PRECEDENCE)
@WebFilter(filterName = "ContentCachingFilter", urlPatterns = "/*")
public class CachedHttpServletRequestFilter extends OncePerRequestFilter {
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // Skip all processing if not a POST or PUT request to improve efficiency
        if (!isPost(request) && !isPut(request)) {
            filterChain.doFilter(request, response);

        RequestTelemetryContext context = ThreadContext.getRequestTelemetryContext();
        RequestTelemetry requestTelemetry = context.getHttpRequestTelemetry();

        CachedBodyHttpServletRequest cachedRequest = new CachedBodyHttpServletRequest(request);
        String body = cachedRequest.getBody();
        requestTelemetry.getProperties().put("Request Body", body);

        filterChain.doFilter(cachedRequest, response);

    private boolean isPost(HttpServletRequest request) {
        return request.getMethod().equalsIgnoreCase(HttpMethod.POST.toString());

    private boolean isPut(HttpServletRequest request) {
        return request.getMethod().equalsIgnoreCase(HttpMethod.PUT.toString());


To verify, start running the application and make a few calls. When observing the requests in App Insights, you should be able to see the recorded Request Body on all POST and PUT calls:

Integrate Application Insights into a Spring Boot Application

To get started, we’ll set up a basic Spring Boot application, and then add Application Insights in the next step.

Creating a Skeleton Spring Boot App

To get started, go to Spring Boot Initializr and create an app with the following selected:

  • Create a gradle project
  • Add the Spring Web dependency

Once that’s done, extract the archive file given and open in your Java IDE of choice (IntelliJ, for example).

Add the following Controller ApiController.javato add an API endpoint:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

public class ApiController {

    public String doGet() {
        return "Hello World!";

Start the application using clean, build, and bootRun. You can verify the endpoint is working with a tool such as Insomnia and calling at http://localhost:8080 to have “Hello World!” displayed.

Once you have this working, you’re ready to start integrating Application Insights to provide analytics.

Integrating Application Insights

First, add the Application Insights dependencies to your build.gradle file:

dependencies {
    ...  // other dependencies
    compile group: '', name: 'applicationinsights-web-auto', version: '2.5.0'

Then add the Resources/ApplicationInsights.xml file:

<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns="" schemaVersion="2014-05-30">
   <!-- HTTP request component (not required for bare API) -->
      <Add type=""/>
      <Add type=""/>
      <Add type=""/>

   <!-- Events correlation (not required for bare API) -->
   <!-- These initializers add context data to each event -->
      <Add type=""/>
      <Add type=""/>
      <Add type=""/>
      <Add type=""/>
      <Add type=""/>


Finally, you’ll add the section in startup to add the App Insights Instrumentation Key to the codebase, to link the resource to send data to. A quick note on this – you can add the key to the .xml file, but I prefer to add it in as an environment variable, so this can be changed across different environments when deployed.

Add the following method to the file:

private static final Logger log = LoggerFactory.getLogger(DemoApplication.class);


	private void init() {
		String appInsightsKey = System.getenv("AppInsightsKey");
		if (appInsightsKey == null) {
			log.warn("App Insights Key not provided, no analytics will report.");


Verifying in App Insights

With the changes made in place, the last step is verifying everything is in place. To start the application with App Insights enabled:

  • Add the App Insights Instrumentation Key to an environment variable called AppInsightsKey.
  • Start the application.
  • Call the endpoint at http://localhost:8080
  • View the results at the App Insights screen.

Further Reading

Developing with nopCommerce using VSCode and Linux


To get started, you’ll need to set up:

  • VSCode with the C# and vscode-solution-explorer extensions installed
  • SQL Server

Download and Build nopCommerce Source Code

To get started, get a copy of the nopCommerce source code at their Github page.

After downloading the source code, open the /src folder using VSCode.

For cleaning and building the project, you have two choices:

  1. Use dotnet to run clean and build while in the src/ directory.
  2. Using the solution viewer plugin, you can clean and build the project:

After cleaning and building, you can run using either:

  1. dotnet run in the src/Presentation/Nop.Web directory.
  2. Using the VSCode solution explorer.

Once started running, you’ll be able to access nopCommerce below:

With this, you are able to run locally and perform all tasks related to administering nopCommerce, including running the installation and anything else.

Setting up A Jitsi Meet Server on Azure

With the COVID-19 pandemic going on, video chat is on the rise, with people using tools like Zoom, WebEx, and others. One option is to use an open-source solution like Jitsi Meet, which provides both a public cloud version you can use easily, and a version you can host on your own.

Why host on your own? You should consider hosting on your own if:

  • You’re concerned with security – hosting the software on your own ensures a third part is uninvolved.
  • You want more hands on capability to change the specs on the server for performance based on when you’re using video software.
  • You can control the location of the hardware, which depending on where everyone is located, may improve performance.

Server Creation and Jitsi Installation

First, create an Azure VM with the following:

  • Running Ubuntu
  • Open inbound ports 80, 443, and 22.
  • SSH key access (recommended)

Once the VM is created, SSH into the server and install the Jitsi full suite:

wget -qO - | sudo apt-key add -

sudo sh -c "echo 'deb stable/' > /etc/apt/sources.list.d/jitsi-stable.list"

sudo apt-get -y update

sudo apt-get -y install jitsi-meet

when installing, you’ll be asked to configure jitsi-videobridge2, use the URL you plan to use for this Jitsi instance. Afterwards, generate a self-signed certificate (we’ll create one with Let’s Encrypt later).

To verify installation worked successfully, visit the IP address in a browser, making sure you use HTTPS.

Set up SSL with Let’s Encrypt

To set up SSL, you’ll need to set up a domain name for the server in place – you can either:

  • Configure the Public IP assigned by Azure to create a domain name.
  • Using the process above, but also creating an A or CNAME record for a purchase domain pointing to the server IP

Once this is done, you can run the following to automatically create a cert:

sudo /usr/share/jitsi-meet/scripts/


Adding a Custom Method to a nopCommerce Core Service in a Plugin

In nopCommerce, you can extend a core service to include a new method and new functionality using both dependency injection and inheritance. For this example, we’ll use TaxService provided in Nop.Services.


First, create a new interface in your plugin that inherits from the core service interface:

// ICustomTaxService.cs

using Nop.Services.Tax;

namespace YourNamespace
    public interface ICustomTaxService : ITaxService
        void YourCustomMethod(); 

After creating the interface, create the implementation class, inheriting from both the newly created interface and the base service:

// CustomTaxService.cs

namespace YourNamespace
    public class CustomTaxService : TaxService, ICustomTaxService
        // constructor passing in all dependencies using base()
        public CustomTaxService(...) : base(...)
        public void YourCustomMethod() {
            // implementation here

Finally, make sure to connect the interface to the implementation in your plugin’s DependencyRegistrar.cs file:

class DependencyRegistrar : IDependencyRegistrar
    public int Order { get { return 1; } }

    public void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)


Once the above is created, you can reference the new service using the interface created (usually, this is done in a controller or a service):

private readonly ICustomTaxService _customTaxService;

public WarrantyTaxService(
    ICustomTaxService customTaxService
    _customTaxService = customTaxService;

// now use the service for anything you need!

Book Notes – Digital Minimalism

Digital Minimalism by Cal Newport lays out the benefits and then steps of using a more deliberate approach of selecting technology that benefits oneself. This book shares the same writing tone of Deep Work, providing a lot of examples for the different principles explained in the book.

Digital Minimalism is defined as “a philosophy of technology use in which you focus your online time on a small number of carefully selected and optimized activities that strongly support things you value, and then happily miss out on everything else.”

Overall, a pretty good read. For someone that generally follows Cal’s line of thinking, it was nice to read some of the examples (gives some inspiration) alongside the different ideas provided to balance between using technology for your own benefit and getting sucked into overuse.


The book provides a collection of steps that can be outlined below:

Digital Minimalism Principles:

  • Clutter is costly.
  • Optimization is important.
  • Intentionality is satisfying (intention trumps convenience)

Digital Declutter process – set aside all optional technologies for 30 days, then reintroduce them slowly into life after the habit for checking regularly disappears.

Digital Minimalism Practices:

  • Leave your phone at home
  • Take long walks (without headphones, use this time to think intentionally)
  • Write letters to yourself (process of writing more important than outcome)
  • Spent time alone (provides solitude)
  • Don’t click “Like” (prioritize high-value conversation vs. low-value communication, more below)
  • Consolidate texting
  • Fix or build something every week
  • Schedule your low-quality leisure
  • Join something
  • Follow leisure plans
  • Delete social media from your phone (just use on desktop/laptop)
  • Turn your devices into single-purpose computers (limit options available for general-purpose device at certain times)
  • Use social media like a professional (remember that social media companies spend millions to keep you invested as long as possible, get just the basic needs you need from it)
  • Embrace slow media
  • Dumb down your smartphone

Conversation vs. Communication

In her book Reclaiming Conversation, Turkle makes a distinction between:

  • High-value conversation, committed face to face conversation with others
  • Low-value communcation, asynchronous communication with others usually via digital means

This ties into the idea of staying away from something like “Likes” on social media – focus on dedicated conversations with others, which makes for a more fulfilling social life as a whole over trying to spread communication thin.

Personal thought: Interesting the idea that something like using social media makes you think you could miss out on what people are doing- but you can always reach out individually to the people you care about to check in!