Skip to content

Instantly share code, notes, and snippets.

@daniel-shuy
Last active November 20, 2024 11:46
Show Gist options
  • Save daniel-shuy/7575d0734680b6a2335d8c75237acb5c to your computer and use it in GitHub Desktop.
Save daniel-shuy/7575d0734680b6a2335d8c75237acb5c to your computer and use it in GitHub Desktop.
Dev Journal

2024

Keycloak

By default, keycloak.js uses a Session status iframe to detect if the user has logged out from the application in another browser tab/window.

Unfortunately, this feature doesn't work in some modern browsers, unless SSL/TLS is enabled.

As a workaround, we can conditionally disable the feature (using the checkLoginIframe option) if there is no SSL/TLS by checking if the webpage protocol is HTTPS, e.g.

keycloak.init({
    // ...
    initOptions: {
        // ...
        checkLoginIframe: window.location.protocol === 'https:',
        // ...
    },
    // ...
});

Spring

TestRestTemplate only ignores redirects and cookies if Apache Http Client is on the classpath.

2023

CSRF

Passing JWT tokens in a header (e.g. Authorization header) protects against CSRF attacks.

Git

Avoid adding IDE-specific paths to project repository's .gitignore files, as you then need to add them for every project and for every IDE used by contributors. Instead, add them to a global .gitignore file, e.g.

git config --global core.excludesFile ~/.gitignore

Gradle

The best way to define constant versions is to create a version catalog with a gradle/libs.versions.toml file.

