Prevent and clean up corrupted application links - #509
Conversation
There was a problem hiding this comment.
Pull request overview
Improves application-link recovery and cleanup when Atlassian link records are corrupted.
Changes:
- Restores links after failed recreation and corrects OAuth configuration targeting.
- Purges corrupted remnants during create, update, and delete operations.
- Returns 404 for unknown IDs and validates installed application types.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
DefaultApplicationLinksServiceImpl.java |
Implements recovery, cleanup, validation, and 404 handling. |
DefaultApplicationLinkServiceTest.java |
Adds tests for corruption and recovery paths. |
Suppressed comments (1)
commons/src/main/java/com/deftdevs/bootstrapi/commons/service/DefaultApplicationLinksServiceImpl.java:227
- Catching
TypeNotInstalledExceptiondoes not prove this ID is a corrupted remnant; it can be an existing valid link whose type module is currently unavailable. This create path would silently delete that link merely because its URL-derived ID collides. Verify the raw record is actually missing its type property before purging, and otherwise preserve the existing link and report the collision/unavailable module.
} catch (TypeNotInstalledException e) {
log.warn("Removing corrupted application link '{}' before creating a new link for URL '{}'",
generatedApplicationId, applicationLinkModel.getRpcUrl());
deleteCorruptedApplicationLink(generatedApplicationId);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
f14cbc2 to
37a0526
Compare
a5f2327 to
bb26d53
Compare
Overriding an application link is a non-atomic delete and recreate. If the recreation failed, the instance was left with a registered application link ID without properties, causing repeated 'Link is corrupted' warnings. Now the original link is restored when the recreation fails, a missing application type module aborts the request before the delete, the OAuth configs are applied to the recreated link instead of the deleted instance, and an unknown UUID returns 404 instead of an NPE. Already corrupted links cannot be retrieved through the applinks API: enumeration skips them, retrieval by ID throws TypeNotInstalledException, and creating a link whose URL-derived ID collides with the remnant fails with 'already exists'. Deletion, however, only requires the ID, so a minimal ApplicationLink implementation carrying just the ID is enough to purge the remnant. Corrupted links are now cleaned up in all three paths: deleting by UUID purges an unretrievable link instead of failing with a 400, creating a link first removes a corrupted remnant registered under the ID derived from the rpc URL, and overriding by UUID purges and recreates the link from the supplied configuration.
bb26d53 to
91556ff
Compare
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
commons/src/main/java/com/deftdevs/bootstrapi/commons/service/DefaultApplicationLinksServiceImpl.java:289
TypeNotInstalledExceptionalso represents a valid link whose application-type module is temporarily unavailable, not only a corrupted link. This branch currently deletes both cases, causing data loss for a valid link; preserve the previous 400 response unlessisCorrupted(e)confirms the reservedunknowntype.
} catch (TypeNotInstalledException e) {
// the ID is registered, but the link cannot be retrieved because its type
// property is missing ("Link is corrupted") or its type module is not installed
log.warn("Deleting application link '{}' that cannot be retrieved: {}", applicationId, e.getMessage());
deleteCorruptedApplicationLink(applicationId);
commons/src/main/java/com/deftdevs/bootstrapi/commons/service/DefaultApplicationLinksServiceImpl.java:201
- When
addApplicationLinkfails in the corruption scenario described by this PR, it has already leftapplicationIdregistered without properties. Re-adding the original link with that same ID therefore fails as “already exists,” so this restore path cannot actually recover the case it targets. Purge the partial registration before callingrestoreApplicationLink; the test should also model the failed call leaving the ID occupied until deletion.
} catch (Exception e) {
if (originalApplicationLinkDetails != null) {
restoreApplicationLink(applicationId, originalApplicationType, originalApplicationLinkDetails,
originalOutgoingOAuthConfig, originalIncomingConsumerKey);



Overriding an application link is a non-atomic delete and recreate. If the recreation failed, the instance was left with a registered application link ID without properties, causing repeated 'Link is corrupted' warnings. Now the original link is restored when the recreation fails, a missing application type module aborts the request before the delete, the OAuth configs are applied to the recreated link instead of the deleted instance, and an unknown UUID returns 404 instead of an NPE.
Already corrupted links cannot be retrieved through the applinks API: enumeration skips them, retrieval by ID throws
TypeNotInstalledException, and creating a link whose URL-derived ID collides with the remnant fails with 'already exists'. Deletion, however, only requires the ID, so a minimal ApplicationLink implementation carrying just the ID is enough to purge the remnant. Corrupted links are now cleaned up in all three paths: deleting by UUID purges an unretrievable link instead of failing with a 400, creating a link first removes a corrupted remnant registered under the ID derived from the rpc URL, and overriding by UUID purges and recreates the link from the supplied configuration.