Skip to content

Instantly share code, notes, and snippets.

@drewwiens
drewwiens / Jenkinsfile
Last active June 27, 2022 17:30
Cypress e2e and Jenkinsfile: Retries the failed e2e spec files. Passes the stage (don't fail) if all test cases that failed the first time pass the second time. Ignores test cases that fail the second time that passed the first time.
// The stage for e2e in the Jenkinsfile:
stage('E2E') {
steps {
script {
nodejs(your_jenkins_nodejs_plugin_config_options_here) {
try {
sh 'yarn start-server-and-script e2e:prod'
} catch (err) {
def specsToRetryFilePath = 'tmp/app-e2e/specs-to-retry.txt';
// Don't retry if file doesn't exist which happens
@drewwiens
drewwiens / main.ts
Last active May 11, 2022 18:59
Silly: Extract a single-spa-angular app's base path from the parent application's routes using ngContext that's added by Ivy to root component's DOM element. This relies on at least one component importing Router somewhere in the app. It's not that performant either, taking a couple hundred ms to find it.
import { enableProdMode, NgZone } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { Router, NavigationStart } from '@angular/router';
import { AppProps } from 'single-spa';
import {
singleSpaAngular,
getSingleSpaExtraProviders,
} from 'single-spa-angular';
import { AppModule } from './app/app.module';
@drewwiens
drewwiens / workaround-azure-gateway-timeout-for-a-slow-response.ts
Last active May 11, 2022 18:47
Work around Azure App Gateway timeout by returning a response as a stream that sends newline character periodically and then the response JSON. The response body to the client is a bunch of newlines followed by the JSON, which is still valid JSON. Only use if you for some reason can't get the gateway timeout limit increased.
import { finalize, firstValueFrom, timeout } from 'rxjs';
const KEEP_ALIVE_INTERVAL = 3 * 1000;
const KEEP_ALIVE_TIMEOUT = 2 * 60 * 1000;
export interface ErrorInHttpResponse {
errorMsg: string;
status: number;
name?: string;
}
@drewwiens
drewwiens / notify.service.ts
Created April 14, 2022 20:05
One-line console log/warn/error/info + Angular Material snackbar messages
// One-line console log/warn/error/info + Angular Material snackbar messages
//
// Example Usage:
//
// constructor(private notifySvc: NotifyService) {}
//
// Replaces:
// console.error(e);
// this.notifySvc.notify(e);
//
@drewwiens
drewwiens / app.module.ts
Last active April 1, 2022 16:27
Better NestJS pino-pretty logger output for local development
// Compact terminal output, colorized, with the following fields highlighted with
// brighter text for easier readability: request ID, request method, request URL,
// request Authorization header, response status code, and context which is usually
// a string passed when creating each logger object e.g. "SomeController".
import { Module } from '@nestjs/common';
import { blackBright, inverse } from 'cli-color';
import { pick } from 'lodash';
import { LoggerModule } from 'nestjs-pino';
@drewwiens
drewwiens / serve-static-files.module.ts
Last active October 22, 2021 15:26
NestJS module to serve a single page app and set its base href from an environment variable on startup
import { join } from 'path';
import { Module } from '@nestjs/common';
import { ServeStaticModule } from '@nestjs/serve-static';
import { replaceInFile } from 'replace-in-file';
const STATIC_FILES_DIR = join(__dirname, 'client');
const YOUR_GLOBAL_PREFIX = 'api'; // Typically imported from some other file
@Module({
@drewwiens
drewwiens / nestjs-expressjs-sub-app-sub-path.ts
Last active January 14, 2022 19:12
Serve a NestJS application from a sub-path using ExpressJS sub-app feature
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { urlencoded, json } from 'express';
import { AppModule } from './app/app.module';
(async () => {
const nestExpressAppAdapter = new ExpressAdapter();
const app = await NestFactory.create(AppModule, nestExpressAppAdapter, {});
@drewwiens
drewwiens / prune-node-modules.sh
Last active October 28, 2021 19:24
Simple shell script to prune unneeded packages from node_modules. For example, as a pre-processing step when building a Docker image of a Node app in an Nx monorepo that also contains frontend app(s). This reduced our Docker image by ~650 MB.
#!/bin/bash
# This script is somewhat unsafe, but may be useful. This script deletes dependencies
# in package.json by deleting any lines that match
# the regexp in the grep, runs yarn install --prod, and then reverts package.json and
# yarn.lock back to their original contents.
#
# This script assumes yarn 1.x, i.e. yarn classic. If using npm instead of yarn 1.x:
# 1. Replace "yarn.lock" with "package-lock.json"
# 2. Replace "yarn install --prod" with "npm install --only=prod"
@drewwiens
drewwiens / compress-filename-in-url.js
Last active July 16, 2021 15:40
JavaScript String Compression of Filenames in a URL
// JavaScript String Compression of Filenames in a URL
//
// Problem: Reduce length of URL caused by storing a list of long filenames in URL query parameter.
//
// Solution: Switch between a 5-bit alphabet of letters and 4-bit alphabet of numbers. Use a special
// character to switch between lowercase and uppercase ("shift"). Represent the resulting bit
// string as a URL-safe base62 encoded string.
//
// Results: From my test set of ~100 strings from real filenames on a project:
// avg compression ratio: 0.87
@drewwiens
drewwiens / drag-drop.command.ts
Created March 1, 2021 20:45
Testing Angular Material drag and drop with Cypress
Cypress.Commands.add(
'dragDrop',
{ prevSubject: false },
(selector: string, originIdx: number, destIdx: number) => {
// Based on https://stackoverflow.com/a/55436989
const elements = Cypress.$(selector);
[originIdx, destIdx].forEach((idx) => {
if (originIdx < 0 || originIdx >= elements.length) {
throw Error(
`Cannot index element ${idx} of ${elements.length} draggable elements.`,