mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
refactor(jira): group attachment modules with concrete owner imports (#31683)
This commit is contained in:
@@ -21,6 +21,18 @@ const {
|
||||
|
||||
vi.mock('./request-queue', () => ({ acquire: acquireMock, release: releaseMock }))
|
||||
|
||||
const { JiraApiError: JiraApiErrorMock } = vi.hoisted(() => ({
|
||||
JiraApiError: class JiraApiError extends Error {
|
||||
status: number | null
|
||||
constructor(message: string, status: number | null = null) {
|
||||
super(message)
|
||||
this.status = status
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('./jira-api-error', () => ({ JiraApiError: JiraApiErrorMock }))
|
||||
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
apiBasePath: (site: { authType?: string }) =>
|
||||
site.authType === 'server' ? '/rest/api/2' : '/rest/api/3',
|
||||
@@ -29,13 +41,7 @@ vi.mock('./authenticated-request', () => ({
|
||||
isAuthError: (...args: unknown[]) => isAuthErrorMock(...args),
|
||||
jiraRequest: (...args: unknown[]) => jiraRequestMock(...args),
|
||||
jiraRequestBinary: (...args: unknown[]) => jiraRequestBinaryMock(...args),
|
||||
JiraApiError: class JiraApiError extends Error {
|
||||
status: number | null
|
||||
constructor(message: string, status: number | null = null) {
|
||||
super(message)
|
||||
this.status = status
|
||||
}
|
||||
}
|
||||
JiraApiError: JiraApiErrorMock
|
||||
}))
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
@@ -80,7 +86,7 @@ describe('Jira ADF link mark mapping', () => {
|
||||
releaseMock.mockImplementation(() => {})
|
||||
jiraRequestBinaryMock.mockReset()
|
||||
jiraRequestMock.mockReset()
|
||||
const { _resetAttachmentImageCache } = await import('./attachment-image-cache')
|
||||
const { _resetAttachmentImageCache } = await import('./attachments/image-cache')
|
||||
_resetAttachmentImageCache()
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { JiraSite } from '../../shared/jira-types'
|
||||
import { apiBasePath } from './authenticated-request'
|
||||
import { isJiraGatewayBaseUrl } from './gateway-base-url'
|
||||
import type { AttachmentMeta } from './attachment-meta'
|
||||
import type { JiraSite } from '../../../shared/jira-types'
|
||||
import { apiBasePath } from '../authenticated-request'
|
||||
import { isJiraGatewayBaseUrl } from '../gateway-base-url'
|
||||
import type { AttachmentMeta } from './meta'
|
||||
|
||||
export function attachmentContentUrl(site: JiraSite, meta: AttachmentMeta): URL | null {
|
||||
if (!meta.contentUrl && site.authType === 'server') {
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { JiraAdfMediaAttrs } from './adf-markdown'
|
||||
import { MAX_IMAGES, parseImageAttachmentMetas } from './attachment-meta'
|
||||
import type { JiraAdfMediaAttrs } from '../adf-markdown'
|
||||
import { MAX_IMAGES, parseImageAttachmentMetas } from './meta'
|
||||
|
||||
/**
|
||||
* Pull attachment content IDs from Jira rendered HTML in document order.
|
||||
@@ -1,20 +1,20 @@
|
||||
import { jiraRequestBinary, type JiraClientForSite } from './authenticated-request'
|
||||
import type { AttachmentMeta } from './attachment-meta'
|
||||
import { attachmentContentUrl } from './attachment-content-url'
|
||||
import { jiraRequestBinary, type JiraClientForSite } from '../authenticated-request'
|
||||
import type { AttachmentMeta } from './meta'
|
||||
import { attachmentContentUrl } from './content-url'
|
||||
import {
|
||||
loadAttachmentBytesWithCache,
|
||||
type AttachmentImageBytes,
|
||||
attachmentImagesEpoch,
|
||||
getCachedAttachmentBytes
|
||||
} from './attachment-image-cache'
|
||||
} from './image-cache'
|
||||
import {
|
||||
JIRA_IMAGE_MAX_BYTES,
|
||||
JIRA_IMAGE_MAX_PENDING_READS,
|
||||
isJiraImageMimeType
|
||||
} from '../../shared/jira-attachment-image'
|
||||
import { withJiraDeadline } from './jira-read-failure'
|
||||
import { PrioritySemaphore } from '../../shared/priority-semaphore'
|
||||
import { waitForPromiseWithSignal } from '../../shared/abort-signal-reason'
|
||||
} from '../../../shared/jira-attachment-image'
|
||||
import { withJiraDeadline } from '../jira-read-failure'
|
||||
import { PrioritySemaphore } from '../../../shared/priority-semaphore'
|
||||
import { waitForPromiseWithSignal } from '../../../shared/abort-signal-reason'
|
||||
|
||||
const downloads = new PrioritySemaphore(3)
|
||||
const interests = new Map<string, { controller: AbortController; readers: number }>()
|
||||
+1
-1
@@ -4,7 +4,7 @@ import {
|
||||
clearAttachmentImagesForSite,
|
||||
getCachedAttachmentDataUrl,
|
||||
loadAttachmentDataUrlWithCache
|
||||
} from './attachment-image-cache'
|
||||
} from './image-cache'
|
||||
|
||||
type Image = { dataUrl: string; byteSize: number } | null
|
||||
function deferredImage() {
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { MAX_TIMER_DELAY_MS } from '../../shared/timer-delay'
|
||||
import { MAX_TIMER_DELAY_MS } from '../../../shared/timer-delay'
|
||||
import {
|
||||
_getAttachmentImageCacheSize,
|
||||
_resetAttachmentImageCache,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getCachedAttachmentDataUrl,
|
||||
loadAttachmentDataUrlWithCache,
|
||||
setCachedAttachmentDataUrl
|
||||
} from './attachment-image-cache'
|
||||
} from './image-cache'
|
||||
|
||||
describe('attachment image cache', () => {
|
||||
beforeEach(() => {
|
||||
@@ -1,6 +1,6 @@
|
||||
// Description, comments, and lazy image reads share one bounded cache.
|
||||
|
||||
import { MAX_TIMER_DELAY_MS } from '../../shared/timer-delay'
|
||||
import { MAX_TIMER_DELAY_MS } from '../../../shared/timer-delay'
|
||||
|
||||
const CACHE_TTL_MS = 30 * 60_000
|
||||
const MAX_CACHE_ENTRIES = 96
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { JIRA_IMAGE_SCHEME } from '../../shared/jira-attachment-image'
|
||||
import { attachmentImagesEpoch } from './attachment-image-cache'
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import type { AttachmentMeta } from './attachment-meta'
|
||||
import { JIRA_IMAGE_SCHEME } from '../../../shared/jira-attachment-image'
|
||||
import { attachmentImagesEpoch } from './image-cache'
|
||||
import type { JiraClientForSite } from '../authenticated-request'
|
||||
import type { AttachmentMeta } from './meta'
|
||||
|
||||
const GRANT_TTL_MS = 30 * 60_000
|
||||
const MAX_GRANTS = 2048
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { protocol } from 'electron'
|
||||
import { JIRA_IMAGE_SCHEME, jiraImageReference } from '../../shared/jira-attachment-image'
|
||||
import { readGrantedAttachmentImage } from './attachment-image-read'
|
||||
import { JIRA_IMAGE_SCHEME, jiraImageReference } from '../../../shared/jira-attachment-image'
|
||||
import { readGrantedAttachmentImage } from './image-read'
|
||||
|
||||
export const JIRA_IMAGE_CUSTOM_SCHEME: Electron.CustomScheme = {
|
||||
scheme: JIRA_IMAGE_SCHEME,
|
||||
+9
-9
@@ -1,22 +1,22 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import { JIRA_IMAGE_MAX_PENDING_READS } from '../../shared/jira-attachment-image'
|
||||
import { readAttachmentImageBytes } from './attachment-image-bytes'
|
||||
import type { JiraClientForSite } from '../authenticated-request'
|
||||
import { JIRA_IMAGE_MAX_PENDING_READS } from '../../../shared/jira-attachment-image'
|
||||
import { readAttachmentImageBytes } from './image-bytes'
|
||||
import {
|
||||
registerAttachmentImageGrant,
|
||||
attachmentImageGrant,
|
||||
resetAttachmentImageGrantsForTest
|
||||
} from './attachment-image-grants'
|
||||
import { readGrantedAttachmentImage } from './attachment-image-read'
|
||||
import { handleJiraImageRequest } from './attachment-image-protocol'
|
||||
import { clearAttachmentImagesForSite, _resetAttachmentImageCache } from './attachment-image-cache'
|
||||
} from './image-grants'
|
||||
import { readGrantedAttachmentImage } from './image-read'
|
||||
import { handleJiraImageRequest } from './image-protocol'
|
||||
import { clearAttachmentImagesForSite, _resetAttachmentImageCache } from './image-cache'
|
||||
|
||||
const { binary, clients } = vi.hoisted(() => ({ binary: vi.fn(), clients: vi.fn() }))
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
vi.mock('../authenticated-request', () => ({
|
||||
apiBasePath: () => '/rest/api/3',
|
||||
jiraRequestBinary: binary
|
||||
}))
|
||||
vi.mock('./client', () => ({ getClients: clients }))
|
||||
vi.mock('../client', () => ({ getClients: clients }))
|
||||
vi.mock('electron', () => ({ protocol: { handle: vi.fn() } }))
|
||||
const client: JiraClientForSite = {
|
||||
site: { id: 'site', siteUrl: 'https://jira.test', email: '', accountId: '', displayName: '' },
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getClients } from './client'
|
||||
import { attachmentImageGrant, isAttachmentImageGrantCurrent } from './attachment-image-grants'
|
||||
import { readAttachmentImageBytes } from './attachment-image-bytes'
|
||||
import type { AttachmentImageBytes } from './attachment-image-cache'
|
||||
import { getClients } from '../client'
|
||||
import { attachmentImageGrant, isAttachmentImageGrantCurrent } from './image-grants'
|
||||
import { readAttachmentImageBytes } from './image-bytes'
|
||||
import type { AttachmentImageBytes } from './image-cache'
|
||||
|
||||
export async function readGrantedAttachmentImage(
|
||||
reference: string,
|
||||
+16
-10
@@ -1,14 +1,20 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { JIRA_IMAGE_MAX_BYTES } from '../../shared/jira-attachment-image'
|
||||
import { loadIssueImageAttachments, createMediaMarkdownResolver } from './attachment-images'
|
||||
import { attachmentImageGrant, resetAttachmentImageGrantsForTest } from './attachment-image-grants'
|
||||
import { clearAttachmentImagesForSite, _resetAttachmentImageCache } from './attachment-image-cache'
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import { JIRA_IMAGE_MAX_BYTES } from '../../../shared/jira-attachment-image'
|
||||
import { loadIssueImageAttachments, createMediaMarkdownResolver } from './images'
|
||||
import { attachmentImageGrant, resetAttachmentImageGrantsForTest } from './image-grants'
|
||||
import { clearAttachmentImagesForSite, _resetAttachmentImageCache } from './image-cache'
|
||||
import type { JiraClientForSite } from '../authenticated-request'
|
||||
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
const { JiraApiError: JiraApiErrorMock } = vi.hoisted(() => ({
|
||||
JiraApiError: class extends Error {}
|
||||
}))
|
||||
|
||||
vi.mock('../jira-api-error', () => ({ JiraApiError: JiraApiErrorMock }))
|
||||
|
||||
vi.mock('../authenticated-request', () => ({
|
||||
apiBasePath: () => '/rest/api/3',
|
||||
jiraRequestBinary: vi.fn(),
|
||||
JiraApiError: class extends Error {}
|
||||
JiraApiError: JiraApiErrorMock
|
||||
}))
|
||||
const client: JiraClientForSite = {
|
||||
site: {
|
||||
@@ -37,7 +43,7 @@ describe('lazy Jira image references', () => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
it('keeps image bytes out of Markdown without downloading the attachment', async () => {
|
||||
const { jiraRequestBinary } = await import('./authenticated-request')
|
||||
const { jiraRequestBinary } = await import('../authenticated-request')
|
||||
const images = await loadIssueImageAttachments(client, metadata, ['1'], 'reference')
|
||||
const markdown = createMediaMarkdownResolver(images, ['1'])({ id: 'media-id', alt: 'shot.png' })
|
||||
expect(markdown).toMatch(/^!\[shot.png\]\(orca-jira-image:\/\/attachment\/[0-9a-f-]+\)$/)
|
||||
@@ -64,7 +70,7 @@ describe('lazy Jira image references', () => {
|
||||
})
|
||||
|
||||
it('discovers larger images by filename when rendered HTML has no attachment IDs', async () => {
|
||||
const { collectIssueMediaRequest } = await import('./jira-issue-media')
|
||||
const { collectIssueMediaRequest } = await import('../jira-issue-media')
|
||||
const raw = {
|
||||
key: 'JIRA-1',
|
||||
fields: {
|
||||
@@ -85,7 +91,7 @@ it('discovers larger images by filename when rendered HTML has no attachment IDs
|
||||
})
|
||||
|
||||
it('keeps the old per-image cap when an old client reuses a newer byte-cache entry', async () => {
|
||||
const { loadAttachmentBytesWithCache } = await import('./attachment-image-cache')
|
||||
const { loadAttachmentBytesWithCache } = await import('./image-cache')
|
||||
_resetAttachmentImageCache()
|
||||
await loadAttachmentBytesWithCache({
|
||||
siteId: client.site.id,
|
||||
+28
-23
@@ -1,14 +1,11 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import type { JiraClientForSite } from '../authenticated-request'
|
||||
|
||||
const { jiraRequestBinaryMock } = vi.hoisted(() => ({
|
||||
jiraRequestBinaryMock: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
jiraRequestBinary: (...args: unknown[]) => jiraRequestBinaryMock(...args),
|
||||
apiBasePath: (site: { authType?: string }) =>
|
||||
site.authType === 'server' ? '/rest/api/2' : '/rest/api/3',
|
||||
const { JiraApiError: JiraApiErrorMock } = vi.hoisted(() => ({
|
||||
JiraApiError: class JiraApiError extends Error {
|
||||
status: number | null
|
||||
constructor(message: string, status: number | null = null) {
|
||||
@@ -18,6 +15,15 @@ vi.mock('./authenticated-request', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('../jira-api-error', () => ({ JiraApiError: JiraApiErrorMock }))
|
||||
|
||||
vi.mock('../authenticated-request', () => ({
|
||||
jiraRequestBinary: (...args: unknown[]) => jiraRequestBinaryMock(...args),
|
||||
apiBasePath: (site: { authType?: string }) =>
|
||||
site.authType === 'server' ? '/rest/api/2' : '/rest/api/3',
|
||||
JiraApiError: JiraApiErrorMock
|
||||
}))
|
||||
|
||||
function makeEntry(): JiraClientForSite {
|
||||
return {
|
||||
site: {
|
||||
@@ -34,12 +40,12 @@ function makeEntry(): JiraClientForSite {
|
||||
describe('attachment image helpers', () => {
|
||||
beforeEach(async () => {
|
||||
jiraRequestBinaryMock.mockReset()
|
||||
const { _resetAttachmentImageCache } = await import('./attachment-image-cache')
|
||||
const { _resetAttachmentImageCache } = await import('./image-cache')
|
||||
_resetAttachmentImageCache()
|
||||
})
|
||||
|
||||
it('extracts attachment content ids from rendered HTML in order', async () => {
|
||||
const { extractAttachmentContentIdsFromHtml } = await import('./attachment-discovery')
|
||||
const { extractAttachmentContentIdsFromHtml } = await import('./discovery')
|
||||
const ids = extractAttachmentContentIdsFromHtml(`
|
||||
<p>intro</p>
|
||||
<img src="https://example.atlassian.net/rest/api/3/attachment/content/101" />
|
||||
@@ -50,7 +56,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('extracts thumbnail attachment ids', async () => {
|
||||
const { extractAttachmentContentIdsFromHtml } = await import('./attachment-discovery')
|
||||
const { extractAttachmentContentIdsFromHtml } = await import('./discovery')
|
||||
expect(
|
||||
extractAttachmentContentIdsFromHtml(
|
||||
'<img src="https://example.atlassian.net/secure/thumbnail/10001/shot.png" />'
|
||||
@@ -70,8 +76,7 @@ describe('attachment image helpers', () => {
|
||||
contentType: 'image/png'
|
||||
})
|
||||
|
||||
const { createMediaMarkdownResolver, loadIssueImageAttachments } =
|
||||
await import('./attachment-images')
|
||||
const { createMediaMarkdownResolver, loadIssueImageAttachments } = await import('./images')
|
||||
|
||||
const images = await loadIssueImageAttachments(
|
||||
makeEntry(),
|
||||
@@ -108,7 +113,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('pairs shared alt filenames to distinct attachments then falls through', async () => {
|
||||
const { createMediaMarkdownResolver } = await import('./attachment-images')
|
||||
const { createMediaMarkdownResolver } = await import('./images')
|
||||
const images = [
|
||||
{
|
||||
id: '1',
|
||||
@@ -132,7 +137,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('does not re-emit an already consumed attachment for a third shared-alt node', async () => {
|
||||
const { createMediaMarkdownResolver } = await import('./attachment-images')
|
||||
const { createMediaMarkdownResolver } = await import('./images')
|
||||
const images = [
|
||||
{
|
||||
id: '1',
|
||||
@@ -156,7 +161,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('escapes hostile external media URLs instead of injecting markdown', async () => {
|
||||
const { createMediaMarkdownResolver } = await import('./attachment-images')
|
||||
const { createMediaMarkdownResolver } = await import('./images')
|
||||
const resolve = createMediaMarkdownResolver([], [])
|
||||
const hostile = 'https://evil.example/x?a=1))'
|
||||
const out = resolve({ url: hostile, alt: 'Image' })
|
||||
@@ -168,14 +173,14 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('returns placeholder for external URLs that remain hostile after encode', async () => {
|
||||
const { createMediaMarkdownResolver } = await import('./attachment-images')
|
||||
const { createMediaMarkdownResolver } = await import('./images')
|
||||
const resolve = createMediaMarkdownResolver([], [])
|
||||
// non-http rejected
|
||||
expect(resolve({ url: 'javascript:alert(1)', alt: 'x' })).toBe('*[x]*')
|
||||
})
|
||||
|
||||
it('selects preferred ids via Option A filename fallback without sweeping all attachments', async () => {
|
||||
const { selectPreferredAttachmentIds } = await import('./attachment-discovery')
|
||||
const { selectPreferredAttachmentIds } = await import('./discovery')
|
||||
const attachments = [
|
||||
{ id: '1', filename: 'a.png', mimeType: 'image/png', size: 1 },
|
||||
{ id: '2', filename: 'b.png', mimeType: 'image/png', size: 1 },
|
||||
@@ -200,7 +205,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('Option A unions multiple same-filename attachments for repeated alts', async () => {
|
||||
const { selectPreferredAttachmentIds } = await import('./attachment-discovery')
|
||||
const { selectPreferredAttachmentIds } = await import('./discovery')
|
||||
const attachments = [
|
||||
{ id: '1', filename: 'image.png', mimeType: 'image/png', size: 1 },
|
||||
{ id: '2', filename: 'image.png', mimeType: 'image/png', size: 1 }
|
||||
@@ -226,7 +231,7 @@ describe('attachment image helpers', () => {
|
||||
data: Uint8Array.from([1]).buffer,
|
||||
contentType: 'image/png'
|
||||
})
|
||||
const { loadIssueImageAttachments } = await import('./attachment-images')
|
||||
const { loadIssueImageAttachments } = await import('./images')
|
||||
const attachments = Array.from({ length: 13 }, (_, index) => ({
|
||||
id: String(index + 1),
|
||||
filename: `${index + 1}.png`,
|
||||
@@ -245,7 +250,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('does not download attachments when rendered content references none', async () => {
|
||||
const { loadIssueImageAttachments } = await import('./attachment-images')
|
||||
const { loadIssueImageAttachments } = await import('./images')
|
||||
|
||||
await expect(
|
||||
loadIssueImageAttachments(
|
||||
@@ -268,7 +273,7 @@ describe('attachment image helpers', () => {
|
||||
siteUrl: 'https://jira.example.com/jira',
|
||||
authType: 'server'
|
||||
}
|
||||
const { loadIssueImageAttachments } = await import('./attachment-images')
|
||||
const { loadIssueImageAttachments } = await import('./images')
|
||||
|
||||
await loadIssueImageAttachments(
|
||||
entry,
|
||||
@@ -291,7 +296,7 @@ describe('attachment image helpers', () => {
|
||||
})
|
||||
|
||||
it('skips oversized and non-image attachments', async () => {
|
||||
const { parseImageAttachmentMetas } = await import('./attachment-meta')
|
||||
const { parseImageAttachmentMetas } = await import('./meta')
|
||||
expect(
|
||||
parseImageAttachmentMetas([
|
||||
{ id: '1', filename: 'big.png', mimeType: 'image/png', size: 20 * 1024 * 1024 },
|
||||
@@ -306,7 +311,7 @@ describe('attachment image helpers', () => {
|
||||
data: Uint8Array.from([1, 2, 3]).buffer,
|
||||
contentType: 'image/png'
|
||||
})
|
||||
const { loadIssueImageAttachments } = await import('./attachment-images')
|
||||
const { loadIssueImageAttachments } = await import('./images')
|
||||
const entry = makeEntry()
|
||||
const field = [{ id: '9', filename: 'c.png', mimeType: 'image/png', size: 3 }]
|
||||
await loadIssueImageAttachments(entry, field, ['9'])
|
||||
@@ -322,7 +327,7 @@ describe('attachment image helpers', () => {
|
||||
resolveDownload = resolve
|
||||
})
|
||||
)
|
||||
const { loadIssueImageAttachments } = await import('./attachment-images')
|
||||
const { loadIssueImageAttachments } = await import('./images')
|
||||
const entry = makeEntry()
|
||||
const field = [{ id: '7', filename: 's.png', mimeType: 'image/png', size: 1 }]
|
||||
const p1 = loadIssueImageAttachments(entry, field, ['7'])
|
||||
@@ -336,7 +341,7 @@ describe('attachment image helpers', () => {
|
||||
|
||||
it('warns when media resolution is incomplete', async () => {
|
||||
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
||||
const { warnIfMediaResolutionIncomplete } = await import('./attachment-discovery')
|
||||
const { warnIfMediaResolutionIncomplete } = await import('./discovery')
|
||||
warnIfMediaResolutionIncomplete({
|
||||
siteId: 's',
|
||||
issueKey: 'ABC-1',
|
||||
@@ -1,10 +1,11 @@
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import { attachmentContentUrl } from './attachment-content-url'
|
||||
import { JiraApiError, jiraRequestBinary } from './authenticated-request'
|
||||
import type { JiraAdfMediaAttrs, JiraAdfMediaResolver } from './adf-markdown'
|
||||
import { escapeMarkdownAlt, unresolvedMediaPlaceholder } from './adf-markdown'
|
||||
import { escapeMarkdownLinkDestination } from './adf-media-destination'
|
||||
import { loadAttachmentDataUrlWithCache } from './attachment-image-cache'
|
||||
import type { JiraClientForSite } from '../authenticated-request'
|
||||
import { attachmentContentUrl } from './content-url'
|
||||
import { jiraRequestBinary } from '../authenticated-request'
|
||||
import { JiraApiError } from '../jira-api-error'
|
||||
import type { JiraAdfMediaAttrs, JiraAdfMediaResolver } from '../adf-markdown'
|
||||
import { escapeMarkdownAlt, unresolvedMediaPlaceholder } from '../adf-markdown'
|
||||
import { escapeMarkdownLinkDestination } from '../adf-media-destination'
|
||||
import { loadAttachmentDataUrlWithCache } from './image-cache'
|
||||
import {
|
||||
MAX_IMAGE_BYTES,
|
||||
MAX_IMAGES,
|
||||
@@ -12,14 +13,14 @@ import {
|
||||
parseImageAttachmentMetas,
|
||||
isImageMimeType,
|
||||
type AttachmentMeta
|
||||
} from './attachment-meta'
|
||||
import { mapWithConcurrency } from '../../shared/map-with-concurrency'
|
||||
} from './meta'
|
||||
import { mapWithConcurrency } from '../../../shared/map-with-concurrency'
|
||||
import {
|
||||
JIRA_IMAGE_MAX_BYTES,
|
||||
JIRA_IMAGE_MAX_REFERENCES,
|
||||
type JiraMediaFormat
|
||||
} from '../../shared/jira-attachment-image'
|
||||
import { registerAttachmentImageGrant } from './attachment-image-grants'
|
||||
} from '../../../shared/jira-attachment-image'
|
||||
import { registerAttachmentImageGrant } from './image-grants'
|
||||
|
||||
const DOWNLOAD_CONCURRENCY = 3
|
||||
|
||||
+12
-6
@@ -1,14 +1,20 @@
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
||||
import { readAttachmentImageBytes } from './attachment-image-bytes'
|
||||
import { loadIssueImageAttachments, type JiraImageAttachment } from './attachment-images'
|
||||
import * as cache from './attachment-image-cache'
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import { readAttachmentImageBytes } from './image-bytes'
|
||||
import { loadIssueImageAttachments, type JiraImageAttachment } from './images'
|
||||
import * as cache from './image-cache'
|
||||
import type { JiraClientForSite } from '../authenticated-request'
|
||||
|
||||
const { binary } = vi.hoisted(() => ({ binary: vi.fn() }))
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
const { JiraApiError: JiraApiErrorMock } = vi.hoisted(() => ({
|
||||
JiraApiError: class extends Error {}
|
||||
}))
|
||||
|
||||
vi.mock('../jira-api-error', () => ({ JiraApiError: JiraApiErrorMock }))
|
||||
|
||||
vi.mock('../authenticated-request', () => ({
|
||||
apiBasePath: () => '/rest/api/3',
|
||||
jiraRequestBinary: binary,
|
||||
JiraApiError: class extends Error {}
|
||||
JiraApiError: JiraApiErrorMock
|
||||
}))
|
||||
|
||||
const client: JiraClientForSite = {
|
||||
@@ -91,7 +91,7 @@ describe('scoped Jira Cloud tokens over HTTP', () => {
|
||||
if (!client) {
|
||||
throw new Error('Expected connected Jira client')
|
||||
}
|
||||
const cache = await import('./attachment-image-cache')
|
||||
const cache = await import('./attachments/image-cache')
|
||||
cache.setCachedAttachmentDataUrl({
|
||||
siteId: client.site.id,
|
||||
attachmentId: 'image',
|
||||
|
||||
@@ -7,7 +7,7 @@ import type {
|
||||
JiraSiteSelection,
|
||||
JiraViewer
|
||||
} from '../../shared/jira-types'
|
||||
import { clearAttachmentImagesForSite } from './attachment-image-cache'
|
||||
import { clearAttachmentImagesForSite } from './attachments/image-cache'
|
||||
import { acquire, release } from './request-queue'
|
||||
import {
|
||||
getCredentialError,
|
||||
|
||||
@@ -28,11 +28,7 @@ vi.mock('./jira-agile-fields', () => ({
|
||||
|
||||
vi.mock('./request-queue', () => ({ acquire: acquireMock, release: releaseMock }))
|
||||
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
apiBasePath: (site: { authType?: string }) =>
|
||||
site.authType === 'server' ? '/rest/api/2' : '/rest/api/3',
|
||||
jiraRequest: (...args: unknown[]) => jiraRequestMock(...args),
|
||||
jiraRequestBinary: (...args: unknown[]) => jiraRequestBinaryMock(...args),
|
||||
const { JiraApiError: JiraApiErrorMock } = vi.hoisted(() => ({
|
||||
JiraApiError: class JiraApiError extends Error {
|
||||
status: number | null
|
||||
constructor(message: string, status: number | null = null) {
|
||||
@@ -42,6 +38,16 @@ vi.mock('./authenticated-request', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('./jira-api-error', () => ({ JiraApiError: JiraApiErrorMock }))
|
||||
|
||||
vi.mock('./authenticated-request', () => ({
|
||||
apiBasePath: (site: { authType?: string }) =>
|
||||
site.authType === 'server' ? '/rest/api/2' : '/rest/api/3',
|
||||
jiraRequest: (...args: unknown[]) => jiraRequestMock(...args),
|
||||
jiraRequestBinary: (...args: unknown[]) => jiraRequestBinaryMock(...args),
|
||||
JiraApiError: JiraApiErrorMock
|
||||
}))
|
||||
|
||||
vi.mock('./client', () => ({
|
||||
clearToken: (...args: unknown[]) => clearTokenMock(...args),
|
||||
getClients: (...args: unknown[]) => getClientsMock(...args),
|
||||
@@ -84,7 +90,7 @@ describe('Jira issue operations', () => {
|
||||
releaseMock.mockImplementation(() => {})
|
||||
jiraRequestBinaryMock.mockReset()
|
||||
jiraRequestMock.mockReset()
|
||||
const { _resetAttachmentImageCache } = await import('./attachment-image-cache')
|
||||
const { _resetAttachmentImageCache } = await import('./attachments/image-cache')
|
||||
_resetAttachmentImageCache()
|
||||
})
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
import {
|
||||
extractAttachmentContentIdsFromHtml,
|
||||
selectPreferredAttachmentIds
|
||||
} from './attachment-discovery'
|
||||
} from './attachments/discovery'
|
||||
import { mapUser } from './jira-issue-mapping'
|
||||
import {
|
||||
flushMediaResolutionWarn,
|
||||
|
||||
@@ -3,12 +3,12 @@ import {
|
||||
extractAttachmentContentIdsFromHtml,
|
||||
selectPreferredAttachmentIds,
|
||||
warnIfMediaResolutionIncomplete
|
||||
} from './attachment-discovery'
|
||||
} from './attachments/discovery'
|
||||
import {
|
||||
createMediaMarkdownResolver,
|
||||
loadIssueImageAttachments,
|
||||
type MediaResolutionStats
|
||||
} from './attachment-images'
|
||||
} from './attachments/images'
|
||||
import type { JiraClientForSite } from './authenticated-request'
|
||||
import { asRecord, asString, type JiraRecord } from './jira-record-pages'
|
||||
import {
|
||||
|
||||
@@ -6,15 +6,15 @@ import { JIRA_ATTACHMENT_IMAGE_METHODS } from './jira-attachment-image'
|
||||
import {
|
||||
registerAttachmentImageGrant,
|
||||
resetAttachmentImageGrantsForTest
|
||||
} from '../../../jira/attachment-image-grants'
|
||||
} from '../../../jira/attachments/image-grants'
|
||||
import {
|
||||
clearAttachmentImagesForSite,
|
||||
_resetAttachmentImageCache
|
||||
} from '../../../jira/attachment-image-cache'
|
||||
} from '../../../jira/attachments/image-cache'
|
||||
import { decodeJiraImageFrame } from '../../../../shared/jira-image-stream-frame'
|
||||
|
||||
const { read } = vi.hoisted(() => ({ read: vi.fn() }))
|
||||
vi.mock('../../../jira/attachment-image-read', () => ({ readGrantedAttachmentImage: read }))
|
||||
vi.mock('../../../jira/attachments/image-read', () => ({ readGrantedAttachmentImage: read }))
|
||||
const transferId = '12345678-1234-4234-8234-123456789012'
|
||||
const runtime = { getRuntimeId: () => 'runtime' }
|
||||
const dispatcher = new RpcDispatcher({
|
||||
|
||||
@@ -5,8 +5,8 @@ import { throwIfSignalAborted } from '../../../../shared/abort-signal-reason'
|
||||
import {
|
||||
attachmentImageGrant,
|
||||
isAttachmentImageGrantCurrent
|
||||
} from '../../../jira/attachment-image-grants'
|
||||
import { readGrantedAttachmentImage } from '../../../jira/attachment-image-read'
|
||||
} from '../../../jira/attachments/image-grants'
|
||||
import { readGrantedAttachmentImage } from '../../../jira/attachments/image-read'
|
||||
import { AttachmentImage } from '../../../../shared/rpc-contract/jira-params'
|
||||
import { defineStreamingMethod } from '../core'
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ import { initOpenCodeUsagePath } from '../opencode-usage/store'
|
||||
import { initMuseUsagePath } from '../muse-usage/store'
|
||||
import { registerDocPreviewSchemePrivileges } from '../browser/doc-preview-protocol'
|
||||
import { MEDIA_PREVIEW_CUSTOM_SCHEME } from '../media/media-preview-protocol'
|
||||
import { JIRA_IMAGE_CUSTOM_SCHEME } from '../jira/attachment-image-protocol'
|
||||
import { JIRA_IMAGE_CUSTOM_SCHEME } from '../jira/attachments/image-protocol'
|
||||
import { startCrashpadCapture } from '../crash-reporting/crashpad-capture'
|
||||
import { CrashReportStore } from '../crash-reporting/crash-report-store'
|
||||
import { recordCrashBreadcrumb } from '../crash-reporting/crash-breadcrumb-store'
|
||||
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
} from '../browser/browser-session-proxy'
|
||||
import { installDocPreviewProtocolHandler } from '../browser/doc-preview-protocol'
|
||||
import { installMediaPreviewProtocolHandler } from '../media/media-preview-protocol'
|
||||
import { installJiraImageProtocolHandler } from '../jira/attachment-image-protocol'
|
||||
import { installJiraImageProtocolHandler } from '../jira/attachments/image-protocol'
|
||||
import { registerDocPreviewGrantHandlers } from '../ipc/doc-preview-grant-ipc'
|
||||
import { initializeBrowserSessionsForApp } from '../browser/browser-session-startup'
|
||||
import { browserSessionRegistry } from '../browser/browser-session-registry'
|
||||
|
||||
Reference in New Issue
Block a user