Confluence REST API - HTTPError: 404 Client Error... appended `rest/api/content/<page_id>` at end of URL

364 views Asked by At

Goal: Establish connection to Confluence page, via. atlassian-python-api==3.41.1.

Configuration:

  • Username - e-mail address
  • Password - API key (regenerated many times)

Permissions:

  • Contacted admins
  • All have a green tick, under my account

URL:

  • Works in browser
  • However, /rest/api/content/<page_id> is appended to the end of URL string.
HTTPError: 404 Client Error: Not Found for url: .../rest/api/content/123456

Questions:

  1. Is the URL appended string the reason for my error?
  2. If so, how can I prevent this behaviour?
  3. If not, what else should I investigate?

Jupyter Notebook:

from atlassian import Confluence

import re

from urllib.parse import urlparse

from parameter_store.params import project_params, ConfluenceParams

from typing import Any, Dict, List, Optional, Tuple
#%%
# USER INPUTS
URL = 'https://company-site.atlassian.net/wiki/spaces/my-space/pages/123456/Confluence+Python+API/' # anonymised
#%%
def establish_connection(url: str, username: str, password: str) -> Confluence:
    return Confluence(url=url, username=username, password=password)
#%%
def get_domain_from_url(url: str) -> str:
    return urlparse(url).netloc
#%%
def get_page_id_from_url(url: str) -> Optional[int]:
    return int(re.search(r'/pages/(\d+)/', url).group(1)) if re.search(r'/pages/(\d+)/', url) else None
#%%
def generate_content(dynamic_text: str = '') -> str:
    static_content = """
    h1. Header
    h2. Sub-header
    This is text.
    # Numbered list
    ## Sub-list
    (/)
    ||Header 1||Header 2||
    |Cell A1|Cell B1|
    {info:title=Info}
    This is an info panel
    {info}
    {code:python}
    print('Hello, world!')
    {code}
    ----
    {quote}
    This is a quote.
    {quote}
    """
    return static_content + f'\nh2. Dynamic Text\n{dynamic_text}'
#%%
def update_confluence_page(confluence, page_id: str, new_content: str) -> None:
    confluence.update_page(
        page_id=page_id,
        title='Your Updated Page Title',
        body=new_content
    )
#%%
def delete_confluence_page(confluence: Confluence, page_id: str) -> None:
    confluence.delete_page(page_id)
#%%
if __name__ == '__main__':
    confluence_params = project_params.confluence
    
    username = confluence_params.username
    password = confluence_params.api_key
    
    confluence = establish_connection(URL, username, password)
    
    domain = get_domain_from_url(URL)
    page_id = get_page_id_from_url(URL)
    page_info = confluence.get_page_by_id(page_id)
    
    content = generate_content()
    update_confluence_page(confluence, page_id, content)

Traceback:

Expecting value: line 3 column 1 (char 10)
---------------------------------------------------------------------------
JSONDecodeError                           Traceback (most recent call last)
File ~/miniconda3/envs/venv/lib/python3.10/site-packages/requests/models.py:971, in Response.json(self, **kwargs)
    970 try:
--> 971     return complexjson.loads(self.text, **kwargs)
    972 except JSONDecodeError as e:
    973     # Catch JSON-related errors and raise as requests.JSONDecodeError
    974     # This aliases json.JSONDecodeError and simplejson.JSONDecodeError

