feat(translate): Add GlobalLink translation connector - #689
feat(translate): Add GlobalLink translation connector#689markdaugherty wants to merge 12 commits into
Conversation
Implements isConnected/connect/sendAllLanguages/getStatusAll/saveItems/
cancelTranslation against GlobalLink's REST API (submission create, source
upload, save/autostart, status polling, deliverable download, and per-language
cancel via targetIds), matching the destructured-object contract the loc UI
actually calls (verified against Smartling/Trados) rather than the positional-arg
signatures the connector originally shipped with.
Requests are proxied through DA_TRANSLATE (new /translate/globallink/<org>/<site>
route in da-translate) instead of hitting GlobalLink directly from the browser,
with the real per-site endpoint passed via an x-globallink-origin header that the
proxy validates against an allowlist.
Fixes found by exercising the connector against a live GlobalLink pilot account:
missing customAttributes support (some projects require mandatory submission
attributes), pageSize=500 exceeding the API's 200 max, waitForSubmissionReady not
recognizing the real "PROCESSED" status, saveAndAutostart trusting HTTP 200 when
the body says the submission didn't actually start, and missing
`lang.translation ??= {}` guards that crashed sendAllLanguages/getStatusAll on
languages with no prior translation state.
DA_TRANSLATE is now resolved via getEnv (matching DA_ETC/DA_FEEDBACK) so it can
point at a local wrangler dev server instead of only production.
Adds connectors/globallink/auth.js, mirroring trados/auth.js: getAccessToken calls da-etc's new /:org/sites/:site/integrations/globallink/login endpoint (carrying only the browser's own DA session auth) and caches the resulting access token in localStorage. isConnected/connect now just delegate to authReady. Removes all local OAuth building from index.js (setTokenDetails/ getTokenDetails/clearToken/basicAuthHeader/requestToken/refreshAccessToken/ refreshTheToken and the module-level token/tokenPolling state) — every call site now gets its token via getAccessToken/authHeaders instead. The GlobalLink OAuth client secret and the user's password no longer transit through the browser at all; only the (non-secret) API endpoint used for the DA_TRANSLATE proxy header stays client-side. Corresponding DA config keys renamed oauthClient/oauthSecret -> clientId/ clientSecret to match da-etc's shared credential-resolution helper.
8787 collides with DA_ADMIN's local port; da-translate's wrangler dev serves on 8788.
|
Hello, I'm the AEM Code Sync Bot and I will run some actions to deploy your branch.
|
- batchName threaded through createSubmission/uploadSourceFile instead of a hardcoded 'Batch1' constant - Defaults to DEFAULT_BATCH_NAME, overridable via service.batchName (translation.service.<env>.batchName)
- generateBatchName(title): title + timestamp, truncated to 64 UTF-8 chars (GlobalLink batch-naming limit) - Removes service.batchName config option entirely
- markTargetsDelivered: mark targets delivered after a successful save, so GlobalLink stops re-surfacing them (POST .../targets/delivered) - uploadSourceFiles: upload all source documents as a single zip (extractArchive=true) instead of one call per file; adds fflate as a dependency (nx2/deps/fflate, mirrors existing lit/mdast bundling) - uploadSourceFiles: detect and warn when GlobalLink splits uploads across additional (untracked) submissions due to the per-submission file limit - saveItems: wait for GlobalLink to report a language's deliverables ready (deliverableLanguages + downloadId polling, 5s interval) before downloading individual targets
| @@ -0,0 +1,790 @@ | |||
| import { Queue } from '../../../../../nx2/public/utils/tree.js'; | |||
There was a problem hiding this comment.
this is a big new connector (790 + 92 lines) with no tests at all, the Smartling one right next to it is well covered. can we at least get auth (token cache/expiry/refresh) and the main send/status/error paths under test before merge?
CC: @mhaack
There was a problem hiding this comment.
This is not ready for review yet, but yes, I am planning to add test coverage (+ moving authentication to either the da-etc or da-translate proxies to obfuscate the credentials) before moving it out of draft status.
| * treated as expired by (already adjusted by {@link TOKEN_BUFFER_MS}). | ||
| * @returns {void} | ||
| */ | ||
| function setTokenDetails(org, site, env, accessToken, expires) { |
There was a problem hiding this comment.
the access token goes in localStorage, that's XSS-readable and sticks around. since it's short-lived, would sessionStorage or in-memory be enough here?
- listTargets: page through /rest/v0/targets (pageNumber) instead of silently truncating to the first 200 results - toFileName: escape existing underscores before flattening path separators, so distinct DA paths can't collide on the same GlobalLink upload file name - sendAllLanguages: persist submissionId onto options.service, matching the Smartling connector's convention
- uploadSourceFiles: record a daBasePath -> documentId map from the upload response, persisted via sendAllLanguages onto options.service.documentIds - matchUrl: match targets by documentId only; drop clientIdentifier (a submission-level field, not per-document) and fuzzy file-name matching - listTargets: page with a 0-based pageNumber; drop the targetStatus/targetLanguage query params (unconfirmed for this endpoint) in favor of client-side filtering in each caller - saveItems/cancelTranslation: filter fetched targets by status (PROCESSED or DELIVERED) and target language locally
- Add test/loc/connectors/globallink/index.test.js covering isConnected/connect/sendAllLanguages/getStatusAll/saveItems/ cancelTranslation, at parity with the Smartling connector tests, including target pagination and documentId-matching regressions - Delete the connector's own auth.js and switch to loc/utils/auth.js (as Trados/Lionbridge already do), fixing a bug where the login URL broke when DA_ETC resolves to undefined - Route every GlobalLink fetch through fetchWithRetry with a 401 onUnauthorized handler, so a stale/revoked token gets one forced refresh-and-retry instead of failing the request outright
…w gate da-translate now requires an IMS bearer token on Authorization for /translate/globallink, mirroring the Google connector's daFetch-based auth. GlobalLink's own credential moves to a new x-globallink-authorization header. isConnected/connect now also require a valid IMS session before making any proxy call. The IMS helpers (imsAccessToken/imsAuthHeader) live in the shared loc/utils/auth.js so other da-etc-backed connectors can reuse them. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nx/blocks/loc/connectors/globallink/index.js:isConnected/connect/sendAllLanguages/getStatusAll/saveItems/cancelTranslationagainst GlobalLink's REST API (submission create, source upload, save/autostart, paginated status polling, deliverable download, per-language cancel viatargetIds)x-globallink-originheaderloc/utils/auth.js(same as Trados/Lionbridge) instead of a connector-specific implementation; requests retry once on a 401 via a forced token refresh, plus standard 429/5xx backoffDA_TRANSLATEinnx2/utils/utils.jsnow resolved viagetEnv(matchesDA_ETC/DA_FEEDBACK) instead of a hardcoded string; added missingstageentry; fixed local dev portuploadSourceFiles: source documents uploaded as a single zip (extractArchive=true) instead of one call per file; addsfflateas a dependency (nx2/deps/fflate, mirrors existing lit/mdast bundling)toFileName: DA paths flattened to upload file names with underscore-escaping so distinct paths can't collide on the same namedaBasePath -> documentIdmap captured at upload time, not fuzzy file-name matchinglistTargets: pages through all of a submission's targets (200/page) instead of only the first pagemarkTargetsDelivered: mark targets delivered after a successful save, so GlobalLink stops re-surfacing themuploadSourceFiles: detect and warn when GlobalLink splits uploads across additional (untracked) submissions due to the per-submission file limitsaveItems: wait for GlobalLink to report a language's deliverables ready before downloading individual targetstest/loc/connectors/globallink/index.test.jscovering all exported functionscustomAttributessupport for projects with mandatory submission attributespageSizecapped at 200 (API max)waitForSubmissionReadyrecognizes thePROCESSEDstatussaveAndAutostartchecksstartedSubmissionIdsin the response body instead of trusting HTTP 200lang.translation ??= {}guards added before every mutation (matches Smartling/Trados)