Skip to content

Commit 209550c

Browse files
committed
feat(confluence): add get user by account ID tool
1 parent 1f3dc52 commit 209550c

File tree

8 files changed

+274
-7
lines changed

8 files changed

+274
-7
lines changed

apps/docs/components/ui/icon-mapping.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ import {
3838
EyeIcon,
3939
FirecrawlIcon,
4040
FirefliesIcon,
41-
GithubIcon,
4241
GitLabIcon,
42+
GithubIcon,
4343
GmailIcon,
4444
GongIcon,
4545
GoogleBooksIcon,
@@ -73,9 +73,9 @@ import {
7373
LinearIcon,
7474
LinkedInIcon,
7575
LinkupIcon,
76+
MailServerIcon,
7677
MailchimpIcon,
7778
MailgunIcon,
78-
MailServerIcon,
7979
Mem0Icon,
8080
MicrosoftDataverseIcon,
8181
MicrosoftExcelIcon,
@@ -108,6 +108,8 @@ import {
108108
ResendIcon,
109109
RevenueCatIcon,
110110
S3Icon,
111+
SQSIcon,
112+
STTIcon,
111113
SalesforceIcon,
112114
SearchIcon,
113115
SendgridIcon,
@@ -119,19 +121,17 @@ import {
119121
SimilarwebIcon,
120122
SlackIcon,
121123
SmtpIcon,
122-
SQSIcon,
123124
SshIcon,
124-
STTIcon,
125125
StagehandIcon,
126126
StripeIcon,
127127
SupabaseIcon,
128+
TTSIcon,
128129
TavilyIcon,
129130
TelegramIcon,
130131
TextractIcon,
131132
TinybirdIcon,
132133
TranslateIcon,
133134
TrelloIcon,
134-
TTSIcon,
135135
TwilioIcon,
136136
TypeformIcon,
137137
UpstashIcon,
@@ -142,11 +142,11 @@ import {
142142
WhatsAppIcon,
143143
WikipediaIcon,
144144
WordpressIcon,
145-
xIcon,
146145
YouTubeIcon,
147146
ZendeskIcon,
148147
ZepIcon,
149148
ZoomIcon,
149+
xIcon,
150150
} from '@/components/icons'
151151

152152
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,4 +1045,28 @@ List all Confluence spaces accessible to the user.
10451045
|`representation` | string | Content representation format \(e.g., plain, view, storage\) |
10461046
| `nextCursor` | string | Cursor for fetching the next page of results |
10471047

1048+
### `confluence_get_user`
1049+
1050+
Get a Confluence user\
1051+
1052+
#### Input
1053+
1054+
| Parameter | Type | Required | Description |
1055+
| --------- | ---- | -------- | ----------- |
1056+
| `domain` | string | Yes | Your Confluence domain \(e.g., yourcompany.atlassian.net\) |
1057+
| `accountId` | string | Yes | The Atlassian account ID of the user to look up |
1058+
| `cloudId` | string | No | Confluence Cloud ID for the instance. If not provided, it will be fetched using the domain. |
1059+
1060+
#### Output
1061+
1062+
| Parameter | Type | Description |
1063+
| --------- | ---- | ----------- |
1064+
| `ts` | string | ISO 8601 timestamp of the operation |
1065+
| `accountId` | string | Atlassian account ID of the user |
1066+
| `displayName` | string | Display name of the user |
1067+
| `email` | string | Email address of the user |
1068+
| `accountType` | string | Account type \(e.g., atlassian, app, customer\) |
1069+
| `profilePicture` | string | Path to the user profile picture |
1070+
| `publicName` | string | Public name of the user |
1071+
10481072

apps/docs/content/docs/en/tools/meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,4 @@
147147
"zep",
148148
"zoom"
149149
]
150-
}
150+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { createLogger } from '@sim/logger'
2+
import { type NextRequest, NextResponse } from 'next/server'
3+
import { checkSessionOrInternalAuth } from '@/lib/auth/hybrid'
4+
import { validateAlphanumericId, validateJiraCloudId } from '@/lib/core/security/input-validation'
5+
import { getConfluenceCloudId } from '@/tools/confluence/utils'
6+
7+
const logger = createLogger('ConfluenceUserAPI')
8+
9+
export const dynamic = 'force-dynamic'
10+
11+
/**
12+
* Get a Confluence user by account ID.
13+
* Uses GET /wiki/rest/api/user?accountId={accountId}
14+
*/
15+
export async function GET(request: NextRequest) {
16+
try {
17+
const auth = await checkSessionOrInternalAuth(request)
18+
if (!auth.success || !auth.userId) {
19+
return NextResponse.json({ error: auth.error || 'Unauthorized' }, { status: 401 })
20+
}
21+
22+
const { searchParams } = new URL(request.url)
23+
const domain = searchParams.get('domain')
24+
const accessToken = searchParams.get('accessToken')
25+
const accountId = searchParams.get('accountId')
26+
const providedCloudId = searchParams.get('cloudId')
27+
28+
if (!domain) {
29+
return NextResponse.json({ error: 'Domain is required' }, { status: 400 })
30+
}
31+
32+
if (!accessToken) {
33+
return NextResponse.json({ error: 'Access token is required' }, { status: 400 })
34+
}
35+
36+
if (!accountId) {
37+
return NextResponse.json({ error: 'Account ID is required' }, { status: 400 })
38+
}
39+
40+
const accountIdValidation = validateAlphanumericId(accountId, 'accountId', 255)
41+
if (!accountIdValidation.isValid) {
42+
return NextResponse.json({ error: accountIdValidation.error }, { status: 400 })
43+
}
44+
45+
const cloudId = providedCloudId || (await getConfluenceCloudId(domain, accessToken))
46+
47+
const cloudIdValidation = validateJiraCloudId(cloudId, 'cloudId')
48+
if (!cloudIdValidation.isValid) {
49+
return NextResponse.json({ error: cloudIdValidation.error }, { status: 400 })
50+
}
51+
52+
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/rest/api/user?accountId=${encodeURIComponent(accountId)}`
53+
54+
const response = await fetch(url, {
55+
method: 'GET',
56+
headers: {
57+
Accept: 'application/json',
58+
Authorization: `Bearer ${accessToken}`,
59+
},
60+
})
61+
62+
if (!response.ok) {
63+
const errorData = await response.json().catch(() => null)
64+
logger.error('Confluence API error response:', {
65+
status: response.status,
66+
statusText: response.statusText,
67+
error: JSON.stringify(errorData, null, 2),
68+
})
69+
const errorMessage =
70+
errorData?.message || `Failed to get Confluence user (${response.status})`
71+
return NextResponse.json({ error: errorMessage }, { status: response.status })
72+
}
73+
74+
const data = await response.json()
75+
return NextResponse.json(data)
76+
} catch (error) {
77+
logger.error('Error getting Confluence user:', error)
78+
return NextResponse.json(
79+
{ error: (error as Error).message || 'Internal server error' },
80+
{ status: 500 }
81+
)
82+
}
83+
}

apps/sim/blocks/blocks/confluence.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const ConfluenceBlock: BlockConfig<ConfluenceResponse> = {
8484
'write:content.property:confluence',
8585
'read:hierarchical-content:confluence',
8686
'read:content.metadata:confluence',
87+
'read:user:confluence',
8788
],
8889
placeholder: 'Select Confluence account',
8990
required: true,
@@ -433,6 +434,8 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
433434
// Space Operations
434435
{ label: 'Get Space', id: 'get_space' },
435436
{ label: 'List Spaces', id: 'list_spaces' },
437+
// User Operations
438+
{ label: 'Get User', id: 'get_user' },
436439
],
437440
value: () => 'read',
438441
},
@@ -472,6 +475,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
472475
'write:content.property:confluence',
473476
'read:hierarchical-content:confluence',
474477
'read:content.metadata:confluence',
478+
'read:user:confluence',
475479
],
476480
placeholder: 'Select Confluence account',
477481
required: true,
@@ -514,6 +518,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
514518
'list_spaces',
515519
'get_pages_by_label',
516520
'list_space_labels',
521+
'get_user',
517522
],
518523
not: true,
519524
},
@@ -560,6 +565,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
560565
'list_spaces',
561566
'get_pages_by_label',
562567
'list_space_labels',
568+
'get_user',
563569
],
564570
not: true,
565571
},
@@ -621,6 +627,14 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
621627
required: true,
622628
condition: { field: 'operation', value: 'get_page_version' },
623629
},
630+
{
631+
id: 'accountId',
632+
title: 'Account ID',
633+
type: 'short-input',
634+
placeholder: 'Enter Atlassian account ID',
635+
required: true,
636+
condition: { field: 'operation', value: 'get_user' },
637+
},
624638
{
625639
id: 'propertyKey',
626640
title: 'Property Key',
@@ -922,6 +936,8 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
922936
// Space Tools
923937
'confluence_get_space',
924938
'confluence_list_spaces',
939+
// User Tools
940+
'confluence_get_user',
925941
],
926942
config: {
927943
tool: (params) => {
@@ -999,6 +1015,9 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
9991015
return 'confluence_get_space'
10001016
case 'list_spaces':
10011017
return 'confluence_list_spaces'
1018+
// User Operations
1019+
case 'get_user':
1020+
return 'confluence_get_user'
10021021
default:
10031022
return 'confluence_retrieve'
10041023
}
@@ -1013,6 +1032,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
10131032
attachmentComment,
10141033
blogPostId,
10151034
versionNumber,
1035+
accountId,
10161036
propertyKey,
10171037
propertyValue,
10181038
propertyId,
@@ -1152,6 +1172,15 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
11521172
}
11531173
}
11541174

1175+
if (operation === 'get_user') {
1176+
return {
1177+
credential: oauthCredential,
1178+
operation,
1179+
accountId: accountId ? String(accountId).trim() : undefined,
1180+
...rest,
1181+
}
1182+
}
1183+
11551184
return {
11561185
credential: oauthCredential,
11571186
pageId: effectivePageId || undefined,
@@ -1171,6 +1200,7 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
11711200
spaceId: { type: 'string', description: 'Space identifier' },
11721201
blogPostId: { type: 'string', description: 'Blog post identifier' },
11731202
versionNumber: { type: 'number', description: 'Page version number' },
1203+
accountId: { type: 'string', description: 'Atlassian account ID' },
11741204
propertyKey: { type: 'string', description: 'Property key/name' },
11751205
propertyValue: { type: 'json', description: 'Property value (JSON)' },
11761206
title: { type: 'string', description: 'Page or blog post title' },
@@ -1242,6 +1272,13 @@ export const ConfluenceV2Block: BlockConfig<ConfluenceResponse> = {
12421272
propertyId: { type: 'string', description: 'Property identifier' },
12431273
propertyKey: { type: 'string', description: 'Property key' },
12441274
propertyValue: { type: 'json', description: 'Property value' },
1275+
// User Results
1276+
accountId: { type: 'string', description: 'Atlassian account ID' },
1277+
displayName: { type: 'string', description: 'User display name' },
1278+
email: { type: 'string', description: 'User email address' },
1279+
accountType: { type: 'string', description: 'Account type (atlassian, app, customer)' },
1280+
profilePicture: { type: 'string', description: 'Path to user profile picture' },
1281+
publicName: { type: 'string', description: 'User public name' },
12451282
// Pagination
12461283
nextCursor: { type: 'string', description: 'Cursor for fetching next page of results' },
12471284
},

0 commit comments

Comments
 (0)