Skip to content

Commit 63603b5

Browse files
committed
fix(connectors): query both space statuses explicitly on exact-key lookup
The exact-key lookup omitted `status`, assuming that matched a space whether it was current or archived. Atlassian documents a `current,archived` default for `/pages` but documents no default for `/spaces`, where `status` takes a single value rather than an array — so the assumption was unverified, and resolving only current spaces would silently miss archived ones. Archived spaces are reachable through the paged path and sync works against them. Queries `current` first and falls back to `archived` only when it finds nothing, so the common case stays one request and the behaviour no longer depends on an undocumented default.
1 parent 58fb25a commit 63603b5

1 file changed

Lines changed: 43 additions & 23 deletions

File tree

  • apps/sim/app/api/tools/confluence/selector-spaces

apps/sim/app/api/tools/confluence/selector-spaces/route.ts

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -101,32 +101,52 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
101101
const baseUrl = `https://api.atlassian.com/ex/confluence/${cloudIdValidation.sanitized}/wiki/api/v2/spaces`
102102
const { status, inner } = parseCursor(cursor)
103103

104-
/**
105-
* Exact-key lookup: one request, no pagination. The dropdown drains pages in
106-
* the background and filters client-side, so a space only becomes findable
107-
* once its page has arrived — on a large site that is tens of seconds away.
108-
* Resolving a known key directly makes it available immediately. `status` is
109-
* left unset so the key matches whether the space is current or archived.
110-
*/
111-
const params = spaceKey
112-
? new URLSearchParams({ keys: spaceKey, limit: String(PAGE_LIMIT) })
113-
: new URLSearchParams({ limit: String(PAGE_LIMIT), status })
114-
if (inner && !spaceKey) params.set('cursor', inner)
115-
const url = `${baseUrl}?${params.toString()}`
116-
117-
const response = await fetch(url, {
118-
method: 'GET',
119-
headers: { Accept: 'application/json', Authorization: `Bearer ${accessToken}` },
120-
})
104+
const requestSpaces = async (
105+
search: URLSearchParams
106+
): Promise<{ ok: true; data: any } | { ok: false; response: NextResponse }> => {
107+
const response = await fetch(`${baseUrl}?${search.toString()}`, {
108+
method: 'GET',
109+
headers: { Accept: 'application/json', Authorization: `Bearer ${accessToken}` },
110+
})
121111

122-
if (!response.ok) {
123-
const errorText = await response.text()
124-
const message = parseAtlassianErrorMessage(response.status, response.statusText, errorText)
125-
logger.error('Confluence API error response', { error: message, status: response.status })
126-
return NextResponse.json({ error: message }, { status: 502 })
112+
if (!response.ok) {
113+
const errorText = await response.text()
114+
const message = parseAtlassianErrorMessage(response.status, response.statusText, errorText)
115+
logger.error('Confluence API error response', { error: message, status: response.status })
116+
return { ok: false, response: NextResponse.json({ error: message }, { status: 502 }) }
117+
}
118+
119+
return { ok: true, data: await response.json() }
127120
}
128121

129-
const data = await response.json()
122+
let data: any
123+
if (spaceKey) {
124+
/**
125+
* Exact-key lookup, bypassing the paged drain the dropdown otherwise depends
126+
* on. `status` is queried explicitly per value rather than omitted: it takes a
127+
* single value on this endpoint (unlike `/pages`, where it is an array with a
128+
* documented `current,archived` default), and no default is documented for
129+
* `/spaces`. Archived spaces are reachable in the paged path and sync works
130+
* against them, so resolving only `current` would silently miss them.
131+
*/
132+
let result = await requestSpaces(
133+
new URLSearchParams({ keys: spaceKey, limit: String(PAGE_LIMIT), status: 'current' })
134+
)
135+
if (!result.ok) return result.response
136+
if (!result.data.results?.length) {
137+
result = await requestSpaces(
138+
new URLSearchParams({ keys: spaceKey, limit: String(PAGE_LIMIT), status: 'archived' })
139+
)
140+
if (!result.ok) return result.response
141+
}
142+
data = result.data
143+
} else {
144+
const params = new URLSearchParams({ limit: String(PAGE_LIMIT), status })
145+
if (inner) params.set('cursor', inner)
146+
const result = await requestSpaces(params)
147+
if (!result.ok) return result.response
148+
data = result.data
149+
}
130150
const spaces = (data.results || []).map(
131151
(space: { id: string; name: string; key: string; status?: SpaceStatus }) => ({
132152
id: space.id,

0 commit comments

Comments
 (0)