Skip to content

feat(imgur): read base and auth URLs from service config - #1519

Open
alexeyqu wants to merge 1 commit into
masterfrom
feat/imgur-configurable-urls
Open

alexeyqu wants to merge 1 commit into
masterfrom
feat/imgur-configurable-urls

Conversation

@alexeyqu

@alexeyqu alexeyqu commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Makes ImgurTransferExtension's API root and ImgurOAuthConfig's authorization and token endpoints configurable, so the adapter can be pointed at a staging environment or a test double without rebuilding it.

All three currently hardcode Imgur's own URLs. They now read config/imgur.yaml from the classpath, falling back to exactly today's values when no such file exists:

serviceConfig:
  baseUrl:  "https://api.imgur.com/3"
  authUrl:  "https://api.imgur.com/oauth2/authorize"
  tokenUrl: "https://api.imgur.com/oauth2/token"

Behaviour is unchanged for every distribution in this repo — none ships a config/imgur.yaml, so every code path falls through to the defaults.

Stack

PR Scope
#1509 (feat/offline-demo-exporter) offline-demo becomes a complete export/import pair
#1516 (test/e2e-offline-demo-compose) the compose + pytest harness — base of this PR
this Imgur URLs become configurable
next the e2e suite that drives Imgur against a WireMock stand-in for api.imgur.com

Based on #1516 rather than master to keep the follow-up harness work off the merge queue. Retarget to master once the parents land.

Why this is a convention, not a new extension point

TransferServiceConfig already exists for exactly this, and WorkerModule already binds a per-extension instance via getForService(ext.getServiceId()). Flickr (FlickrTransferExtension:98), Deezer (DeezerTransferExtension:75) and Synology all consume it; Synology and the generic adapter already read a baseUrl from it. Imgur simply had not adopted the pattern.

Worth noting for anyone weighing this against other adapters: no adapter in the repo had a configurable URL on an export path before this. The two that read URLs from config — GenericTransferExtension and Synology — are import-only (getExporter throws and returns null respectively), which is presumably why they were the ones to need it. Flickr and Deezer consume TransferServiceConfig only for perUserRateLimit.

Imgur is the cheapest place to start, because baseUrl was already a constructor parameter on both ImgurPhotosExporter and ImgurPhotosImporter — only the extension-level constants needed to change. By contrast MicrosoftTransferExtension.BASE_GRAPH_URL is threaded into six importers.

One asymmetry, flagged for review

ImgurTransferExtension takes its config from the injected context.getService(TransferServiceConfig.class), as the convention expects.

ImgurOAuthConfig cannot: an OAuth2ServiceExtension is never handed a service-scoped TransferServiceConfig the way WorkerModule hands one to a TransferExtension. It therefore calls the static TransferServiceConfig.getForService("Imgur") itself, once, at construction.

That means an auth class reads a file named for the transfer side. Both halves genuinely describe the same service's endpoints, and this is the only classpath config mechanism that exists — the alternative was shadowing config/common.yaml, which replaces the jar's copy wholesale rather than merging. Happy to restructure if you'd prefer a separate key or file.

Tests

Adds the first tests for either class — 9 in total, covering defaults, per-key overrides, a config with no serviceConfig section, and a serviceConfig that sets unrelated keys.

There is also a test pinning getServiceName() to "Imgur": PortabilityAuthServiceProviderRegistry does an exact-match lookup on that string, so changing it silently breaks every Imgur job at creation time.

The five pre-existing ImgurPhotoExporterTest cases still pass — verified in a clean worktree containing only this commit, since stacked PRs get no CI in this repo (pull_request: branches: [master] matches the base branch).

docker compose run --rm gradle --no-daemon \
  :extensions:data-transfer:portability-data-transfer-imgur:test \
  :extensions:auth:portability-auth-imgur:test
docker compose run --rm gradle --no-daemon check    # BUILD SUCCESSFUL

@alexeyqu
alexeyqu requested a review from lisad September 8, 2026 01:08
@alexeyqu
alexeyqu marked this pull request as ready for review September 8, 2026 01:08
@alexeyqu
alexeyqu force-pushed the feat/imgur-configurable-urls branch from c6d5b84 to 79e64aa Compare September 9, 2026 01:19
@alexeyqu
alexeyqu force-pushed the feat/imgur-configurable-urls branch from 79e64aa to d9b417f Compare September 14, 2026 23:35
Base automatically changed from test/e2e-offline-demo-compose to master September 14, 2026 23:39
ImgurTransferExtension hardcoded the API root and ImgurOAuthConfig
hardcoded the authorization and token endpoints, so pointing the adapter
at a staging environment or a test double meant rebuilding it.

Both now read from config/imgur.yaml on the classpath, falling back to
Imgur's own endpoints when no file is present:

    serviceConfig:
      baseUrl:  https://api.imgur.com/3
      authUrl:  https://api.imgur.com/oauth2/authorize
      tokenUrl: https://api.imgur.com/oauth2/token

This follows the convention Flickr, Deezer and Synology already use for
other per-service settings; Imgur had simply not adopted it. Both the
exporter and the importer already took baseUrl as a constructor
parameter, so only the extension-level constants needed to change.

One asymmetry worth noting for review: ImgurOAuthConfig calls the static
TransferServiceConfig.getForService rather than taking an injected
instance, because OAuth2ServiceExtension is never handed a
service-scoped one the way WorkerModule hands one to a TransferExtension.

Behaviour is unchanged when no config/imgur.yaml is on the classpath,
which is the case for every distribution in this repo today.

Adds the first tests for either class.
@alexeyqu
alexeyqu force-pushed the feat/imgur-configurable-urls branch from d9b417f to 68226e3 Compare September 14, 2026 23:39
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.

2 participants