File ~/miniconda3/envs/venv/lib/python3.10/json/__init__.py:346, in loads(s, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    343 if (cls is None and object_hook is None and
    344         parse_int is None and parse_float is None and
    345         parse_constant is None and object_pairs_hook is None and not kw):
--> 346     return _default_decoder.decode(s)
    347 if cls is None:

File ~/miniconda3/envs/venv/lib/python3.10/json/decoder.py:337, in JSONDecoder.decode(self, s, _w)
    333 """Return the Python representation of ``s`` (a ``str`` instance
    334 containing a JSON document).
    335 
    336 """
--> 337 obj, end = self.raw_decode(s, idx=_w(s, 0).end())
    338 end = _w(s, end).end()

File ~/miniconda3/envs/venv/lib/python3.10/json/decoder.py:355, in JSONDecoder.raw_decode(self, s, idx)
    354 except StopIteration as err:
--> 355     raise JSONDecodeError("Expecting value", s, err.value) from None
    356 return obj, end

JSONDecodeError: Expecting value: line 3 column 1 (char 10)

During handling of the above exception, another exception occurred:

JSONDecodeError                           Traceback (most recent call last)
File ~/miniconda3/envs/venv/lib/python3.10/site-packages/atlassian/confluence.py:3145, in Confluence.raise_for_status(self, response)
   3144 try:
-> 3145     j = response.json()
   3146     error_msg = j["message"]

File ~/miniconda3/envs/venv/lib/python3.10/site-packages/requests/models.py:975, in Response.json(self, **kwargs)
    972 except JSONDecodeError as e:
    973     # Catch JSON-related errors and raise as requests.JSONDecodeError
    974     # This aliases json.JSONDecodeError and simplejson.JSONDecodeError
--> 975     raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)

JSONDecodeError: Expecting value: line 3 column 1 (char 10)

During handling of the above exception, another exception occurred:

HTTPError                                 Traceback (most recent call last)
File ~/miniconda3/envs/venv/lib/python3.10/site-packages/atlassian/confluence.py:345, in Confluence.get_page_by_id(self, page_id, expand, status, version)
    344 try:
--> 345     response = self.get(url, params=params)
    346 except HTTPError as e:

File ~/miniconda3/envs/venv/lib/python3.10/site-packages/atlassian/rest_client.py:285, in AtlassianRestAPI.get(self, path, data, flags, params, headers, not_json_response, trailing, absolute, advanced_mode)
    272 """
    273 Get request based on the python-requests module. You can override headers, and also, get not json response
    274 :param path:
   (...)
    283 :return:
    284 """
--> 285 response = self.request(
    286     "GET",
    287     path=path,
    288     flags=flags,
    289     params=params,
    290     data=data,
    291     headers=headers,
    292     trailing=trailing,
    293     absolute=absolute,
    294     advanced_mode=advanced_mode,
    295 )
    296 if self.advanced_mode or advanced_mode:

File ~/miniconda3/envs/venv/lib/python3.10/site-packages/atlassian/rest_client.py:257, in AtlassianRestAPI.request(self, method, path, data, json, flags, params, headers, files, trailing, absolute, advanced_mode)
    255     return response
--> 257 self.raise_for_status(response)
    258 return response

File ~/miniconda3/envs/venv/lib/python3.10/site-packages/atlassian/confluence.py:3149, in Confluence.raise_for_status(self, response)
   3148     log.error(e)
-> 3149     response.raise_for_status()
   3150 else:

File ~/miniconda3/envs/venv/lib/python3.10/site-packages/requests/models.py:1021, in Response.raise_for_status(self)
   1020 if http_error_msg:
-> 1021     raise HTTPError(http_error_msg, response=self)

HTTPError: 404 Client Error: Not Found for url: https://company-site.atlassian.net/wiki/spaces/my-space/pages/123456/Confluence+Python+API/rest/api/content/123456

During handling of the above exception, another exception occurred:

ApiError                                  Traceback (most recent call last)
Cell In[130], line 11
      9 domain = get_domain_from_url(URL)
     10 page_id = get_page_id_from_url(URL)
---> 11 page_info = confluence.get_page_by_id(page_id)
     13 content = generate_content()
     14 update_confluence_page(confluence, page_id, content)

File ~/miniconda3/envs/venv/lib/python3.10/site-packages/atlassian/confluence.py:349, in Confluence.get_page_by_id(self, page_id, expand, status, version)
    346 except HTTPError as e:
    347     if e.response.status_code == 404:
    348         # Raise ApiError as the documented reason is ambiguous
--> 349         raise ApiError(
    350             "There is no content with the given id, "
    351             "or the calling user does not have permission to view the content",
    352             reason=e,
    353         )
    355     raise
    357 return response

ApiError: There is no content with the given id, or the calling user does not have permission to view the content
1

There are 1 answers

1
iking1211 On

In the docs the Confluence instance is created with the base-URL of the confluence server. The API library you are using then creates the URL for the resource you are using itself. So please change the URL to:

URL = 'https://company-site.atlassian.net'