@open-mercato/sync-akeneo 0.6.7-develop.6630.1.c8a615e536 → 0.6.7-develop.6645.1.52f0b37813
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -250,6 +250,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
|
|
|
250
250
|
const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
|
|
251
251
|
method: "POST",
|
|
252
252
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
253
|
+
redirect: "manual",
|
|
253
254
|
headers: {
|
|
254
255
|
accept: "application/json",
|
|
255
256
|
"content-type": "application/json"
|
|
@@ -281,6 +282,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
|
|
|
281
282
|
const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
|
|
282
283
|
method: "POST",
|
|
283
284
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
285
|
+
redirect: "manual",
|
|
284
286
|
headers: {
|
|
285
287
|
accept: "application/json",
|
|
286
288
|
"content-type": "application/json"
|
|
@@ -316,6 +318,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
|
|
|
316
318
|
const response = await safeAkeneoFetchWithTimeout(url, {
|
|
317
319
|
...init,
|
|
318
320
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
321
|
+
redirect: "manual",
|
|
319
322
|
headers: {
|
|
320
323
|
accept: "application/json",
|
|
321
324
|
authorization: `Bearer ${token}`,
|
|
@@ -349,6 +352,7 @@ function createAkeneoClient(credentialsInput, deps = {}) {
|
|
|
349
352
|
const response = await safeAkeneoFetchWithTimeout(url, {
|
|
350
353
|
...init,
|
|
351
354
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
355
|
+
redirect: "manual",
|
|
352
356
|
headers: {
|
|
353
357
|
authorization: `Bearer ${token}`,
|
|
354
358
|
...init.headers
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sync_akeneo/lib/client.ts"],
|
|
4
|
-
"sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport {\n FetchTimeoutError,\n type FetchWithTimeoutInit,\n resolveTimeoutMs,\n} from '@open-mercato/shared/lib/http/fetchWithTimeout'\nimport {\n safeOutboundFetch,\n type HostLookup,\n type SafeOutboundFetchOptions,\n type UrlSafetyReason,\n} from '@open-mercato/shared/lib/url-safety'\nimport { dedupeStrings, labelFromLocalizedRecord, safeRecord, type AkeneoAttribute, type AkeneoAttributeOption, type AkeneoCategory, type AkeneoChannel, type AkeneoCredentialShape, type AkeneoFamily, type AkeneoFamilyVariant, type AkeneoLocale, type AkeneoProduct, type AkeneoProductModel } from './shared'\n\ntype TokenState = {\n accessToken: string\n refreshToken?: string | null\n expiresAt: number\n}\n\ntype AkeneoListResponse<T> = {\n _embedded?: {\n items?: T[]\n }\n _links?: {\n next?: {\n href?: string\n }\n }\n items_count?: number\n}\n\ntype AkeneoMediaFile = {\n code: string\n original_filename?: string | null\n mime_type?: string | null\n size?: number | null\n extension?: string | null\n _links?: {\n download?: {\n href?: string\n }\n }\n}\n\nexport type AkeneoClientDeps = {\n lookupHost?: HostLookup\n allowPrivate?: boolean\n fetchImpl?: SafeOutboundFetchOptions['fetchImpl']\n}\n\nexport class UnsafeAkeneoUrlError extends Error {\n public readonly reason: string\n\n constructor(reason: UrlSafetyReason, message?: string) {\n super(message ?? `Akeneo URL rejected: ${reason}`)\n this.name = 'UnsafeAkeneoUrlError'\n this.reason = reason\n }\n}\n\ntype AkeneoClient = ReturnType<typeof createAkeneoClient>\n\nconst AKENEO_URL_SUBJECT = 'Akeneo URL'\nconst DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS = ['*.cloud.akeneo.com', '*.akeneo.cloud'] as const\nconst ALLOWED_AKENEO_HOSTS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOWED_HOSTS',\n 'OPENMERCATO_AKENEO_ALLOWED_HOSTS',\n 'AKENEO_ALLOWED_HOSTS',\n] as const\nconst ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOW_PRIVATE_URLS',\n 'OPENMERCATO_AKENEO_ALLOW_PRIVATE_URLS',\n 'AKENEO_ALLOW_PRIVATE_URLS',\n] as const\n\nconst akeneoUrlErrorFactory = (reason: UrlSafetyReason, message: string) =>\n new UnsafeAkeneoUrlError(reason, message)\n\nfunction normalizeBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/g, '')\n}\n\nfunction readAllowedAkeneoHostPatterns(env: NodeJS.ProcessEnv = process.env): string[] {\n for (const key of ALLOWED_AKENEO_HOSTS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw !== 'string') continue\n const patterns = raw\n .split(',')\n .map((entry) => entry.trim().toLowerCase())\n .filter((entry) => entry.length > 0)\n if (patterns.length > 0) {\n return patterns\n }\n }\n\n return [...DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS]\n}\n\nfunction isAllowPrivateAkeneoUrlsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n for (const key of ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return parseBooleanWithDefault(raw, false)\n }\n }\n return false\n}\n\nfunction matchesAkeneoHostPattern(hostname: string, pattern: string): boolean {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2)\n return hostname === suffix || hostname.endsWith(`.${suffix}`)\n }\n\n return hostname === pattern\n}\n\nexport function validateAkeneoApiUrl(rawUrl: string, env: NodeJS.ProcessEnv = process.env): string {\n let parsed: URL\n try {\n parsed = new URL(rawUrl.trim())\n } catch {\n throw new Error('Akeneo URL must be a valid absolute URL')\n }\n\n if (parsed.protocol !== 'https:') {\n throw new Error('Akeneo URL must use https')\n }\n if (parsed.username || parsed.password) {\n throw new Error('Akeneo URL must not include embedded credentials')\n }\n if (parsed.port && parsed.port !== '443') {\n throw new Error('Akeneo URL must not use a custom port')\n }\n if (parsed.pathname !== '/' && parsed.pathname !== '') {\n throw new Error('Akeneo URL must not include a path')\n }\n if (parsed.search || parsed.hash) {\n throw new Error('Akeneo URL must not include query parameters or fragments')\n }\n\n const hostname = parsed.hostname.toLowerCase()\n const allowedPatterns = readAllowedAkeneoHostPatterns(env)\n if (!allowedPatterns.some((pattern) => matchesAkeneoHostPattern(hostname, pattern))) {\n throw new Error(`Akeneo URL host is not allowed: ${hostname}`)\n }\n\n return normalizeBaseUrl(parsed.origin)\n}\n\nexport function normalizeAkeneoDateTime(value: string | null | undefined): string | null {\n if (typeof value !== 'string' || value.trim().length === 0) return null\n const trimmed = value.trim()\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(trimmed)) {\n return trimmed\n }\n\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) {\n return null\n }\n\n const year = parsed.getUTCFullYear()\n const month = String(parsed.getUTCMonth() + 1).padStart(2, '0')\n const day = String(parsed.getUTCDate()).padStart(2, '0')\n const hours = String(parsed.getUTCHours()).padStart(2, '0')\n const minutes = String(parsed.getUTCMinutes()).padStart(2, '0')\n const seconds = String(parsed.getUTCSeconds()).padStart(2, '0')\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\nexport function encodeAkeneoPathParam(value: string): string {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')\n}\n\nexport function sanitizeAkeneoProductNextUrl(nextUrl: string): string {\n let url: URL\n try {\n url = new URL(nextUrl)\n } catch {\n return nextUrl\n }\n\n const rawSearch = url.searchParams.get('search')\n if (!rawSearch) {\n return url.toString()\n }\n\n try {\n const parsed = JSON.parse(rawSearch) as Record<string, unknown>\n const updatedFilters = Array.isArray(parsed.updated) ? parsed.updated : []\n const sanitizedUpdated = updatedFilters\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const record = entry as Record<string, unknown>\n const normalizedValue = normalizeAkeneoDateTime(\n typeof record.value === 'string' ? record.value : null,\n )\n if (!normalizedValue) return null\n return {\n ...record,\n value: normalizedValue,\n }\n })\n .filter((entry) => entry !== null) as Record<string, unknown>[]\n\n if (sanitizedUpdated.length === 0) {\n delete parsed.updated\n } else {\n parsed.updated = sanitizedUpdated\n }\n\n if (Object.keys(parsed).length === 0) {\n url.searchParams.delete('search')\n } else {\n url.searchParams.set('search', JSON.stringify(parsed))\n }\n return url.toString()\n } catch {\n return nextUrl\n }\n}\n\nfunction coerceCredentials(credentials: Record<string, unknown>): AkeneoCredentialShape {\n const apiUrl = typeof credentials.apiUrl === 'string' ? credentials.apiUrl.trim() : ''\n const clientId = typeof credentials.clientId === 'string' ? credentials.clientId.trim() : ''\n const clientSecret = typeof credentials.clientSecret === 'string' ? credentials.clientSecret : ''\n const username = typeof credentials.username === 'string' ? credentials.username.trim() : ''\n const password = typeof credentials.password === 'string' ? credentials.password : ''\n if (!apiUrl || !clientId || !clientSecret || !username || !password) {\n throw new Error('Akeneo credentials are incomplete')\n }\n return {\n apiUrl: validateAkeneoApiUrl(apiUrl),\n clientId,\n clientSecret,\n username,\n password,\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nconst DEFAULT_AKENEO_REQUEST_TIMEOUT_MS = 30_000\nconst DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES = 5\nconst DEFAULT_AKENEO_RETRY_AFTER_CAP_MS = 60_000\n\nfunction resolvePositiveIntEnv(raw: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(raw ?? '', 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction resolveAkeneoRequestTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_REQUEST_TIMEOUT_MS, DEFAULT_AKENEO_REQUEST_TIMEOUT_MS)\n}\n\nfunction resolveAkeneoMaxRateLimitRetries(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_MAX_RATE_LIMIT_RETRIES, DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES)\n}\n\nfunction clampAkeneoRetryAfterMs(retryAfterHeader: string | null, env: NodeJS.ProcessEnv = process.env): number {\n const retryAfter = Number(retryAfterHeader ?? '1')\n const requestedMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 1000\n const cap = resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_RETRY_AFTER_CAP_MS, DEFAULT_AKENEO_RETRY_AFTER_CAP_MS)\n return Math.min(Math.max(requestedMs, 0), cap)\n}\n\nasync function safeAkeneoFetchWithTimeout(\n input: string,\n init: FetchWithTimeoutInit = {},\n deps: AkeneoClientDeps = {},\n): Promise<Response> {\n const { timeoutMs, signal, ...rest } = init\n const effectiveTimeout = resolveTimeoutMs(timeoutMs)\n const controller = new AbortController()\n const timer: ReturnType<typeof setTimeout> = setTimeout(() => {\n controller.abort(new FetchTimeoutError(input, effectiveTimeout))\n }, effectiveTimeout)\n\n const onExternalAbort = () => {\n controller.abort((signal as AbortSignal | undefined)?.reason)\n }\n\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer)\n throw signal.reason instanceof Error ? signal.reason : new DOMException('Aborted', 'AbortError')\n }\n signal.addEventListener('abort', onExternalAbort, { once: true })\n }\n\n try {\n return await safeOutboundFetch(input, { ...rest, signal: controller.signal }, {\n errorFactory: akeneoUrlErrorFactory,\n subject: AKENEO_URL_SUBJECT,\n allowPrivate: deps.allowPrivate ?? isAllowPrivateAkeneoUrlsEnabled(),\n lookupHost: deps.lookupHost,\n fetchImpl: deps.fetchImpl,\n })\n } catch (err) {\n if ((err as { name?: string } | null)?.name === 'AbortError') {\n const reason = (controller.signal as AbortSignal & { reason?: unknown }).reason\n if (reason instanceof FetchTimeoutError) throw reason\n if (reason instanceof Error) throw reason\n }\n throw err\n } finally {\n clearTimeout(timer)\n if (signal) signal.removeEventListener('abort', onExternalAbort)\n }\n}\n\nexport function createAkeneoClient(credentialsInput: Record<string, unknown>, deps: AkeneoClientDeps = {}) {\n const credentials = coerceCredentials(credentialsInput)\n const akeneoBaseUrl = new URL(`${credentials.apiUrl}/`)\n const tokenEndpointUrl = new URL('/api/oauth/v1/token', akeneoBaseUrl).toString()\n let tokenState: TokenState | null = null\n let lastAttributeOptionRequestAt = 0\n const familyCache = new Map<string, Promise<AkeneoFamily | null>>()\n const familyVariantCache = new Map<string, Promise<AkeneoFamilyVariant | null>>()\n const productModelCache = new Map<string, Promise<AkeneoProductModel | null>>()\n const attributeCache = new Map<string, Promise<AkeneoAttribute | null>>()\n const categoryCache = new Map<string, Promise<AkeneoCategory | null>>()\n const mediaFileCache = new Map<string, Promise<AkeneoMediaFile | null>>()\n\n function resolveAkeneoRequestUrl(pathOrUrl: string): string {\n const resolved = new URL(pathOrUrl, akeneoBaseUrl)\n if (resolved.origin !== akeneoBaseUrl.origin) {\n throw new Error(`Akeneo request URL must stay on the configured host: ${resolved.origin}`)\n }\n return resolved.toString()\n }\n\n function normalizeAkeneoNextUrl(nextUrl: string): string {\n return resolveAkeneoRequestUrl(nextUrl)\n }\n\n async function acquirePasswordGrantToken(): Promise<TokenState> {\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'password',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n username: credentials.username,\n password: credentials.password,\n }),\n }, deps)\n\n if (!response.ok) {\n const message = await response.text()\n throw new Error(`Akeneo authentication failed (${response.status}): ${message}`)\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) {\n throw new Error('Akeneo authentication did not return an access token')\n }\n\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function refreshAccessToken(current: TokenState): Promise<TokenState> {\n if (!current.refreshToken) return acquirePasswordGrantToken()\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n refresh_token: current.refreshToken,\n }),\n }, deps)\n\n if (!response.ok) {\n return acquirePasswordGrantToken()\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) return acquirePasswordGrantToken()\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? current.refreshToken ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function ensureToken(forceRefresh = false): Promise<string> {\n if (!forceRefresh && tokenState && tokenState.expiresAt > Date.now()) {\n return tokenState.accessToken\n }\n tokenState = tokenState ? await refreshAccessToken(tokenState) : await acquirePasswordGrantToken()\n return tokenState.accessToken\n }\n\n async function request<T>(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<T> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return request<T>(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return request<T>(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n if (response.status === 204) {\n return null as T\n }\n\n return response.json() as Promise<T>\n }\n\n async function requestBinary(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n }> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n headers: {\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return requestBinary(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return requestBinary(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n return {\n buffer: Buffer.from(arrayBuffer),\n contentType: response.headers.get('content-type'),\n contentLength: Number(response.headers.get('content-length') ?? '') || arrayBuffer.byteLength || null,\n }\n }\n\n function buildUrl(path: string, params: Record<string, string | number | boolean | undefined | null>): string {\n const url = new URL(resolveAkeneoRequestUrl(path))\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null || value === '') continue\n url.searchParams.set(key, String(value))\n }\n return url.toString()\n }\n\n async function readList<T>(pathOrUrl: string, params?: Record<string, string | number | boolean | undefined | null>): Promise<{\n items: T[]\n nextUrl: string | null\n totalEstimate: number | null\n }> {\n const payload = await request<AkeneoListResponse<T>>(\n params ? buildUrl(pathOrUrl, params) : pathOrUrl,\n )\n return {\n items: Array.isArray(payload?._embedded?.items) ? payload._embedded.items : [],\n nextUrl: typeof payload?._links?.next?.href === 'string'\n ? normalizeAkeneoNextUrl(payload._links.next.href)\n : null,\n totalEstimate: typeof payload?.items_count === 'number' ? payload.items_count : null,\n }\n }\n\n async function countProducts(updatedAfter?: string | null): Promise<number | null> {\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: 1,\n pagination_type: 'page',\n with_count: true,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n\n const page = await readList<AkeneoProduct>('/api/rest/v1/products-uuid', params)\n return page.totalEstimate\n }\n\n async function getSystemProbe(): Promise<{ version: string | null }> {\n try {\n const result = await request<Record<string, unknown>>('/api/rest/v1/system-information')\n return {\n version: typeof result.pim_version === 'string' ? result.pim_version : null,\n }\n } catch {\n await readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: 1,\n pagination_type: 'page',\n })\n return { version: null }\n }\n }\n\n async function listProducts(options: {\n nextUrl?: string | null\n batchSize: number\n updatedAfter?: string | null\n }): Promise<{ items: AkeneoProduct[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (options.nextUrl) {\n const page = await readList<AkeneoProduct>(normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(options.nextUrl)))\n return {\n ...page,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: Math.min(Math.max(options.batchSize, 1), 100),\n pagination_type: 'search_after',\n with_count: false,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(options.updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n const [page, totalEstimate] = await Promise.all([\n readList<AkeneoProduct>('/api/rest/v1/products-uuid', params),\n countProducts(normalizedUpdatedAfter).catch(() => null),\n ])\n return {\n ...page,\n totalEstimate: totalEstimate ?? page.totalEstimate,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n\n async function listCategories(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoCategory[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoCategory>(nextUrl)\n return readList<AkeneoCategory>('/api/rest/v1/categories', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listAttributes(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoAttribute[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoAttribute>(nextUrl)\n return readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilies(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoFamily[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamily>(nextUrl)\n return readList<AkeneoFamily>('/api/rest/v1/families', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilyVariants(\n familyCode: string,\n nextUrl?: string | null,\n batchSize = 100,\n ): Promise<{ items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamilyVariant>(nextUrl)\n return readList<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants`, {\n limit: Math.min(Math.max(batchSize, 1), 100),\n with_count: false,\n })\n }\n\n async function listChannels(): Promise<AkeneoChannel[]> {\n const response = await readList<AkeneoChannel>('/api/rest/v1/channels', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function listLocales(): Promise<AkeneoLocale[]> {\n const response = await readList<AkeneoLocale>('/api/rest/v1/locales', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function getCategory(code: string): Promise<AkeneoCategory | null> {\n if (!categoryCache.has(code)) {\n categoryCache.set(code, request<AkeneoCategory>(`/api/rest/v1/categories/${encodeURIComponent(code)}`).catch(() => null))\n }\n return categoryCache.get(code) ?? null\n }\n\n async function getAttribute(code: string): Promise<AkeneoAttribute | null> {\n if (!attributeCache.has(code)) {\n attributeCache.set(code, request<AkeneoAttribute>(`/api/rest/v1/attributes/${encodeURIComponent(code)}`).catch(() => null))\n }\n return attributeCache.get(code) ?? null\n }\n\n async function listAttributeOptions(attributeCode: string): Promise<AkeneoAttributeOption[]> {\n const options: AkeneoAttributeOption[] = []\n let nextUrl: string | null | undefined = null\n do {\n const now = Date.now()\n const waitMs = Math.max(0, 350 - (now - lastAttributeOptionRequestAt))\n if (waitMs > 0) await sleep(waitMs)\n lastAttributeOptionRequestAt = Date.now()\n const page: { items: AkeneoAttributeOption[]; nextUrl: string | null; totalEstimate: number | null } = await readList<AkeneoAttributeOption>(\n nextUrl ?? `/api/rest/v1/attributes/${encodeURIComponent(attributeCode)}/options`,\n nextUrl\n ? undefined\n : {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n },\n )\n options.push(...page.items)\n nextUrl = page.nextUrl\n } while (nextUrl)\n return options\n }\n\n async function getFamily(code: string): Promise<AkeneoFamily | null> {\n if (!familyCache.has(code)) {\n familyCache.set(code, request<AkeneoFamily>(`/api/rest/v1/families/${encodeURIComponent(code)}`).catch(() => null))\n }\n return familyCache.get(code) ?? null\n }\n\n async function getFamilyVariant(familyCode: string, familyVariantCode: string): Promise<AkeneoFamilyVariant | null> {\n const cacheKey = `${familyCode}:${familyVariantCode}`\n if (!familyVariantCache.has(cacheKey)) {\n familyVariantCache.set(\n cacheKey,\n request<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants/${encodeURIComponent(familyVariantCode)}`).catch(() => null),\n )\n }\n return familyVariantCache.get(cacheKey) ?? null\n }\n\n async function getProductModel(code: string): Promise<AkeneoProductModel | null> {\n if (!productModelCache.has(code)) {\n productModelCache.set(code, request<AkeneoProductModel>(`/api/rest/v1/product-models/${encodeURIComponent(code)}`).catch(() => null))\n }\n return productModelCache.get(code) ?? null\n }\n\n async function getMediaFile(code: string): Promise<AkeneoMediaFile | null> {\n if (!mediaFileCache.has(code)) {\n mediaFileCache.set(code, request<AkeneoMediaFile>(`/api/rest/v1/media-files/${encodeAkeneoPathParam(code)}`).catch(() => null))\n }\n return mediaFileCache.get(code) ?? null\n }\n\n async function downloadMediaFile(codeOrUrl: string): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n fileName: string | null\n code: string | null\n }> {\n if (codeOrUrl.startsWith('http://') || codeOrUrl.startsWith('https://') || codeOrUrl.startsWith('/')) {\n const binary = await requestBinary(codeOrUrl)\n return {\n ...binary,\n fileName: null,\n code: null,\n }\n }\n\n const mediaFile = await getMediaFile(codeOrUrl)\n if (!mediaFile) {\n throw new Error(`Akeneo media file ${codeOrUrl} was not found`)\n }\n const downloadHref = mediaFile._links?.download?.href\n const binary = await requestBinary(downloadHref || `/api/rest/v1/media-files/${encodeAkeneoPathParam(codeOrUrl)}/download`)\n return {\n ...binary,\n fileName: typeof mediaFile.original_filename === 'string' && mediaFile.original_filename.trim().length > 0\n ? mediaFile.original_filename.trim()\n : null,\n code: mediaFile.code ?? codeOrUrl,\n }\n }\n\n async function collectDiscoveryData(): Promise<{\n locales: Array<{ code: string; label: string; enabled: boolean }>\n channels: Array<{ code: string; label: string; locales: string[] }>\n attributes: Array<{ code: string; type: string; label: string; localizable: boolean; scopable: boolean; group?: string; metricFamily?: string }>\n families: Array<{ code: string; label: string; attributeCount: number }>\n familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }>\n version: string | null\n }> {\n const [locales, channels, attributes, families, probe] = await Promise.all([\n listLocales().catch(() => []),\n listChannels().catch(() => []),\n listAttributes(null, 100),\n listFamilies(null, 100),\n getSystemProbe().catch(() => ({ version: null })),\n ])\n\n const familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }> = []\n for (const family of families.items) {\n let nextUrl: string | null = null\n do {\n const page: { items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null } = await listFamilyVariants(family.code, nextUrl, 100).catch(() => ({\n items: [] as AkeneoFamilyVariant[],\n nextUrl: null,\n totalEstimate: null,\n }))\n familyVariants.push(...page.items.map((familyVariant) => ({\n familyCode: family.code,\n code: familyVariant.code,\n label: labelFromLocalizedRecord(familyVariant.labels ?? null, null, familyVariant.code),\n axes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { axes?: string[] }) => Array.isArray(set.axes) ? set.axes : []) ?? []),\n attributes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { attributes?: string[] }) => Array.isArray(set.attributes) ? set.attributes : []) ?? []),\n })))\n nextUrl = page.nextUrl\n } while (nextUrl)\n }\n\n return {\n locales: locales.map((locale) => ({\n code: locale.code,\n label: labelFromLocalizedRecord(locale.labels ?? null, null, locale.code),\n enabled: locale.enabled ?? false,\n })),\n channels: channels.map((channel) => ({\n code: channel.code,\n label: labelFromLocalizedRecord(channel.labels ?? null, null, channel.code),\n locales: dedupeStrings(channel.locales ?? []),\n })),\n attributes: attributes.items.map((attribute) => ({\n code: attribute.code,\n type: attribute.type,\n label: labelFromLocalizedRecord(attribute.labels ?? null, null, attribute.code),\n localizable: Boolean(attribute.localizable),\n scopable: Boolean(attribute.scopable),\n group: typeof attribute.group === 'string' && attribute.group.trim().length > 0 ? attribute.group.trim() : undefined,\n metricFamily: typeof attribute.metric_family === 'string' && attribute.metric_family.trim().length > 0\n ? attribute.metric_family.trim()\n : undefined,\n })),\n families: families.items.map((family) => ({\n code: family.code,\n label: labelFromLocalizedRecord(family.labels ?? null, null, family.code),\n attributeCount: Array.isArray(family.attributes) ? family.attributes.length : 0,\n })),\n familyVariants,\n version: probe.version,\n }\n }\n\n return {\n credentials,\n getSystemProbe,\n collectDiscoveryData,\n listProducts,\n listCategories,\n listAttributes,\n listFamilies,\n listFamilyVariants,\n listChannels,\n listLocales,\n getCategory,\n getAttribute,\n listAttributeOptions,\n getFamily,\n getFamilyVariant,\n getProductModel,\n getMediaFile,\n downloadMediaFile,\n }\n}\n\nexport type { AkeneoClient }\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'\nimport {\n FetchTimeoutError,\n type FetchWithTimeoutInit,\n resolveTimeoutMs,\n} from '@open-mercato/shared/lib/http/fetchWithTimeout'\nimport {\n safeOutboundFetch,\n type HostLookup,\n type SafeOutboundFetchOptions,\n type UrlSafetyReason,\n} from '@open-mercato/shared/lib/url-safety'\nimport { dedupeStrings, labelFromLocalizedRecord, safeRecord, type AkeneoAttribute, type AkeneoAttributeOption, type AkeneoCategory, type AkeneoChannel, type AkeneoCredentialShape, type AkeneoFamily, type AkeneoFamilyVariant, type AkeneoLocale, type AkeneoProduct, type AkeneoProductModel } from './shared'\n\ntype TokenState = {\n accessToken: string\n refreshToken?: string | null\n expiresAt: number\n}\n\ntype AkeneoListResponse<T> = {\n _embedded?: {\n items?: T[]\n }\n _links?: {\n next?: {\n href?: string\n }\n }\n items_count?: number\n}\n\ntype AkeneoMediaFile = {\n code: string\n original_filename?: string | null\n mime_type?: string | null\n size?: number | null\n extension?: string | null\n _links?: {\n download?: {\n href?: string\n }\n }\n}\n\nexport type AkeneoClientDeps = {\n lookupHost?: HostLookup\n allowPrivate?: boolean\n fetchImpl?: SafeOutboundFetchOptions['fetchImpl']\n}\n\nexport class UnsafeAkeneoUrlError extends Error {\n public readonly reason: string\n\n constructor(reason: UrlSafetyReason, message?: string) {\n super(message ?? `Akeneo URL rejected: ${reason}`)\n this.name = 'UnsafeAkeneoUrlError'\n this.reason = reason\n }\n}\n\ntype AkeneoClient = ReturnType<typeof createAkeneoClient>\n\nconst AKENEO_URL_SUBJECT = 'Akeneo URL'\nconst DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS = ['*.cloud.akeneo.com', '*.akeneo.cloud'] as const\nconst ALLOWED_AKENEO_HOSTS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOWED_HOSTS',\n 'OPENMERCATO_AKENEO_ALLOWED_HOSTS',\n 'AKENEO_ALLOWED_HOSTS',\n] as const\nconst ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS = [\n 'OM_INTEGRATION_AKENEO_ALLOW_PRIVATE_URLS',\n 'OPENMERCATO_AKENEO_ALLOW_PRIVATE_URLS',\n 'AKENEO_ALLOW_PRIVATE_URLS',\n] as const\n\nconst akeneoUrlErrorFactory = (reason: UrlSafetyReason, message: string) =>\n new UnsafeAkeneoUrlError(reason, message)\n\nfunction normalizeBaseUrl(url: string): string {\n return url.trim().replace(/\\/+$/g, '')\n}\n\nfunction readAllowedAkeneoHostPatterns(env: NodeJS.ProcessEnv = process.env): string[] {\n for (const key of ALLOWED_AKENEO_HOSTS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw !== 'string') continue\n const patterns = raw\n .split(',')\n .map((entry) => entry.trim().toLowerCase())\n .filter((entry) => entry.length > 0)\n if (patterns.length > 0) {\n return patterns\n }\n }\n\n return [...DEFAULT_ALLOWED_AKENEO_HOST_PATTERNS]\n}\n\nfunction isAllowPrivateAkeneoUrlsEnabled(env: NodeJS.ProcessEnv = process.env): boolean {\n for (const key of ALLOW_PRIVATE_AKENEO_URLS_ENV_KEYS) {\n const raw = env[key]\n if (typeof raw === 'string' && raw.trim().length > 0) {\n return parseBooleanWithDefault(raw, false)\n }\n }\n return false\n}\n\nfunction matchesAkeneoHostPattern(hostname: string, pattern: string): boolean {\n if (pattern.startsWith('*.')) {\n const suffix = pattern.slice(2)\n return hostname === suffix || hostname.endsWith(`.${suffix}`)\n }\n\n return hostname === pattern\n}\n\nexport function validateAkeneoApiUrl(rawUrl: string, env: NodeJS.ProcessEnv = process.env): string {\n let parsed: URL\n try {\n parsed = new URL(rawUrl.trim())\n } catch {\n throw new Error('Akeneo URL must be a valid absolute URL')\n }\n\n if (parsed.protocol !== 'https:') {\n throw new Error('Akeneo URL must use https')\n }\n if (parsed.username || parsed.password) {\n throw new Error('Akeneo URL must not include embedded credentials')\n }\n if (parsed.port && parsed.port !== '443') {\n throw new Error('Akeneo URL must not use a custom port')\n }\n if (parsed.pathname !== '/' && parsed.pathname !== '') {\n throw new Error('Akeneo URL must not include a path')\n }\n if (parsed.search || parsed.hash) {\n throw new Error('Akeneo URL must not include query parameters or fragments')\n }\n\n const hostname = parsed.hostname.toLowerCase()\n const allowedPatterns = readAllowedAkeneoHostPatterns(env)\n if (!allowedPatterns.some((pattern) => matchesAkeneoHostPattern(hostname, pattern))) {\n throw new Error(`Akeneo URL host is not allowed: ${hostname}`)\n }\n\n return normalizeBaseUrl(parsed.origin)\n}\n\nexport function normalizeAkeneoDateTime(value: string | null | undefined): string | null {\n if (typeof value !== 'string' || value.trim().length === 0) return null\n const trimmed = value.trim()\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(trimmed)) {\n return trimmed\n }\n\n const parsed = new Date(trimmed)\n if (Number.isNaN(parsed.getTime())) {\n return null\n }\n\n const year = parsed.getUTCFullYear()\n const month = String(parsed.getUTCMonth() + 1).padStart(2, '0')\n const day = String(parsed.getUTCDate()).padStart(2, '0')\n const hours = String(parsed.getUTCHours()).padStart(2, '0')\n const minutes = String(parsed.getUTCMinutes()).padStart(2, '0')\n const seconds = String(parsed.getUTCSeconds()).padStart(2, '0')\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\nexport function encodeAkeneoPathParam(value: string): string {\n return value\n .split('/')\n .map((segment) => encodeURIComponent(segment))\n .join('/')\n}\n\nexport function sanitizeAkeneoProductNextUrl(nextUrl: string): string {\n let url: URL\n try {\n url = new URL(nextUrl)\n } catch {\n return nextUrl\n }\n\n const rawSearch = url.searchParams.get('search')\n if (!rawSearch) {\n return url.toString()\n }\n\n try {\n const parsed = JSON.parse(rawSearch) as Record<string, unknown>\n const updatedFilters = Array.isArray(parsed.updated) ? parsed.updated : []\n const sanitizedUpdated = updatedFilters\n .map((entry) => {\n if (!entry || typeof entry !== 'object') return null\n const record = entry as Record<string, unknown>\n const normalizedValue = normalizeAkeneoDateTime(\n typeof record.value === 'string' ? record.value : null,\n )\n if (!normalizedValue) return null\n return {\n ...record,\n value: normalizedValue,\n }\n })\n .filter((entry) => entry !== null) as Record<string, unknown>[]\n\n if (sanitizedUpdated.length === 0) {\n delete parsed.updated\n } else {\n parsed.updated = sanitizedUpdated\n }\n\n if (Object.keys(parsed).length === 0) {\n url.searchParams.delete('search')\n } else {\n url.searchParams.set('search', JSON.stringify(parsed))\n }\n return url.toString()\n } catch {\n return nextUrl\n }\n}\n\nfunction coerceCredentials(credentials: Record<string, unknown>): AkeneoCredentialShape {\n const apiUrl = typeof credentials.apiUrl === 'string' ? credentials.apiUrl.trim() : ''\n const clientId = typeof credentials.clientId === 'string' ? credentials.clientId.trim() : ''\n const clientSecret = typeof credentials.clientSecret === 'string' ? credentials.clientSecret : ''\n const username = typeof credentials.username === 'string' ? credentials.username.trim() : ''\n const password = typeof credentials.password === 'string' ? credentials.password : ''\n if (!apiUrl || !clientId || !clientSecret || !username || !password) {\n throw new Error('Akeneo credentials are incomplete')\n }\n return {\n apiUrl: validateAkeneoApiUrl(apiUrl),\n clientId,\n clientSecret,\n username,\n password,\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nconst DEFAULT_AKENEO_REQUEST_TIMEOUT_MS = 30_000\nconst DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES = 5\nconst DEFAULT_AKENEO_RETRY_AFTER_CAP_MS = 60_000\n\nfunction resolvePositiveIntEnv(raw: string | undefined, fallback: number): number {\n const parsed = Number.parseInt(raw ?? '', 10)\n return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback\n}\n\nfunction resolveAkeneoRequestTimeoutMs(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_REQUEST_TIMEOUT_MS, DEFAULT_AKENEO_REQUEST_TIMEOUT_MS)\n}\n\nfunction resolveAkeneoMaxRateLimitRetries(env: NodeJS.ProcessEnv = process.env): number {\n return resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_MAX_RATE_LIMIT_RETRIES, DEFAULT_AKENEO_MAX_RATE_LIMIT_RETRIES)\n}\n\nfunction clampAkeneoRetryAfterMs(retryAfterHeader: string | null, env: NodeJS.ProcessEnv = process.env): number {\n const retryAfter = Number(retryAfterHeader ?? '1')\n const requestedMs = Number.isFinite(retryAfter) ? retryAfter * 1000 : 1000\n const cap = resolvePositiveIntEnv(env.OM_INTEGRATION_AKENEO_RETRY_AFTER_CAP_MS, DEFAULT_AKENEO_RETRY_AFTER_CAP_MS)\n return Math.min(Math.max(requestedMs, 0), cap)\n}\n\nasync function safeAkeneoFetchWithTimeout(\n input: string,\n init: FetchWithTimeoutInit = {},\n deps: AkeneoClientDeps = {},\n): Promise<Response> {\n const { timeoutMs, signal, ...rest } = init\n const effectiveTimeout = resolveTimeoutMs(timeoutMs)\n const controller = new AbortController()\n const timer: ReturnType<typeof setTimeout> = setTimeout(() => {\n controller.abort(new FetchTimeoutError(input, effectiveTimeout))\n }, effectiveTimeout)\n\n const onExternalAbort = () => {\n controller.abort((signal as AbortSignal | undefined)?.reason)\n }\n\n if (signal) {\n if (signal.aborted) {\n clearTimeout(timer)\n throw signal.reason instanceof Error ? signal.reason : new DOMException('Aborted', 'AbortError')\n }\n signal.addEventListener('abort', onExternalAbort, { once: true })\n }\n\n try {\n return await safeOutboundFetch(input, { ...rest, signal: controller.signal }, {\n errorFactory: akeneoUrlErrorFactory,\n subject: AKENEO_URL_SUBJECT,\n allowPrivate: deps.allowPrivate ?? isAllowPrivateAkeneoUrlsEnabled(),\n lookupHost: deps.lookupHost,\n fetchImpl: deps.fetchImpl,\n })\n } catch (err) {\n if ((err as { name?: string } | null)?.name === 'AbortError') {\n const reason = (controller.signal as AbortSignal & { reason?: unknown }).reason\n if (reason instanceof FetchTimeoutError) throw reason\n if (reason instanceof Error) throw reason\n }\n throw err\n } finally {\n clearTimeout(timer)\n if (signal) signal.removeEventListener('abort', onExternalAbort)\n }\n}\n\nexport function createAkeneoClient(credentialsInput: Record<string, unknown>, deps: AkeneoClientDeps = {}) {\n const credentials = coerceCredentials(credentialsInput)\n const akeneoBaseUrl = new URL(`${credentials.apiUrl}/`)\n const tokenEndpointUrl = new URL('/api/oauth/v1/token', akeneoBaseUrl).toString()\n let tokenState: TokenState | null = null\n let lastAttributeOptionRequestAt = 0\n const familyCache = new Map<string, Promise<AkeneoFamily | null>>()\n const familyVariantCache = new Map<string, Promise<AkeneoFamilyVariant | null>>()\n const productModelCache = new Map<string, Promise<AkeneoProductModel | null>>()\n const attributeCache = new Map<string, Promise<AkeneoAttribute | null>>()\n const categoryCache = new Map<string, Promise<AkeneoCategory | null>>()\n const mediaFileCache = new Map<string, Promise<AkeneoMediaFile | null>>()\n\n function resolveAkeneoRequestUrl(pathOrUrl: string): string {\n const resolved = new URL(pathOrUrl, akeneoBaseUrl)\n if (resolved.origin !== akeneoBaseUrl.origin) {\n throw new Error(`Akeneo request URL must stay on the configured host: ${resolved.origin}`)\n }\n return resolved.toString()\n }\n\n function normalizeAkeneoNextUrl(nextUrl: string): string {\n return resolveAkeneoRequestUrl(nextUrl)\n }\n\n async function acquirePasswordGrantToken(): Promise<TokenState> {\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'password',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n username: credentials.username,\n password: credentials.password,\n }),\n }, deps)\n\n if (!response.ok) {\n const message = await response.text()\n throw new Error(`Akeneo authentication failed (${response.status}): ${message}`)\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) {\n throw new Error('Akeneo authentication did not return an access token')\n }\n\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function refreshAccessToken(current: TokenState): Promise<TokenState> {\n if (!current.refreshToken) return acquirePasswordGrantToken()\n const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {\n method: 'POST',\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n client_id: credentials.clientId,\n client_secret: credentials.clientSecret,\n refresh_token: current.refreshToken,\n }),\n }, deps)\n\n if (!response.ok) {\n return acquirePasswordGrantToken()\n }\n\n const payload = await response.json() as {\n access_token?: string\n refresh_token?: string\n expires_in?: number\n }\n if (!payload.access_token) return acquirePasswordGrantToken()\n return {\n accessToken: payload.access_token,\n refreshToken: payload.refresh_token ?? current.refreshToken ?? null,\n expiresAt: Date.now() + ((payload.expires_in ?? 3600) * 1000) - 10_000,\n }\n }\n\n async function ensureToken(forceRefresh = false): Promise<string> {\n if (!forceRefresh && tokenState && tokenState.expiresAt > Date.now()) {\n return tokenState.accessToken\n }\n tokenState = tokenState ? await refreshAccessToken(tokenState) : await acquirePasswordGrantToken()\n return tokenState.accessToken\n }\n\n async function request<T>(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<T> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return request<T>(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return request<T>(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n if (response.status === 204) {\n return null as T\n }\n\n return response.json() as Promise<T>\n }\n\n async function requestBinary(\n pathOrUrl: string,\n init: RequestInit = {},\n retried = false,\n rateLimitAttempts = 0,\n ): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n }> {\n const url = resolveAkeneoRequestUrl(pathOrUrl)\n const token = await ensureToken()\n\n const response = await safeAkeneoFetchWithTimeout(url, {\n ...init,\n timeoutMs: resolveAkeneoRequestTimeoutMs(),\n redirect: 'manual',\n headers: {\n authorization: `Bearer ${token}`,\n ...init.headers,\n },\n }, deps)\n\n if (response.status === 401 && !retried) {\n await ensureToken(true)\n return requestBinary(pathOrUrl, init, true, rateLimitAttempts)\n }\n\n if (response.status === 429) {\n if (rateLimitAttempts >= resolveAkeneoMaxRateLimitRetries()) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (429): ${body}`)\n }\n await sleep(clampAkeneoRetryAfterMs(response.headers.get('retry-after')))\n return requestBinary(pathOrUrl, init, retried, rateLimitAttempts + 1)\n }\n\n if (!response.ok) {\n const body = await response.text()\n throw new Error(`Akeneo request failed (${response.status}): ${body}`)\n }\n\n const arrayBuffer = await response.arrayBuffer()\n return {\n buffer: Buffer.from(arrayBuffer),\n contentType: response.headers.get('content-type'),\n contentLength: Number(response.headers.get('content-length') ?? '') || arrayBuffer.byteLength || null,\n }\n }\n\n function buildUrl(path: string, params: Record<string, string | number | boolean | undefined | null>): string {\n const url = new URL(resolveAkeneoRequestUrl(path))\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null || value === '') continue\n url.searchParams.set(key, String(value))\n }\n return url.toString()\n }\n\n async function readList<T>(pathOrUrl: string, params?: Record<string, string | number | boolean | undefined | null>): Promise<{\n items: T[]\n nextUrl: string | null\n totalEstimate: number | null\n }> {\n const payload = await request<AkeneoListResponse<T>>(\n params ? buildUrl(pathOrUrl, params) : pathOrUrl,\n )\n return {\n items: Array.isArray(payload?._embedded?.items) ? payload._embedded.items : [],\n nextUrl: typeof payload?._links?.next?.href === 'string'\n ? normalizeAkeneoNextUrl(payload._links.next.href)\n : null,\n totalEstimate: typeof payload?.items_count === 'number' ? payload.items_count : null,\n }\n }\n\n async function countProducts(updatedAfter?: string | null): Promise<number | null> {\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: 1,\n pagination_type: 'page',\n with_count: true,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n\n const page = await readList<AkeneoProduct>('/api/rest/v1/products-uuid', params)\n return page.totalEstimate\n }\n\n async function getSystemProbe(): Promise<{ version: string | null }> {\n try {\n const result = await request<Record<string, unknown>>('/api/rest/v1/system-information')\n return {\n version: typeof result.pim_version === 'string' ? result.pim_version : null,\n }\n } catch {\n await readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: 1,\n pagination_type: 'page',\n })\n return { version: null }\n }\n }\n\n async function listProducts(options: {\n nextUrl?: string | null\n batchSize: number\n updatedAfter?: string | null\n }): Promise<{ items: AkeneoProduct[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (options.nextUrl) {\n const page = await readList<AkeneoProduct>(normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(options.nextUrl)))\n return {\n ...page,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n const params: Record<string, string | number | boolean | undefined | null> = {\n limit: Math.min(Math.max(options.batchSize, 1), 100),\n pagination_type: 'search_after',\n with_count: false,\n }\n const normalizedUpdatedAfter = normalizeAkeneoDateTime(options.updatedAfter)\n if (normalizedUpdatedAfter) {\n params.search = JSON.stringify({\n updated: [\n {\n operator: '>',\n value: normalizedUpdatedAfter,\n },\n ],\n })\n }\n const [page, totalEstimate] = await Promise.all([\n readList<AkeneoProduct>('/api/rest/v1/products-uuid', params),\n countProducts(normalizedUpdatedAfter).catch(() => null),\n ])\n return {\n ...page,\n totalEstimate: totalEstimate ?? page.totalEstimate,\n nextUrl: page.nextUrl ? normalizeAkeneoNextUrl(sanitizeAkeneoProductNextUrl(page.nextUrl)) : null,\n }\n }\n\n async function listCategories(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoCategory[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoCategory>(nextUrl)\n return readList<AkeneoCategory>('/api/rest/v1/categories', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listAttributes(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoAttribute[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoAttribute>(nextUrl)\n return readList<AkeneoAttribute>('/api/rest/v1/attributes', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilies(nextUrl?: string | null, batchSize = 100): Promise<{ items: AkeneoFamily[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamily>(nextUrl)\n return readList<AkeneoFamily>('/api/rest/v1/families', {\n limit: Math.min(Math.max(batchSize, 1), 100),\n pagination_type: 'page',\n with_count: true,\n })\n }\n\n async function listFamilyVariants(\n familyCode: string,\n nextUrl?: string | null,\n batchSize = 100,\n ): Promise<{ items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null }> {\n if (nextUrl) return readList<AkeneoFamilyVariant>(nextUrl)\n return readList<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants`, {\n limit: Math.min(Math.max(batchSize, 1), 100),\n with_count: false,\n })\n }\n\n async function listChannels(): Promise<AkeneoChannel[]> {\n const response = await readList<AkeneoChannel>('/api/rest/v1/channels', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function listLocales(): Promise<AkeneoLocale[]> {\n const response = await readList<AkeneoLocale>('/api/rest/v1/locales', {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n })\n return response.items\n }\n\n async function getCategory(code: string): Promise<AkeneoCategory | null> {\n if (!categoryCache.has(code)) {\n categoryCache.set(code, request<AkeneoCategory>(`/api/rest/v1/categories/${encodeURIComponent(code)}`).catch(() => null))\n }\n return categoryCache.get(code) ?? null\n }\n\n async function getAttribute(code: string): Promise<AkeneoAttribute | null> {\n if (!attributeCache.has(code)) {\n attributeCache.set(code, request<AkeneoAttribute>(`/api/rest/v1/attributes/${encodeURIComponent(code)}`).catch(() => null))\n }\n return attributeCache.get(code) ?? null\n }\n\n async function listAttributeOptions(attributeCode: string): Promise<AkeneoAttributeOption[]> {\n const options: AkeneoAttributeOption[] = []\n let nextUrl: string | null | undefined = null\n do {\n const now = Date.now()\n const waitMs = Math.max(0, 350 - (now - lastAttributeOptionRequestAt))\n if (waitMs > 0) await sleep(waitMs)\n lastAttributeOptionRequestAt = Date.now()\n const page: { items: AkeneoAttributeOption[]; nextUrl: string | null; totalEstimate: number | null } = await readList<AkeneoAttributeOption>(\n nextUrl ?? `/api/rest/v1/attributes/${encodeURIComponent(attributeCode)}/options`,\n nextUrl\n ? undefined\n : {\n limit: 100,\n pagination_type: 'page',\n with_count: true,\n },\n )\n options.push(...page.items)\n nextUrl = page.nextUrl\n } while (nextUrl)\n return options\n }\n\n async function getFamily(code: string): Promise<AkeneoFamily | null> {\n if (!familyCache.has(code)) {\n familyCache.set(code, request<AkeneoFamily>(`/api/rest/v1/families/${encodeURIComponent(code)}`).catch(() => null))\n }\n return familyCache.get(code) ?? null\n }\n\n async function getFamilyVariant(familyCode: string, familyVariantCode: string): Promise<AkeneoFamilyVariant | null> {\n const cacheKey = `${familyCode}:${familyVariantCode}`\n if (!familyVariantCache.has(cacheKey)) {\n familyVariantCache.set(\n cacheKey,\n request<AkeneoFamilyVariant>(`/api/rest/v1/families/${encodeURIComponent(familyCode)}/variants/${encodeURIComponent(familyVariantCode)}`).catch(() => null),\n )\n }\n return familyVariantCache.get(cacheKey) ?? null\n }\n\n async function getProductModel(code: string): Promise<AkeneoProductModel | null> {\n if (!productModelCache.has(code)) {\n productModelCache.set(code, request<AkeneoProductModel>(`/api/rest/v1/product-models/${encodeURIComponent(code)}`).catch(() => null))\n }\n return productModelCache.get(code) ?? null\n }\n\n async function getMediaFile(code: string): Promise<AkeneoMediaFile | null> {\n if (!mediaFileCache.has(code)) {\n mediaFileCache.set(code, request<AkeneoMediaFile>(`/api/rest/v1/media-files/${encodeAkeneoPathParam(code)}`).catch(() => null))\n }\n return mediaFileCache.get(code) ?? null\n }\n\n async function downloadMediaFile(codeOrUrl: string): Promise<{\n buffer: Buffer\n contentType: string | null\n contentLength: number | null\n fileName: string | null\n code: string | null\n }> {\n if (codeOrUrl.startsWith('http://') || codeOrUrl.startsWith('https://') || codeOrUrl.startsWith('/')) {\n const binary = await requestBinary(codeOrUrl)\n return {\n ...binary,\n fileName: null,\n code: null,\n }\n }\n\n const mediaFile = await getMediaFile(codeOrUrl)\n if (!mediaFile) {\n throw new Error(`Akeneo media file ${codeOrUrl} was not found`)\n }\n const downloadHref = mediaFile._links?.download?.href\n const binary = await requestBinary(downloadHref || `/api/rest/v1/media-files/${encodeAkeneoPathParam(codeOrUrl)}/download`)\n return {\n ...binary,\n fileName: typeof mediaFile.original_filename === 'string' && mediaFile.original_filename.trim().length > 0\n ? mediaFile.original_filename.trim()\n : null,\n code: mediaFile.code ?? codeOrUrl,\n }\n }\n\n async function collectDiscoveryData(): Promise<{\n locales: Array<{ code: string; label: string; enabled: boolean }>\n channels: Array<{ code: string; label: string; locales: string[] }>\n attributes: Array<{ code: string; type: string; label: string; localizable: boolean; scopable: boolean; group?: string; metricFamily?: string }>\n families: Array<{ code: string; label: string; attributeCount: number }>\n familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }>\n version: string | null\n }> {\n const [locales, channels, attributes, families, probe] = await Promise.all([\n listLocales().catch(() => []),\n listChannels().catch(() => []),\n listAttributes(null, 100),\n listFamilies(null, 100),\n getSystemProbe().catch(() => ({ version: null })),\n ])\n\n const familyVariants: Array<{ familyCode: string; code: string; label: string; axes: string[]; attributes: string[] }> = []\n for (const family of families.items) {\n let nextUrl: string | null = null\n do {\n const page: { items: AkeneoFamilyVariant[]; nextUrl: string | null; totalEstimate: number | null } = await listFamilyVariants(family.code, nextUrl, 100).catch(() => ({\n items: [] as AkeneoFamilyVariant[],\n nextUrl: null,\n totalEstimate: null,\n }))\n familyVariants.push(...page.items.map((familyVariant) => ({\n familyCode: family.code,\n code: familyVariant.code,\n label: labelFromLocalizedRecord(familyVariant.labels ?? null, null, familyVariant.code),\n axes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { axes?: string[] }) => Array.isArray(set.axes) ? set.axes : []) ?? []),\n attributes: dedupeStrings(familyVariant.variant_attribute_sets?.flatMap((set: { attributes?: string[] }) => Array.isArray(set.attributes) ? set.attributes : []) ?? []),\n })))\n nextUrl = page.nextUrl\n } while (nextUrl)\n }\n\n return {\n locales: locales.map((locale) => ({\n code: locale.code,\n label: labelFromLocalizedRecord(locale.labels ?? null, null, locale.code),\n enabled: locale.enabled ?? false,\n })),\n channels: channels.map((channel) => ({\n code: channel.code,\n label: labelFromLocalizedRecord(channel.labels ?? null, null, channel.code),\n locales: dedupeStrings(channel.locales ?? []),\n })),\n attributes: attributes.items.map((attribute) => ({\n code: attribute.code,\n type: attribute.type,\n label: labelFromLocalizedRecord(attribute.labels ?? null, null, attribute.code),\n localizable: Boolean(attribute.localizable),\n scopable: Boolean(attribute.scopable),\n group: typeof attribute.group === 'string' && attribute.group.trim().length > 0 ? attribute.group.trim() : undefined,\n metricFamily: typeof attribute.metric_family === 'string' && attribute.metric_family.trim().length > 0\n ? attribute.metric_family.trim()\n : undefined,\n })),\n families: families.items.map((family) => ({\n code: family.code,\n label: labelFromLocalizedRecord(family.labels ?? null, null, family.code),\n attributeCount: Array.isArray(family.attributes) ? family.attributes.length : 0,\n })),\n familyVariants,\n version: probe.version,\n }\n }\n\n return {\n credentials,\n getSystemProbe,\n collectDiscoveryData,\n listProducts,\n listCategories,\n listAttributes,\n listFamilies,\n listFamilyVariants,\n listChannels,\n listLocales,\n getCategory,\n getAttribute,\n listAttributeOptions,\n getFamily,\n getFamilyVariant,\n getProductModel,\n getMediaFile,\n downloadMediaFile,\n }\n}\n\nexport type { AkeneoClient }\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["page", "binary"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/sync-akeneo",
|
|
3
|
-
"version": "0.6.7-develop.
|
|
3
|
+
"version": "0.6.7-develop.6645.1.52f0b37813",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,18 +53,18 @@
|
|
|
53
53
|
}
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@open-mercato/core": "0.6.7-develop.
|
|
57
|
-
"@open-mercato/ui": "0.6.7-develop.
|
|
56
|
+
"@open-mercato/core": "0.6.7-develop.6645.1.52f0b37813",
|
|
57
|
+
"@open-mercato/ui": "0.6.7-develop.6645.1.52f0b37813",
|
|
58
58
|
"node-html-markdown": "^2.0.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@mikro-orm/postgresql": "^7.0.14",
|
|
62
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
62
|
+
"@open-mercato/shared": "0.6.7-develop.6645.1.52f0b37813",
|
|
63
63
|
"react": "^19.0.0",
|
|
64
64
|
"react-dom": "^19.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@open-mercato/shared": "0.6.7-develop.
|
|
67
|
+
"@open-mercato/shared": "0.6.7-develop.6645.1.52f0b37813",
|
|
68
68
|
"@types/jest": "^30.0.0",
|
|
69
69
|
"@types/react": "^19.2.17",
|
|
70
70
|
"@types/react-dom": "^19.2.3",
|
|
@@ -32,6 +32,15 @@ function jsonResponse(body: unknown, init?: ResponseInit): Response {
|
|
|
32
32
|
})
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function redirectResponse(): Response {
|
|
36
|
+
return new Response('redirected', {
|
|
37
|
+
status: 302,
|
|
38
|
+
headers: {
|
|
39
|
+
location: 'http://169.254.169.254/latest/meta-data/',
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
35
44
|
describe('akeneo client helpers', () => {
|
|
36
45
|
it('normalizes ISO timestamps to Akeneo query format', () => {
|
|
37
46
|
expect(normalizeAkeneoDateTime('2026-03-10T12:15:30.000Z')).toBe('2026-03-10 12:15:30')
|
|
@@ -174,6 +183,60 @@ describe('akeneo client security', () => {
|
|
|
174
183
|
}))
|
|
175
184
|
})
|
|
176
185
|
|
|
186
|
+
it('does not automatically follow redirects while acquiring an access token', async () => {
|
|
187
|
+
const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
|
|
188
|
+
fetchMock.mockImplementation(async (_input, init) => (
|
|
189
|
+
(init as RequestInit | undefined)?.redirect === 'manual'
|
|
190
|
+
? redirectResponse()
|
|
191
|
+
: new Response('internal metadata leaked through redirect', { status: 500 })
|
|
192
|
+
))
|
|
193
|
+
|
|
194
|
+
const client = createAkeneoClient(validCredentials)
|
|
195
|
+
await expect(client.getSystemProbe()).rejects.toThrow('Akeneo authentication failed (302)')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('does not automatically follow redirects on authenticated API requests', async () => {
|
|
199
|
+
const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
|
|
200
|
+
fetchMock.mockImplementation(async (input, init) => {
|
|
201
|
+
if (String(input).endsWith('/api/oauth/v1/token')) {
|
|
202
|
+
return jsonResponse({ access_token: 'token-123', expires_in: 3600 })
|
|
203
|
+
}
|
|
204
|
+
return (init as RequestInit | undefined)?.redirect === 'manual'
|
|
205
|
+
? redirectResponse()
|
|
206
|
+
: new Response('internal metadata leaked through redirect', { status: 500 })
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
const client = createAkeneoClient(validCredentials)
|
|
210
|
+
await expect(client.getSystemProbe()).rejects.toThrow('Akeneo request failed (302)')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('does not automatically follow redirects on media downloads', async () => {
|
|
214
|
+
const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
|
|
215
|
+
fetchMock.mockImplementation(async (input, init) => {
|
|
216
|
+
const url = String(input)
|
|
217
|
+
if (url.endsWith('/api/oauth/v1/token')) {
|
|
218
|
+
return jsonResponse({ access_token: 'token-123', expires_in: 3600 })
|
|
219
|
+
}
|
|
220
|
+
if (url.endsWith('/api/rest/v1/media-files/asset-1')) {
|
|
221
|
+
return jsonResponse({
|
|
222
|
+
code: 'asset-1',
|
|
223
|
+
original_filename: 'asset.jpg',
|
|
224
|
+
_links: {
|
|
225
|
+
download: {
|
|
226
|
+
href: 'https://tenant.cloud.akeneo.com/api/rest/v1/media-files/asset-1/download',
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
return (init as RequestInit | undefined)?.redirect === 'manual'
|
|
232
|
+
? redirectResponse()
|
|
233
|
+
: new Response('internal metadata leaked through redirect', { status: 500 })
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
const client = createAkeneoClient(validCredentials)
|
|
237
|
+
await expect(client.downloadMediaFile('asset-1')).rejects.toThrow('Akeneo request failed (302)')
|
|
238
|
+
})
|
|
239
|
+
|
|
177
240
|
it('falls back to the attributes reachability probe and reports an unknown version (issue #3621)', async () => {
|
|
178
241
|
const fetchMock = global.fetch as jest.MockedFunction<typeof fetch>
|
|
179
242
|
fetchMock
|
|
@@ -345,6 +345,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
|
|
|
345
345
|
const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
|
|
346
346
|
method: 'POST',
|
|
347
347
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
348
|
+
redirect: 'manual',
|
|
348
349
|
headers: {
|
|
349
350
|
accept: 'application/json',
|
|
350
351
|
'content-type': 'application/json',
|
|
@@ -384,6 +385,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
|
|
|
384
385
|
const response = await safeAkeneoFetchWithTimeout(tokenEndpointUrl, {
|
|
385
386
|
method: 'POST',
|
|
386
387
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
388
|
+
redirect: 'manual',
|
|
387
389
|
headers: {
|
|
388
390
|
accept: 'application/json',
|
|
389
391
|
'content-type': 'application/json',
|
|
@@ -433,6 +435,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
|
|
|
433
435
|
const response = await safeAkeneoFetchWithTimeout(url, {
|
|
434
436
|
...init,
|
|
435
437
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
438
|
+
redirect: 'manual',
|
|
436
439
|
headers: {
|
|
437
440
|
accept: 'application/json',
|
|
438
441
|
authorization: `Bearer ${token}`,
|
|
@@ -482,6 +485,7 @@ export function createAkeneoClient(credentialsInput: Record<string, unknown>, de
|
|
|
482
485
|
const response = await safeAkeneoFetchWithTimeout(url, {
|
|
483
486
|
...init,
|
|
484
487
|
timeoutMs: resolveAkeneoRequestTimeoutMs(),
|
|
488
|
+
redirect: 'manual',
|
|
485
489
|
headers: {
|
|
486
490
|
authorization: `Bearer ${token}`,
|
|
487
491
|
...init.headers,
|