Skip to content

Fix/#32861#9

Merged
micharoesler merged 7 commits into
mainfrom
fix/#32861
Jun 10, 2026
Merged

Fix/#32861#9
micharoesler merged 7 commits into
mainfrom
fix/#32861

Conversation

@micharoesler

Copy link
Copy Markdown
Contributor

#32861 — Fix Confluence-Attachment-Download (REST-Endpoint) + Tests & CI

Problem

Attachments (z. B. die „Schnellhilfe"-Bilder) wurden nicht mehr heruntergeladen und
erschienen in der synchronisierten Dokumentation als 404. Ursache: Das Paket hat Attachments
über das Legacy-Servlet /wiki/download geladen, das API-Token-Authentifizierung nicht mehr
akzeptiert und HTTP 401 zurückgibt
. Da der Fehler verschluckt wurde (ein bloßes echo +
return), wirkte der Sync erfolgreich, während jedes Attachment still übersprungen wurde.

Fix

Attachments werden stattdessen über den unterstützten REST-Endpoint geladen:

/wiki/rest/api/content/{pageId}/child/attachment/{attachmentId}/download

  • Die Authentifizierung bleibt unverändert — es werden dieselben Basic-Auth-Credentials
    (E-Mail + API-Token) gesendet wie bei jedem anderen Aufruf; nur der Endpoint wurde auf die
    REST-API umgestellt.
  • downloadAttachment() erhält jetzt die pageId, und ConfluenceAttachment::getId() wurde
    ergänzt, um die REST-URL zu bauen.

Fehlerbehandlung (keine stillen Fehlschläge mehr)

  • ensureDownloadFolder() wirft jetzt eine RuntimeException, wenn der Download-Ordner nicht
    angelegt werden kann, statt auf stdout auszugeben und weiterzulaufen.
  • ConfluencePageContentDownloader fängt Exceptions und meldet sie über einen injizierten
    PSR-3-LoggerInterface (Default: NullLogger, das Verhalten bleibt also unverändert, wenn
    kein Logger übergeben wird). Fügt eine psr/log-Abhängigkeit hinzu.

Cleanup / Aufräumen

  • Das nun tote ConfluenceAttachment::findDownloadPath() entfernt (einziger Nutzer des kaputten
    Legacy-Pfads), damit der 401-anfällige Pfad nicht versehentlich wiederverwendet werden kann.
  • Die Attachment-Download-URL auf einen relativen Pfad normalisiert (kein führender Slash),
    konsistent mit den übrigen Content-API-Aufrufen.
  • Die eine verbliebene deutsche Exception-Message ins Englische übersetzt (konsistent mit den
    anderen).
  • Doc-Blocks und @throws-Annotationen an den Content- und Download-Methoden ergänzt/erweitert
    und eine lokale Variable $filemtime$fileModificationTime umbenannt.

Tests & CI

  • 21 Unit-Tests hinzugefügt, die den Fix abdecken: die REST-Attachment-URL, den Basic-Auth-Header,
    die Skip-/Re-Download-Entscheidung (Datei-mtime vs. Attachment-lastUpdated), das Anlegen des
    Download-Ordners und die neue RuntimeException, das JSON→DTO-Parsing, die Non-200-/5xx-Behandlung
    sowie den neuen Error-Logging-Pfad.
  • Sämtliches HTTP wird über Guzzles MockHandler bedient — kein Test ruft jemals die Live-
    Confluence-API auf
    , Builds bleiben damit netzwerkfrei.
  • Die Dev-Abhängigkeiten (phpunit/phpunit, phpstan/phpstan-phpunit), eine phpunit.xml und ein
    composer test-Script wieder hinzugefügt.
  • Einen eigenständigen PHPUnit-GitHub-Actions-Workflow ergänzt (PHP 8.2 + 8.3, gespiegelt an der
    PHPStan-Matrix), da der geteilte Workflows-Repo keinen wiederverwendbaren phpunit-Workflow hat.
    Die Tests laufen jetzt in jedem Build.

Warum PHPUnit 11 (und nicht 12)

Wir sind bewusst auf phpunit/phpunit ^11.5 geblieben statt auf dem zuvor entfernten ^12.5.6:
phpunit 12 benötigt PHP ≥ 8.3, der Floor dieser Library ist aber php: >=8.2 und die CI
fährt einen 8.2-Matrix-Leg. Mit phpunit 12 würde composer install auf PHP 8.2 fehlschlagen und den
Build brechen. PHPUnit 11.5 unterstützt PHP 8.2–8.4 und behält damit den 8.2-Support, ohne die
Runtime-Constraint require.php anzufassen (Consumer sind nicht betroffen — Dev-Abhängigkeiten
werden nicht transitiv installiert).

Verifikation

End-to-End in einer echten AGP-Installation über einen erzwungenen Voll-Sync verifiziert: Alle
Attachments wurden korrekt heruntergeladen, inklusive des Bildes, das zuvor 404 lieferte; Text und
Bilder werden in der UI gerendert. PHPUnit (21 Tests) und PHPStan Level 8 (über src + tests) sind
grün.

micharoesler and others added 7 commits June 9, 2026 16:25
The legacy /wiki/download servlet no longer accepts API-token
authentication (HTTP 401). Download attachments via the supported
REST endpoint /wiki/rest/api/content/{pageId}/child/attachment/{id}/download
instead; pageId is now passed through downloadAttachment().

Also replace the silent echo+return on folder/exception errors with a
RuntimeException (download folder) and PSR-3 logger->error() (page
download), so failures are no longer swallowed. Adds psr/log dependency
and ConfluenceAttachment::getId().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…wnload URL

findDownloadPath() was the only consumer of the broken legacy
/wiki/download path and is now unused — remove it so the 401-prone
path cannot be reused by accident.

Normalize the attachment download URL to a relative path (no leading
slash) to match findChildAttachments(); both resolve identically
against the host-root base_uri. Clarify the comment: auth is unchanged
(same email + API token), only the endpoint moved to the REST API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e to English

Align the attachment-retrieval exception message with the other two
messages in Content.php, which are already English.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the missing @throws annotations (GuzzleException from the HTTP
client, Exception on non-200 responses) to findPagesInSpace,
findPageContent and findChildAttachments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nts doc blocks

Describe what each method does, its $pageId parameter, return value and
error cases. Correct the findChildAttachments summary: it uses the
child/attachment endpoint, not descendants.attachment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…riable name

Add doc blocks (description, @param, @throws) to ensureDownloadFolder,
downloadPageContent, downloadAttachment and shouldAttachmentBeUpdated,
and import GuzzleException so the @throws reference resolves. Rename the
local $filemtime to $fileModificationTime for readability.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-add phpunit/phpunit (^11.5, compatible with the >=8.2 floor) and
phpstan/phpstan-phpunit, plus a phpunit.xml and a 'composer test' script.

Add 21 unit tests covering the #32861 fix, all served by a Guzzle
MockHandler so no build ever calls the live Confluence API:
- DownloadTest: REST attachment URL, Basic-auth header, skip/re-download
  by mtime, download-folder creation and RuntimeException on failure
- ContentTest: JSON-to-DTO parsing, non-200 guard, 5xx GuzzleException,
  pagination termination
- ConfluenceAttachmentTest: id/title and lastUpdated parsing
- ConfluencePageContentDownloaderTest: macro replacers, withAttachments
  and null-pageId guards, and the new error-logging path

Add a self-contained PHPUnit GitHub Actions workflow (PHP 8.2 + 8.3,
mirroring the PHPStan matrix) so the tests run on every build; the
shared workflows repo has no phpunit workflow to reuse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@micharoesler micharoesler merged commit 79488e6 into main Jun 10, 2026
5 checks passed
@micharoesler micharoesler deleted the fix/#32861 branch June 10, 2026 07:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants