Bad JavaScript. Expected '(' in our webpack eval chunk

This is a quick blog post for resolving an issue this morning from webpack uglified/minified JavaScript. The error happens in IE and Edge, it simply says:

Expected ‘(‘

This was happening in the middle of a webpack minified chunk so the entire block was wrapped inside eval(““)

Steps

  1. Navigate to the end of the webpack chunk, which will tell us the source of this chunk. Scroll past the sourcemap section to the end of the chunk (not the end of the file).

  2. Find the original file that the chunk was created from - now run ESLint over it.
    I didn’t have this handy in the build chain, so I ran it online https://eslint.org/demo/

  3. The error was spotted as there was a catch{} statement - in plain JavaScript - catch has to collect an argument. So the first syntax is incorrect. It must be the second syntax.

try {
    // do something
}
catch {
    // incorrect  
}
  
try {
    // do something
}
catch(e) {
    // correct, even if we don't use e  
}


Modern browsers are more lenient so this error did not appear on all browsers.

Migrate Angular SPA from ADALJS to MSAL because it is awesome

jon-tyson-420512-unsplash-resized.jpg

This is a public service announcement for all office devs.

If you are using ADALJS - you need to upgrade your project to MSAL. It is awesome. It has everything you want, and it worked the way we expected it to, right out of the box. Plus a bunch more new features.

It is 2018, we can finally put away ADALJS. Do it ASAP.


Plan

  • High level ideas - replace A with B

  • New things we can now do

  • Code diff snippets (if you want to compare notes with your own)

High Level

ADALJS was never released stand-alone. It was released as part of an Adal-AngularJS library. In the many years after several community produced wrappers were created to wrap ADALJS into various frameworks. The one I was using is ng2-adal. But there are others.

  • AdalService -> MsalService

  • this.adalService.userInfo -> this.msalService.getUser()

  • replace auth route guards with MsalGuard

  • move adalService.config(adalConfig) to MsalModule imports dependency injection

  • add msalInterceptor to HTTP_INTERCEPTORS which automatically attach the correct bearer token

  • switch http (from httpModule) to httpClient (from httpClientModule) which listens to HTTP_INTERCEPTORS

  • a handy tip to detect if SPA is running inside an adalFrame and disable route-outlets (this disables sub-components from loading inside the iframe - this is a great tip for adaljs as well)

MSAL provides three libraries with examples - make sure you switch to the relevant library instead.

MSAL is Awesome!

  • Listen to BroadcastService observables to be notified when tokens are received successfully or expired, or if consent level has changed.

  • MSAL supports incremental consent.

  • Can use acquireTokenSilent to obtain tokens silently, listen to event subscription to catch if grant isn’t available - then call acquireTokenRedirect or acquireTokenPopUp.
    Generally listen to “consent_required” or “interaction_required”.

  • App should be registered with the set of permissions that an admin can grant for the whole organization.

  • But MSAL can request additional consent separately. Users will have to grant those.

  • MSAL can issue both v1 and v2 tokens so it has no problems talking to APIs that still need v1 tokens.

  • Switch library has no visible effect to your end users - they will not see any new consent and everything will happen silently in the back.

The conversion took me two evenings to wrap up. I’m now in the process of adding new incremental consent.

Code Diff Snippets

While the whole thing is still fresh in my mind, I want to write this blog post.

Here’s what I start with

  • Angular 7

  • ng2-adal

  • adal-angular (includes adal.js)

I end up with

  • @azure/msal-angular

npm

npm install @azure/msal-angular --save

npm uninstall ng2-adal adal-angular --save-dev

app.module.ts

Change imports to msal-angular, remove my own auth.guard and use MsalGuard instead.

Use MsalIntercepter to automatically add bearer token on HttpClient calls (so replacing HttpModule’s Http with the new HttpClientModule’s HttpClient).

Initialize MsalModule with config (this is traditionally the adalConfig.

// remove
import { AdalService } from 'ng2-adal/dist/core';
import { AuthGuard } from "./my/auth.guard";
// replace
import { MsalModule } from "@azure/msal-angular";
import { MsalInterceptor } from "@azure/msal-angular";
import { LogLevel } from 'msal';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';

export function loggerCallback(logLevel, message, piiEnabled) {
    console.log("client logging" + message);
}
export const protectedResourceMap: [string, string[]][] = [
    ['https://graph.microsoft.com/v1.0/me', ['user.read']],
    // ... other scopes
];

// imports
imports: [
    ...
    // HttpModule // remove
    HttpClientModule, // replace
    MsalModule.forRoot({
        clientID: '00000000-0000-0000-0000-000000000000',
        authority: "https://login.microsoftonline.com/common/",
        validateAuthority: true,
        redirectUri: window.location.origin,
        cacheLocation: 'localStorage',
        postLogoutRedirectUri: window.location.origin,
        navigateToLoginRequestUrl: false,
        popUp: false,
        unprotectedResources: ["https://www.microsoft.com/en-us/"],
        protectedResourceMap: protectedResourceMap,
        logger: loggerCallback,
        correlationId: "1000",
        level: LogLevel.Info,
        piiLoggingEnabled: true
    })
    ....
],
providers: [
    // AdalService,  // remove
    // AuthGuard,    // remove
    {provide: HTTP_INTERCEPTORS, useClass: MsalInterceptor, multi: true}   // add
    ],

app.component.ts

constructor(
  ...
  //private adalService: AdalService,
  private broadcastService: BroadcastService,
  private authService: MsalService
) {
  this.isIframe = window !== window.parent && !window.opener;
  //this.adalService.init(this.secretService.adalConfig);
  //this is moved to app.module.ts
}

ngOnInit(): void {
  this.subscription = this.broadcastService.subscribe("msal:loginSuccess", 
    (payload) => {
      console.log("login success " + JSON.stringify(payload));    
      this.loggedIn = true;
      this.user = this.msalService.getUser();
    });  
}

ngOnDestroy() {
  // disconnect from broadcast service on component destroy
  this.broadcastService.getMSALSubject().next(1);
  if (this.subscription) {
    this.subscription.unsubscribe();
  }
}

app.component.html

<div>
  ...
  <router-outlet style="text-align:center;" *ngIf="!isIframe"></router-outlet>
</div>

app.routing-module.ts

...
import { MsalGuard } from '@azure/msal-angular';

const routes: Routes = [
    { path: '', 
        component: DemoComponent,
        canActivate: [MsalGuard],
        children: [
          ...
        ]
    },
];

Summary

TLDR - the first two paragraphs outline the high level changes we need to apply.

Sorry for the big massive screens and screens of text - these code are from my Flow Studio - and I have to cut out parts of the code to show specific changes that we need to make.

Flow Studio subscription discount finishes very soon

Photo by Fabian Blank on Unsplash

Photo by Fabian Blank on Unsplash

This will very brief.

At the end of this weekend - Most of the Flow Studio customer’s accounts will flip from Trial to Free status. There is a current discount that will expire in less than two days.

Going forward, I intend to build out more and more features but they will all be behind the subscription. Aside from general UX changes the free tier is unlikely to receive additional features.

Subscriptions

As part of this initial roll over, I wanted to do a special discount to thank my current customers and encourage everyone to move to subscription. This discount is set at $70 / first year.

Future Discounts?

Since I believe Flow Studio to be excellent value at $10/month (or $100/year), I’m unlikely to offer any future discounts. I’m more interested in building compelling features that Makers, partners and businesses needs to be extra-successful.

I didn’t make this a big marketing fanfare, as I’m a learning startup founder and selling things is something I’m learning.

Currently status: I’m still better at making things than actually selling them.

Partnerships

Many of the current subscriptions are sold and tied to the Service User account that enterprise Flows are running under. Essentially this is a “prod” license.

We see a lot of conversations in the months ahead with Makers directly for their business, or with the consultancies that are implementing Microsoft Flow across tenants, to make sure the Flow Studio subscription is a great product both for creating better Flows as well as monitoring your running Flows.

Talk to us about these partnerships. Whether it is bulk licensing, or multiple tenant scenarios. We are very early in this stage so you get to get in the door first.

Some upcoming plans over the next two months:

  • Two major features still under development - if you are interested let me know we can talk privately about feature priorities.

  • A series of short YouTube videos that explains the hundreds of use cases that you need Flow Studio.

  • Probably a FlowStudio main site make over for users visiting and not logged in.

  • May be look for a co-founder?


Thank you

Thank you so much for your time, energy, support over the last 6 months since I pushed Flow Studio out the door. It’s a journey I’m very much enjoying, and I loved talking to you all about how to use it and why you would need it.

P.S sorry I said brief and then posted a wall of text again!

Flow Studio trial ends soon, what's next?

flow-studio-logo-90.png

This is a Flow Studio startup post. A few weeks back I locked myself in a room and added subscriptions to Flow Studio. In less than two weeks, the initial wave of free monthly trial will end. I’m writing this to be clear what we would be expecting.

Plan

  • Inspirations

  • Explain what will happen

  • Different Tiers of membership

  • v0.1.53

  • Where are we going next

  • Feedback


Inspirations

I have been listening to MS Cloud Show episode 276 where JD Trask from Raygun talks about startup with CJ and AC.

I have listened to it 5 times now.

  • (yes!) Add Subscription

  • (yes!) Do it via Strip

  • What do you mean paying is optional for your customers

  • It is a fear of rejection, it isn’t good enough and people won’t use it if it’s not free

OK. Wow. Startups are hard. Business is hard. Selling is hard. Development is actually easy, comparatively.

What will happen next?

Flow Studio subscription tiers were added on October 18 - so on November 18 - Trial status currently granted to all existing customers will flip over to Free status. Customers that joined after October 18 will have a full month of trial, so may flip after November 18.

This means these features will require a subscription

  • Edit JSON

  • Rename Actions

  • Admin

  • Get All Runs (slow) //better name pending**

  • (Bulk) trigger re-run


Different Tiers of Membership

These features will remain in a Free tier

  • Sort

  • Filter

  • Tag

  • Get Latest Runs // this pulls the latest page of 50 runs

The subscription tier is US$10 / month. There is a yearly discount at $100 / year.


New release Version 0.1.53

A lot of work has gone in to move Flow Runs into its own tab page, this gives us a lot of performance improvements to read lots of Flow Runs.

The expanding master-child grid is nice to look at but very difficult to keep the UI performance when there are a lot of Flows and Runs. There was also not a lot of space to do more work with the Flow Runs screen.


Where are we going next?

I’m hoping that customers will add weight to help me prioritize future features. Flow makers comes from all kinds of backgrounds and have very different use cases and needs.

I am adding more analytics - both for error trapping as well as feature detection, to work out what customers are using Flow Studio for. For example I know customers use sort a lot, but I don’t actually know which column they are all sorting by…

I also know customers click on /admin but I don’t know if customers actually have access to their tenant admin environment. So whether that is just exploration, or is that something that’s critical to their workload - and this affect what features I can add to admin.

Feedback

I’m always here - comment here, on Flow Studio issues or support @ flowstudio.app

These next two weeks will be a lot of thinking and planning for the next feature. Customers have the power to influence what that is.

Summary

  • Flow Studio trial wraps up on Sunday November 18 - customers will revert to Free status

  • Get All Runs and Bulk re-trigger becomes subscription feature

  • Email if you want to talk about bulk discounts for your company


/Back to work

Sending email with inline images via MicrosoftGraph and MicrosoftFlow

I had previously written how we can use Send Email as Anyone in Microsoft Graph, and as a bonus wrote a section on how we can use it to send inline images.

There was a small problem - sending email is very hard for app-only permissions - app accounts don’t have email boxes. So for that scenario to work, app accounts need a super crazy “send email as anyone” permission.

Sending email is a lot easier with delegate permission - if we have delegate permission Mail.Send - we can send inline attachments very easily. This is not an admin-tenant approval required permission, so any user can grant this.

Plan

Combine two techniques:

To aad.portal.azure.com

Add Mail.Send (delegate permission)

If you can “grant permissions” to your tenant, doing it here will immediately grant this to your current connection.

Otherwise we have to go back to our Flow connectors and make a new connection for batch.

Create our Flow

Here we are creating a MSGraph mail object JSON getting it ready for send

Using a $batch connector - call /me/sendMail

Results

Inline images are super useful for newsletter or emails where you want to include a nice header, signature or whatever in-between. This is a way to send these with Microsoft Graph and Microsoft Flow.