(
attachments: Array<{
content: string
filename: string
mimeType: string
size: number
id: string
}>,
accessToken: string
)
| 99 | * Returns an array of downloaded files with base64-encoded data. |
| 100 | */ |
| 101 | export async function downloadJiraAttachments( |
| 102 | attachments: Array<{ |
| 103 | content: string |
| 104 | filename: string |
| 105 | mimeType: string |
| 106 | size: number |
| 107 | id: string |
| 108 | }>, |
| 109 | accessToken: string |
| 110 | ): Promise<Array<{ name: string; mimeType: string; data: string; size: number }>> { |
| 111 | const downloaded: Array<{ name: string; mimeType: string; data: string; size: number }> = [] |
| 112 | |
| 113 | for (const att of attachments) { |
| 114 | if (!att.content) continue |
| 115 | if (att.size > MAX_ATTACHMENT_SIZE) { |
| 116 | logger.warn(`Skipping attachment ${att.filename} (${att.size} bytes): exceeds size limit`) |
| 117 | continue |
| 118 | } |
| 119 | try { |
| 120 | const response = await fetchWithRetry(att.content, { |
| 121 | headers: { |
| 122 | Authorization: `Bearer ${accessToken}`, |
| 123 | Accept: '*/*', |
| 124 | }, |
| 125 | }) |
| 126 | |
| 127 | if (!response.ok) { |
| 128 | logger.warn(`Failed to download attachment ${att.filename}: HTTP ${response.status}`) |
| 129 | continue |
| 130 | } |
| 131 | |
| 132 | const arrayBuffer = await response.arrayBuffer() |
| 133 | const buffer = Buffer.from(arrayBuffer) |
| 134 | |
| 135 | downloaded.push({ |
| 136 | name: att.filename || `attachment-${att.id}`, |
| 137 | mimeType: att.mimeType || 'application/octet-stream', |
| 138 | data: buffer.toString('base64'), |
| 139 | size: buffer.length, |
| 140 | }) |
| 141 | } catch (error) { |
| 142 | logger.warn(`Failed to download attachment ${att.filename}:`, error) |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | return downloaded |
| 147 | } |
| 148 | |
| 149 | /** |
| 150 | * Normalizes an ISO timestamp into the format Jira's worklog API requires: |
no test coverage detected