From 54021438d6ab4e27807579fb62e6315431a19430 Mon Sep 17 00:00:00 2001 From: Chet Bortz Date: Tue, 26 May 2026 13:24:06 -0400 Subject: [PATCH 1/4] enforce javadoc warnings as build failures --- .github/workflows/ci.yml | 15 +++++++++++++++ pom.xml | 3 +++ 2 files changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0829f09..d995545 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,3 +25,18 @@ jobs: - name: Run tests run: ./mvnw -B test + + javadoc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 11 + cache: maven + + - name: Verify Javadoc + run: ./mvnw -B javadoc:jar diff --git a/pom.xml b/pom.xml index ad404de..c2ccc53 100644 --- a/pom.xml +++ b/pom.xml @@ -174,6 +174,9 @@ org.apache.maven.plugins maven-javadoc-plugin 3.4.1 + + true + attach-javadocs From 6f4c30fc48306dc96ee3ca769ea54c1b7b8e2051 Mon Sep 17 00:00:00 2001 From: Chet Bortz Date: Tue, 26 May 2026 13:30:59 -0400 Subject: [PATCH 2/4] add missing @param and @return tags --- .../accessgrid/AccessGridClient.java | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/main/java/com/organization/accessgrid/AccessGridClient.java b/src/main/java/com/organization/accessgrid/AccessGridClient.java index a735082..88b6389 100644 --- a/src/main/java/com/organization/accessgrid/AccessGridClient.java +++ b/src/main/java/com/organization/accessgrid/AccessGridClient.java @@ -77,6 +77,8 @@ public AccessGridClient(String accountId, String apiSecret, HttpSender httpSende /** * Get the account ID. + * + * @return the AccessGrid account identifier this client was constructed with */ public String getAccountId() { return this.accountId; @@ -84,6 +86,8 @@ public String getAccountId() { /** * Access Cards API operations. + * + * @return an {@link AccessCardsApi} for issuing, listing, and managing access cards */ public AccessCardsApi accessCards() { return new AccessCardsApi(this); @@ -91,6 +95,8 @@ public AccessCardsApi accessCards() { /** * Console Management API operations. + * + * @return a {@link ConsoleApi} for card templates, landing pages, webhooks, HID, and billing */ public ConsoleApi console() { return new ConsoleApi(this); @@ -108,6 +114,9 @@ public static class AccessCardsApi { /** * Provision a new access card. + * + * @param request issuance parameters (template id, employee fields, expiration, etc.) + * @return the newly issued Card with server-assigned id, state, and install_url */ public Models.Card provision(Models.ProvisionCardRequest request) { String payload = client.serialize(request); @@ -116,6 +125,9 @@ public Models.Card provision(Models.ProvisionCardRequest request) { /** * Get details about a specific access card. + * + * @param cardId the access pass ex_id + * @return the Card record, including current state and metadata */ public Models.Card get(String cardId) { return client.get("/key-cards/" + cardId, cardId, Models.Card.class); @@ -123,6 +135,9 @@ public Models.Card get(String cardId) { /** * Update an existing access card. + * + * @param request update parameters; the card id is read from {@code request.getCardId()} + * @return the updated Card */ public Models.Card update(Models.UpdateCardRequest request) { String payload = client.serialize(request); @@ -131,6 +146,9 @@ public Models.Card update(Models.UpdateCardRequest request) { /** * List access cards with optional filters. + * + * @param params optional filters (template_id, state); may be null for no filters + * @return matching cards; empty list if none match */ public java.util.List list(Models.ListKeysParams params) { StringBuilder query = new StringBuilder(); @@ -150,6 +168,8 @@ public java.util.List list(Models.ListKeysParams params) { /** * List access cards without filters. + * + * @return all cards on the account */ public java.util.List list() { return list(null); @@ -157,6 +177,8 @@ public java.util.List list() { /** * Suspend an access card. + * + * @param cardId the access pass ex_id to suspend */ public void suspend(String cardId) { client.postEmpty("/key-cards/" + cardId + "/suspend", cardId); @@ -164,6 +186,8 @@ public void suspend(String cardId) { /** * Resume a suspended access card. + * + * @param cardId the access pass ex_id to resume */ public void resume(String cardId) { client.postEmpty("/key-cards/" + cardId + "/resume", cardId); @@ -171,6 +195,8 @@ public void resume(String cardId) { /** * Unlink an access card from its device. + * + * @param cardId the access pass ex_id to unlink */ public void unlink(String cardId) { client.postEmpty("/key-cards/" + cardId + "/unlink", cardId); @@ -178,6 +204,8 @@ public void unlink(String cardId) { /** * Delete an access card. + * + * @param cardId the access pass ex_id to delete */ public void delete(String cardId) { client.postEmpty("/key-cards/" + cardId + "/delete", cardId); @@ -203,6 +231,9 @@ public static class ConsoleApi { /** * Create a new card template. + * + * @param request template configuration (name, platform, use_case, protocol, styling, etc.) + * @return the created Template with server-assigned id */ public Models.Template createTemplate(Models.CreateTemplateRequest request) { String payload = client.serialize(request); @@ -211,6 +242,9 @@ public Models.Template createTemplate(Models.CreateTemplateRequest request) { /** * Update an existing card template. + * + * @param request fields to update; the template id is read from {@code request.getCardTemplateId()} + * @return the updated Template */ public Models.Template updateTemplate(Models.UpdateTemplateRequest request) { String payload = client.serialize(request); @@ -219,6 +253,9 @@ public Models.Template updateTemplate(Models.UpdateTemplateRequest request) { /** * Read a card template by ID. + * + * @param templateId the card template ex_id + * @return the full Template, including styling and association data */ public Models.Template readTemplate(String templateId) { return client.get("/console/card-templates/" + templateId, templateId, Models.Template.class); @@ -228,6 +265,9 @@ public Models.Template readTemplate(String templateId) { * Publish a card template. For Apple templates this transitions the * template to "in-review"; for Android (Google) templates it becomes * "ready" immediately. + * + * @param templateId the card template ex_id to publish + * @return the template id and resulting status ("publishing", "in-review", or "ready") */ public Models.PublishTemplateResponse publishTemplate(String templateId) { return client.post( @@ -245,6 +285,9 @@ public Models.PublishTemplateResponse publishTemplate(String templateId) { * + AES-256-GCM) so the private key never leaves this host in * plaintext. Each call must use a fresh public key — the server rejects * reuse. + * + * @param templateId the card template ex_id (must be a published Google SmartTap template) + * @return the decrypted SmartTap private key PEM plus key version, collector id, and pubkey fingerprint */ public Models.RevealTemplatePrivateKeyResponse revealTemplatePrivateKey(String templateId) { if (templateId == null || templateId.isEmpty()) @@ -280,6 +323,10 @@ public Models.RevealTemplatePrivateKeyResponse revealTemplatePrivateKey(String t /** * Get event logs for a card template. + * + * @param templateId the card template ex_id + * @param filters optional filters (device, start_date, end_date, event_type); may be null + * @return matching events; empty list if none */ public java.util.List eventLog(String templateId, Models.EventLogFilters filters) { StringBuilder query = new StringBuilder(); @@ -305,6 +352,9 @@ public java.util.List eventLog(String templateId, Models.EventLogF /** * Get event logs for a card template without filters. + * + * @param templateId the card template ex_id + * @return all events for the template */ public java.util.List eventLog(String templateId) { return eventLog(templateId, null); @@ -312,6 +362,9 @@ public java.util.List eventLog(String templateId) { /** * Get ledger/billing items. + * + * @param params optional pagination + date filters (page, per_page, start_date, end_date); may be null + * @return the matching ledger items with pagination metadata */ public Models.LedgerItemsResult ledgerItems(Models.LedgerItemsParams params) { StringBuilder query = new StringBuilder(); @@ -335,6 +388,8 @@ public Models.LedgerItemsResult ledgerItems(Models.LedgerItemsParams params) { /** * Get ledger/billing items without filters. + * + * @return the first page of ledger items at the server's default page size */ public Models.LedgerItemsResult ledgerItems() { return ledgerItems(null); @@ -342,6 +397,10 @@ public Models.LedgerItemsResult ledgerItems() { /** * iOS In-App Provisioning preflight. + * + * @param cardTemplateId the card template ex_id + * @param accessPassExId the access pass ex_id being provisioned + * @return the identifiers required to drive the Apple Wallet In-App Provisioning flow */ public Models.IosPreflightResponse iosPreflight(String cardTemplateId, String accessPassExId) { String payload = client.serialize(java.util.Map.of("access_pass_ex_id", accessPassExId)); @@ -350,6 +409,8 @@ public Models.IosPreflightResponse iosPreflight(String cardTemplateId, String ac /** * List all landing pages. + * + * @return every landing page on the account */ public java.util.List listLandingPages() { return java.util.Arrays.asList( @@ -359,6 +420,9 @@ public java.util.List listLandingPages() { /** * Create a new landing page. + * + * @param request landing-page configuration (name, kind, password-protection, styling, etc.) + * @return the created LandingPage with server-assigned ex_id */ public Models.LandingPage createLandingPage(Models.CreateLandingPageRequest request) { String payload = client.serialize(request); @@ -367,6 +431,9 @@ public Models.LandingPage createLandingPage(Models.CreateLandingPageRequest requ /** * Update an existing landing page. + * + * @param request fields to update; the landing page id is read from {@code request.getLandingPageId()} + * @return the updated LandingPage */ public Models.LandingPage updateLandingPage(Models.UpdateLandingPageRequest request) { String payload = client.serialize(request); @@ -375,6 +442,8 @@ public Models.LandingPage updateLandingPage(Models.UpdateLandingPageRequest requ /** * List pass template pairs. + * + * @return every pass template pair on the account */ public java.util.List listPassTemplatePairs() { Models.PassTemplatePairsResponse response = client.getWithParams( @@ -387,6 +456,9 @@ public java.util.List listPassTemplatePairs() { /** * Create a pass template pair. + * + * @param request the iOS + Android template ids to pair (both must be published and use the same protocol) + * @return the created PassTemplatePair */ public Models.PassTemplatePair createPassTemplatePair(Models.CreatePassTemplatePairRequest request) { String payload = client.serialize(request); @@ -395,6 +467,8 @@ public Models.PassTemplatePair createPassTemplatePair(Models.CreatePassTemplateP /** * Credential profile operations. + * + * @return a {@link CredentialProfilesApi} for listing and creating credential profiles */ public CredentialProfilesApi credentialProfiles() { return new CredentialProfilesApi(client); @@ -402,6 +476,8 @@ public CredentialProfilesApi credentialProfiles() { /** * Webhook operations. + * + * @return a {@link WebhooksApi} for listing, creating, and deleting webhook subscriptions */ public WebhooksApi webhooks() { return new WebhooksApi(client); @@ -409,6 +485,8 @@ public WebhooksApi webhooks() { /** * HID-related services. + * + * @return an {@link HIDApi} entry point for HID Origo organization operations */ public HIDApi hid() { return new HIDApi(client); @@ -434,6 +512,8 @@ public static class HIDApi { /** * HID Organizations API. + * + * @return a {@link HIDOrgsApi} for creating, listing, and activating HID organizations */ public HIDOrgsApi orgs() { return new HIDOrgsApi(client); @@ -452,6 +532,9 @@ public static class HIDOrgsApi { /** * Create a new HID organization. + * + * @param params organization creation parameters (name, contact, address) + * @return the created HIDOrg; check {@code status} for current state */ public Models.HIDOrg create(Models.CreateHIDOrgParams params) { String payload = client.serialize(params); @@ -460,6 +543,8 @@ public Models.HIDOrg create(Models.CreateHIDOrgParams params) { /** * List all HID organizations. + * + * @return every HID organization on the account */ public java.util.List list() { return java.util.Arrays.asList( @@ -469,6 +554,9 @@ public java.util.List list() { /** * Complete HID org registration with credentials. + * + * @param params activation parameters (org slug + the credentials returned by HID) + * @return the activated HIDOrg */ public Models.HIDOrg activate(Models.CompleteHIDOrgParams params) { String payload = client.serialize(params); @@ -488,6 +576,8 @@ public static class CredentialProfilesApi { /** * List all credential profiles. + * + * @return every credential profile on the account */ public java.util.List list() { return java.util.Arrays.asList( @@ -497,6 +587,9 @@ public java.util.List list() { /** * Create a new credential profile. + * + * @param request credential profile configuration (name, kind, bit format, key diversification, etc.) + * @return the created CredentialProfile with server-assigned ex_id */ public Models.CredentialProfile create(Models.CreateCredentialProfileRequest request) { String payload = client.serialize(request); @@ -516,6 +609,8 @@ public static class WebhooksApi { /** * List all webhooks. + * + * @return every webhook subscription on the account */ public java.util.List list() { Models.WebhooksResponse response = client.getWithParams( @@ -528,6 +623,9 @@ public java.util.List list() { /** * Create a new webhook. + * + * @param request webhook configuration (URL, auth_method, subscribed_events) + * @return the created Webhook; on bearer_token auth the {@code privateKey} is only present here */ public Models.Webhook create(Models.CreateWebhookRequest request) { String payload = client.serialize(request); @@ -536,6 +634,8 @@ public Models.Webhook create(Models.CreateWebhookRequest request) { /** * Delete a webhook. + * + * @param webhookId the webhook id to delete */ public void delete(String webhookId) { client.delete("/console/webhooks/" + webhookId); From ab05742060a596aae8e2bb05f46c4bf343ccde27 Mon Sep 17 00:00:00 2001 From: Chet Bortz Date: Tue, 26 May 2026 13:38:32 -0400 Subject: [PATCH 3/4] FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d995545..8ce104d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,13 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Opt in to Node 24 ahead of GitHub's 2026-06-02 forced upgrade. Both + # actions/checkout@v4 and actions/setup-java@v4 are already the latest + # major versions and ship Node 20 binaries; this env var lets the runner + # execute them under Node 24 today. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" + jobs: test: runs-on: ubuntu-latest From 578b1ec2ee0d6a2f827a0d73a373bb75eee86875 Mon Sep 17 00:00:00 2001 From: Chet Bortz Date: Tue, 26 May 2026 13:38:40 -0400 Subject: [PATCH 4/4] bump v 1.4.1 --- pom.xml | 2 +- .../accessgrid/AccessGridClient.java | 2 +- .../accessgrid/VersionConsistencyTest.java | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/organization/accessgrid/VersionConsistencyTest.java diff --git a/pom.xml b/pom.xml index c2ccc53..b647b23 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.accessgrid access-grid-sdk - 1.4.0 + 1.4.1 Access Grid SDK Java SDK for Access Grid API diff --git a/src/main/java/com/organization/accessgrid/AccessGridClient.java b/src/main/java/com/organization/accessgrid/AccessGridClient.java index 88b6389..803787d 100644 --- a/src/main/java/com/organization/accessgrid/AccessGridClient.java +++ b/src/main/java/com/organization/accessgrid/AccessGridClient.java @@ -22,7 +22,7 @@ */ public class AccessGridClient { private static final String DEFAULT_BASE_URL = "https://api.accessgrid.com/v1"; - private static final String VERSION = "1.3.0"; + private static final String VERSION = "1.4.1"; private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(30); private final String accountId; diff --git a/src/test/java/com/organization/accessgrid/VersionConsistencyTest.java b/src/test/java/com/organization/accessgrid/VersionConsistencyTest.java new file mode 100644 index 0000000..5101be4 --- /dev/null +++ b/src/test/java/com/organization/accessgrid/VersionConsistencyTest.java @@ -0,0 +1,41 @@ +package com.organization.accessgrid; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Locks the AccessGridClient.VERSION constant to the pom.xml project version. + * Drift between the two has shipped before (1.3.0 constant under a 1.4.0 pom), + * so this test fails the build if they disagree. + */ +public class VersionConsistencyTest { + + @Test + public void versionConstantMatchesPomXml() throws IOException, NoSuchFieldException, IllegalAccessException { + String pom = Files.readString(Paths.get("pom.xml")); + + Matcher m = Pattern.compile( + "access-grid-sdk\\s*([^<]+)" + ).matcher(pom); + + assertTrue(m.find(), "Could not find for access-grid-sdk in pom.xml"); + String pomVersion = m.group(1); + + Field versionField = AccessGridClient.class.getDeclaredField("VERSION"); + versionField.setAccessible(true); + String constantVersion = (String) versionField.get(null); + + assertEquals( + pomVersion, + constantVersion, + "AccessGridClient.VERSION must match pom.xml . Update both when bumping." + ); + } +}