示例#1
0
    def put(self, key, value, data):
        restUrl = self.url + API_REST_BIND_PATH + '/' + key + '/' + value
        try:
            rsp = self.session.put(restUrl, json=data)
        except requests.exceptions.Timeout:
            raise ConfluenceTimeoutError(self.url)
        except requests.exceptions.SSLError as ex:
            raise ConfluenceSslError(self.url, ex)
        except requests.exceptions.ConnectionError as ex:
            raise ConfluenceBadServerUrlError(self.url, ex)
        if rsp.status_code == 401:
            raise ConfluenceAuthenticationFailedUrlError
        if rsp.status_code == 403:
            raise ConfluencePermissionError("REST PUT")
        if rsp.status_code == 407:
            raise ConfluenceProxyPermissionError
        if not rsp.ok:
            errdata = self._format_error(rsp, key)
            if self.verbosity > 0:
                errdata += "\n"
                errdata += json.dumps(data, indent=2)
            raise ConfluenceBadApiError(errdata)
        if not rsp.text:
            raise ConfluenceSeraphAuthenticationFailedUrlError

        try:
            rsp.encoding = self.CONFLUENCE_DEFAULT_ENCODING
            json_data = json.loads(rsp.text)
        except ValueError:
            raise ConfluenceBadServerUrlError(
                self.url, "REST reply did not provide valid JSON data.")

        return json_data
示例#2
0
    def get(self, key, params):
        restUrl = self.url + API_REST_BIND_PATH + '/' + key
        try:
            rsp = self.session.get(restUrl, params=params)
        except requests.exceptions.Timeout:
            raise ConfluenceTimeoutError(self.url)
        except requests.exceptions.SSLError as ex:
            raise ConfluenceSslError(self.url, ex)
        except requests.exceptions.ConnectionError as ex:
            raise ConfluenceBadServerUrlError(self.url, ex)
        if rsp.status_code == 401:
            raise ConfluenceAuthenticationFailedUrlError
        if rsp.status_code == 403:
            raise ConfluencePermissionError("REST GET")
        if rsp.status_code == 407:
            raise ConfluenceProxyPermissionError
        if not rsp.ok:
            raise ConfluenceBadApiError(self._format_error(rsp, key))
        if not rsp.text:
            raise ConfluenceSeraphAuthenticationFailedUrlError

        try:
            rsp.encoding = self.CONFLUENCE_DEFAULT_ENCODING
            json_data = json.loads(rsp.text)
        except ValueError:
            raise ConfluenceBadServerUrlError(
                self.url, "REST reply did not provide valid JSON data.")

        return json_data
示例#3
0
    def delete(self, key, value):
        rest_url = self.url + self.bind_path + '/' + key + '/' + str(value)

        rsp = self.session.delete(rest_url, timeout=self.timeout)
        self._handle_common_request(rsp)

        if not rsp.ok:
            errdata = self._format_error(rsp, key)
            raise ConfluenceBadApiError(rsp.status_code, errdata)
