Skip to content

Commit 4132d7f

Browse files
authored
Merge pull request atlassian-api#283 from andreas-j-hauser/issue-280
issue 280: implement the new method confluence.attach_content()
2 parents 9c2be32 + 41d273e commit 4132d7f

File tree

3 files changed

+90
-29
lines changed

3 files changed

+90
-29
lines changed

atlassian/confluence.py

+35-13
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ def add_comment(self, page_id, text):
390390
'body': self._create_body(text, 'storage')}
391391
return self.post('rest/api/content/', data=data)
392392

393-
def attach_file(self, filename, page_id=None, title=None, space=None, comment=None):
393+
def attach_content(self, content, name, content_type='application/binary', page_id=None, title=None, space=None, comment=None):
394394
"""
395395
Attach (upload) a file to a page, if it exists it will update the
396396
automatically version the new file and keep the old one.
@@ -400,22 +400,22 @@ def attach_file(self, filename, page_id=None, title=None, space=None, comment=No
400400
:type space: ``str``
401401
:param page_id: The page id to which we would like to upload the file
402402
:type page_id: ``str``
403-
:param filename: The file to upload
404-
:type filename: ``str``
403+
:param name: The name of the attachment
404+
:type name: ``str``
405+
:param content: Contains the content which should be uplaoded
406+
:type content: ``binary``
407+
:param content_type: Specify the HTTP content type. The default is
408+
:type content_type: ``str``
405409
:param comment: A comment describing this upload/file
406410
:type comment: ``str``
407411
"""
408412
page_id = self.get_page_id(space=space, title=title) if page_id is None else page_id
409413
type = 'attachment'
410414
if page_id is not None:
411-
# get base name of the file to get the attachment from confluence.
412-
file_base_name = os.path.basename(filename)
413-
extension = os.path.splitext(filename)[-1]
414-
content_type = self.content_types.get(extension, "application/binary")
415-
comment = comment if comment else "Uploaded {filename}.".format(filename=filename)
415+
comment = comment if comment else "Uploaded {filename}.".format(filename=name)
416416
data = {
417417
'type': type,
418-
"fileName": file_base_name,
418+
"fileName": name,
419419
"contentType": content_type,
420420
"comment": comment,
421421
"minorEdit": "true"}
@@ -424,16 +424,38 @@ def attach_file(self, filename, page_id=None, title=None, space=None, comment=No
424424
'Accept': 'application/json'}
425425
path = 'rest/api/content/{page_id}/child/attachment'.format(page_id=page_id)
426426
# Check if there is already a file with the same name
427-
attachments = self.get(path=path, headers=headers, params={'filename': file_base_name})
427+
attachments = self.get(path=path, headers=headers, params={'filename': name})
428428
if attachments['size']:
429429
path = path + '/' + attachments['results'][0]['id'] + '/data'
430-
with open(filename, 'rb') as infile:
431-
return self.post(path=path, data=data, headers=headers,
432-
files={'file': (file_base_name, infile, content_type)})
430+
return self.post(path=path, data=data, headers=headers,
431+
files={'file': (name, content, content_type)})
433432
else:
434433
log.warning("No 'page_id' found, not uploading attachments")
435434
return None
436435