The best way to share build logic between subprojects is to use convention plugins (https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html). Cross project configuration (subprojects/allprojects) is now discouraged (https://docs.gradle.org/current/userguide/sharing_build_logic_between_subprojects.html#sec:convention_plugins_vs_cross_configuration).

Properties can be shared using gradle.properties. Properties can be overridden in subprojects by creating another gradle.properties at the subproject level.

Advantages of Gradle over Maven:

  • Version Conflict Resolution: Gradle selects the highest version while Maven uses a nearest-first strategy (https://docs.gradle.org/current/userguide/dependency_resolution.html#sub:resolution-strategy). Maven's strategy can be problematic because it may resolve to a lower version of a transitive dependency than what a dependency requires, and fails at runtime. This can be mitigated using maven-enforcer-plugin's requireUpperBoundDeps rule.
  • Configuration Reuse: Maven uses parent POMs, which does not allow multiple inheritance. Gradle uses plugins, which allow multiple inheritance and are much more powerful. Plugins can even be defined in the same project using convention plugins.
  • Maven configuration uses XML, while Gradle configuration uses Groovy/Kotlin code, which is much more powerful (e.g. create new tasks, add dependencies between tasks, etc).
  • To publish a test library in Maven, the tests need to be packaged as a separate library. Gradle has test fixtures.
  • Incremental build that is reliable. No more having to run clean before every build!

Java

Enum names can be used as compile-time constants using Lombok's @FieldNameConstants, by setting @FieldNameConstants#onlyExplicitlyIncluded to true, and annotating enum fields with @FieldNameConstants.Include (see projectlombok/lombok#2731 (comment)), e.g.

@FieldNameConstants(onlyExplicitlyIncluded = true)
public enum Level {
    @FieldNameConstants.Include LOW,
    @FieldNameConstants.Include MEDIUM,
    @FieldNameConstants.Include HIGH,
    ;
}

To get the PID of a running Java process, run:

jps

(requires a JDK to be on the PATH)


To capture a heap dump of a running Java process, run:

jmap -dump:live,format=b,file=/output/file/path.hprof <PID>

(requires a JDK to be on the PATH)

JavaScript

To export a module as a CommonJS, UMD and ES module:

// ES: directly export the module features
export function isPrime(x) {
  // ...
}

// CommonJS/UMD: export namespace
export as namespace mathLib;

Usage example:

<!-- UMD -->
<script src="math-lib.js"></script>
<script>
    mathLib.isPrime( ... );
</script>
// CommonJS
var mathLib = require('math-lib');

mathLib.isPrime( ... );
// ES
import { isPrime } from "math-lib";

isPrime( ... );

Kotlin

For libraries, enable Explicit API Mode to improve the library's public API, e.g.

// build.gradle.kts

kotlin {
  explicitApi()
}

To build with a lower version of Kotlin, use the latest Kotlin JVM Gradle plugin , but set kotlin.coreLibrariesVersion to the target Kotlin version, e.g.

# gradle/libs.versions.toml

[versions]
kotlinLib = "1.6.10"
kotlinPlugin = "1.8.22"

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlinPlugin" }
// build.gradle.kts

kotlin {
    coreLibrariesVersion = libs.versions.kotlinLib.get()
}

The version of the Kotlin JVM Gradle plugin cannot be defined more than once in the classpath (even if it is the same version everywhere). As a workaround, define the plugin at the root project, and disable it if it is not used in the root project, e.g.

Version Catalog (libs.versions.toml)

# gradle/libs.versions.toml

[versions]
kotlin = "1.8.22"

[plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
// build.gradle.kts

plugins {
  alias(libs.plugins.kotlin.jvm) // Kotlin Gradle plugin must be loaded in the parent/root project
}

Gradle Properties (gradle.properties)

# gradle.properties
kotlinVersion=1.8.22
// build.gradle.kts

plugins {
  id("org.jetbrains.kotlin.jvm") version "${kotlinVersion}" apply false // Kotlin Gradle plugin must be loaded in the parent/root project
}

Keycloak

If relative paths are used for client Valid Redirect URIs, they will be relative to the client root URL, which defaults to the auth server root URL. This is especially useful when used with a configuration as code tool like keycloak-config-cli, because the clients' Valid Redirect URIs does not need to be customized for each environment (e.g. sqa, prod).

Selenium

Since Selenium 4.6, there is no longer a need to manually install web drivers, Selenium will automatically download the right web drivers if they are not specified/found (https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location/#use-the-latest-version-of-selenium).

Sonatype OSSRH

Use a user token instead of username/password for publishing artifacts to Maven Central. To generate a user token, login to the Sonatype Nexus Repository Manager (https://oss.sonatype.org/), then go to Profile -> User Token -> Access -> Access User Token.

Spring Boot

When configuring the path of a resource files from classpath (e.g. src/main/resources) in a @ConfigurationProperties class, instead of using String (and loading it using ClassLoader#getResourceAsStream()), use a Resource (and load it using Resource#getInputStream()).

The configuration file can then use the classpath:/classpath*: prefix to specify that it is a ClassPathResource (e.g. classpath:path/to/resource/file). This gives the property flexibility to specify other kinds of Resources, e.g. https:, file:, etc. (see https://docs.spring.io/spring-framework/reference/core/resources.html).


If a @Configuration class does not use inter-bean references, the proxyBeanMethods parameter for the @Configuration annotation can be set to false to disable CGLIB subclass processing. This is usually used in Spring Boot Auto-configuration libraries.

The docs for @LocalServerPort suggests using field injection. But to use field injection in Kotlin, the field needs to be lateinit, or needs to have a default value, and lateinit does not work with primitives, and @LocalServerPort does not seem to work with delegated properties. Since @LocalServerPort is an Int, the field must be typed as Number instead to work with lateinit. A much easier way is to annotate the test class with @TestConstructor and inject @LocalServerPort in the constructor instead.


Since Spring Boot 2.3.x, the ClassLoader implementation used in Spring Boot executable JARs, LaunchedURLClassLoader, loads application classes (in BOOT-INF/classes) before library classes (in BOOT-INF/lib) (see spring-projects/spring-boot#9128 and spring-projects/spring-boot#45b1ab4). This means that we can override classes in libraries with classes in the application by placing it in the same classpath. This is especially useful for core/customization projects, because resource files can be overridden by giving it the same path, without having to change the path configuration.

WSL

To open web links with the default browser on Windows:

  1. Install wslu
  2. Set export BROWSER=wslview in WSL

Use wslgit instead of Git for Windows, because:

  • Git for Windows doesn't preserve file mode bits (which is used for permissions)
  • wslgit allows running Git hooks in WSL (where programming tools should be installed/ran)

Install git-credential-manager (GCM) for Windows standalone (https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/install.md#standalone-installation). Configure git in WSL to use GCM for Windows (https://github.com/git-ecosystem/git-credential-manager/blob/release/docs/wsl.md#configuring-wsl-without-git-for-windows).

To use KDiff3 as the diff tool in both Windows and WSL, configure the following in .gitconfig (WSL):

[diff]
    guitool = kdiff3
[difftool]
    prompt = false
[difftool "kdiff3"]
    path = "/mnt/c/Program Files/KDiff3/bin/kdiff3.exe"
    # Unix style paths must be converted to windows path style
    cmd = kdiff3.exe \"`wslpath -w $LOCAL`\" \"`wslpath -w $REMOTE`\"
    trustExitCode = false
[merge]
    tool = kdiff3
[mergetool]
    keepBackup = false
    prompt = false
    path = "/mnt/c/Program Files/KDiff3/bin/kdiff3.exe"
    trustExitCode = false

To install Jetbrains IDEs (e.g. Intellij IDEA) in WSL:

  1. Install libfuse2, libgtk-3-bin, mesa-utils, libnss3 and libasound2:
    sudo apt update; sudo apt install libfuse2 libgtk-3-bin mesa-utils libnss3 libasound2
  2. Download Jetbrains Toolbox for Linux (.tar.gz)
  3. Untar the .tar.gz file and run it, which will install Jetbrains Toolbox in ~/.local/share/JetBrains/Toolbox
  4. Add ~/.local/share/JetBrains/Toolbox/bin and ~/.local/share/JetBrains/Toolbox/scripts to $PATH
  5. Run Jetbrains Toolbox using jetbrains-toolbox. Use Jetbrains Toolbox to install IDEs

2022

Webpack DevServer proxy (and by extension, Angular proxy) bypass requires target to be configured, even if it is not used (eg. proxy to file using fs.createReadStream())

Keycloak

REST API for Quarkus distribution (the default since Keycloak 17) no longer has /auth in the context path. Set KC_HTTP_RELATIVE_PATH to /auth to re-introduce it (https://www.keycloak.org/migration/migrating-to-quarkus#_default_context_path_changed)

The official Docker image for Keycloak is located at Quay.io (quay.io/keycloak/keycloak). Since Keycloak 17 though, the official Docker images are also published to Docker Hub (keycloak/keycloak). See keycloak/keycloak#11987 for more information.

Official Keycloak Docker image does not define a default ENTRYPOINT. A command needs to be passed to /opt/keycloak/bin/kc.sh. Use:

NPM

Use npm link to test libraries locally. Use --save to save the file: reference to package.json and package-lock.json to better indicate that you are linking to a local dependency (but don't commit the changes to Git).

Angular

If provider has dependencies (deps), function passed to useFunction cannot be an arrow function.

Docker

  • When pinning a Docker image to a specific SHA256 digest, the digest for the overall manifest list should be used, so that it will work on any OS/ARCH platform.
  • Unfortunately, Docker Hub UI does not display the overall manifest list digest (see docker/roadmap#262). Until it is implemented, the current workaround to get the latest overall manifest list digest is to either pull the image tag without specifying a digest, or using the Docker Hub Registry API (docker/roadmap#262 (comment)).

Jenkins

The job DSL API for plugins can be viewed at <Jenkins URL>/plugin/job-dsl/api-viewer/index.html.

To restart Jenkins from the web UI, go to <Jenkins URL>/safeRestart.

Maven

  • To override a scalar property (e.g. String) of a plugin's configuration that was defined in a parent POM as null, use combine.self="override" (normally, scalar properties will be directly overridden, while vector properties will be merged. However, empty/null child scalar properties will be ignored). For example:
<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <classifier combine.self="override"/>
      </configuration>
    </plugin>
  </plugins>
</build>

Android

Android Chrome browser doesn't have DevTools. To debug a web application in Android, connect it to a desktop (also works with emulators), then go to chrome://inspect/#devices on the desktop's Chrome browser to remote target the Android Chrome browser.

If Chrome browser can't load or keeps crashing in Android emulator, disable Vulkan by adding the following to ~/.android/advancedFeatures.ini:

Vulkan = off
GLDirectMem = on

Flutter

To test Flutter Web applications on mobile browsers, run it on Web Server (flutter run -d web-server), then in an Android emulator, open Chrome and navigate to http://10.0.2.2:<port> (10.0.2.2 is the Host IP Address for AVD, and will differ for different emulators).

Jest

If a test needs to wait until all Promises complete before asserting (e.g. code that uses ExtendableEvent.waitUntil()), use:

await Promise(process.nextTick);

// assertions
expect(...)

Haskell

Data.HashMap.Strict and Data.HashSet from unordered-containers are Haskell's implementation of hashtables (similar to Java's HashMap and HashSet).

For maintain insertion order, use Data.HashMap.Strict.InsOrd or Data.HashSet.InsOrd from insert-ordered-containers (note that they are from different libraries, and do not extend a common class), which are Haskell's implementation of hashtables with linked list (similar to Java's LinkedHashMap and LinkedHashSet).

To sort by natural order (e.g. to sort Strings alphabetically), use Data.Set from containers (note that they are from different libraries, and do not extend a common class), which are Haskell's implementation of self-balancing binary tree (similar to Java's TreeMap and TreeSet).

Git

Signing commits ensures that the committer is who they claim to be. To autosign all commits with GPG:

  • Install gpg if not already installed. E.g. on Windows, install gpg4win, which comes with Kleopatra, a GUI tool to manage certificates.
  • Generate a new RSA secret key using gpg --full-generate-key. Choose the ECC key algorithm and Curve 25519 elliptic curve.
  • Publish the public key to a public keyserver (e.g. GitHub account settings).
  • List all public/private keys with their long form key IDs with gpg --list-secret-keys --keyid-format=long.
  • Copy the key ID of the private/secret key (sec) (after e.g. ed25519/).
  • Run git config --global user.signingKey <key ID>! to configure the secret key to use. Note the exclaimation mark (!) after the key ID, this is used to force GPG to use the specified key and not try to calculate which key to use (see https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html).
  • Run git config --global commit.gpgsign true to configure the Git client to automatically sign all commits with GPG.
  • On Windows, it may be required to run git config --global gpg.program <path to gpg> (Git for Windows defaults to using the embedded gpg). The path to gpg can be found by running where gpg (where.exe gpg in PowerShell).
  • Add the public key to the Git server, e.g. for GitHub, see https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-gpg-key-to-your-github-account.

Golang

  • One of the biggest pitfalls in Golang is the lack of file-level scopes. Files are imported by package. There is no way to differentiate between package and file scope. That means that even private members (members with names that start with lowercase) can be accessed by another file within the same package.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment