Skip to content

Releases: tsanetgit/Connect_SDK

v5.0.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 31 Oct 22:59
47cdb4a

connect-library 5.0.0, attachment-receiver 5.0.0, connect-e2ee 5.0.0

Major version. connect-e2ee now reports a sender's keys being unavailable as their own verification outcome, instead of a SENDER_KEY refusal (tsanetgit/Connect_SDK#147). That adds a case to a sealed type, which breaks exhaustive switches at source. TsaNetApiSession becomes AutoCloseable, and the deprecated CaseNoteDto and CollaborationRequestStatusDto constructors are removed (tsanetgit/Connect_SDK#139). PinnedJwksPartnerKeys waits a minute after a failed fetch before fetching that company's keys again (tsanetgit/Connect_SDK#173), and a refused OAuth token call names its error code (tsanetgit/Connect_SDK#45). A token request that fails without being refused is now its own ConnectApiException, and PinnedJwksPartnerKeys is now AutoCloseable. The release also redacts credentials, tokens and partner-written text from the toString() of SDK records, and refuses JWKS URLs that have no host (tsanetgit/Connect_SDK#162). connect-library makes its mapping from the generated OpenAPI models to its records public, as OpenApiRecords (tsanetgit/Connect_SDK#146). attachment-receiver has no changes of its own; it moves to 5.0.0 with the others. Runtime unchanged: Spring Boot 4.1, Jackson 3, JDK 21.

The upgrade guide is the 5.0.0 entry in skills/connect-sdk-deploy/references/consume-artifact.md. Coming from 3.0.0 or earlier, apply the earlier entries first.

New in 5.0.0

  1. Verification.KeysUnavailable (#166). E2eeProfile.verifyCase and verifyNote return it when PartnerKeys throws IOException fetching the sending company's keys, either at first or on the refetch for an unknown kid.

    • Retry, don't refuse: it carries no value and the record wasn't judged. It comes before the replay check, so no jti is used up, and verifying the same record again later is checked from scratch, including as FIRST_RECEIPT.
    • Back off, and bound the retries: after a failed fetch, PinnedJwksPartnerKeys doesn't fetch that company's keys again for a minute. A retry inside the minute returns KeysUnavailable again without contacting the partner, and keys can stay unavailable for up to a minute after the last failed fetch, even once its endpoint has recovered. A set it holds that hasn't expired still answers for a kid in that set, and a kid that isn't there reads as KeysUnavailable during the wait. A timeout or a non-200 answer can clear on its own. But PinnedJwksPartnerKeys also throws for a company with no configured JWKS URL, and for a set that is too large, unparseable or holds a private key, which a retry won't fix until the configuration or the partner's set changes. Cap retries and alert when they run out.
    • Every other SENDER_KEY refusal is unchanged.
  2. Sensitive values are redacted in diagnostics (#165).

    • SDK records print <redacted> in toString() for passwords, client and webhook secrets, and authentication tokens.
    • Case, note and request tokens count as sensitive, because they work as capabilities, not just IDs. They are redacted too, including in webhook raw payloads.
    • Attachment parameters print their names only.
    • CaseNoteDto, CaseResponseDto, CollaborationRequestStatusDto and StoredAttachmentForwardResultDto leave partner-written text and personal details out of toString().
    • Constructors and accessors are unchanged. JSON serialization and explicit accessor calls are not redacted.
  3. OpenApiRecords, the public mapping from generated models to records (#168, #169). A connector that calls the generated client directly can build the records connect-e2ee takes with the same code the facades use, instead of keeping its own copy. A copy that drops a value a manifest covers gets the record refused at FIELDS.

    • collaborationRequest(CollaborationRequestStatusDTO) returns a CollaborationRequestStatusDto.
    • caseNote(CaseNoteDTO, caseToken) returns a CaseNoteDto. The generated note carries no case token, so pass the token of the case the note was read under; verifyNote compares it with the case's.
    • formField(FieldMetadataDTO) returns a FormFieldDto.
    • The parameters are generated types, so a rename in the Connect OpenAPI spec changes these public signatures.
  4. Sessions can be closed (#179). TsaNetApiSession is AutoCloseable. Each session holds a JDK HttpClient, with its selector thread and pooled connections, from the moment it opens, and until now nothing released them. Close a session you drop, for example on a credential change.

    • It doesn't wait: requests already sent run to completion. A later one fails the way an unreachable API does: ConnectApiException of kind CONNECTIVITY, or AttachmentV2Exception client/connectivity on the V2 facades. A send still running can't complete its grant, which stays open until it expires.
    • AccountScopedTsaNetApiSession goes back to having no session until the next login, and a login or authenticate as a different account closes the previous account's session.
    • Each V2 upload runs on a client of its own, stopped when it ends, so uploads no longer share pooled connections.
    • A Spring @Bean that returns a session is now closed when its context shuts down.
  5. PinnedJwksPartnerKeys is now AutoCloseable (#181). Close an instance you drop: a fetch already sent finishes, a set it holds still answers for a kid in that set, and a lookup that would fetch reads as KeysUnavailable.

Breaking changes in 5.0.0

  • A switch over Verification must handle KeysUnavailable. Verification is sealed, and now permits Verified, Refused and KeysUnavailable. An exhaustive switch without the new case stops compiling. A consumer that bumps without recompiling links, but throws MatchException at the first unavailable-keys read. Add the case and retry the record, with a limit:

    switch (e2ee.verifyCase(record, receipt)) {
        case Verified<DecryptedCase> ok -> handle(ok.content());
        case Refused<DecryptedCase> no -> audit(no.step(), no.field(), no.reason());
        case KeysUnavailable<DecryptedCase> later -> retryLater(record);   // a bounded number of times
    }
  • A default arm or an instanceof chain over Verification still compiles, and KeysUnavailable lands in that branch, or throws ClassCastException on a cast. Check those too, and treat KeysUnavailable as retry-with-a-limit, never as refused or verified.

  • TsaNetApiSession gains an abstract close(). Code that implements it must add one.

  • The deprecated record constructors are gone: CaseNoteDto's 14- and 18-argument forms and CollaborationRequestStatusDto's 10-, 11- and 13-argument forms. Call the canonical constructor (20 and 16 arguments), passing null for a value you don't have.

Also check in 5.0.0

  • Refusal steps that move. Step 3's party checks now run before the key lookup: the record naming no receiving party, and this member being neither its iss nor its aud. A record whose parties don't include this member is refused at CLAIMS, where it used to be refused at SENDER_KEY (wrong kid) or SIGNATURE. Anything that audits or alerts by step will see that shift. The reason strings are unchanged.
  • Unavailable keys aren't refusals any more. Code that treated a SENDER_KEY refusal with the reason "the sending company's public keys are unavailable" as retryable, by matching the string, should switch on KeysUnavailable instead. That refusal is no longer produced.
  • PinnedJwksPartnerKeys refuses a host-less URL when it's constructed. It throws IllegalArgumentException("A JWKS URL must have a host"). Before, such a URL failed only when it was fetched, as an unchecked exception. This covers every entry in the map, including companies you never read from.
  • toString() output changes on the records above. Code that parsed toString(), or that relied on a log line carrying a token, a note's text or a case's description, sees <redacted> or no field at all.
  • Messages without tokens. The SDK's own exception and sync messages no longer contain request tokens. WebhookInboundService masks the request token in a failed sync's message. Rows in webhook_inbound_event written by 4.0.0 keep their old messages.
  • A refused OAuth token call names its error code. The IllegalStateException message is OAuth client-credentials request failed: HTTP <status> (<code>) when the token endpoint answers with a known OAuth error code, for example invalid_client. Otherwise it stays ... HTTP <status>. Entra's error_description still isn't included, because it names the app. Code that matched the old message exactly should match its prefix.
  • Tokens and parse errors (#181). A token response whose access token isn't all visible ASCII now fails sign-in with a fixed message, and a malformed token response, an unusable token URL or a webhook callback URL that isn't a URI no longer has its value quoted in an exception. A token request that fails without being refused is now ConnectApiException of kind OTHER, status 0, title Token request: an unusable token URL or API base URL at sign-in, a token or login response the client can't read, a malformed one, a 5xx, 408 or 429 from the token endpoint, or a login response with no access token. Those were IllegalArgumentException, RestClientException or IllegalStateException, so a 4.0.0 catch stops matching, and an expired session that meets one no longer reads as a 401 Session expired. A refusal by the token endpoint stays IllegalStateException, and a 401 Session expired on renewal of an expired token.
  • A note with no priority is refused with A note priority is required (#181).
  • Malformed partner input no longer throws. A manifest or protected value that is the JSON null, or a recipient kid that is...
Read more

v4.0.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 29 Oct 14:44
3adf1a3

connect-library 4.0.0, attachment-receiver 4.0.0, connect-e2ee 4.0.0 (new)

Major version. Adds optional end-to-end encryption of Connect cases and notes, to the tsanet-e2ee-1 profile in the workgroup specification "Connect E2E Encryption" (tsanetgit/Connect_SDK#140). The platform holds no keys: each member encrypts and signs at its own connector. A connector that doesn't use E2EE needs no change to its calls. Runtime unchanged: Spring Boot 4.1, Jackson 3, JDK 21.

This release also carries everything in 3.0.0 (published 2026-10-21), repeated under "Included from 3.0.0" so a member coming from 2.0.0 has one list. Coming from 2.0.0, apply the 3.0.0 changes first.

The full upgrade guide is the 3.0.0 and 4.0.0 entries in skills/connect-sdk-deploy/references/consume-artifact.md.

New in 4.0.0

  1. connect-e2ee, a new optional artifact (#143). It encrypts and signs a case or note for a create, and verifies and decrypts one on a read.
    • Sending: E2eeProfile.encryptCase and encryptNote check the plaintext (required form fields and the spec's lengths), encrypt each protected field for the partner and the sender, and sign a manifest. The result goes straight into the create overloads below.
    • Receiving: verifyCase and verifyNote run the profile's six checks in order. The result is the decrypted content, or a refusal naming the step and the field that never carries a value. There is no plaintext fallback.
    • Keys: private keys stay behind KeyOperations, so they can live in a key service (SoftwareKeyOperations is for tests). Partner public keys come from pinned JWKS URLs (PinnedJwksPartnerKeys).
    • Replay: a ReplayGuard checks first receipt (an in-memory one is included).
    • Dependency: the module brings com.nimbusds:nimbus-jose-jwt 10.10. A connector that doesn't add the module gets no JOSE library.
    • Setup and the full API are in connect-e2ee/README.md.
  2. connect-library carries the E2EE fields (#141, #142).
    • createRequest (both forms) and createNote gain an overload taking E2eeFields(manifest, encrypted), sent exactly as given. With encrypted true, values are sent unstripped and the note length limits don't apply.
    • CaseNoteDto gains manifest and encrypted.
    • CollaborationRequestStatusDto gains manifest, encrypted, submitterCaseNumber, description and customFields (CaseCustomFieldDto(id, fieldName, value)).
    • The SQLite cache stores all of them, adding the columns to an existing cache on first open.

Breaking changes in 4.0.0

Existing calls compile, and a consumer that bumps without recompiling still links. Two kinds of code break at source, which is why this is a major:

  • Implementers and decorators of CollaborationRequestsFacade or CaseNotesFacade must add the new abstract overloads, and move their logic to them. The old overloads now forward to the new ones, so an override of the old overload alone no longer sees an E2EE create. Point interceptors and test stubs at the new overloads too. E2eeFields is in connect-library, so this needs no connect-e2ee.
  • Record patterns over CaseNoteDto or CollaborationRequestStatusDto gain components, so a 3.0.0 pattern stops compiling. The earlier constructors are deprecated but still work, which a -Werror build will flag.

Also check in 4.0.0

  • Records cached by 3.0.0 read the new components as null ("not known") until fetched again. On stored reads, null-check customFields and test Boolean.TRUE.equals(encrypted()) rather than unboxing encrypted(), which throws on an old row. For the same reason an old record doesn't equal a fresh read of the same case or note.
  • Logs and JSON: toString and JSON serialization include the new components, so logging or forwarding a CollaborationRequestStatusDto now carries the case's description and custom-field answers, not only its summary.
  • Case text on disk: the cache now holds each case's description and custom-field answers as well as its summary. For an encrypted case it holds the ciphertext the platform returned; connect-e2ee never writes decrypted text to it. CacheRetention.sweep evicts closed and rejected cases; it never sweeps an open one.

Known limits in 4.0.0

  • Form rules: connect-e2ee checks required and lengths, but not a form field's options or validationRules, whose format the API doesn't document (tsanetgit/Connect-API-Code#221).
  • Two encryption keys: a partner publishing two encryption keys is refused until the workgroup sets a rule. Publish only your current encryption key.
  • Sanitizer workaround: while the API's sanitizer rewrites encrypted values (tsanetgit/Connect-API-Code#214), the module restores them before checking, as the Power App does.

Included from 3.0.0

The V2 attachment client is rebuilt on the grant model in the Connect OpenAPI spec, and its public types change incompatibly. Senders can restrict which companies receive their V2 attachments, and receivers can configure their own storage. Case notes take their types from the server. /v1/ failures now arrive as problem details, and Connect API calls run on the JDK's HTTP client.

Breaking changes in 3.0.0

  1. The V2 attachment client is rebuilt from the spec (#96, #100). attachmentsV2() has one method per Connect API call: createGrant, getGrant, listGrants, singleUploadLink, s3PartLinks, azureBlockLinks, the three complete* calls and abandon. It also keeps complete for the grant's mode, upload and a four-argument send that returns the completed AttachmentGrant.

    • Removed: grant, the old upload and complete signatures, the six-argument send, and AttachmentGrantRequest, AttachmentCompleteRequest and AttachmentCompleteResult.
    • AttachmentGrant is now the server's grant: a long id, Status, UploadMode and UploadPlan. It takes no content type, SHA-256 or description, and has no DELIVERED or note-id outcome.
    • AttachmentV2Exception: problemType() and isProblem are replaced by code() and is(code), with codes chosen by HTTP status. Several 2.0.0 codes are removed; the upgrade guide maps each one.
    • send no longer re-grants when a grant expires before complete.
  2. /v1/ failures are problem details (#127, #129). The library now sends Accept: application/json, application/problem+json. A failed /v1/ call now carries the documented 4xx status where 2.0.0 often saw a 500, with type() and title() set, and the message reads HTTP <status> <title> (<type suffix>): <detail>. Code that branches on kind() == LEGACY, on status() or on the message text sees different values: sweep for it. A wrong password is a PROBLEM too.

  3. Connect API calls use java.net.http.HttpClient (#128, #131 to #134), one client per session. The JVM's proxy selector and SSL context still apply.

    • No longer honored: a default Authenticator for an authenticating proxy, and HttpsURLConnection's default socket factory and hostname verifier.
    • Redirects: a redirected GET is no longer followed. Any 3xx is OTHER, and the message never names the Location.
    • Interrupts: an interrupted call stops with OTHER, status 0, and leaves the thread interrupted.
  4. Interface and record changes.

    • TsaNetApiSession gains attachmentStorage().
    • CaseNotesFacade gains listNoteTypes() and a five-argument createNote.
    • ApplicationUserAccount and TsaNetApiConfiguration gain an allowedReceiverCompanyIds component.
    • CaseNoteDto grows from 14 to 18 components.

    Callers are unaffected. Implementers and decorators must add the new methods. A record pattern over a changed record stops compiling. The 2.0.0 constructors still compile, and the CaseNoteDto one is deprecated.

  5. A V2 upload link missing expiresAt or sizeBytes is refused (#138). An upload-link answer without expiresAt (any link), or without sizeBytes (an S3 part or Azure block), fails with AttachmentV2Exception CLIENT_PRECONDITION "the answer has no ...". It used to silently turn off link refresh or the part-size check. The spec requires both fields, and the platform sends them.

New in 3.0.0

  • Receiver allowlist, part 1 of #92 (#104). Set it with ApplicationUserAccount.withAllowedReceiverCompanyIds, or with allowed-receiver-company-ids in the console's account config.
    • With a list set: createGrant (and so send) refuses a case whose receiving company isn't on it with attachment/receiver-not-allowed, before any grant is requested. The platform's own allowlist refusal (403) is reported as the same code.
    • Empty or null: unrestricted.
  • Receiver storage (#106, #107). session.attachmentStorage() gets, registers and tests the company's S3 or Azure Blob storage. The demo-ui has a Settings page for it, read-only unless TSANET_DEMO_RECEIVER_STORAGE_EDITABLE is set.
  • Note types from the server (#99, #103, reworked in #124 to #126).
    • Listing types: listNoteTypes() returns the codes a caller may send, from GET /v1/note-types.
    • Creating: createNote(..., type) sends the code as given, and a code the server doesn't offer is a 400 invalid-note-type.
    • No default: a null type creates a note with no type, and nothing becomes partner.
    • Reading notes: they carry type, typeDisplayName, companyId and direction.
    • The SQLite cache adds the four columns at startup.
  • Upload loop on the spec's links.
    • Links: requested just before use, in batches of up to 1,000, and refreshed when they expire within 60 seconds.
    • Retries: bounded per part, honoring Retry-After.
    • Wire: link headers are sent unchanged, S3 ETags are kept for complete, and links never appear in logs or messages.

Unchanged

  • **V1 attachments and every other e...
Read more

v3.0.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 21 Oct 01:19
23b7489

connect-library 3.0.0, attachment-receiver 3.0.0

Major version. The V2 attachment client is rebuilt on the grant model in the Connect OpenAPI spec, and its public types change incompatibly. Senders can restrict which companies receive their V2 attachments, and receivers can configure their own storage. Case notes take their types from the server. /v1/ failures now arrive as problem details, and Connect API calls run on the JDK's HTTP client. Runtime unchanged from 2.0.0: Spring Boot 4.1, Jackson 3, JDK 21.

The full upgrade guide, with every removed type, method and error code, is the 3.0.0 entry in skills/connect-sdk-deploy/references/consume-artifact.md.

Breaking changes for consumers

  1. The V2 attachment client is rebuilt from the spec (#96, #100). attachmentsV2() has one method per Connect API call: createGrant, getGrant, listGrants, singleUploadLink, s3PartLinks, azureBlockLinks, the three complete* calls and abandon. It also keeps complete for the grant's mode, upload and a four-argument send that returns the completed AttachmentGrant.

    • Removed: grant, the old upload and complete signatures, the six-argument send, and AttachmentGrantRequest, AttachmentCompleteRequest and AttachmentCompleteResult.
    • AttachmentGrant is now the server's grant: a long id, Status, UploadMode and UploadPlan. It takes no content type, SHA-256 or description, and has no DELIVERED or note-id outcome.
    • AttachmentV2Exception: problemType() and isProblem are replaced by code() and is(code), with codes chosen by HTTP status. Several 2.0.0 codes are removed; the upgrade guide maps each one.
    • send no longer re-grants when a grant expires before complete.
  2. /v1/ failures are problem details (#127, #129). The library now sends Accept: application/json, application/problem+json. A failed /v1/ call now carries the documented 4xx status where 2.0.0 often saw a 500, with type() and title() set, and the message reads HTTP <status> <title> (<type suffix>): <detail>. Code that branches on kind() == LEGACY, on status() or on the message text sees different values: sweep for it. A wrong password is a PROBLEM too.

  3. Connect API calls use java.net.http.HttpClient (#128, #131 to #134), one client per session. The JVM's proxy selector and SSL context still apply.

    • No longer honored: a default Authenticator for an authenticating proxy, and HttpsURLConnection's default socket factory and hostname verifier.
    • Redirects: a redirected GET is no longer followed. Any 3xx is OTHER, and the message never names the Location.
    • Interrupts: an interrupted call stops with OTHER, status 0, and leaves the thread interrupted.
  4. Interface and record changes.

    • TsaNetApiSession gains attachmentStorage().
    • CaseNotesFacade gains listNoteTypes() and a five-argument createNote.
    • ApplicationUserAccount and TsaNetApiConfiguration gain an allowedReceiverCompanyIds component.
    • CaseNoteDto grows from 14 to 18 components.

    Callers are unaffected. Implementers and decorators must add the new methods. A record pattern over a changed record stops compiling. The 2.0.0 constructors still compile, and the CaseNoteDto one is deprecated.

  5. A V2 upload link missing expiresAt or sizeBytes is refused (#138). An upload-link answer without expiresAt (any link), or without sizeBytes (an S3 part or Azure block), fails with AttachmentV2Exception CLIENT_PRECONDITION "the answer has no ...". It used to silently turn off link refresh or the part-size check. The spec requires both fields, and the platform sends them.

New

  • Receiver allowlist, part 1 of #92 (#104). Set it with ApplicationUserAccount.withAllowedReceiverCompanyIds, or with allowed-receiver-company-ids in the console's account config.
    • With a list set: createGrant (and so send) refuses a case whose receiving company isn't on it with attachment/receiver-not-allowed, before any grant is requested. The platform's own allowlist refusal (403) is reported as the same code.
    • Empty or null: unrestricted.
  • Receiver storage (#106, #107). session.attachmentStorage() gets, registers and tests the company's S3 or Azure Blob storage. The demo-ui has a Settings page for it, read-only unless TSANET_DEMO_RECEIVER_STORAGE_EDITABLE is set.
  • Note types from the server (#99, #103, reworked in #124 to #126).
    • Listing types: listNoteTypes() returns the codes a caller may send, from GET /v1/note-types.
    • Creating: createNote(..., type) sends the code as given, and a code the server doesn't offer is a 400 invalid-note-type.
    • No default: a null type creates a note with no type, and nothing becomes partner.
    • Reading notes: they carry type, typeDisplayName, companyId and direction.
    • The SQLite cache adds the four columns at startup.
  • Upload loop on the spec's links.
    • Links: requested just before use, in batches of up to 1,000, and refreshed when they expire within 60 seconds.
    • Retries: bounded per part, honoring Retry-After.
    • Wire: link headers are sent unchanged, S3 ETags are kept for complete, and links never appear in logs or messages.

Unchanged

  • V1 attachments and every other V1 facade signature, apart from the additions above.
  • Runtime line: Spring Boot 4.1, Jackson 3, JDK 21.
  • attachment-receiver: its source is unchanged since 2.0.0. It is published at 3.0.0 because the two modules release together.

Consuming the artifacts

Get com.tsanet:connect-library:3.0.0 and com.tsanet:attachment-receiver:3.0.0 from https://maven.pkg.github.com/tsanetgit/Connect_SDK. A read:packages token is required even for public packages.

Building from source needs a Connect-API-Code spec branch that has the Attachment Grants and Attachment Storage Config operations. beta has them since tsanetgit/Connect-API-Code#208.

The V2 endpoints are x-stability-level: alpha in the spec.

Known consumers

  • tsanetgit/Connect_Gateway pins connect-library.version 2.0.0. Moving to 3.0.0 is its own change, and the V2 and /v1/ error changes above apply.
  • tsanetgit/Fin-Intercom_App consumes gateway-engine, not the library directly, so it follows the gateway.

Verification

  • CI at the release commit: verify on main 23b7489, built against Connect-API-Code beta 46fc91f, whose spec is byte-identical to the one BETA serves. Every module passed with zero failures:

    Module Tests
    connect-library 297
    scripted demo 9
    console 44
    attachment-receiver 182 (40 environment-gated live tests skipped)
    demo-ui 44
  • Reviews: a whole-branch design review of the promotion (#136), with every finding dispositioned on the PR. Item 1 was fixed in #138. The promotion and #138 each had a separate-session QA review.

  • Live on BETA at the release commit (2026-10-21): the demo-ui image built from 23b7489 sent two V2 attachments as a test member, to a test receiver whose storage is an S3 bucket:

    • Single: a 1 MiB file in single mode, grant completed.
    • Multipart: a 12 MiB file in s3Multipart mode, three 5 MiB parts, grant completed.

    The platform checks the stored upload before it completes a grant. The same image now runs the hosted demo.

v2.0.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 09 Sep 04:50

connect-library 2.0.0, attachment-receiver 2.0.0

Major version: API failures now arrive as one typed, value-free exception, the session validates every login against /v1/me and renews in both credential modes, the V2 direct-delivery attachment client ships, and the attachment-receiver module is published for the first time. Runtime unchanged from 1.0.0: Spring Boot 4.1, Jackson 3, JDK 21.

Breaking changes for consumers

  1. ConnectApiException replaces Spring's exceptions. Every failure the API answers, and every transport failure, is thrown as com.tsanet.api.ConnectApiException (unchecked, not a RestClientException). kind() is PROBLEM (an RFC 7807 body; the library sends Accept: application/json, application/problem+json on every call), LEGACY (the API's {"message"} body, classified by content), CONNECTIVITY (unreachable; detail() names only the failure's class) or OTHER. status() is the status the API asserted (a problem body's own status when present). type(), title(), detail(), instance() and isProblem("case-update-error") replace body parsing. The message is value-free by construction: never the URL, a header or a token. A catch (HttpClientErrorException | RestClientResponseException | ResourceAccessException) around library calls compiles and no longer matches; sweep for it. The generated client's own retry loop, which caught HttpServerErrorException, is inert for the same reason; retries belong in the caller that knows the operation is idempotent.
  2. Interface additions. TsaNetApiSession gained attachmentsV2(); AuthFacade gained currentUserContext(). Implementers and decorators must add them; callers are unaffected.
  3. Login semantics. Every successful authenticate() or login() now ends with a /v1/me call; if it fails, the login fails and the session stays logged out (an unattended authenticate() retries connectivity failures three times, under a second). Password sessions with configured credentials track expiresIn and renew transparently, before expiry and once after a 401; a password typed into login(username, password) is never retained, so that session is not renewed and its next call after expiry fails with a classified 401.
  4. attachment-receiver is published at this version from this release on (see below). Its four adapter dependencies are required, not optional: about 170 runtime artifacts across the AWS, Azure and Google Cloud SDKs.
  5. The always-test createRequest shims are removed. The four-argument and five-argument overloads that silently passed testSubmission=true, deprecated forRemoval since 0.1.0, are gone; both forms take the explicit per-call flag. A caller that passed no flag fails at compile; a consumer that bumps the version without recompiling fails at the call with NoSuchMethodError, since removing a default interface method breaks binary compatibility too. (The CollaborationRequestStatusDto compatibility constructor from before testCase existed stays, deprecated, with 3.0.0 as its removal target.)

New

  • V2 direct-delivery attachment client (session.attachmentsV2(), #77): grant, upload, complete, abandon and a one-call send. Executes the grant's upload plan verbatim (single, multipart, resumable, relay), streaming from disk one part at a time with exact content lengths; bounded per-part retries; relay retries connect failures only. The outcome is the platform's recorded status (DELIVERED, DELIVERED_UNVERIFIED, FAILED, EXPIRED), never the client's own evidence. Built against the draft grant/complete contract in tsanetgit/Connect-API-Code#147; the platform endpoint is not live yet, and the hand-written request and result types will be replaced by generated ones when the contract lands in the spec. Member-facing walkthrough: docs/attachments-v2-client.md. The demo has a "Deliver directly (V2)" action with live progress; the console has deliver-attachment.
  • Connection hardening (#78, #80): the exception above; AuthFacade.currentUserContext(); renewal in both modes with concurrent callers sharing one renewal; an atomic session store; a 2xx whose body fails mid-read classified as connectivity instead of surfacing a URL-bearing exception; ConnectApiBaseUrl.requireHttps(baseUrl, allowInsecure, baseUrlSetting, optOutSetting), a pure helper the console, the scripted demo and the demo-ui call at startup (the library's own paths never do).
  • Apps refuse plain http at startup. The console and scripted demo (tsanet.api.allow-insecure-http) and the demo-ui (tsanet.demo.allow-insecure-http) ship https defaults and refuse a plain-http base URL unless opted in for a local mock.
  • attachment-receiver: Azure Blob Storage adapter, container-scoped, alongside Azure Files (#72, #74); GCS adapter and the concrete RegisteringStorageFactory (#68); the ambiguous-finish case (upload completed on the store, confirmation lost) resolved by attempt identity, not size, in S3 and GCS (#75); the go-live verifier validates read and write on every backend. The stale httpclient5 override is gone; the Boot 4 parent's 5.6.x satisfies the AWS SDK's 5.4 floor, and the factory test that constructs a real S3Client proves it.

Unchanged

  • CacheRetention.sweep() and the storage schema. Webhook signature verification. The OAuth client-credentials flow. Every V1 facade signature apart from the two additions above.
  • Runtime line: Spring Boot 4.1.1, Jackson 3, JDK 21. Consumers on Jackson 2 stay on 0.2.0.

Consuming the artifacts

com.tsanet:connect-library:2.0.0 and com.tsanet:attachment-receiver:2.0.0 from https://maven.pkg.github.com/tsanetgit/Connect_SDK (a read:packages token is required even for public packages). The receiver is a plain library jar with no main class; its intended consumer is the gateway's attachments-only profile (tsanetgit/Connect_Gateway#35). It does not depend on connect-library.

Known consumers

  • tsanetgit/Connect_Gateway (gateway-engine): on 2.0.0 since 2026-09-08 (tsanetgit/Connect_Gateway#37, merged as PR #39). Its engine runs the generated APIs on its own HTTP client, so operation errors still arrive as Spring's types there; the migration classified a failed login (the one path that now throws ConnectApiException) and moved that login outside the operation's wall-clock budget.
  • tsanetgit/Fin-Intercom_App consumes gateway-engine, not the library directly; it follows the gateway's migration. Its own SDK-facing code (SdkCacheRetentionSweep) is unaffected.

Verification

Whole reactor at 3fd5f0e, then connect-library again at d667ab9 after the shim removal (181 tests, one fewer by design), and the cut itself at 42bd3a5 (docs only): connect-library 182 then 181 tests, attachment-receiver 182 (40 environment-gated live tests skipped in CI; the S3 and Azure Blob adapters were proven live during their PRs), demo-ui 19, console 37, scripted demo 8, zero failures. Every PR in this release (#68, #70, #72, #74, #75, #77, #78, #80, #83, #84) carried an independent separate-session QA review before merge, each round dispositioned on the PR. Not live-verified: the V2 client end to end (no platform endpoint yet) and the GCS adapter (no credentials; env-gated mirror test ready).

v1.0.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 28 Aug 18:29
2d38f4a

connect-library 1.0.0

Major version: the library moves from Spring Boot 3.3.4 / Jackson 2 to Spring Boot 4.1.1 / Jackson 3. Spring Boot 3.3 is past both OSS end-of-life (2025-06-30) and commercial extended support (2026-06-30); 4.1 is the current OSS-supported line. Estate tracking: tsanetgit/Fin-Intercom_App#76.

Breaking changes for consumers

  1. Jackson 3 coordinates. The library's Jackson dependency is now tools.jackson.core:jackson-databind. Consuming applications must be on Jackson 3 (com.fasterxml.jackson.annotation is unchanged — annotations kept their package).
  2. Unchecked parse exceptions. Jackson 3's JacksonException extends RuntimeException; Jackson 2's JsonProcessingException extended IOException. Any catch (IOException) around mapper calls still compiles but silently stops catching malformed JSON — sweep for this when upgrading.
  3. JsonNullable accessors removed from CaseApprovalUpdateDTO and CollaborationRequestSubmitterUpdateDTO (openApiNullable=false; jackson-databind-nullable has no Jackson 3 release). Plain accessors are unchanged: getCaseNumber() still returns String. Grep across all known consumers found zero uses of the removed _JsonNullable variants.
  4. Stricter primitive handling by default. Jackson 3 enables FAIL_ON_NULL_FOR_PRIMITIVES. The library's own cache read-back mapper is configured to keep Jackson 2 semantics (old cache rows still load), but consumers' own mappers inherit the strict default unless they set it themselves.
  5. jackson-datatype-jsr310 no longer declared — Jackson 3 folds java.time support into databind itself.

Unchanged

  • Public API signatures: no Jackson types leak into public method signatures; all facades unchanged.
  • CacheRetention.sweep() (added in 0.2.0) and the full storage schema.
  • Webhook signature verification, OAuth client-credentials flow, all lifecycle facades.

Verification

Full reactor green with test counts identical to the 0.2.0 baseline (94 connect-library tests). Both downstream consumers (tsanetgit/Connect_Gateway#34, tsanetgit/Fin-Intercom_App#77) compiled and ran their suites against this exact code pre-release. QA: independent separate-session review on #67; one regression found and fixed pre-merge (unpublished CLI module).

v0.2.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 28 Aug 02:34
2eed253

Adds age-based retention for the SDK's operational SQLite cache: com.tsanet.api.storage.CacheRetention.

What this version carries

  • CacheRetention.sweep(JdbcTemplate, Duration, Instant): evicts terminal-case (CLOSED/REJECTED) content older than a caller-supplied window, in one transaction. OPEN cases never age out. attachment_forward_result is deliberately never touched — it has no API read-back path, so eviction there would be unrecoverable — and children with no cached parent are never age-swept either, since three fetch paths (getNotes, getResponses, getAttachmentConfig) cache them without ever writing the parent row, so a parentless child is normal steady state for a webhook-driven consumer, not residue (#66).
  • RetentionSweepException carries the failed phase and the committed-so-far counts on a mid-run failure — a partial run can still have deleted rows, and callers get that in the exception rather than a silent guess.
  • SQL isolated in a package-private CacheRetentionRepository, not inline in the sweep orchestration.
  • No consumer wiring in this release; that lands separately per consuming adapter (first: Fin-Intercom_App#66, retiring its interim hand-rolled sweep).

Consuming the artifact

com.tsanet:connect-library:0.2.0 from https://maven.pkg.github.com/tsanetgit/Connect_SDK.

Certification

Cut from main after #66 merged: 94 tests green in connect-library, addressed across two QA rounds (a non-SQLException partial-commit gap, two corrected doc claims, and the scope narrowing described above).

Note on the version number

v0.1.0's notes named 0.2.0 as the version that would remove the deprecated always-test constructor shims. This release does not touch those shims — that removal will land under a later version number instead.

v0.1.0

Choose a tag to compare

@shawn-tsanet shawn-tsanet released this 11 Aug 15:02

First versioned release of the Connect SDK: the Java client for the TSANet Connect API, with the session facade and the console reference app.

What this version carries

  • OAuth 2.0 client-credentials support (Entra) alongside password auth - the oauth stack, merged to main.
  • Works outside Spring Boot: jackson-datatype-jsr310 is now a declared dependency, so embedding connect-library in a plain Java service no longer fails on time serialization (#39).
  • Test cases are distinguishable: testCase now travels through the facade DTO and the local cache, so a consumer can badge and filter test cases (#40).
  • Real cases are possible, deliberately: the create API takes a per-call testSubmission flag with no silent default. The old always-test signatures remain as deprecated shims for compatibility and are scheduled for removal in 0.2.0 - migrate callers to the explicit flag (#41).
  • The build is documented: codegen reads the Connect OpenAPI spec from a sibling Connect-API-Code clone on beta; the README carries the exact working steps and the branch state (#42).
  • This release publishes itself: connect-library and its parent pom ship to GitHub Packages on release (#43).

Consuming the artifact

com.tsanet:connect-library:0.1.0 from https://maven.pkg.github.com/tsanetgit/Connect_SDK. GitHub Packages requires a token with read:packages even for public artifacts - see the publish workflow PR (#43) for the settings.xml shape.

Certification

Cut from main after the oauth merge: full reactor 122 tests green, built standalone against the beta specification. Module poms stay 0.0.1-SNAPSHOT by design - the release workflow stamps the version, and this tag is the pin.