Skip to content

Commit f625482

Browse files
authored
feat(confluence): return page content in get page version tool (#3344)
* feat(confluence): return page content in get page version tool * lint
1 parent 16f337f commit f625482

File tree

4 files changed

+111
-27
lines changed

4 files changed

+111
-27
lines changed

apps/docs/content/docs/en/tools/confluence.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,8 @@ Get details about a specific version of a Confluence page.
326326
| --------- | ---- | ----------- |
327327
| `ts` | string | ISO 8601 timestamp of the operation |
328328
| `pageId` | string | ID of the page |
329+
| `title` | string | Page title at this version |
330+
| `content` | string | Page content with HTML tags stripped at this version |
329331
| `version` | object | Detailed version information |
330332
|`number` | number | Version number |
331333
|`message` | string | Version message |
@@ -336,6 +338,9 @@ Get details about a specific version of a Confluence page.
336338
|`collaborators` | array | List of collaborator account IDs for this version |
337339
|`prevVersion` | number | Previous version number |
338340
|`nextVersion` | number | Next version number |
341+
| `body` | object | Raw page body content in storage format at this version |
342+
|`value` | string | The content value in the specified format |
343+
|`representation` | string | Content representation type |
339344

340345
### `confluence_list_page_properties`
341346

apps/sim/app/api/tools/confluence/page-versions/route.ts

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createLogger } from '@sim/logger'
22
import { type NextRequest, NextResponse } from 'next/server'
33
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
44
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
5-
import { getConfluenceCloudId } from '@/tools/confluence/utils'
5+
import { cleanHtmlContent, getConfluenceCloudId } from '@/tools/confluence/utils'
66

77
const logger = createLogger('ConfluencePageVersionsAPI')
88

@@ -55,42 +55,79 @@ export async function POST(request: NextRequest) {
5555
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
5656
}
5757

