Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
dfcf1cc
docs: fix compilation errors in README examples
mcnulty-fp Feb 25, 2026
0f880a9
chore: update year in LICENSE
mcnulty-fp Feb 25, 2026
684d290
chore: fix package in SealedResults example
mcnulty-fp Feb 26, 2026
918898a
ci: add format-check job to test workflow
mcnulty-fp Feb 23, 2026
060c347
chore: set jvmArgs for google-java-format invocation
mcnulty-fp Feb 23, 2026
768947b
fix: use GraalVM native binaries for google-java-format
mcnulty-fp Feb 24, 2026
0b357e1
chore: update google-java-format options
mcnulty-fp Feb 24, 2026
138fd00
style: reformat source files to align with Google Java style
mcnulty-fp Feb 24, 2026
71516e5
fix: make sure generated code is formatted correctly
mcnulty-fp Feb 24, 2026
2d148c9
chore: externalize google-java-format version to libs.versions.toml
mcnulty-fp Feb 24, 2026
353a62a
chore: sort versions
mcnulty-fp Feb 24, 2026
ade4957
chore: remove unused import from RFC3339JavaTimeModule
mcnulty-fp Feb 24, 2026
7a86e28
chore: remove unused `getDiscriminatorPropertyName` method
mcnulty-fp Feb 24, 2026
98e55e2
chore: fail build on warnings
mcnulty-fp Feb 24, 2026
18c4e0f
chore: require source to only use Java 11 features
mcnulty-fp Feb 24, 2026
8cc3431
test: add ruleset evaluation tests
mcnulty-fp Feb 24, 2026
85b751a
chore: updates for latest schema
mcnulty-fp Feb 25, 2026
1362f86
chore: address compiler warning in generated ApiException on Java 21
mcnulty-fp Feb 26, 2026
78595e2
chore: disable a warning when compiling the template on Java 21
mcnulty-fp Feb 26, 2026
a2a7151
chore: remove wild card imports from test code
mcnulty-fp Feb 26, 2026
d43ff2b
fix: fix compilation errors in FingerprintApi examples
mcnulty-fp Feb 26, 2026
f11b4a3
chore: add Java 25 GH workflow matrices
mcnulty-fp Feb 26, 2026
d998704
fix: make enum value deserialization forward compatible
mcnulty-fp Feb 27, 2026
4f461d4
chore: fix typo in java parameter name
mcnulty-fp Feb 27, 2026
8b28246
chore: fix typo in FingerprintApiTest
mcnulty-fp Feb 27, 2026
36b2040
Merge pull request #2 from fingerprintjs/style/INTER-1443-fix-imports
mcnulty-fp Feb 27, 2026
4d3f397
Merge pull request #7 from fingerprintjs/fix/INTER-1819-future-enum-v…
mcnulty-fp Feb 27, 2026
5cb6e12
Merge pull request #6 from fingerprintjs/test/INTER-1819-improvements
mcnulty-fp Feb 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/functional.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,14 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ '11', '17', '21' ]
java: [ '11', '17', '21', '25' ]

steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '${{ matrix.java }}'
- run: ./gradlew runFunctionalTests
# Gradle will detect the JVM installation from the ubuntu-latest image and use it
- run: ./gradlew runFunctionalTests -PjavaVersion=${{ matrix.java }} --stacktrace
env:
FPJS_API_SECRET: "${{ secrets.FPJS_API_SECRET }}"
FPJS_API_REGION: "${{ secrets.FPJS_API_REGION }}"
Expand Down
18 changes: 11 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ on:
- main

jobs:
format-check:
runs-on: ubuntu-latest
name: Check source code formatting
steps:
- uses: actions/checkout@v4
- run: ./gradlew :sdk:checkFormat :examples:checkFormat --continue

test:
runs-on: ubuntu-latest
name: Java ${{ matrix.Java }}
name: Java ${{ matrix.java }}
strategy:
matrix:
java: [ '11', '17', '21' ]
java: [ '11', '17', '21', '25' ]

steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: '${{ matrix.java }}'
# Gradle will detect the JVM installation from the ubuntu-latest image and use it
# includes execution of unit tests
- run: ./gradlew build
- run: ./gradlew build -PjavaVersion=${{ matrix.java }} --stacktrace
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 FingerprintJS
Copyright (c) 2026 FingerprintJS

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
44 changes: 18 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,12 @@ Then manually install the following JARs:
Please follow the [installation](#installation) instruction and execute the following Java code:

```java
package main;
package com.fingerprint.example;

import com.fingerprint.v4.api.FingerprintApi;
import com.fingerprint.v4.model.Events;
import com.fingerprint.v4.model.UpdateEvents;
import com.fingerprint.v4.model.VisitorsGetResponse;
import com.fingerprint.v4.model.Event;
import com.fingerprint.v4.model.EventSearch;
import com.fingerprint.v4.model.EventUpdate;
import com.fingerprint.v4.sdk.ApiClient;
import com.fingerprint.v4.sdk.ApiException;
import com.fingerprint.v4.sdk.Configuration;
Expand Down Expand Up @@ -146,32 +146,32 @@ public class FingerprintApiExample {
FingerprintApi api = new FingerprintApi(client);
// Get an event with a given requestId
try {
// Fetch the event with a given requestId
Event response = api.getEvent(FPJS_EVENT_ID);
System.out.println(response.getProducts().toString());
// Fetch the event with a given eventId
Event event = api.getEvent(FPJS_EVENT_ID);
System.out.println(event.getIdentification().getVisitorId());
} catch (ApiException e) {
System.err.println("Exception when calling FingerprintApi.getEvent:" + e.getMessage());
}

// Search events with custom filters
try {
// By visitorId
SearchEventsResponse response = api.searchEvents(LIMIT, new FingerprintApi.SearchEventsOptionalParams().setVisitorId(FPJS_VISITOR_ID));
EventSearch response = api.searchEvents(new FingerprintApi.SearchEventsOptionalParams().setLimit(LIMIT).setVisitorId(FPJS_VISITOR_ID));
// Next page
// SearchEventsResponse response = api.searchEvents(LIMIT, new FingerprintApi.SearchEventsOptionalParams().setPaginationKey(response.getPaginationKey()).setVisitorId(FPJS_VISITOR_ID));
// EventSearch response = api.searchEvents(new FingerprintApi.SearchEventsOptionalParams().setLimit(LIMIT).setPaginationKey(PAGINATION_KEY).setVisitorId(FPJS_VISITOR_ID));
// Bad bot
// SearchEventsResponse response = api.searchEvents(LIMIT, new FingerprintApi.SearchEventsOptionalParams().setBot("bad"));
// EventSearch response = api.searchEvents(new FingerprintApi.SearchEventsOptionalParams().setLimit(LIMIT).setBot(SearchEventsBot.BAD));
// Filtered by IP
// SearchEventsResponse response = api.searchEvents(LIMIT, new FingerprintApi.SearchEventsOptionalParams().setIpAddress("192.168.0.1/32"));
System.out.println(response.getEvents().toString());
// EventSearch response = api.searchEvents(new FingerprintApi.SearchEventsOptionalParams().setLimit(LIMIT).setIpAddress("192.168.0.1/32"));
System.out.println(response.getEvents());
} catch (ApiException e) {
System.err.println("Exception when calling FingerprintApi.searchEvents:" + e.getMessage());
}

// Update an event with a given requestId
try {
EventUpdate request = new EventUpdate();
request.setLinkedId("myNewLinkedId");
request.setLinkedId(FPJS_LINKED_ID);
api.updateEvent(FPJS_EVENT_ID, request);
} catch (ApiException e) {
System.err.println("Exception when calling FingerprintApi.updateEvent:" + e.getMessage());
Expand All @@ -191,10 +191,10 @@ public class FingerprintApiExample {

This SDK provides utility methods for decoding [sealed results](https://dev.fingerprint.com/docs/sealed-client-results).
```java
package com.fingerprint.v4.example;
package com.fingerprint.example;

import com.fingerprint.v4.Sealed;
import com.fingerprint.v4.model.EventsGetResponse;
import com.fingerprint.v4.model.Event;

import java.util.Base64;

Expand All @@ -206,7 +206,7 @@ public class SealedResults {
// Base64 encoded key generated in the dashboard.
String SEALED_KEY = System.getenv("BASE64_KEY");

final EventsGetResponse event = Sealed.unsealEventResponse(
final Event event = Sealed.unsealEventResponse(
Base64.getDecoder().decode(SEALED_RESULT),
// You can provide more than one key to support key rotation. The SDK will try to decrypt the result with each key.
new Sealed.DecryptionKey[]{
Expand All @@ -220,7 +220,6 @@ public class SealedResults {
// Do something with unsealed response, e.g: send it back to the frontend.
}
}

```
To learn more, see the [Sealed results example](/examples/src/main/java/com/fingerprint/example/SealedResults.java).

Expand All @@ -229,6 +228,8 @@ This SDK provides utility method for verifying the HMAC signature of the incomin

Here is an example implementation using Spring Boot:
```java
package com.fingerprint.example;

import com.fingerprint.v4.sdk.WebhookValidation;

@RestController
Expand Down Expand Up @@ -319,15 +320,6 @@ Class | Method | HTTP request | Description
- [WebGlExtensions](docs/WebGlExtensions.md)


## Documentation for Authorization

Authentication schemes defined for the API:
### bearerAuth


- **Type**: HTTP basic authentication


## Documentation for sealed results

- [Sealed](docs/Sealed.md)
Expand Down
129 changes: 129 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,136 @@
import java.net.URL
import java.nio.file.Files
import java.nio.file.StandardCopyOption

val projectVersion: String by project

allprojects {
group = "com.github.fingerprintjs"
version = projectVersion
}

plugins {
alias(libs.plugins.google.osdetector)
}

// Register formatting tasks for each subproject

val googleJavaFormatExeFile = layout.buildDirectory.file("google-java-format.exe")
val googleJavaFormatOsSuffixMap = mapOf(
"osx" to "darwin",
"linux" to "linux",
"windows" to "windows",
)
val googleJavaFormatArchSuffixMap = mapOf(
"x86_64" to "x86-64",
"aarch_64" to "arm64"
)
val googleJavaFormatBinarySuffixMap = mapOf(
"osx" to "",
"linux" to "",
"windows" to ".exe"
)

tasks.register("downloadGoogleJavaFormat") {
val osSuffix = googleJavaFormatOsSuffixMap[osdetector.os]
val archSuffix = googleJavaFormatArchSuffixMap[osdetector.arch]
val binarySuffix = googleJavaFormatBinarySuffixMap[osdetector.os]
val googleJavaFormatVersion = libs.versions.google.java.format.get()
val downloadUrl = "https://github.com/google/google-java-format/releases/download/v$googleJavaFormatVersion/google-java-format_${osSuffix}-${archSuffix}${binarySuffix}"
outputs.file(googleJavaFormatExeFile)

doLast {
val file = googleJavaFormatExeFile.get().asFile
if (!file.exists()) {
println("Downloading google-java-format for $osSuffix-$archSuffix")
URL(downloadUrl).openStream().use { input ->
Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
file.setExecutable(true)
} else {
println("google-java-format already downloaded")
}
}
}


fun Project.registerFormatTasks() {
val inputFiles = fileTree("src") {
include("**/*.java")
}.files.map { it.absolutePath }

tasks.register("format") {
dependsOn(rootProject.tasks.named("downloadGoogleJavaFormat"))

doLast {
// First, fix the imports
exec {
executable = googleJavaFormatExeFile.get().asFile.toPath().toString()
args = listOf("--replace", "--fix-imports-only") + inputFiles
}

// Second, format the code. This ensures that the removal of unused imports does not
// introduce the need to run another formatting pass to pass the formatting checks.
exec {
executable = googleJavaFormatExeFile.get().asFile.toPath().toString()
args = listOf("--replace", "--skip-javadoc-formatting", "--skip-reflowing-long-strings") + inputFiles
}
}
}

tasks.register<Exec>("checkFormat") {
dependsOn(rootProject.tasks.named("downloadGoogleJavaFormat"))

doFirst {
println("The following source files need to be formatted:")
}

executable = googleJavaFormatExeFile.get().asFile.toPath().toString()
args = listOf("--dry-run", "--set-exit-if-changed", "--skip-javadoc-formatting", "--skip-reflowing-long-strings") + inputFiles
}
}

subprojects {
registerFormatTasks()

plugins.withType<org.gradle.api.plugins.JavaPlugin> {
tasks.withType<JavaCompile> {
// Target java 11
options.release = 11

// Enable all warnings and fail the build for any warnings
options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror"))
}

// Print Java toolchain for compilation
tasks.withType<JavaCompile>().configureEach {
doFirst {
println("Compiling with Java: ${javaCompiler.get().metadata.installationPath} (version: ${javaCompiler.get().metadata.languageVersion})")
}
}

// Print Java toolchain used for running tests
tasks.withType<Test>().configureEach {
doFirst {
val launcher = javaLauncher.orNull
if (launcher != null) {
println("Testing with Java: ${launcher.metadata.installationPath} (version: ${launcher.metadata.languageVersion})")
} else {
println("Testing with Java: launcher not set")
}
}
}

// Print Java toolchain used for running
tasks.withType<JavaExec>().configureEach {
doFirst {
val launcher = javaLauncher.orNull
if (launcher != null) {
println("Running JavaExec with Java: ${launcher.metadata.installationPath} (version: ${launcher.metadata.languageVersion})")
} else {
println("Running JavaExec with Java: launcher not set")
}
}
}
}
}
2 changes: 2 additions & 0 deletions docs/BotInfo.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Extended bot information.
| SIGNED | "signed" |
| SPOOFED | "spoofed" |
| UNKNOWN | "unknown" |
| UNSUPPORTED_VALUE_SDK_UPGRADE_REQUIRED | "unsupported_value_sdk_upgrade_required" |



Expand All @@ -35,6 +36,7 @@ Extended bot information.
| LOW | "low" |
| MEDIUM | "medium" |
| HIGH | "high" |
| UNSUPPORTED_VALUE_SDK_UPGRADE_REQUIRED | "unsupported_value_sdk_upgrade_required" |



2 changes: 2 additions & 0 deletions docs/BotResult.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ Bot detection result:

* `NOT_DETECTED` (value: `"not_detected"`)

* `UNSUPPORTED_VALUE_SDK_UPGRADE_REQUIRED` (value: `"unsupported_value_sdk_upgrade_required"`)



2 changes: 2 additions & 0 deletions docs/ErrorCode.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,7 @@ Error code:

* `RULESET_NOT_FOUND` (value: `"ruleset_not_found"`)

* `UNSUPPORTED_VALUE_SDK_UPGRADE_REQUIRED` (value: `"unsupported_value_sdk_upgrade_required"`)



Loading
Loading