436+
def attach_file(self, filename, page_id=None, title=None, space=None, comment=None):
437+
"""
438+
Attach (upload) a file to a page, if it exists it will update the
439+
automatically version the new file and keep the old one.
440+
:param title: The page name
441+
:type title: ``str``
442+
:param space: The space name
443+
:type space: ``str``
444+
:param page_id: The page id to which we would like to upload the file
445+
:type page_id: ``str``
446+
:param filename: The file to upload
447+
:type filename: ``str``
448+
:param comment: A comment describing this upload/file
449+
:type comment: ``str``
450+
"""
451+
# get base name of the file to get the attachment from confluence.
452+
name = os.path.basename(filename)
453+
extension = os.path.splitext(filename)[-1]
454+
content_type = self.content_types.get(extension, "application/binary")
455+
with open(filename, 'rb') as infile:
456+
content = infile.read()
457+
return self.attach_content( content, name, content_type, page_id=page_id, title=title, space=space, comment=comment )
458+
437459
def delete_attachment(self, page_id, filename, version=None):
438460
"""
439461
Remove completely a file if version is None or delete version

docs/confluence.rst

+4
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ Page actions
9292
# automatically version the new file and keep the old one
9393
confluence.attach_file(filename, page_id=None, title=None, space=None, comment=None)
9494
95+
# Attach (upload) a content to a page, if it exists it will update the
96+
# automatically version the new file and keep the old one
97+
confluence.attach_content(content, name=None, content_type=None, page_id=None, title=None, space=None, comment=None)
98+
9599
# Export page as PDF
96100
# api_version needs to be set to 'cloud' when exporting from Confluence Cloud.
97101
confluence.export_page(page_id)

tests/test-confluence-attach.py

+51-16
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,28 @@
55
from atlassian import Confluence
66

77

8-
class TestConfluenceAuth(unittest.TestCase):
8+
class TestConfluenceAttach(unittest.TestCase):
9+
secret_file = '../credentials.secret'
910

10-
def test_confluence_auth(self):
11+
'''
12+
Keep the credentials private, the file is excluded. There is an example for credentials.secret
13+
See also: http://www.blacktechdiva.com/hide-api-keys/
14+
15+
{
16+
"host" : "https://localhost:8080",
17+
"username" : "john_doe",
18+
"password" : "12345678"
19+
}
20+
'''
21+
22+
def test_confluence_attach(self):
1123
credentials = None
12-
secret_file = '../credentials.secret'
13-
14-
'''
15-
Keep the credentials private, the file is excluded. There is an example for credentials.secret
16-
See also: http://www.blacktechdiva.com/hide-api-keys/
17-
18-
{
19-
"host" : "https://localhost:8080",
20-
"username" : "john_doe",
21-
"password" : "12345678"
22-
}
23-
'''
2424

2525
try:
26-
with open(secret_file) as json_file:
26+
with open(self.secret_file) as json_file:
2727
credentials = json.load(json_file)
2828
except Exception as err:
29-
self.fail('[{0}]: {1}'.format(secret_file, err))
29+
self.fail('[{0}]: {1}'.format(self.secret_file, err))
3030

3131
confluence = Confluence(
3232
url=credentials['host'],
@@ -60,6 +60,41 @@ def test_confluence_auth(self):
6060
os.close(fd)
6161
os.remove(filename)
6262

63+
def test_confluence_attach_data(self):
64+
credentials = None
65+
66+
try:
67+
with open(self.secret_file) as json_file:
68+
credentials = json.load(json_file)
69+
except Exception as err:
70+
self.fail('[{0}]: {1}'.format(self.secret_file, err))
71+
72+
confluence = Confluence(
73+
url=credentials['host'],
74+
username=credentials['username'],
75+
password=credentials['password'])
76+
77+
# individual configuration
78+
space = 'SAN'
79+
title = 'atlassian-python-rest-api-wrapper'
80+
81+
attachment_name = os.path.basename(tempfile.mktemp())
82+
83+
# upload a new file
84+
content = b'Hello World - Version 1'
85+
result = confluence.attach_content( content, attachment_name, 'text/plain', title=title, space=space, comment='upload from unittest')
86+
87+
# attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
88+
self.assertTrue('results' in result)
89+
self.assertFalse('statusCode' in result)
90+
91+
# upload a new version of an existing file
92+
content = b'Hello Universe - Version 2'
93+
result = confluence.attach_content( content, attachment_name, 'text/plain', title=title, space=space, comment='upload from unittest')
94+
95+
# attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
96+
self.assertTrue('id' in result)
97+
self.assertFalse('statusCode' in result)
6398

6499
if __name__ == '__main__':
65100
unittest.main()

0 commit comments

Comments
 (0)