示例#4
0
    def post(self, key, data, files=None):
        restUrl = self.url + API_REST_BIND_PATH + '/' + key
        try:
            headers = dict(self.session.headers)

            # Atlassian's documenation indicates to the security token check
            # when publishing attachments [1][2]. If adding files, set a
            # 'nocheck' value to the token.
            #
            # [1]: https://developer.atlassian.com/cloud/confluence/rest/#api-content-id-child-attachment-post
            # [2]: https://developer.atlassian.com/server/jira/platform/form-token-handling/
            if files:
                headers['X-Atlassian-Token'] = 'nocheck'

            rsp = self.session.post(restUrl,
                                    json=data,
                                    files=files,
                                    headers=headers)
        except requests.exceptions.Timeout:
            raise ConfluenceTimeoutError(self.url)
        except requests.exceptions.SSLError as ex:
            raise ConfluenceSslError(self.url, ex)
        except requests.exceptions.ConnectionError as ex:
            raise ConfluenceBadServerUrlError(self.url, ex)
        if rsp.status_code == 401:
            raise ConfluenceAuthenticationFailedUrlError
        if rsp.status_code == 403:
            raise ConfluencePermissionError("REST POST")
        if rsp.status_code == 407:
            raise ConfluenceProxyPermissionError
        if not rsp.ok:
            errdata = self._format_error(rsp, key)
            if self.verbosity > 0:
                errdata += "\n"
                errdata += json.dumps(data, indent=2)
            raise ConfluenceBadApiError(errdata)
        if not rsp.text:
            raise ConfluenceSeraphAuthenticationFailedUrlError

        try:
            rsp.encoding = self.CONFLUENCE_DEFAULT_ENCODING
            json_data = json.loads(rsp.text)
        except ValueError:
            raise ConfluenceBadServerUrlError(
                self.url, "REST reply did not provide valid JSON data.")

        return json_data
示例#5
0
 def delete(self, key, value):
     restUrl = self.url + API_REST_BIND_PATH + '/' + key + '/' + value
     try:
         rsp = self.session.delete(restUrl)
     except requests.exceptions.Timeout:
         raise ConfluenceTimeoutError(self.url)
     except requests.exceptions.SSLError as ex:
         raise ConfluenceSslError(self.url, ex)
     except requests.exceptions.ConnectionError as ex:
         raise ConfluenceBadServerUrlError(self.url, ex)
     if rsp.status_code == 401:
         raise ConfluenceAuthenticationFailedUrlError
     if rsp.status_code == 403:
         raise ConfluencePermissionError("REST DELETE")
     if rsp.status_code == 407:
         raise ConfluenceProxyPermissionError
     if not rsp.ok:
         raise ConfluenceBadApiError(self._format_error(rsp, key))
示例#6
0
    def get(self, key, params):
        rest_url = self.url + self.bind_path + '/' + key

        rsp = self.session.get(rest_url, params=params, timeout=self.timeout)
        self._handle_common_request(rsp)

        if not rsp.ok:
            errdata = self._format_error(rsp, key)
            raise ConfluenceBadApiError(rsp.status_code, errdata)
        if not rsp.text:
            raise ConfluenceSeraphAuthenticationFailedUrlError

        try:
            rsp.encoding = self.CONFLUENCE_DEFAULT_ENCODING
            json_data = json.loads(rsp.text)
        except ValueError:
            raise ConfluenceBadServerUrlError(self.url,
                "REST reply did not provide valid JSON data.")

        return json_data
示例#7
0
    def put(self, key, value, data):
        rest_url = self.url + self.bind_path + '/' + key + '/' + str(value)

        rsp = self.session.put(rest_url, json=data, timeout=self.timeout)
        self._handle_common_request(rsp)

        if not rsp.ok:
            errdata = self._format_error(rsp, key)
            if self.verbosity > 0:
                errdata += "\n"
                errdata += json.dumps(data, indent=2)
            raise ConfluenceBadApiError(rsp.status_code, errdata)
        if not rsp.text:
            raise ConfluenceSeraphAuthenticationFailedUrlError

        try:
            rsp.encoding = self.CONFLUENCE_DEFAULT_ENCODING
            json_data = json.loads(rsp.text)
        except ValueError:
            raise ConfluenceBadServerUrlError(self.url,
                "REST reply did not provide valid JSON data.")

        return json_data
