Updating a confluence page with Rest API: problem with ancestors
Asked by Fabio Lucarelli:

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


Answers:
author: Amos Shapira
body: 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](http://docs.python-requests.org)
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
```

comments: author: Joshua Senecal
body: Very nice! This is likely the sort of thing I'll have to implement as well to ensure my code is robust.


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


author: Amos Shapira
body: 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)
```


author: Joshua Senecal
body: 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

comments: author: Catie Jo
body: Thanks! This worked like a charm for me.


author: Fabio Lucarelli
body: 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.

comments: author: Amos Shapira
body: Yes, I'd say it's a bug.


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


author: Amos Shapira
body: No I haven't logged an issue about this particular problem.


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


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


author: Monique Khairuliana
body: Hi Everyone,

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

* <https://jira.atlassian.com/browse/CRA-487>


Tags: confluence-administration