58-
// If versionNumber is provided, get specific version
58+
// If versionNumber is provided, get specific version with page content
5959
if (versionNumber !== undefined && versionNumber !== null) {
60-
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/versions/${versionNumber}`
60+
const versionUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/versions/${versionNumber}`
61+
const pageUrl = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}?version=${versionNumber}&body-format=storage`
6162

6263
logger.info(`Fetching version ${versionNumber} for page ${pageId}`)
6364

64-
const response = await fetch(url, {
65-
method: 'GET',
66-
headers: {
67-
Accept: 'application/json',
68-
Authorization: `Bearer ${accessToken}`,
69-
},
70-
})
71-
72-
if (!response.ok) {
73-
const errorData = await response.json().catch(() => null)
65+
const [versionResponse, pageResponse] = await Promise.all([
66+
fetch(versionUrl, {
67+
method: 'GET',
68+
headers: {
69+
Accept: 'application/json',
70+
Authorization: `Bearer ${accessToken}`,
71+
},
72+
}),
73+
fetch(pageUrl, {
74+
method: 'GET',
75+
headers: {
76+
Accept: 'application/json',
77+
Authorization: `Bearer ${accessToken}`,
78+
},
79+
}),
80+
])
81+
82+
if (!versionResponse.ok) {
83+
const errorData = await versionResponse.json().catch(() => null)
7484
logger.error('Confluence API error response:', {
75-
status: response.status,
76-
statusText: response.statusText,
85+
status: versionResponse.status,
86+
statusText: versionResponse.statusText,
7787
error: JSON.stringify(errorData, null, 2),
7888
})
79-
const errorMessage = errorData?.message || `Failed to get page version (${response.status})`
80-
return NextResponse.json({ error: errorMessage }, { status: response.status })
89+
const errorMessage =
90+
errorData?.message || `Failed to get page version (${versionResponse.status})`
91+
return NextResponse.json({ error: errorMessage }, { status: versionResponse.status })
8192
}
8293

83-
const data = await response.json()
94+
const versionData = await versionResponse.json()
95+
96+
let title: string | null = null
97+
let content: string | null = null
98+
let body: Record<string, unknown> | null = null
99+
100+
if (pageResponse.ok) {
101+
const pageData = await pageResponse.json()
102+
title = pageData.title ?? null
103+
body = pageData.body ?? null
104+
105+
const rawContent =
106+
pageData.body?.storage?.value ||
107+
pageData.body?.view?.value ||
108+
pageData.body?.atlas_doc_format?.value ||
109+
''
110+
if (rawContent) {
111+
content = cleanHtmlContent(rawContent)
112+
}
113+
} else {
114+
logger.warn(
115+
`Could not fetch page content for version ${versionNumber}: ${pageResponse.status}`
116+
)
117+
}
84118

85119
return NextResponse.json({
86120
version: {
87-
number: data.number,
88-
message: data.message ?? null,
89-
minorEdit: data.minorEdit ?? false,
90-
authorId: data.authorId ?? null,
91-
createdAt: data.createdAt ?? null,
121+
number: versionData.number,
122+
message: versionData.message ?? null,
123+
minorEdit: versionData.minorEdit ?? false,
124+
authorId: versionData.authorId ?? null,
125+
createdAt: versionData.createdAt ?? null,
92126
},
93127
pageId,
128+
title,
129+
content,
130+
body,
94131
})
95132
}
96133
// List all versions

apps/sim/tools/confluence/get_page_version.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { DETAILED_VERSION_OUTPUT_PROPERTIES, TIMESTAMP_OUTPUT } from '@/tools/confluence/types'
1+
import {
2+
BODY_FORMAT_PROPERTIES,
3+
DETAILED_VERSION_OUTPUT_PROPERTIES,
4+
TIMESTAMP_OUTPUT,
5+
} from '@/tools/confluence/types'
26
import type { ToolConfig } from '@/tools/types'
37

48
export interface ConfluenceGetPageVersionParams {
@@ -14,6 +18,8 @@ export interface ConfluenceGetPageVersionResponse {
1418
output: {
1519
ts: string
1620
pageId: string
21+
title: string | null
22+
content: string | null
1723
version: {
1824
number: number
1925
message: string | null
@@ -25,6 +31,12 @@ export interface ConfluenceGetPageVersionResponse {
2531
prevVersion: number | null
2632
nextVersion: number | null
2733
}
34+
body: {
35+
storage?: {
36+
value: string
37+
representation: string
38+
}
39+
} | null
2840
}
2941
}
3042

@@ -100,24 +112,46 @@ export const confluenceGetPageVersionTool: ToolConfig<
100112
output: {
101113
ts: new Date().toISOString(),
102114
pageId: data.pageId ?? '',
115+
title: data.title ?? null,
116+
content: data.content ?? null,
103117
version: data.version ?? {
104118
number: 0,
105119
message: null,
106120
minorEdit: false,
107121
authorId: null,
108122
createdAt: null,
109123
},
124+
body: data.body ?? null,
110125
},
111126
}
112127
},
113128

114129
outputs: {
115130
ts: TIMESTAMP_OUTPUT,
116131
pageId: { type: 'string', description: 'ID of the page' },
132+
title: { type: 'string', description: 'Page title at this version', optional: true },
133+
content: {
134+
type: 'string',
135+
description: 'Page content with HTML tags stripped at this version',
136+
optional: true,
137+
},
117138
version: {
118139
type: 'object',
119140
description: 'Detailed version information',
120141
properties: DETAILED_VERSION_OUTPUT_PROPERTIES,
121142
},
143+
body: {
144+
type: 'object',
145+
description: 'Raw page body content in storage format at this version',
146+
properties: {
147+
storage: {
148+
type: 'object',
149+
description: 'Body in storage format (Confluence markup)',
150+
properties: BODY_FORMAT_PROPERTIES,
151+
optional: true,
152+
},
153+
},
154+
optional: true,
155+
},
122156
},
123157
}

apps/sim/tools/confluence/utils.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,21 @@ function stripHtmlTags(html: string): string {
5656
return text.trim()
5757
}
5858

59+
/**
60+
* Strips HTML tags and decodes HTML entities from raw Confluence content.
61+
*/
62+
export function cleanHtmlContent(rawContent: string): string {
63+
let content = stripHtmlTags(rawContent)
64+
content = decodeHtmlEntities(content)
65+
content = content.replace(/\s+/g, ' ').trim()
66+
return content
67+
}
68+
5969
export function transformPageData(data: any) {
6070
const rawContent =
6171
data.body?.storage?.value || data.body?.view?.value || data.body?.atlas_doc_format?.value || ''
6272

63-
let cleanContent = stripHtmlTags(rawContent)
64-
cleanContent = decodeHtmlEntities(cleanContent)
65-
cleanContent = cleanContent.replace(/\s+/g, ' ').trim()
73+
const cleanContent = cleanHtmlContent(rawContent)
6674

6775
return {
6876
success: true,

0 commit comments

Comments
 (0)