Updating a confluence page with Rest API: problem with ancestors

Fabio Lucarelli September 9, 2014

Hello all,

I've recently switched from the SOAP api to the rest API to create and update confluence pages (I'm using Confluence 5.5.6).

I would like to report a strange behaviour with the API when updating a page.

First, it's important to know that if you use the example from the Rest API example page ( https://developer.atlassian.com/display/CONFDEV/Confluence+REST+API+Examples ), your page will loose its ancestor. The page ancestor is deleted and the page ancestor is its space: you cannot browse it with the treeview. In order to avoid that problem, you have to provide the ancestor in the request.

Second, to update a JIRA issue or a confluence page with the REST API, I send a request with all the elements needed to update the issue or the page, I modify the JSON received and I send it back (it seems to be pretty obvious behaviour).

So in order to update a confluence page the minimal request has this form: http://<host>:8080/confluence/rest/api/content/<id>?expand=body.storage,version,ancestors

body.storage and version are updated in the JSON and a PUT request is sent back.

Unfortunately, if you do so, your page will still loose its ancestor... And it took me a while to figure out why: the returned JSON has ALL the ancestors of the page: the direct ancestor of the page, the ancestor of the ancestor and so on until the space root. So the array has several ancestors... And if you don't remove them except the direct ancestor, your page will loose its ancestor.

So I wonder, anyone else came accross that problem ? Is it a normal behaviour or a bug/feature ?

Any opinions welcome.

Fabio

4 answers

2 votes
Amos Shapira December 2, 2014

I took Joshua Senecal's response a bit further and I think I address his caveat - before uploading a new version of the page (from a Python script), I find the id of the direct ancestor of that page. The method looks at the page's "ancestors" attribute:

  1. If there is no ancestor found (attribute is empty) then this is a "root" page, and I drop the "ancestor" entry from the PUT payload.
  2. If there is only one ancestor then use it (e.g. the page is probably a direct descendent of the root page, but that doesn't matter to the code)
  3. If there are more than one ancestors then loop through them, fetch their "/content/<id>/child/page" and see whether the current page's id appears there, if so then use that ancestor.

With this code I could update a space of 342 pages at different tree depths without moving the pages. They also maintained their order relative to their siblings.

Here is the python code, in this code:

  1. page_tree is a dictionary with keys being page id's and values being set()'s of children id's
  2. get() is a simple wrapper around requests
  3. 'url' is the base URL to the Confluence instance root
page_tree = dict()
API_URL_PATH = '/rest/api'


def get_parent_id(user, password, space_key, url, page):
  if not 'ancestors' in page or len(page['ancestors']) == 0:
    return None
  if len(page['ancestors']) == 1:
    return page['ancestors'][0]['id']
  page_id = page['id']
  for ancestor in reversed(page['ancestors']):
    parent_id = ancestor['id']
    if parent_id not in page_tree:
      page_tree[parent_id] = get_page_children(user, password, space_key, url, parent_id)
    if page_id in page_tree[parent_id]:
      return parent_id
  return None
 
def get_page_children(user, password, space_key, url, page_id):
  children = set()
  location = url + API_URL_PATH + ('/content/%s/child/page' % page_id)
  while True:
    response = get(user, password, location, { 'spaceKey': space_key })
    children.update(map(lambda child: child['id'], response['results']))
    if 'next' in response['_links']:
      location = url + response['_links']['next']
    else:
      break


  return children
Joshua Senecal December 2, 2014

Very nice! This is likely the sort of thing I'll have to implement as well to ensure my code is robust.

Runo Icrimed September 5, 2016

What exactly do you do in the wrapper method get(...) (line:23)?

Amos Shapira September 6, 2016

Here is a rough version of get:

def get(user, password, payload, url):
  request = requests.Request('POST', url, data = json.dumps(payload), headers = headers = {
    'content-type': 'application/json',
    'Authorization': 'Basic %s' % base64.encodestring('%s:%s' % (user, password)).replace('\n', '')
  })

  prepared = request.prepare()
  return session.send(prepared)
2 votes
Joshua Senecal November 7, 2014

I'm in the process of moving code to using the REST API from the XML-RPC API. I ran into this problem, and here's how I worked around it.

I need to preface this with a warning that (as you've probably discovered) the Confluence documentation isn't very good. Either there's still a lot of functionality not available in the REST API yet, or it's not documented. Either way, the solution I have here is based on my own observations, and not on any documentation I've read.

I'm using Confluence 5.6.3, interacting with it using Perl and the JSON, URI, and LWP libraries.

For starters, I have not found a way to get the immediate ancestor of a page (I'll be posting a question about this). As you've observed, you can only get a list of all ancestors. However, I noticed that the list is ordered (usually, more on this below) such that the last item on the list is the immediate parent of the page I'm working with. So after parsing the JSON-formatted string and getting a list, I pull the page ID from the last item in the list, and use that.

In another post somewhere someone said to only set the ID of the page's immediate ancestor. There's no need to set it for all the parents. Something like this:

..., "ancestors":[{"id", "12345"}], ...

You're essentially giving it a one-element list, with the ID of the immediate ancestor. This seems to work.

Now, a big warning: I've come across circumstances when the list of ancestors returned by the REST API is not in order, and even contains duplicate entries. This appears to happen only when I've fouled up a page's ancestry while testing and debugging code. The pages all end up on the space root, and in the wiki I manually drag them back into the proper order, Everything on the wiki looks good, but after doing this the ancestor list I get from the REST API is no longer nicely ordered.

One workaround to this might be to sort the ancestors by page ID number. On our system I noticed that the further down the tree I go, the higher the page numbers are. So if I sort the ancestor list and then take the highest-numbered ID, that should be the ID I want.

I hope this is helpful. If there's anything that needs clarifying let me know.

 

-Josh

Catie Jo January 7, 2015

Thanks! This worked like a charm for me.

1 vote
Fabio Lucarelli December 3, 2014

Hello,

Thank you for your feedback!

Can we all agree it's a bug ? smile

By bug I mean that you request some data, you modify it and you cannot send it back as it is, you have to clean it otherwise the ancestors are lost.

Anyway unlike Joshua I've never came across a case where the ancestors list is not sorted (the first one in the list is the farthest one). But of course Amos's method is the safest one.

 

 

Amos Shapira December 3, 2014

Yes, I'd say it's a bug.

Xabier Davila December 11, 2014

Has this been logged with Atlassian? If that's the case, it would be good to post here the ticket number

Amos Shapira December 11, 2014

No I haven't logged an issue about this particular problem.

Xabier Davila December 11, 2014

I have logged this issue, please add any comments you consider relevant: [CRA-487](https://jira.atlassian.com/browse/CRA-487)

Amos Shapira April 15, 2015

Update - the issue was marked as Resolved in version 5.8 (and I assumed current cloud version, dunno how to see this).

0 votes
Monique Khairuliana
Atlassian Team
Atlassian Team members are employees working across the company in a wide variety of roles.
June 14, 2016

Hi Everyone,

This is a known bug in confluence. It has been fixed in 5.8 and above:

 

Suggest an answer

Log in or Sign up to answer
TAGS
AUG Leaders

Atlassian Community Events