In previous posts I wrote how to automate the work with Atlassian Jira, including automated ticket labeling. Now let’s try to use REST API of another popular Atlassian product – Confluence wiki engine.
What you may want to automate in Confluence? Obviously, it may be useful to read the pages that your colleagues regularly update and then use this data in some scripts as an input. You may also want to update your own Confluence pages, for example to post Vulnerability Scanning results. π
Reading pages
You can read the page by Id using this function:
import json
def get_page_json(page_id, expand = False):
if expand:
suffix = "?expand=" + expand
#body.storage
else:
suffix = ""
url="https://confluence.corporation.com/rest/api/content/" + page_id + suffix
response = requests.get(url, auth=(user, password))
response.encoding = "utf8"
return json.loads(response.text)
print(get_page_json("287346883", "body.storage"))
Output:
{u'status': u'current', u'body': {u'_expandable': {u'export_view': u'', u'styled_view': u'', u'editor': u'', u'anonymous_export_view': u'', u'view': u''}, u'storage': {u'_expandable': {u'content': u'/rest/api/content/287346883'}, u'representation': u'storage', u'value': u'<p>Test content</p>'}}, u'title': u'Test page', u'_expandable': {u'operations': u'', u'ancestors': u'', u'container': u'/rest/api/space/INFO', u'descendants': u'/rest/api/content/287346883/descendant', u'space': u'/rest/api/space/INFO', u'version': u'', u'metadata': u'', u'children': u'/rest/api/content/287346883/child', u'history': u'/rest/api/content/287346883/history'}, u'extensions': {u'position': u'none'}, u'_links': {u'self': u'https://confluence.corporation.com/rest/api/content/287346883', u'collection': u'/rest/api/content', u'tinyui': u'/x/-QWIE', u'base': u'https://confluence.corporation.com', u'webui': u'/display/INFO/Test+page', u'context': u''}, u'type': u'page', u'id': u'287346883'}
Without “?expand=body.storage” Confluence will return detailed information about the version, but will not return the full content of the page toΒ body -> storage -> value
. π
As you can see, JSON data is very convenient. You can easily read the title and html content:
print(json_data['title'])
print(json_data['body']['storage']['value'])
Output:
Test page
<p>Test content</p>
Editing
OK. How can we change the content of this page? We need to send some minimal JSON to the Confluence API, for example using this function:
def set_page_json(page_id,json_content):
headers = {
'Content-Type': 'application/json',
}
response = requests.put("https://confluence.corporation.com/rest/api/content/" + page_id, headers=headers, data=json.dumps(json_content),
auth=(user, password))
return(response.text)
What should be the structure of this JSON?
I cook it this way:
json_data = functions_confluence.get_page_json("277387103")
new_json_data = dict()
new_json_data['id'] = json_data['id']
new_json_data['type'] = json_data['type']
new_json_data['title'] = json_data['title']
new_json_data['type'] = json_data['type']
new_json_data['version'] = {"number": json_data['version']['number'] + 1}
if not 'key' in json_data:
new_json_data['key'] = json_data['space']['key']
else:
new_json_data['key'] = json_data['key']
new_json_data['body'] = {'storage':{'value':'<p>This is the updated text for the new page</p>','representation':'storage'}}
There should be something like this in new_json_data:
{'body': {'storage': {'representation': 'storage', 'value': '<p>This is the updated text for the new page</p>'}}, 'title': u'Test page', 'version': {'number': 2}, 'key': u'INFO', 'type': u'page', 'id': u'277387103'}
The response after sending JSON will be:
{"id":"277387103","type":"page","status":"current","title":"Test page", ....
And the content of the page should change.
Tables
What if we try to add a table?
new_json_data['body'] = {'storage':{'value':'<p><table><tr><th>head1</th><th>head2</th></tr><tr><td>value1</td><td>value2</td></tr><tr><td>value3</td><td>value4</td></tr></table></p>','representation':'storage'}}
It’s simple!
Images and other attacments
Unfortunately, it’s not possible to send a picture in base64 this way =(
What if I attach a file to a page and use a link to it in code? It’s possible.
I’m uploading the file using this function:
def attach_file_to_page(page_id, filepath, filename):
headers = {
'X-Atlassian-Token': 'no-check',
}
files = {
'file': (filename, open(filepath, 'rb')),
}
#delete attach with the same name
response = requests.get("https://confluence.corporation.com/rest/api/content/" + page_id + "/child/attachment", auth=(user, password))
for attachment in json.loads(response.text)['results']:
if attachment['title'] == filename:
requests.delete("https://confluence.corporation.com/rest/api/content/" + attachment['id'], headers=headers, auth=(user, password))
#attack files
response = requests.post("https://confluence.corporation.com/rest/api/content/" +page_id+ "/child/attachment", headers=headers, files=files,
auth=(user, password))
return(response.text)
Note that before you upload a file, you need to verify that the file with such name is not attached already or you will get an error. So I check all the attached files and delete files with the specified name.
URL of the file will be https://confluence.corporation.com/download/attachments/277387103/test.jpg and we can use it in code:
functions_confluence.attach_file_to_page("277387103", "data/test.jpg", "test.jpg")
img_url = "https://confluence.corporation.com/download/attachments/277387103/test.jpg"
new_json_data['body'] = {'storage':{'value': '<img src="'+img_url+'"></img>','representation':'storage'}}
Output:
{"id":"277387103","type":"page","status":"current","title":"Test page", ...
And test.jpg</ code> will appear on the page:
Useful curl one-liners you can also find on the official website here: https://developer.atlassian.com/server/confluence/confluence-rest-api-examples/
Hey Alex, just wanted to thank you for such a wonderfully fantastic tutorial – this is JUST what I’ve been scouring the internet for. Your code worked so well, and it is so incredibly well-written, I feel bad for essentially copying it but there is nothing I can do to improve it!
I did, however, have a small question. By using the body/storage/value functions, writing new json data to the confluence page overrides the existing data and replaces it with the content of new_json_data. Is there a way to add new data while keeping existing data on the page, without having to include all the existing data in the new_json_data dictionary?
Thank you again for your tutorial!
Pingback: Asset Inventory for Network Perimeter: from Declarations to Active Scanning | Alexander V. Leonov
Pingback: Asset Inventory for Internal Network: problems with Active Scanning and advantages of Splunk | Alexander V. Leonov
Hi,
Your tutorial is really helpful π
@ Lorenzo
The Value is just a HTML code , in your code you can append value as string and at end you can use the function set page json.
@ Lorenzo
I misread your question ,
I have same question with get_page_json can we get current data which is in confluence page , so that we can append instead of overwrite it.