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