chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1604 @@
1
+ ---
2
+ name: confluence
3
+ description: "Confluence Cloud API coding guidelines for Python using the atlassian-python-api library"
4
+ metadata:
5
+ languages: "python"
6
+ versions: "4.0.7"
7
+ updated-on: "2026-03-02"
8
+ source: maintainer
9
+ tags: "atlassian,confluence,wiki,documentation,collaboration"
10
+ ---
11
+
12
+ # Confluence Cloud API Coding Guidelines (Python)
13
+
14
+ You are a **Confluence Cloud API coding expert**. Help me write correct, idiomatic Python code that calls the Atlassian Confluence Cloud REST API using the atlassian-python-api library.
15
+
16
+ Use **only official Atlassian sources** for API behavior, fields, and constraints. This guide summarizes key patterns for Python applications.
17
+
18
+ > Ground truth: Atlassian Confluence Cloud REST API documentation at developer.atlassian.com/cloud/confluence/
19
+
20
+ ## Golden Rule: Use atlassian-python-api Library
21
+
22
+ **CRITICAL:** Use the `atlassian-python-api` package (version 4.0.7 or later) for interacting with Confluence Cloud REST API. This is the most comprehensive and actively maintained community library for Atlassian products.
23
+
24
+ **DO NOT use:**
25
+ - `pyatlassian` (different package, less maintained)
26
+ - `confluence-api` (JavaScript package, not Python)
27
+ - Direct REST API calls with `requests` without a client library (error-prone)
28
+
29
+ **Install:**
30
+ ```bash
31
+ pip install atlassian-python-api
32
+ # or
33
+ poetry add atlassian-python-api
34
+ # or
35
+ uv pip install atlassian-python-api
36
+ ```
37
+
38
+ The library supports Python 3.8+ and provides comprehensive coverage of Confluence Cloud and Server APIs.
39
+
40
+ ## Authentication
41
+
42
+ Confluence Cloud requires API token authentication. Username/password authentication is deprecated.
43
+
44
+ ### API Token Authentication - Required for Cloud
45
+
46
+ Generate an API token from your Atlassian Account Settings (https://id.atlassian.com/manage-profile/security/api-tokens).
47
+
48
+ ```python
49
+ from atlassian import Confluence
50
+
51
+ confluence = Confluence(
52
+ url='https://your-domain.atlassian.net',
53
+ username='your-email@example.com',
54
+ password='your-api-token', # This is your API token, not your password
55
+ cloud=True
56
+ )
57
+ ```
58
+
59
+ **IMPORTANT:** For Confluence Cloud, you must:
60
+ 1. Use your email address as the username
61
+ 2. Use an API token (not your account password) as the password
62
+ 3. Set `cloud=True`
63
+
64
+ ### Environment Variables Setup
65
+
66
+ ```bash
67
+ # .env file
68
+ CONFLUENCE_URL=https://your-domain.atlassian.net
69
+ CONFLUENCE_USERNAME=your-email@example.com
70
+ CONFLUENCE_API_TOKEN=your_api_token_here
71
+ ```
72
+
73
+ **Loading in Python:**
74
+
75
+ ```python
76
+ import os
77
+ from dotenv import load_dotenv
78
+ from atlassian import Confluence
79
+
80
+ load_dotenv()
81
+
82
+ confluence = Confluence(
83
+ url=os.getenv('CONFLUENCE_URL'),
84
+ username=os.getenv('CONFLUENCE_USERNAME'),
85
+ password=os.getenv('CONFLUENCE_API_TOKEN'),
86
+ cloud=True
87
+ )
88
+ ```
89
+
90
+ ### OAuth 2.0 Authentication
91
+
92
+ For apps using OAuth 2.0 access tokens:
93
+
94
+ ```python
95
+ from atlassian import Confluence
96
+
97
+ confluence = Confluence(
98
+ url='https://your-domain.atlassian.net',
99
+ token='your-oauth-access-token',
100
+ cloud=True
101
+ )
102
+ ```
103
+
104
+ ### Server/Data Center Authentication (On-Premise)
105
+
106
+ For self-hosted Confluence instances:
107
+
108
+ ```python
109
+ from atlassian import Confluence
110
+
111
+ confluence = Confluence(
112
+ url='https://confluence.company.com',
113
+ username='your-username',
114
+ password='your-password',
115
+ cloud=False # Important for Server/Data Center
116
+ )
117
+ ```
118
+
119
+ ## Initialization Patterns
120
+
121
+ ### Basic Initialization
122
+
123
+ ```python
124
+ from atlassian import Confluence
125
+
126
+ confluence = Confluence(
127
+ url='https://your-domain.atlassian.net',
128
+ username='your-email@example.com',
129
+ password='your-api-token',
130
+ cloud=True
131
+ )
132
+ ```
133
+
134
+ ### With Timeout Configuration
135
+
136
+ ```python
137
+ confluence = Confluence(
138
+ url='https://your-domain.atlassian.net',
139
+ username='your-email@example.com',
140
+ password='your-api-token',
141
+ cloud=True,
142
+ timeout=60 # Seconds
143
+ )
144
+ ```
145
+
146
+ ### With Proxy Support
147
+
148
+ ```python
149
+ confluence = Confluence(
150
+ url='https://your-domain.atlassian.net',
151
+ username='your-email@example.com',
152
+ password='your-api-token',
153
+ cloud=True,
154
+ proxies={
155
+ 'http': 'http://proxy.company.com:8080',
156
+ 'https': 'https://proxy.company.com:8080',
157
+ }
158
+ )
159
+ ```
160
+
161
+ ### Verify SSL Certificates
162
+
163
+ ```python
164
+ # Disable SSL verification (not recommended for production)
165
+ confluence = Confluence(
166
+ url='https://your-domain.atlassian.net',
167
+ username='your-email@example.com',
168
+ password='your-api-token',
169
+ cloud=True,
170
+ verify_ssl=False
171
+ )
172
+ ```
173
+
174
+ ## Pages API
175
+
176
+ ### Get Page by ID
177
+
178
+ ```python
179
+ page = confluence.get_page_by_id(
180
+ page_id='123456789',
181
+ expand='body.storage,version,space'
182
+ )
183
+
184
+ print(f"Title: {page['title']}")
185
+ print(f"Space: {page['space']['key']}")
186
+ print(f"Version: {page['version']['number']}")
187
+ print(f"Content: {page['body']['storage']['value']}")
188
+ ```
189
+
190
+ ### Get Page by Title
191
+
192
+ ```python
193
+ page = confluence.get_page_by_title(
194
+ space='DEMO',
195
+ title='Getting Started'
196
+ )
197
+
198
+ if page:
199
+ print(f"Page ID: {page['id']}")
200
+ print(f"Title: {page['title']}")
201
+ else:
202
+ print("Page not found")
203
+ ```
204
+
205
+ ### Get Page ID Only
206
+
207
+ ```python
208
+ page_id = confluence.get_page_id(
209
+ space='DEMO',
210
+ title='Getting Started'
211
+ )
212
+
213
+ if page_id:
214
+ print(f"Found page: {page_id}")
215
+ ```
216
+
217
+ ### Get All Pages from Space
218
+
219
+ ```python
220
+ pages = confluence.get_all_pages_from_space(
221
+ space='DEMO',
222
+ start=0,
223
+ limit=100,
224
+ status='current',
225
+ expand='version,space'
226
+ )
227
+
228
+ for page in pages:
229
+ print(f"{page['title']} - {page['id']}")
230
+ ```
231
+
232
+ ### Get All Pages as Generator (Memory Efficient)
233
+
234
+ ```python
235
+ for page in confluence.get_all_pages_from_space_as_generator(
236
+ space='DEMO',
237
+ expand='body.storage,version'
238
+ ):
239
+ print(f"Processing: {page['title']}")
240
+ # Process each page without loading all into memory
241
+ ```
242
+
243
+ ### Create a Page
244
+
245
+ ```python
246
+ page = confluence.create_page(
247
+ space='DEMO',
248
+ title='Getting Started Guide',
249
+ body='<h1>Welcome</h1><p>This is the introduction to our project.</p>',
250
+ parent_id=None, # Optional: specify parent page ID
251
+ type='page',
252
+ representation='storage'
253
+ )
254
+
255
+ print(f"Created page ID: {page['id']}")
256
+ print(f"URL: {page['_links']['webui']}")
257
+ ```
258
+
259
+ ### Create a Child Page
260
+
261
+ ```python
262
+ parent_id = confluence.get_page_id(space='DEMO', title='Parent Page')
263
+
264
+ child_page = confluence.create_page(
265
+ space='DEMO',
266
+ title='Child Page',
267
+ body='<p>This is a child page.</p>',
268
+ parent_id=parent_id
269
+ )
270
+
271
+ print(f"Created child page: {child_page['id']}")
272
+ ```
273
+
274
+ ### Update a Page
275
+
276
+ **IMPORTANT:** Always increment the version number when updating.
277
+
278
+ ```python
279
+ # Get current page to retrieve version
280
+ page = confluence.get_page_by_id(
281
+ page_id='123456789',
282
+ expand='version,space'
283
+ )
284
+
285
+ # Update with incremented version
286
+ updated = confluence.update_page(
287
+ page_id='123456789',
288
+ title='Updated Title',
289
+ body='<h1>Updated Content</h1><p>New information here.</p>',
290
+ version_number=page['version']['number'] + 1,
291
+ representation='storage'
292
+ )
293
+
294
+ print(f"Updated to version {updated['version']['number']}")
295
+ ```
296
+
297
+ ### Update Page Content Only (Keep Title)
298
+
299
+ ```python
300
+ page = confluence.get_page_by_id(
301
+ page_id='123456789',
302
+ expand='version,body.storage'
303
+ )
304
+
305
+ confluence.update_page(
306
+ page_id='123456789',
307
+ title=page['title'], # Keep existing title
308
+ body='<p>Only the content changed.</p>',
309
+ version_number=page['version']['number'] + 1
310
+ )
311
+ ```
312
+
313
+ ### Append to Page Content
314
+
315
+ ```python
316
+ page = confluence.get_page_by_id(
317
+ page_id='123456789',
318
+ expand='version,body.storage'
319
+ )
320
+
321
+ current_content = page['body']['storage']['value']
322
+ new_content = current_content + '<p>Additional content appended.</p>'
323
+
324
+ confluence.update_page(
325
+ page_id='123456789',
326
+ title=page['title'],
327
+ body=new_content,
328
+ version_number=page['version']['number'] + 1
329
+ )
330
+ ```
331
+
332
+ ### Delete a Page
333
+
334
+ ```python
335
+ confluence.remove_page(
336
+ page_id='123456789',
337
+ status=None # Use 'trashed' for soft delete
338
+ )
339
+
340
+ print("Page deleted successfully")
341
+ ```
342
+
343
+ ### Move Page to Trash (Soft Delete)
344
+
345
+ ```python
346
+ confluence.set_page_property(
347
+ page_id='123456789',
348
+ data={'status': 'trashed'}
349
+ )
350
+ ```
351
+
352
+ ### Get Page Children
353
+
354
+ ```python
355
+ children = confluence.get_page_child_by_type(
356
+ page_id='123456789',
357
+ type='page',
358
+ start=0,
359
+ limit=50
360
+ )
361
+
362
+ for child in children:
363
+ print(f"Child: {child['title']} ({child['id']})")
364
+ ```
365
+
366
+ ### Get Page Descendants
367
+
368
+ ```python
369
+ descendants = confluence.get_page_descendants(
370
+ page_id='123456789',
371
+ type='page'
372
+ )
373
+
374
+ for page in descendants:
375
+ print(f"Descendant: {page['title']}")
376
+ ```
377
+
378
+ ### Get Page Ancestors
379
+
380
+ ```python
381
+ page = confluence.get_page_by_id(
382
+ page_id='123456789',
383
+ expand='ancestors'
384
+ )
385
+
386
+ for ancestor in page.get('ancestors', []):
387
+ print(f"Ancestor: {ancestor['title']} ({ancestor['id']})")
388
+ ```
389
+
390
+ ### Get Page History
391
+
392
+ ```python
393
+ history = confluence.history(page_id='123456789')
394
+
395
+ print(f"Created by: {history['createdBy']['displayName']}")
396
+ print(f"Created date: {history['createdDate']}")
397
+ print(f"Latest version: {history['latest']['number']}")
398
+ ```
399
+
400
+ ### Get Page Version Information
401
+
402
+ ```python
403
+ version = confluence.get_page_version(
404
+ page_id='123456789',
405
+ version_number=None # None for latest, or specify version number
406
+ )
407
+
408
+ print(f"Version: {version['number']}")
409
+ print(f"Modified by: {version['by']['displayName']}")
410
+ print(f"Modified when: {version['when']}")
411
+ ```
412
+
413
+ ## Spaces API
414
+
415
+ ### Get Space
416
+
417
+ ```python
418
+ space = confluence.get_space(
419
+ space_key='DEMO',
420
+ expand='description.plain,homepage'
421
+ )
422
+
423
+ print(f"Name: {space['name']}")
424
+ print(f"Key: {space['key']}")
425
+ print(f"Type: {space['type']}")
426
+ if 'description' in space:
427
+ print(f"Description: {space['description']['plain']['value']}")
428
+ ```
429
+
430
+ ### Get All Spaces
431
+
432
+ ```python
433
+ spaces = confluence.get_all_spaces(
434
+ start=0,
435
+ limit=100,
436
+ expand='description.plain'
437
+ )
438
+
439
+ for space in spaces['results']:
440
+ print(f"{space['key']}: {space['name']}")
441
+ ```
442
+
443
+ ### Get All Spaces as Generator
444
+
445
+ ```python
446
+ for space in confluence.get_all_spaces_as_generator():
447
+ print(f"Space: {space['key']} - {space['name']}")
448
+ ```
449
+
450
+ ### Create a Space
451
+
452
+ ```python
453
+ space = confluence.create_space(
454
+ space_key='PROJ',
455
+ space_name='Project Galaxy',
456
+ description='Documentation for Project Galaxy'
457
+ )
458
+
459
+ print(f"Created space: {space['key']}")
460
+ print(f"URL: {space['_links']['webui']}")
461
+ ```
462
+
463
+ ### Update Space
464
+
465
+ ```python
466
+ confluence.update_space(
467
+ space_key='DEMO',
468
+ name='Demo Space - Updated',
469
+ description='Updated description'
470
+ )
471
+ ```
472
+
473
+ ### Delete Space
474
+
475
+ ```python
476
+ confluence.delete_space(space_key='OLDSPACE')
477
+ print("Space deleted successfully")
478
+ ```
479
+
480
+ ### Get Space Content
481
+
482
+ ```python
483
+ content = confluence.get_space_content(
484
+ space_key='DEMO',
485
+ depth='all',
486
+ start=0,
487
+ limit=100
488
+ )
489
+
490
+ for item in content.get('page', {}).get('results', []):
491
+ print(f"Page: {item['title']} ({item['id']})")
492
+ ```
493
+
494
+ ### Get Space Homepage
495
+
496
+ ```python
497
+ space = confluence.get_space(
498
+ space_key='DEMO',
499
+ expand='homepage'
500
+ )
501
+
502
+ if 'homepage' in space:
503
+ homepage = space['homepage']
504
+ print(f"Homepage: {homepage['title']} ({homepage['id']})")
505
+ ```
506
+
507
+ ## Search API
508
+
509
+ ### CQL Search (Confluence Query Language)
510
+
511
+ ```python
512
+ results = confluence.cql(
513
+ cql='type=page AND space=DEMO AND title~"getting started"',
514
+ start=0,
515
+ limit=20,
516
+ expand='content.space,content.version'
517
+ )
518
+
519
+ for result in results.get('results', []):
520
+ content = result.get('content', {})
521
+ print(f"{content.get('title')} - {content.get('id')}")
522
+ ```
523
+
524
+ ### Common CQL Query Patterns
525
+
526
+ ```python
527
+ # Find all pages in a space
528
+ results = confluence.cql('type=page AND space=DEMO')
529
+
530
+ # Find pages by creator
531
+ results = confluence.cql('type=page AND creator=currentUser()')
532
+
533
+ # Find recently modified content
534
+ results = confluence.cql(
535
+ 'type=page AND lastModified > now("-7d") ORDER BY lastModified DESC',
536
+ limit=10
537
+ )
538
+
539
+ # Find pages with specific label
540
+ results = confluence.cql('type=page AND label="documentation"')
541
+
542
+ # Complex search with AND/OR
543
+ results = confluence.cql(
544
+ 'type=page AND space IN (DEMO, PROJ) AND (title~"guide" OR text~"tutorial")'
545
+ )
546
+
547
+ # Find pages modified by specific user
548
+ results = confluence.cql('type=page AND contributor="john.doe@example.com"')
549
+
550
+ # Find pages created this month
551
+ results = confluence.cql('type=page AND created >= startOfMonth()')
552
+ ```
553
+
554
+ ### Search Content by Title
555
+
556
+ ```python
557
+ # Using get_page_by_title for exact match
558
+ page = confluence.get_page_by_title(
559
+ space='DEMO',
560
+ title='Installation'
561
+ )
562
+
563
+ # Using CQL for partial match
564
+ results = confluence.cql('type=page AND title~"installation"')
565
+ ```
566
+
567
+ ### Full-Text Search
568
+
569
+ ```python
570
+ results = confluence.cql(
571
+ 'type=page AND text~"kubernetes deployment"',
572
+ limit=50
573
+ )
574
+
575
+ for result in results.get('results', []):
576
+ content = result.get('content', {})
577
+ print(f"Found: {content.get('title')}")
578
+ ```
579
+
580
+ ## Attachments API
581
+
582
+ ### Get Attachments for Page
583
+
584
+ ```python
585
+ attachments = confluence.get_attachments_from_content(
586
+ page_id='123456789',
587
+ start=0,
588
+ limit=50,
589
+ filename=None, # Optional: filter by filename
590
+ media_type=None # Optional: filter by media type
591
+ )
592
+
593
+ for attachment in attachments.get('results', []):
594
+ print(f"{attachment['title']} - {attachment['extensions']['fileSize']} bytes")
595
+ print(f"Download: {attachment['_links']['download']}")
596
+ ```
597
+
598
+ ### Attach File to Page
599
+
600
+ ```python
601
+ # Upload a file
602
+ result = confluence.attach_file(
603
+ filename='/path/to/document.pdf',
604
+ page_id='123456789',
605
+ title='Document', # Optional: display name
606
+ comment='Uploaded via API'
607
+ )
608
+
609
+ print(f"Uploaded: {result['title']}")
610
+ ```
611
+
612
+ ### Attach File from Content
613
+
614
+ ```python
615
+ # Upload file from bytes or file object
616
+ with open('/path/to/image.png', 'rb') as f:
617
+ result = confluence.attach_content(
618
+ content=f.read(),
619
+ name='screenshot.png',
620
+ content_type='image/png',
621
+ page_id='123456789',
622
+ comment='Screenshot of the dashboard'
623
+ )
624
+ ```
625
+
626
+ ### Update Existing Attachment
627
+
628
+ ```python
629
+ # Re-upload with same filename updates the attachment
630
+ result = confluence.attach_file(
631
+ filename='/path/to/updated-document.pdf',
632
+ page_id='123456789',
633
+ title='Document',
634
+ comment='Updated version'
635
+ )
636
+ ```
637
+
638
+ ### Download Attachment
639
+
640
+ ```python
641
+ # Get attachment info first
642
+ attachments = confluence.get_attachments_from_content(page_id='123456789')
643
+
644
+ for attachment in attachments.get('results', []):
645
+ if attachment['title'] == 'document.pdf':
646
+ download_link = attachment['_links']['download']
647
+
648
+ # Download the file
649
+ import requests
650
+ response = requests.get(
651
+ f"{confluence.url}{download_link}",
652
+ auth=(confluence.username, confluence.password)
653
+ )
654
+
655
+ with open('downloaded-document.pdf', 'wb') as f:
656
+ f.write(response.content)
657
+
658
+ break
659
+ ```
660
+
661
+ ### Delete Attachment
662
+
663
+ ```python
664
+ confluence.delete_attachment(
665
+ page_id='123456789',
666
+ filename='old-document.pdf'
667
+ )
668
+ ```
669
+
670
+ ## Labels API
671
+
672
+ ### Get Labels for Content
673
+
674
+ ```python
675
+ labels = confluence.get_page_labels(page_id='123456789')
676
+
677
+ for label in labels.get('results', []):
678
+ print(f"Label: {label['name']}")
679
+ ```
680
+
681
+ ### Add Label to Content
682
+
683
+ ```python
684
+ confluence.add_label(
685
+ page_id='123456789',
686
+ label='documentation'
687
+ )
688
+
689
+ print("Label added successfully")
690
+ ```
691
+
692
+ ### Add Multiple Labels
693
+
694
+ ```python
695
+ labels = ['documentation', 'getting-started', 'tutorial']
696
+
697
+ for label in labels:
698
+ confluence.add_label(
699
+ page_id='123456789',
700
+ label=label
701
+ )
702
+ ```
703
+
704
+ ### Remove Label from Content
705
+
706
+ ```python
707
+ confluence.remove_label(
708
+ page_id='123456789',
709
+ label='old-label'
710
+ )
711
+ ```
712
+
713
+ ### Search Pages by Label
714
+
715
+ ```python
716
+ results = confluence.cql('type=page AND label="documentation"')
717
+
718
+ for result in results.get('results', []):
719
+ content = result.get('content', {})
720
+ print(f"{content.get('title')} - {content.get('id')}")
721
+ ```
722
+
723
+ ## Comments API
724
+
725
+ ### Get Comments for Page
726
+
727
+ ```python
728
+ comments = confluence.get_page_comments(
729
+ page_id='123456789',
730
+ expand='body.storage',
731
+ depth='all'
732
+ )
733
+
734
+ for comment in comments:
735
+ print(f"Comment: {comment['title']}")
736
+ print(f"Content: {comment['body']['storage']['value']}")
737
+ ```
738
+
739
+ ### Add Comment to Page
740
+
741
+ ```python
742
+ comment = confluence.create_page(
743
+ space='DEMO',
744
+ title='Re: Page Title', # Comment title
745
+ body='<p>This is a helpful comment!</p>',
746
+ parent_id='123456789',
747
+ type='comment'
748
+ )
749
+
750
+ print(f"Comment added: {comment['id']}")
751
+ ```
752
+
753
+ ## User and Group APIs
754
+
755
+ ### Get Current User
756
+
757
+ ```python
758
+ user = confluence.get_current_user()
759
+
760
+ print(f"Display Name: {user['displayName']}")
761
+ print(f"Email: {user['email']}")
762
+ print(f"Account ID: {user['accountId']}")
763
+ ```
764
+
765
+ ### Get User by Username (Server/Data Center)
766
+
767
+ ```python
768
+ user = confluence.get_user_details_by_username(
769
+ username='john.doe'
770
+ )
771
+ ```
772
+
773
+ ### Get User by Account ID (Cloud)
774
+
775
+ ```python
776
+ user = confluence.get_user_details_by_accountid(
777
+ account_id='5a1234567890123456789012'
778
+ )
779
+
780
+ print(f"Display Name: {user['displayName']}")
781
+ ```
782
+
783
+ ### Get Group Members
784
+
785
+ ```python
786
+ members = confluence.get_group_members(
787
+ group_name='confluence-administrators',
788
+ start=0,
789
+ limit=50
790
+ )
791
+
792
+ for member in members:
793
+ print(f"Member: {member['displayName']}")
794
+ ```
795
+
796
+ ### Check if User is in Group
797
+
798
+ ```python
799
+ # Get all group members and check
800
+ members = confluence.get_group_members('confluence-users')
801
+ user_ids = [m['accountId'] for m in members]
802
+
803
+ is_member = 'user-account-id' in user_ids
804
+ print(f"User is member: {is_member}")
805
+ ```
806
+
807
+ ## Content Properties (Metadata)
808
+
809
+ ### Get Content Properties
810
+
811
+ ```python
812
+ properties = confluence.get_page_properties(page_id='123456789')
813
+
814
+ for prop in properties:
815
+ print(f"Key: {prop['key']}")
816
+ print(f"Value: {prop['value']}")
817
+ ```
818
+
819
+ ### Get Specific Content Property
820
+
821
+ ```python
822
+ property_value = confluence.get_page_property(
823
+ page_id='123456789',
824
+ page_property_key='custom-metadata'
825
+ )
826
+
827
+ print(f"Property value: {property_value}")
828
+ ```
829
+
830
+ ### Set Content Property
831
+
832
+ ```python
833
+ confluence.set_page_property(
834
+ page_id='123456789',
835
+ data={
836
+ 'key': 'custom-metadata',
837
+ 'value': {
838
+ 'lastReviewed': '2025-11-07',
839
+ 'reviewer': 'john.doe@example.com',
840
+ 'status': 'approved'
841
+ }
842
+ }
843
+ )
844
+ ```
845
+
846
+ ### Update Content Property
847
+
848
+ ```python
849
+ # Get current property
850
+ current = confluence.get_page_property(
851
+ page_id='123456789',
852
+ page_property_key='custom-metadata'
853
+ )
854
+
855
+ # Update with new data
856
+ current['value']['status'] = 'needs-review'
857
+ current['version']['number'] += 1
858
+
859
+ confluence.update_page_property(
860
+ page_id='123456789',
861
+ page_property_key='custom-metadata',
862
+ data=current
863
+ )
864
+ ```
865
+
866
+ ### Delete Content Property
867
+
868
+ ```python
869
+ confluence.delete_page_property(
870
+ page_id='123456789',
871
+ page_property_key='custom-metadata'
872
+ )
873
+ ```
874
+
875
+ ## Advanced Page Operations
876
+
877
+ ### Copy a Page
878
+
879
+ ```python
880
+ # Get source page
881
+ source = confluence.get_page_by_id(
882
+ page_id='123456789',
883
+ expand='body.storage,space'
884
+ )
885
+
886
+ # Create copy
887
+ copy = confluence.create_page(
888
+ space=source['space']['key'],
889
+ title=f"{source['title']} (Copy)",
890
+ body=source['body']['storage']['value'],
891
+ parent_id=None
892
+ )
893
+
894
+ print(f"Created copy: {copy['id']}")
895
+ ```
896
+
897
+ ### Copy Page with Children
898
+
899
+ ```python
900
+ def copy_page_tree(confluence, source_page_id, target_space, parent_id=None):
901
+ """Recursively copy a page and all its children."""
902
+ # Get source page
903
+ source = confluence.get_page_by_id(
904
+ page_id=source_page_id,
905
+ expand='body.storage,space'
906
+ )
907
+
908
+ # Create copy
909
+ copy = confluence.create_page(
910
+ space=target_space,
911
+ title=source['title'],
912
+ body=source['body']['storage']['value'],
913
+ parent_id=parent_id
914
+ )
915
+
916
+ # Copy children
917
+ children = confluence.get_page_child_by_type(
918
+ page_id=source_page_id,
919
+ type='page'
920
+ )
921
+
922
+ for child in children:
923
+ copy_page_tree(confluence, child['id'], target_space, copy['id'])
924
+
925
+ return copy
926
+ ```
927
+
928
+ ### Move Page to Different Parent
929
+
930
+ ```python
931
+ # Get current page
932
+ page = confluence.get_page_by_id(
933
+ page_id='123456789',
934
+ expand='version,body.storage,space,ancestors'
935
+ )
936
+
937
+ # Update with new parent
938
+ confluence.update_page(
939
+ page_id='123456789',
940
+ title=page['title'],
941
+ body=page['body']['storage']['value'],
942
+ parent_id='new-parent-id',
943
+ version_number=page['version']['number'] + 1
944
+ )
945
+ ```
946
+
947
+ ### Export Page as PDF
948
+
949
+ ```python
950
+ # Get page content in export view
951
+ page = confluence.get_page_by_id(
952
+ page_id='123456789',
953
+ expand='body.export_view'
954
+ )
955
+
956
+ export_html = page['body']['export_view']['value']
957
+
958
+ # Use a PDF library to convert HTML to PDF
959
+ # Example with pdfkit (requires wkhtmltopdf)
960
+ import pdfkit
961
+ pdfkit.from_string(export_html, 'output.pdf')
962
+ ```
963
+
964
+ ## Content Restrictions (Permissions)
965
+
966
+ ### Get Content Restrictions
967
+
968
+ ```python
969
+ restrictions = confluence.get_content_restrictions(
970
+ content_id='123456789',
971
+ expand='read.restrictions.user,update.restrictions.user'
972
+ )
973
+
974
+ print("Read restrictions:", restrictions.get('read'))
975
+ print("Update restrictions:", restrictions.get('update'))
976
+ ```
977
+
978
+ ### Add Read Restriction
979
+
980
+ ```python
981
+ confluence.add_content_restriction(
982
+ content_id='123456789',
983
+ operation='read',
984
+ restrictions={
985
+ 'user': [
986
+ {'accountId': '5a1234567890123456789012'}
987
+ ],
988
+ 'group': [
989
+ {'name': 'confluence-users'}
990
+ ]
991
+ }
992
+ )
993
+ ```
994
+
995
+ ### Remove Content Restrictions
996
+
997
+ ```python
998
+ confluence.remove_content_restriction(
999
+ content_id='123456789',
1000
+ operation='read'
1001
+ )
1002
+ ```
1003
+
1004
+ ## Macros in Content
1005
+
1006
+ ### Table of Contents Macro
1007
+
1008
+ ```python
1009
+ content = '''
1010
+ <h1>Table of Contents</h1>
1011
+ <ac:structured-macro ac:name="toc" ac:schema-version="1">
1012
+ <ac:parameter ac:name="maxLevel">3</ac:parameter>
1013
+ </ac:structured-macro>
1014
+ <h2>Section 1</h2>
1015
+ <p>Content here...</p>
1016
+ '''
1017
+
1018
+ confluence.create_page(
1019
+ space='DEMO',
1020
+ title='Documentation Index',
1021
+ body=content
1022
+ )
1023
+ ```
1024
+
1025
+ ### Status Macro
1026
+
1027
+ ```python
1028
+ content = '''
1029
+ <p>Project status:
1030
+ <ac:structured-macro ac:name="status" ac:schema-version="1">
1031
+ <ac:parameter ac:name="colour">Green</ac:parameter>
1032
+ <ac:parameter ac:name="title">Active</ac:parameter>
1033
+ </ac:structured-macro>
1034
+ </p>
1035
+ '''
1036
+ ```
1037
+
1038
+ ### Code Block Macro
1039
+
1040
+ ```python
1041
+ content = '''
1042
+ <ac:structured-macro ac:name="code" ac:schema-version="1">
1043
+ <ac:parameter ac:name="language">python</ac:parameter>
1044
+ <ac:plain-text-body><![CDATA[
1045
+ def greet(name):
1046
+ return f"Hello, {name}!"
1047
+
1048
+ print(greet("World"))
1049
+ ]]></ac:plain-text-body>
1050
+ </ac:structured-macro>
1051
+ '''
1052
+ ```
1053
+
1054
+ ### Info Panel Macro
1055
+
1056
+ ```python
1057
+ content = '''
1058
+ <ac:structured-macro ac:name="info" ac:schema-version="1">
1059
+ <ac:rich-text-body>
1060
+ <p>This is important information for users to know.</p>
1061
+ </ac:rich-text-body>
1062
+ </ac:structured-macro>
1063
+ '''
1064
+ ```
1065
+
1066
+ ### Warning Panel Macro
1067
+
1068
+ ```python
1069
+ content = '''
1070
+ <ac:structured-macro ac:name="warning" ac:schema-version="1">
1071
+ <ac:rich-text-body>
1072
+ <p><strong>Warning:</strong> This action cannot be undone.</p>
1073
+ </ac:rich-text-body>
1074
+ </ac:structured-macro>
1075
+ '''
1076
+ ```
1077
+
1078
+ ### Note Panel Macro
1079
+
1080
+ ```python
1081
+ content = '''
1082
+ <ac:structured-macro ac:name="note" ac:schema-version="1">
1083
+ <ac:rich-text-body>
1084
+ <p>Remember to save your work frequently.</p>
1085
+ </ac:rich-text-body>
1086
+ </ac:structured-macro>
1087
+ '''
1088
+ ```
1089
+
1090
+ ### Excerpt Macro
1091
+
1092
+ ```python
1093
+ content = '''
1094
+ <ac:structured-macro ac:name="excerpt" ac:schema-version="1">
1095
+ <ac:rich-text-body>
1096
+ <p>This is an excerpt that can be included in other pages.</p>
1097
+ </ac:rich-text-body>
1098
+ </ac:structured-macro>
1099
+ '''
1100
+ ```
1101
+
1102
+ ### Include Page Macro
1103
+
1104
+ ```python
1105
+ content = '''
1106
+ <ac:structured-macro ac:name="include" ac:schema-version="1">
1107
+ <ac:parameter ac:name=""><ac:link>
1108
+ <ri:page ri:content-title="Page to Include"/>
1109
+ </ac:link></ac:parameter>
1110
+ </ac:structured-macro>
1111
+ '''
1112
+ ```
1113
+
1114
+ ## Error Handling
1115
+
1116
+ ### Basic Error Handling
1117
+
1118
+ ```python
1119
+ from atlassian import Confluence
1120
+ from requests.exceptions import HTTPError
1121
+
1122
+ try:
1123
+ page = confluence.get_page_by_id(page_id='invalid-id')
1124
+ except HTTPError as e:
1125
+ print(f"HTTP Error: {e.response.status_code}")
1126
+ print(f"Message: {e.response.text}")
1127
+ except Exception as e:
1128
+ print(f"Error: {str(e)}")
1129
+ ```
1130
+
1131
+ ### Common HTTP Status Codes
1132
+
1133
+ - `400 Bad Request`: Invalid parameters or request body
1134
+ - `401 Unauthorized`: Invalid or missing authentication
1135
+ - `403 Forbidden`: Insufficient permissions
1136
+ - `404 Not Found`: Content or space does not exist
1137
+ - `409 Conflict`: Version conflict (update with wrong version number)
1138
+ - `429 Too Many Requests`: Rate limit exceeded
1139
+
1140
+ ### Handling Version Conflicts
1141
+
1142
+ ```python
1143
+ def update_page_with_retry(confluence, page_id, new_content, max_retries=3):
1144
+ """Update page with automatic retry on version conflicts."""
1145
+ import time
1146
+
1147
+ for attempt in range(max_retries):
1148
+ try:
1149
+ # Get current version
1150
+ page = confluence.get_page_by_id(
1151
+ page_id=page_id,
1152
+ expand='version,body.storage'
1153
+ )
1154
+
1155
+ # Update with incremented version
1156
+ updated = confluence.update_page(
1157
+ page_id=page_id,
1158
+ title=page['title'],
1159
+ body=new_content,
1160
+ version_number=page['version']['number'] + 1
1161
+ )
1162
+
1163
+ return updated
1164
+
1165
+ except HTTPError as e:
1166
+ if e.response.status_code == 409 and attempt < max_retries - 1:
1167
+ print(f"Conflict detected, retrying ({attempt + 1}/{max_retries})...")
1168
+ time.sleep(1)
1169
+ continue
1170
+ raise
1171
+
1172
+ raise Exception(f"Failed after {max_retries} retries")
1173
+ ```
1174
+
1175
+ ### Rate Limit Handling
1176
+
1177
+ ```python
1178
+ import time
1179
+ from requests.exceptions import HTTPError
1180
+
1181
+ def api_call_with_backoff(api_call, *args, max_retries=5, **kwargs):
1182
+ """Execute API call with exponential backoff on rate limits."""
1183
+ for attempt in range(max_retries):
1184
+ try:
1185
+ return api_call(*args, **kwargs)
1186
+ except HTTPError as e:
1187
+ if e.response.status_code == 429 and attempt < max_retries - 1:
1188
+ retry_after = int(e.response.headers.get('Retry-After', 2 ** attempt))
1189
+ print(f"Rate limited, waiting {retry_after}s before retry...")
1190
+ time.sleep(retry_after)
1191
+ continue
1192
+ raise
1193
+
1194
+ raise Exception(f"Max retries ({max_retries}) exceeded")
1195
+
1196
+ # Usage
1197
+ page = api_call_with_backoff(
1198
+ confluence.get_page_by_id,
1199
+ page_id='123456789'
1200
+ )
1201
+ ```
1202
+
1203
+ ### Comprehensive Error Handler
1204
+
1205
+ ```python
1206
+ from requests.exceptions import HTTPError, Timeout, ConnectionError
1207
+
1208
+ def safe_api_call(func, *args, **kwargs):
1209
+ """Wrapper for safe API calls with comprehensive error handling."""
1210
+ try:
1211
+ return func(*args, **kwargs)
1212
+ except HTTPError as e:
1213
+ status = e.response.status_code
1214
+ if status == 401:
1215
+ print("Authentication failed. Check your API token.")
1216
+ elif status == 403:
1217
+ print("Permission denied. Check user permissions.")
1218
+ elif status == 404:
1219
+ print("Resource not found.")
1220
+ elif status == 409:
1221
+ print("Version conflict. Refresh and try again.")
1222
+ elif status == 429:
1223
+ print("Rate limit exceeded. Slow down requests.")
1224
+ else:
1225
+ print(f"HTTP Error {status}: {e.response.text}")
1226
+ raise
1227
+ except Timeout:
1228
+ print("Request timed out. Try again later.")
1229
+ raise
1230
+ except ConnectionError:
1231
+ print("Connection error. Check network connectivity.")
1232
+ raise
1233
+ except Exception as e:
1234
+ print(f"Unexpected error: {str(e)}")
1235
+ raise
1236
+ ```
1237
+
1238
+ ## Bulk Operations
1239
+
1240
+ ### Get Multiple Pages by IDs
1241
+
1242
+ ```python
1243
+ def get_pages_by_ids(confluence, page_ids):
1244
+ """Fetch multiple pages by their IDs."""
1245
+ pages = []
1246
+
1247
+ for page_id in page_ids:
1248
+ try:
1249
+ page = confluence.get_page_by_id(
1250
+ page_id=page_id,
1251
+ expand='body.storage,version'
1252
+ )
1253
+ pages.append(page)
1254
+ except HTTPError as e:
1255
+ print(f"Failed to fetch page {page_id}: {e}")
1256
+
1257
+ return pages
1258
+ ```
1259
+
1260
+ ### Batch Create Pages
1261
+
1262
+ ```python
1263
+ import time
1264
+
1265
+ def create_multiple_pages(confluence, space_key, page_data):
1266
+ """Create multiple pages in a space."""
1267
+ created = []
1268
+
1269
+ for data in page_data:
1270
+ page = confluence.create_page(
1271
+ space=space_key,
1272
+ title=data['title'],
1273
+ body=data['content'],
1274
+ parent_id=data.get('parent_id')
1275
+ )
1276
+ created.append(page)
1277
+
1278
+ # Rate limit protection: wait between requests
1279
+ time.sleep(0.2)
1280
+
1281
+ return created
1282
+
1283
+ # Usage
1284
+ pages = [
1285
+ {'title': 'Page 1', 'content': '<p>Content 1</p>'},
1286
+ {'title': 'Page 2', 'content': '<p>Content 2</p>'},
1287
+ {'title': 'Page 3', 'content': '<p>Content 3</p>'},
1288
+ ]
1289
+
1290
+ created_pages = create_multiple_pages(confluence, 'DEMO', pages)
1291
+ ```
1292
+
1293
+ ### Batch Update Pages
1294
+
1295
+ ```python
1296
+ def update_multiple_pages(confluence, updates):
1297
+ """Update multiple pages."""
1298
+ results = []
1299
+
1300
+ for update in updates:
1301
+ page = confluence.get_page_by_id(
1302
+ page_id=update['id'],
1303
+ expand='version,body.storage'
1304
+ )
1305
+
1306
+ updated = confluence.update_page(
1307
+ page_id=update['id'],
1308
+ title=update.get('title', page['title']),
1309
+ body=update['content'],
1310
+ version_number=page['version']['number'] + 1
1311
+ )
1312
+
1313
+ results.append(updated)
1314
+ time.sleep(0.2)
1315
+
1316
+ return results
1317
+ ```
1318
+
1319
+ ### Export All Pages from Space
1320
+
1321
+ ```python
1322
+ import json
1323
+
1324
+ def export_space_to_json(confluence, space_key, output_file):
1325
+ """Export all pages from a space to JSON file."""
1326
+ pages_data = []
1327
+
1328
+ for page in confluence.get_all_pages_from_space_as_generator(
1329
+ space=space_key,
1330
+ expand='body.storage,version'
1331
+ ):
1332
+ pages_data.append({
1333
+ 'id': page['id'],
1334
+ 'title': page['title'],
1335
+ 'content': page['body']['storage']['value'],
1336
+ 'version': page['version']['number']
1337
+ })
1338
+
1339
+ with open(output_file, 'w', encoding='utf-8') as f:
1340
+ json.dump(pages_data, f, indent=2, ensure_ascii=False)
1341
+
1342
+ print(f"Exported {len(pages_data)} pages to {output_file}")
1343
+ ```
1344
+
1345
+ ### Clone Space to Another Space
1346
+
1347
+ ```python
1348
+ def clone_space(confluence, source_space, target_space):
1349
+ """Clone all pages from source space to target space."""
1350
+ # Get all pages from source
1351
+ for page in confluence.get_all_pages_from_space_as_generator(
1352
+ space=source_space,
1353
+ expand='body.storage'
1354
+ ):
1355
+ try:
1356
+ confluence.create_page(
1357
+ space=target_space,
1358
+ title=page['title'],
1359
+ body=page['body']['storage']['value']
1360
+ )
1361
+ print(f"Cloned: {page['title']}")
1362
+ time.sleep(0.2)
1363
+ except Exception as e:
1364
+ print(f"Failed to clone {page['title']}: {e}")
1365
+ ```
1366
+
1367
+ ## Performance and Optimization
1368
+
1369
+ ### Use Expand Parameter Wisely
1370
+
1371
+ ```python
1372
+ # Bad - expands everything (slow)
1373
+ page = confluence.get_page_by_id(
1374
+ page_id='123456789',
1375
+ expand='body.storage,body.view,body.editor,version,space,history,ancestors,descendants,container'
1376
+ )
1377
+
1378
+ # Good - only what you need (fast)
1379
+ page = confluence.get_page_by_id(
1380
+ page_id='123456789',
1381
+ expand='body.storage,version'
1382
+ )
1383
+ ```
1384
+
1385
+ ### Pagination Best Practices
1386
+
1387
+ ```python
1388
+ def get_all_pages_paginated(confluence, space_key, batch_size=100):
1389
+ """Get all pages with manual pagination control."""
1390
+ all_pages = []
1391
+ start = 0
1392
+
1393
+ while True:
1394
+ batch = confluence.get_all_pages_from_space(
1395
+ space=space_key,
1396
+ start=start,
1397
+ limit=batch_size
1398
+ )
1399
+
1400
+ if not batch:
1401
+ break
1402
+
1403
+ all_pages.extend(batch)
1404
+
1405
+ if len(batch) < batch_size:
1406
+ break
1407
+
1408
+ start += batch_size
1409
+
1410
+ return all_pages
1411
+ ```
1412
+
1413
+ ### Use Generators for Large Datasets
1414
+
1415
+ ```python
1416
+ # Memory efficient - processes one page at a time
1417
+ def process_all_pages(confluence, space_key):
1418
+ for page in confluence.get_all_pages_from_space_as_generator(space=space_key):
1419
+ # Process each page
1420
+ process_page(page)
1421
+ # Memory is released after each iteration
1422
+
1423
+ # Memory inefficient - loads all pages into memory
1424
+ def process_all_pages_bad(confluence, space_key):
1425
+ all_pages = confluence.get_all_pages_from_space(space=space_key, limit=10000)
1426
+ for page in all_pages:
1427
+ process_page(page)
1428
+ ```
1429
+
1430
+ ### Caching Strategies
1431
+
1432
+ ```python
1433
+ from functools import lru_cache
1434
+ import time
1435
+
1436
+ class CachedConfluence:
1437
+ def __init__(self, confluence):
1438
+ self.confluence = confluence
1439
+ self.cache = {}
1440
+ self.cache_ttl = 300 # 5 minutes
1441
+
1442
+ def get_page_cached(self, page_id):
1443
+ """Get page with caching."""
1444
+ cache_key = f"page_{page_id}"
1445
+
1446
+ if cache_key in self.cache:
1447
+ cached_data, cached_time = self.cache[cache_key]
1448
+ if time.time() - cached_time < self.cache_ttl:
1449
+ return cached_data
1450
+
1451
+ # Fetch fresh data
1452
+ page = self.confluence.get_page_by_id(page_id=page_id)
1453
+ self.cache[cache_key] = (page, time.time())
1454
+
1455
+ return page
1456
+
1457
+ def invalidate_cache(self, page_id=None):
1458
+ """Invalidate cache for specific page or all."""
1459
+ if page_id:
1460
+ cache_key = f"page_{page_id}"
1461
+ self.cache.pop(cache_key, None)
1462
+ else:
1463
+ self.cache.clear()
1464
+ ```
1465
+
1466
+ ## Common Patterns
1467
+
1468
+ ### Create or Update Page
1469
+
1470
+ ```python
1471
+ def create_or_update_page(confluence, space_key, title, content):
1472
+ """Create page if it doesn't exist, update if it does."""
1473
+ existing = confluence.get_page_by_title(
1474
+ space=space_key,
1475
+ title=title
1476
+ )
1477
+
1478
+ if existing:
1479
+ # Update existing page
1480
+ page = confluence.get_page_by_id(
1481
+ page_id=existing['id'],
1482
+ expand='version'
1483
+ )
1484
+
1485
+ return confluence.update_page(
1486
+ page_id=existing['id'],
1487
+ title=title,
1488
+ body=content,
1489
+ version_number=page['version']['number'] + 1
1490
+ )
1491
+ else:
1492
+ # Create new page
1493
+ return confluence.create_page(
1494
+ space=space_key,
1495
+ title=title,
1496
+ body=content
1497
+ )
1498
+ ```
1499
+
1500
+ ### Build Table of Contents
1501
+
1502
+ ```python
1503
+ def build_table_of_contents(confluence, space_key):
1504
+ """Generate HTML table of contents for all pages in space."""
1505
+ pages = confluence.get_all_pages_from_space(space=space_key)
1506
+
1507
+ toc_html = '<h2>Table of Contents</h2><ul>'
1508
+
1509
+ for page in pages:
1510
+ toc_html += f'''
1511
+ <li>
1512
+ <ac:link>
1513
+ <ri:page ri:content-title="{page['title']}"/>
1514
+ </ac:link>
1515
+ </li>
1516
+ '''
1517
+
1518
+ toc_html += '</ul>'
1519
+
1520
+ return toc_html
1521
+ ```
1522
+
1523
+ ### Sync Local Files to Confluence
1524
+
1525
+ ```python
1526
+ import os
1527
+ from pathlib import Path
1528
+
1529
+ def sync_markdown_to_confluence(confluence, directory, space_key, parent_id=None):
1530
+ """Sync Markdown files to Confluence pages."""
1531
+ import markdown
1532
+
1533
+ for md_file in Path(directory).glob('*.md'):
1534
+ # Read Markdown file
1535
+ with open(md_file, 'r', encoding='utf-8') as f:
1536
+ md_content = f.read()
1537
+
1538
+ # Convert Markdown to HTML
1539
+ html_content = markdown.markdown(md_content)
1540
+
1541
+ # Extract title from filename
1542
+ title = md_file.stem.replace('-', ' ').title()
1543
+
1544
+ # Create or update page
1545
+ create_or_update_page(
1546
+ confluence,
1547
+ space_key=space_key,
1548
+ title=title,
1549
+ content=html_content
1550
+ )
1551
+
1552
+ print(f"Synced: {title}")
1553
+ ```
1554
+
1555
+ ## Content Body Formats
1556
+
1557
+ Confluence supports multiple body representations:
1558
+
1559
+ ```python
1560
+ # Get page with different body formats
1561
+ page = confluence.get_page_by_id(
1562
+ page_id='123456789',
1563
+ expand='body.storage,body.view,body.export_view'
1564
+ )
1565
+
1566
+ # Storage format (XHTML for create/update)
1567
+ storage = page['body']['storage']['value']
1568
+
1569
+ # View format (HTML for rendering)
1570
+ view = page['body']['view']['value']
1571
+
1572
+ # Export format (HTML optimized for export)
1573
+ export = page['body']['export_view']['value']
1574
+ ```
1575
+
1576
+ ## Common Mistakes to Avoid
1577
+
1578
+ - **Not incrementing version number** when updating (causes 409 conflict)
1579
+ - **Using password instead of API token** for Cloud (causes 401 error)
1580
+ - **Forgetting cloud=True** for Confluence Cloud instances
1581
+ - **Not using expand parameter** to get needed fields (results in missing data)
1582
+ - **Using storage format incorrectly** (must be valid XHTML)
1583
+ - **Not handling pagination** for large result sets (misses data beyond first page)
1584
+ - **Hardcoding credentials** in code instead of environment variables (security risk)
1585
+ - **Not implementing rate limiting** in bulk operations (causes 429 errors)
1586
+ - **Creating duplicate pages** without checking if title exists first
1587
+ - **Not escaping HTML** in content (causes malformed XML errors)
1588
+ - **Using get_all_pages_from_space** without limit for large spaces (memory issues)
1589
+ - **Not using generators** for processing large datasets (runs out of memory)
1590
+ - **Ignoring HTTP errors** without proper error handling (silent failures)
1591
+ - **Not validating page exists** before operations (causes 404 errors)
1592
+
1593
+ ## Reference Links
1594
+
1595
+ - **atlassian-python-api Documentation**: https://atlassian-python-api.readthedocs.io/
1596
+ - **Confluence Module Docs**: https://atlassian-python-api.readthedocs.io/confluence.html
1597
+ - **Confluence Cloud REST API v2**: https://developer.atlassian.com/cloud/confluence/rest/v2/
1598
+ - **Confluence Cloud REST API v1**: https://developer.atlassian.com/cloud/confluence/rest/v1/
1599
+ - **CQL (Confluence Query Language)**: https://developer.atlassian.com/cloud/confluence/advanced-searching-using-cql/
1600
+ - **Storage Format Guide**: https://confluence.atlassian.com/doc/confluence-storage-format-790796544.html
1601
+ - **Atlassian API Tokens**: https://id.atlassian.com/manage-profile/security/api-tokens
1602
+ - **Rate Limits**: https://developer.atlassian.com/cloud/confluence/rate-limiting/
1603
+ - **PyPI Package**: https://pypi.org/project/atlassian-python-api/
1604
+ - **GitHub Repository**: https://github.com/atlassian-api/atlassian-python-api