Gradle 5.0 and Runtime Dependencies

I recently ran into a problem with CI, where an upgrade to Gradle 5 in their containers broke all of my builds. It seemed like some of the nested dependencies weren’t being downloaded and therefore not found at compile time, so I was getting a ton of Java compilation errors:

package xxxxx does not exist.

My leading theory is this was a consequence of Gradle 5 separating out compile and runtime dependencies.

So, I suspect that some of my dependencies list their dependencies as runtime dependencies, which had previously been included at compilation time by default. However, with Gradle 5 not doing that anymore, the nested dependencies were not being downloaded at compile time and therefore missing. Since I don’t have control of these third-party libraries, I had to explicitly list the missing dependencies as my own compile dependencies to work around the compilation errors.

Angular In-Memory-Web-Api For E2E Testing

Recently, I updated an Angular app with an HTTP interceptor to handle HTTP exceptions, from this tutorial — what an amazingly simple way to add that kind of feature! While adding an alert window to notify the user of a failed REST call was simple, I then found out that my e2e tests with Protractor failed because I didn’t have a live backend during the tests … which meant that my alert windows popped up automatically during each e2e test. I couldn’t figure out how to mock out my REST API, though. nock wasn’t working at all, and it seems like Angular took out their $httpMockBackend (plus Protractor doesn’t play well with it, even if it was still present).

Then, I stumbled upon the In-Memory-Web-Api for Angular, and it seemed like the perfect solution. However, turns out it doesn’t automagically work for nested REST URLs, i.e. api/namespace/resources and api/namespace2/entities — it has a very strict expectation for the REST API pattern by default, which I didn’t understand from the documentation. It assumes that the first path element after the apiBase (in the config) is the collection name, and the second is the element id — for example, namespace would be the collection and resources the id of a given namespace entity. i.e.

   GET /api/resources
   GET /api/resources/1

would work with the default methods, but

   GET /api/students/resources
   GET /api/students/resources/1
   GET /api/teachers/resources
   GET /api/teachers/resources/5

would require some customization.

So if your API pattern is a little different, you have to get creative. What I wound up doing was to parse all the get requests per my own pattern, similar to this example, which is duplicated below:

  // HTTP GET interceptor
  get(reqInfo: RequestInfo) {
    const collectionName = reqInfo.collectionName;
    if (collectionName === 'villains') {
      return this.getVillains(reqInfo);
    return undefined; // let the default GET handle all others

You can also create a custom URL parser, which is probably cleaner than my hack. Code duplicated below:

  // parseRequestUrl override
  // Do this to manipulate the request URL or the parsed result
  // into something your data store can handle.
  // This example turns a request for `/foo/heroes` into just `/heroes`.
  // It leaves other URLs untouched and forwards to the default parser.
  // It also logs the result of the default parser.
  parseRequestUrl(url: string, utils: RequestInfoUtilities): ParsedRequestUrl {
    const newUrl = url.replace(/\/foo\/heroes/, '/heroes');
    // console.log('newUrl', newUrl);
    const parsed = utils.parseRequestUrl(newUrl);
    console.log(`parseRequestUrl override of '${url}':`, parsed);
    return parsed;

Installing Angular Custom Libraries Locally With NPM

For one of my projects, I’m using Angular with a custom library called services, which I share across several apps (all in a monorepo). I then link the built version of services locally via npm link, so it appears in package.json as a dependency (this looks like "services": "file:dist/services").

However, when I tried consolidating the code and running it through CI, I kept running into an error when running $ npm install:

#!/bin/bash -eo pipefail
npm install
npm WARN checkPermissions Missing write access to node_modules/services
npm ERR! path node_modules/services
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall access
npm ERR! enoent ENOENT: no such file or directory, access 'node_modules/services'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent

npm ERR! A complete log of this run can be found in:
npm ERR!     ~/.npm/_logs/xxxxx-debug.log
Exited with code 254

Even if I created node_modules/services before running $ npm install, I would get the same error. Turns out that because "services" is a locally installed module, npm tries to create a symlink from node_modules/services to dist/services — which doesn’t exist in a clean CI container. Chicken-and-egg problem (can’t run $ ng build services to populate dist/services without having the other NPM modules installed). So I actually had to create dist/services manually before running $ npm install. Oops!