示例#8
0
    def storePage(self, page_name, data, parent_id=None):
        uploaded_page_id = None

        if self.config.confluence_adv_trace_data:
            ConfluenceLogger.trace('data', data['content'])

        if self.dryrun:
            _, page = self.getPage(page_name, 'version,ancestors')

            if not page:
                self._dryrun('adding new page ' + page_name)
                return None
            else:
                misc = ''
                if parent_id and 'ancestors' in page:
                    if not any(a['id'] == parent_id for a in page['ancestors']):
                        if parent_id in self._name_cache:
                            misc += '[new parent page {} ({})]'.format(
                                self._name_cache[parent_id], parent_id)
                        else:
                            misc += '[new parent page]'

                self._dryrun('updating existing page', page['id'], misc)
                return page['id']

        can_labels = 'labels' not in self.config.confluence_adv_restricted
        expand = 'version'
        if can_labels and self.append_labels:
            expand += ',metadata.labels'

        _, page = self.getPage(page_name, expand=expand)

        if self.onlynew and page:
            self._onlynew('skipping existing page', page['id'])
            return page['id']

        try:
            # new page
            if not page:
                newPage = {
                    'type': 'page',
                    'title': page_name,
                    'body': {
                        'storage': {
                            'representation': 'storage',
                            'value': data['content'],
                        }
                    },
                    'space': {
                        'key': self.space_name
                    },
                }

                if can_labels:
                    self._populate_labels(newPage, data['labels'])

                if parent_id:
                    newPage['ancestors'] = [{'id': parent_id}]

                try:
                    rsp = self.rest_client.post('content', newPage)

                    if 'id' not in rsp:
                        api_err = ('Confluence reports a successful page ' +
                                  'creation; however, provided no ' +
                                  'identifier.\n\n')
                        try:
                            api_err += 'DATA: {}'.format(json.dumps(
                                rsp, indent=2))
                        except TypeError:
                            api_err += 'DATA: <not-or-invalid-json>'
                        raise ConfluenceBadApiError(api_err)

                    uploaded_page_id = rsp['id']
                except ConfluenceBadApiError as ex:
                    # Check if Confluence reports that the new page request
                    # fails, indicating it already exists. This is usually
                    # (outside of possible permission use cases) that the page
                    # name's casing does not match. In this case, attempt to
                    # re-check for the page in a case-insensitive fashion. If
                    # found, attempt to perform an update request instead.
                    if str(ex).find('title already exists') == -1:
                        raise

                    ConfluenceLogger.verbose('title already exists warning '
                        'for page {}'.format(page_name))

                    _, page = self.getPageCaseInsensitive(page_name)
                    if not page:
                        raise

                    if self.onlynew:
                        self._onlynew('skipping existing page', page['id'])
                        return page['id']

            # update existing page
            if page:
                last_version = int(page['version']['number'])
                updatePage = {
                    'id': page['id'],
                    'type': 'page',
                    'title': page_name,
                    'body': {
                        'storage': {
                            'representation': 'storage',
                            'value': data['content'],
                        }
                    },
                    'space': {
                        'key': self.space_name
                    },
                    'version': {
                        'number': last_version + 1
                    },
                }

                if can_labels:
                    labels = list(data['labels'])
                    if self.append_labels:
                        labels.extend([lbl.get('name')
                            for lbl in page.get('metadata', {}).get(
                                'labels', {}).get('results', {})
                        ])

                    self._populate_labels(updatePage, labels)

                if not self.notify:
                    updatePage['version']['minorEdit'] = True

                if parent_id:
                    updatePage['ancestors'] = [{'id': parent_id}]

                try:
                    self.rest_client.put('content', page['id'], updatePage)
                except ConfluenceBadApiError as ex:
                    if str(ex).find('unreconciled') != -1:
                        raise ConfluenceUnreconciledPageError(
                            page_name, page['id'], self.server_url, ex)

                    # Confluence Cloud may (rarely) fail to complete a
                    # content request with an OptimisticLockException/
                    # StaleObjectStateException exception. It is suspected
                    # that this is just an instance timing/processing issue.
                    # If this is observed, wait a moment and retry the
                    # content request. If it happens again, the put request
                    # will fail as it normally would.
                    if str(ex).find('OptimisticLockException') == -1:
                        raise
                    ConfluenceLogger.warn(
                        'remote page updated failed; retrying...')
                    time.sleep(1)
                    self.rest_client.put('content', page['id'], updatePage)

                uploaded_page_id = page['id']
        except ConfluencePermissionError:
            raise ConfluencePermissionError(
                """Publish user does not have permission to add page """
                """content to the configured space."""
            )

        if not self.watch:
            self.rest_client.delete('user/watch/content', uploaded_page_id)

        return uploaded_page_id
    def store_page(self, page_name, data, parent_id=None):
        """
        request to store page information to a confluence instance

        Performs a request which will attempt to store the provided page
        information and publish it to either a new page with the provided page
        name or update an existing page with a matching page name. Pages will be
        published at the root of a Confluence space unless a provided parent
        page identifier is provided.

        Args:
            page_name: the page title to use on the updated page
            data: the page data to apply
            parent_id (optional): the id of the ancestor to use
        """
        uploaded_page_id = None

        if self.config.confluence_adv_trace_data:
            logger.trace('data', data['content'])

        if self.dryrun:
            _, page = self.get_page(page_name, 'version,ancestors')

            if not page:
                self._dryrun('adding new page ' + page_name)
                return None
            else:
                misc = ''
                if parent_id and 'ancestors' in page:
                    if not any(a['id'] == parent_id
                               for a in page['ancestors']):
                        if parent_id in self._name_cache:
                            misc += '[new parent page {} ({})]'.format(
                                self._name_cache[parent_id], parent_id)
                        else:
                            misc += '[new parent page]'

                self._dryrun('updating existing page', page['id'], misc)
                return page['id']

        expand = 'version'
        if self.append_labels:
            expand += ',metadata.labels'

        _, page = self.get_page(page_name, expand=expand)

        if self.onlynew and page:
            self._onlynew('skipping existing page', page['id'])
            return page['id']

        try:
            # new page
            if not page:
                new_page = self._build_page(page_name, data)
                self._populate_labels(new_page, data['labels'])

                if parent_id:
                    new_page['ancestors'] = [{'id': parent_id}]

                try:
                    rsp = self.rest_client.post('content', new_page)

                    if 'id' not in rsp:
                        api_err = ('Confluence reports a successful page ' +
                                   'creation; however, provided no ' +
                                   'identifier.\n\n')
                        try:
                            api_err += 'DATA: {}'.format(
                                json.dumps(rsp, indent=2))
                        except TypeError:
                            api_err += 'DATA: <not-or-invalid-json>'
                        raise ConfluenceBadApiError(-1, api_err)

                    uploaded_page_id = rsp['id']

                    # if we have labels and this is a non-cloud instance,
                    # initial labels need to be applied in their own request
                    labels = new_page['metadata']['labels']
                    if not self.cloud and labels:
                        url = 'content/{}/label'.format(uploaded_page_id)
                        self.rest_client.post(url, labels)

                except ConfluenceBadApiError as ex:
                    # Check if Confluence reports that the new page request
                    # fails, indicating it already exists. This is usually
                    # (outside of possible permission use cases) that the page
                    # name's casing does not match. In this case, attempt to
                    # re-check for the page in a case-insensitive fashion. If
                    # found, attempt to perform an update request instead.
                    if str(ex).find('title already exists') == -1:
                        raise

                    logger.verbose('title already exists warning '
                                   'for page {}'.format(page_name))

                    _, page = self.get_page_case_insensitive(page_name)
                    if not page:
                        raise

                    if self.onlynew:
                        self._onlynew('skipping existing page', page['id'])
                        return page['id']

            # update existing page
            if page:
                self._update_page(page, page_name, data, parent_id=parent_id)
                uploaded_page_id = page['id']

        except ConfluencePermissionError:
            raise ConfluencePermissionError(
                """Publish user does not have permission to add page """
                """content to the configured space.""")

        if not self.watch:
            self.rest_client.delete('user/watch/content', uploaded_page_id)

        return uploaded_page_id