def test_confluence_attach_file_2(self):
        credentials = None

        try:
            with open(self.secret_file) as json_file:
                credentials = json.load(json_file)
        except Exception as err:
            self.fail("[{0}]: {1}".format(self.secret_file, err))

        confluence = Confluence(
            url=credentials["host"],
            username=credentials["username"],
            password=credentials["password"],
        )

        # individual configuration
        space = "SAN"
        title = "atlassian-python-rest-api-wrapper"

        # TODO: check if page are exists

        fd, filename = tempfile.mkstemp("w")
        os.write(fd, b"Hello World - Version 1")

        name = os.path.basename(tempfile.mktemp()) + ".txt"

        # upload a new file
        result = confluence.attach_file(
            filename,
            name,
            content_type="text/plain",
            title=title,
            space=space,
            comment="upload from unittest",
        )

        # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue("results" in result)
        self.assertFalse("statusCode" in result)

        # upload a new version of an existing file
        os.lseek(fd, 0, 0)
        os.write(fd, b"Hello Universe - Version 2")
        result = confluence.attach_file(
            filename,
            name,
            content_type="text/plain",
            title=title,
            space=space,
            comment="upload from unittest",
        )

        # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue("id" in result)
        self.assertFalse("statusCode" in result)

        os.close(fd)
        os.remove(filename)
Esempio n. 2
0
    def test_confluence_attach_file_2(self):
        credentials = None

        try:
            with open(self.secret_file) as json_file:
                credentials = json.load(json_file)
        except Exception as err:
            self.fail('[{0}]: {1}'.format(self.secret_file, err))

        confluence = Confluence(url=credentials['host'],
                                username=credentials['username'],
                                password=credentials['password'])

        # individual configuration
        space = 'SAN'
        title = 'atlassian-python-rest-api-wrapper'

        # TODO: check if page are exists

        fd, filename = tempfile.mkstemp('w')
        os.write(fd, b'Hello World - Version 1')

        name = os.path.basename(tempfile.mktemp()) + ".txt"

        # upload a new file
        result = confluence.attach_file(filename,
                                        name,
                                        content_type='text/plain',
                                        title=title,
                                        space=space,
                                        comment='upload from unittest')

        # attach_file() returns: {'results': [{'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue('results' in result)
        self.assertFalse('statusCode' in result)

        # upload a new version of an existing file
        os.lseek(fd, 0, 0)
        os.write(fd, b'Hello Universe - Version 2')
        result = confluence.attach_file(filename,
                                        name,
                                        content_type='text/plain',
                                        title=title,
                                        space=space,
                                        comment='upload from unittest')

        # attach_file() returns: {'id': 'att144005326', 'type': 'attachment', ...
        self.assertTrue('id' in result)
        self.assertFalse('statusCode' in result)

        os.close(fd)
        os.remove(filename)
Esempio n. 3
0
def run() -> None:
    """Main program loop"""

    args = parse_args()

    dm = DataModel(args.url, args.http_username, args.http_password, args.uid)
    dm.load()

    if dm.status != 200:
        print("{} Status code {}".format(str(datetime.datetime.now()),
                                         dm.status))
        return

    try:
        old_dm = pickle.load(open('cache.dat', 'rb'))
        if old_dm == dm:
            return
    except FileNotFoundError:
        print("First run")

    print("{} Graphing changes".format(str(datetime.datetime.now())))

    pickle.dump(dm, open('cache.dat', 'wb'))

    dot = graphviz.Digraph(comment='Double edge facts')

    # for obj in dm.objects:
    #     dot.node(obj, obj)

    for fact in dm.facts:
        name, s, d, di = fact
        if name == 'mentions':
            continue
        if not d:
            continue

        dot.node(s, s)
        dot.node(d, d)
        dot.edge(s, d, label=name)
        if di:
            dot.edge(s, d, label=name)

    dot.render('output/double', format='png', renderer='cairo')

    dot = graphviz.Digraph(comment='Single edge facts')

    for fact in dm.facts:

        name, s, d, di = fact

        if d:
            continue

        dot.node(name, label=name, shape='diamond')
        dot.node(s, s)

        dot.edge(name, s)

    dot.render('output/single', format='png', renderer='cairo')

    dot = graphviz.Digraph(comment='All Double edge facts')

    for obj in dm.objects:
        dot.node(obj, obj)

    for fact in dm.facts:
        name, s, d, di = fact
        if not d:
            continue

        dot.node(s, s)
        dot.node(d, d)
        dot.edge(s, d, label=name)
        if di and not s == d:
            dot.edge(s, d, label=name)

    dot.render('output/complete', format='png', renderer='cairo')

    if args.parent_id:
        os.environ.pop('https_proxy')
        os.environ.pop('http_proxy')
        confluence = Confluence(url=args.confluence_url,
                                username=args.confluence_user,
                                password=args.confluence_password)
        confluence.attach_file('output/double.cairo.png',
                               page_id=args.parent_id,
                               title='Double Edged Facts')
        confluence.attach_file('output/single.cairo.png',
                               page_id=args.parent_id,
                               title='Single Edged Facts')
        confluence.attach_file('output/complete.cairo.png',
                               page_id=args.parent_id,
                               title='Single Edged Facts')
Esempio n. 4
0
# coding=utf-8
import logging
from datetime import datetime

from atlassian import Confluence

confluence = Confluence(
    url="http://localhost:8090",
    username="******",
    password="******",
)

logging.basicConfig(level=logging.DEBUG)

filename = "test_file.txt"
with open(filename, "w") as f:
    f.write(str(datetime.utcnow()))

confluence.attach_file(filename, page_id="123456789")

link = """<p>
  <ac:link>
    <ri:attachment ri:filename="{}"/>
  </ac:link>
</p>""".format(filename)

confluence.append_page(123456789, "Page Title", link)
Esempio n. 5
0
def run() -> None:
    """Main program loop"""

    args = parse_args()

    dm = DataModel(args.url, args.http_username, args.http_password, args.uid,
                   args.cacert)
    dm.load()

    if dm.status != 200:
        print("{} Status code {}".format(str(datetime.datetime.now()),
                                         dm.status))
        return

    try:
        old_dm = pickle.load(open("cache.dat", "rb"))
        if old_dm == dm:
            return
    except FileNotFoundError:
        print("First run")

    print("{} Graphing changes".format(str(datetime.datetime.now())))

    pickle.dump(dm, open("cache.dat", "wb"))

    dot = graphviz.Digraph(comment="Double edge facts")

    # for obj in dm.objects:
    #     dot.node(obj, obj)

    for fact in dm.facts:
        name, s, d, di = fact
        if name == "mentions":
            continue
        if not d:
            continue

        dot.node(s, s)
        dot.node(d, d)
        if di:
            dot.edge(s, d, label=name, dir="both")
        else:
            dot.edge(s, d, label=name)

    if args.dump_source:
        with open(os.path.join(args.dump_source, "double.dot"), "w") as f:
            f.write(dot.source)

    dot.render("output/double", format="png", renderer="cairo")

    dot = graphviz.Digraph(comment="Single edge facts")

    for fact in dm.facts:

        name, s, d, di = fact

        if d:
            continue

        dot.node(name, label=name, shape="diamond")
        dot.node(s, s)

        dot.edge(s, name)

    if args.dump_source:
        with open(os.path.join(args.dump_source, "single.dot"), "w") as f:
            f.write(dot.source)

    dot.render("output/single", format="png", renderer="cairo")

    dot = graphviz.Digraph(comment="All Double edge facts")

    for obj in dm.objects:
        dot.node(obj, obj)

    for fact in dm.facts:
        name, s, d, di = fact
        if not d:
            continue

        dot.node(s, s)
        dot.node(d, d)
        if di:
            dot.edge(s, d, label=name, dir="both")
        else:
            dot.edge(s, d, label=name)

    if args.dump_source:
        with open(os.path.join(args.dump_source, "complete.dot"), "w") as f:
            f.write(dot.source)

    dot.render("output/complete", format="png", renderer="cairo")

    if args.parent_id:
        try:
            os.environ.pop("https_proxy")
        except KeyError:
            pass
        try:
            os.environ.pop("http_proxy")
        except KeyError:
            pass

        verify_ssl = False if args.cacert else True

        confluence = Confluence(
            url=args.confluence_url,
            username=args.confluence_user,
            password=args.confluence_password,
            verify_ssl=verify_ssl,
        )
        confluence.attach_file(
            "output/double.cairo.png",
            page_id=args.parent_id,
            title="Double Edged Facts",
        )
        confluence.attach_file(
            "output/single.cairo.png",
            page_id=args.parent_id,
            title="Single Edged Facts",
        )
        confluence.attach_file(
            "output/complete.cairo.png",
            page_id=args.parent_id,
            title="Single Edged Facts",
        )

        if args.dump_source:
            confluence.attach_file(
                os.path.join(args.dump_source, "complete.dot"),
                page_id=args.parent_id,
                title="complete source",
            )
            confluence.attach_file(
                os.path.join(args.dump_source, "double.dot"),
                page_id=args.parent_id,
                title="double source",
            )
            confluence.attach_file(
                os.path.join(args.dump_source, "single.dot"),
                page_id=args.parent_id,
                title="single source",
            )
Esempio n. 6
0
class ConfluencePublisher():
    def __init__(
        self,
        url,
        username,
        apiToken,
        pageTitlePrefix,
        markdownDir,
        dbPath,
        space,
        parentPageId,
        forceUpdate=False,
        forceDelete=False,
        skipUpdate=False,
    ):
        self.api = Confluence(url=url, username=username, password=apiToken)
        self.pageTitlePrefix = pageTitlePrefix
        self.markdownDir = markdownDir
        self.kv = KeyValue(dbPath)
        self.space = space
        self.parentPageId = parentPageId
        self.forceUpdate = forceUpdate
        self.forceDelete = forceDelete
        self.skipUpdate = skipUpdate
        self.confluenceRenderer = ConfluenceRenderer(url)
        self.metadataPlugin = MetadataPlugin()
        self.renderer = mistune.create_markdown(
            renderer=self.confluenceRenderer,
            plugins=[
                'strikethrough', 'footnotes', 'table', 'url',
                self.metadataPlugin.plugin_metadata
            ])

        # Hack to allow metadata plugin to work (See mistune/block_parser.py)
        self.renderer.block.rules.remove('thematic_break')

    def __getFileContent(self, filepath):
        file = open(filepath, mode='r')
        content = file.read()
        file.close()
        return content

    def __updatePage(self, space, parentId, filepath, autoindex=False):

        if autoindex:
            markdown = ''
        else:
            markdown = self.__getFileContent(filepath)

        metadata = self.kv.load(filepath)

        currentTitle = metadata['title']
        currentHash = metadata['sha256']
        hash = self.kv.sha256(markdown)

        # --- Render (BEGIN)
        self.metadataPlugin.stack['title'] = None

        if autoindex:
            body = self.confluenceRenderer.generate_autoindex()
        else:
            body = self.renderer(markdown)

        if self.metadataPlugin.stack['title'] is None:
            if autoindex:
                title = 'Folder ' + os.path.basename(os.path.dirname(filepath))
            else:
                title = os.path.basename(filepath)
        else:
            title = self.metadataPlugin.stack['title']

        title = self.pageTitlePrefix + title
        # >>> Removed: + " [" + self.kv.sha256(filepath)[-6:] + "]"
        # --- Render (END)

        if currentTitle and currentTitle != title:
            print('REN => Title: ' + title)
            pageId = self.api.get_page_id(space, currentTitle)
            self.api.update_page(pageId, title, body)

        if currentHash != hash or self.forceUpdate:
            if autoindex:
                print('IDX => Title: ' + title)
            else:
                print('UPD => Title: ' + title)

            if self.api.update_or_create(parent_id=parentId,
                                         title=title,
                                         body=body,
                                         representation='storage'):
                id = self.api.get_page_id(space, title)
                self.kv.save(filepath, {
                    'id': id,
                    'title': title,
                    'sha256': hash
                })
                return id
            else:
                return None
        else:
            print('SKP => Title: ' + title)
            return self.api.get_page_id(space, title)

    def __deleteAttachment(self, filepath):
        metadata = self.kv.load(filepath)
        filename = os.path.basename(filepath)
        if metadata['id']:
            try:
                print('DEL Att. => Title: ' + filename)
                # https://confluence.atlassian.com/confkb/confluence-rest-api-lacks-delete-method-for-attachments-715361922.html
                # self.api.delete_attachment_by_id(metadata['id'], 1)
                self.api.remove_content(metadata['id'])
            except (HTTPError, ApiError):
                pass

    def __updateAttachment(self, space, pageId, filepath):
        filename = os.path.basename(filepath)

        print('UPD Att. => Title: ' + filename)
        results = self.api.attach_file(filepath,
                                       name=filename,
                                       page_id=pageId,
                                       space=space)
        id = results['id'] if 'id' in results else results['results'][0]['id']
        self.kv.save(filepath, {'id': id, 'title': filename, 'sha256': None})
        return id

    def __publishRecursive(self, space, parentId, path):
        # File: _index.md
        indexParentId = parentId
        indexPath = path + os.sep + '_index.md'
        if os.path.isfile(indexPath):
            # Use local _index.md file
            indexParentId = self.__updatePage(space, parentId, indexPath)
        else:
            # Autoindex simulate _index.md in Confluence if missing locally
            # Except for (root) parentPageId because missing in markdownDir!
            if parentId != self.parentPageId:
                indexParentId = self.__updatePage(space, parentId, indexPath,
                                                  True)

        # Directories: */
        for f in os.scandir(path):
            if f.is_dir():
                self.__publishRecursive(space, indexParentId, f.path)

        # Files: *.* (Except _index.md)
        for f in os.scandir(path):
            if f.is_file():
                if f.path.endswith(".md"):
                    if not f.path.endswith(os.sep + '_index.md'):
                        self.__updatePage(space, indexParentId, f.path)
                else:
                    self.__deleteAttachment(f.path)
                    self.__updateAttachment(space, indexParentId, f.path)

    def delete(self):
        for filepath in sorted(self.kv.keys()):
            metadata = self.kv.load(filepath)

            # Page has Sub-pages (Childs)?
            indexWithChilds = False
            if filepath.endswith('_index.md'):
                childs = 0
                if os.path.isdir(os.path.dirname(filepath)):
                    for f in os.scandir(os.path.dirname(filepath)):
                        if f.path.endswith(".md") and \
                           not f.path.endswith('_index.md'):
                            childs = childs + 1
                indexWithChilds = childs > 0

            if self.forceDelete \
               or (not os.path.isfile(filepath) and not indexWithChilds):
                print('DEL => Id: ' + metadata['id'] + ', Title: ' +
                      metadata['title'])
                if filepath.endswith(".md"):
                    try:
                        if self.api.get_page_by_id(metadata['id']):
                            self.api.remove_page(metadata['id'])
                    except HTTPError as ex:
                        code = ex.response.status_code
                        if code != 404:
                            print("DEL Pag. (Error):" + str(code))
                        else:
                            pass
                else:
                    self.__deleteAttachment(filepath)

                self.kv.remove(filepath)

    def publish(self):
        self.__publishRecursive(self.space, self.parentPageId,
                                self.markdownDir)
Esempio n. 7
0
class ConfluencePublisher():

    def __init__(
            self, url, username, api_token,
            page_title_prefix, markdown_dir, db_path, space, parent_pageid,
            force_update=False, force_delete=False, skip_update=False,
            verbose=False):

        self.api = Confluence(url=url, username=username, password=api_token)
        self.page_title_prefix = page_title_prefix
        self.markdown_dir = markdown_dir
        self.kv = KeyValue(db_path)
        self.space = space
        self.parent_pageid = parent_pageid
        self.force_update = force_update
        self.force_delete = force_delete
        self.skip_update = skip_update
        self.confluence_renderer = ConfluenceRenderer(verbose)
        self.renderer = mistune.create_markdown(
            renderer=self.confluence_renderer,
            plugins=[
                plugin_front_matter,
                DirectiveInclude(),
                HugoRefLinkPlugin(self.markdown_dir),
                'strikethrough',
                'footnotes',
                'table',
                'url',
                Admonition(),
                plugin_html_comment,
            ]
        )

    def __update_page(self, space, parentid, filepath, autoindex=False):

        metadata = self.kv.load(filepath)

        current_title = metadata['title']
        current_hash = metadata['sha256']
        sha_hash = get_file_sha256(filepath)

        # --- Render (BEGIN)

        body = ''
        state = {'front_matter': {}}

        if autoindex:
            body = generate_autoindex()
            state['front_matter']['title'] = \
                os.path.basename(os.path.dirname(filepath)).title()
        else:
            if filepath.endswith("_index.md"):
                body = generate_autoindex()
            body += self.renderer.read(filepath, state)

        title = '{}{}'.format(self.page_title_prefix,
                              state['front_matter']['title'])

        # --- Render (END)

        if current_title and current_title != title:
            print('REN => Title: ' + title)
            confluence_page_id = self.api.get_page_id(space, current_title)
            self.api.update_page(confluence_page_id, title, body)

        if current_hash != sha_hash or self.force_update:
            if autoindex:
                print('IDX => Title: ' + title)
            else:
                print('UPD => Title: ' + title)

            if self.api.update_or_create(
                parent_id=parentid,
                title=title,
                body=body,
                representation='storage'
            ):
                confluence_page_id = self.api.get_page_id(space, title)
                self.kv.save(filepath,
                             {'id': confluence_page_id, 'title': title, 'sha256': sha_hash})
                return confluence_page_id

            return None
        else:
            print('SKP => Title: ' + title)
            return self.api.get_page_id(space, title)

    def __delete_attachment(self, filepath):
        metadata = self.kv.load(filepath)
        filename = os.path.basename(filepath)
        if metadata['id']:
            try:
                print('DEL Att. => Title: ' + filename)
                # https://confluence.atlassian.com/confkb/confluence-rest-api-lacks-delete-method-for-attachments-715361922.html
                # self.api.delete_attachment_by_id(metadata['id'], 1)
                self.api.remove_content(metadata['id'])
            except (HTTPError, ApiError):
                pass

    def __update_attachment(self, space, pageid, filepath):
        filename = os.path.basename(filepath)

        print('UPD Att. => Title: ' + filename)
        results = self.api.attach_file(filepath,
                                       name=filename,
                                       page_id=pageid,
                                       space=space)
        confluence_page_id = results['id'] if 'id' in results else results['results'][0]['id']
        self.kv.save(filepath, {'id': confluence_page_id,
                                'title': filename, 'sha256': None})
        return confluence_page_id

    def __publish_recursive(self, space, parentid, path, root=False):
        # File: _index.md
        index_parentid = parentid
        index_path = path + os.sep + '_index.md'
        if not root:
            if os.path.isfile(index_path):
                # Use local _index.md file
                index_parentid = self.__update_page(
                    space, parentid, index_path)
            else:
                # Autoindex simulate _index.md in Confluence if missing locally
                index_parentid = self.__update_page(
                    space, parentid, index_path, True)

        # Directories: */
        for f in os.scandir(path):
            if f.is_dir():
                self.__publish_recursive(space, index_parentid, f.path)

        # Files: *.* (Except _index.md)
        for f in os.scandir(path):
            if f.is_file():
                if f.path.endswith(".md"):
                    if not f.path.endswith(os.sep + '_index.md'):
                        self.__update_page(space, index_parentid, f.path)
                else:
                    self.__delete_attachment(f.path)
                    self.__update_attachment(space, index_parentid, f.path)

    def delete(self):
        for filepath in sorted(self.kv.keys()):
            metadata = self.kv.load(filepath)

            # Page has Sub-pages (Childs)?
            index_with_childs = False
            if filepath.endswith('_index.md'):
                childs = 0
                if os.path.isdir(os.path.dirname(filepath)):
                    for f in os.scandir(os.path.dirname(filepath)):
                        if f.path.endswith(".md") and \
                           not f.path.endswith('_index.md'):
                            childs = childs + 1
                index_with_childs = childs > 0

            if self.force_delete \
               or (not os.path.isfile(filepath) and not index_with_childs):
                print('DEL => Id: '
                      + metadata['id'] + ', Title: ' + metadata['title'])
                if filepath.endswith(".md"):
                    try:
                        if self.api.get_page_by_id(metadata['id']):
                            self.api.remove_page(metadata['id'])
                    except HTTPError as ex:
                        code = ex.response.status_code
                        if code != 404:
                            print("DEL Pag. (Error):" + str(code))
                        else:
                            pass
                else:
                    self.__delete_attachment(filepath)

                self.kv.remove(filepath)

    def publish(self):
        self.__publish_recursive(
            self.space, self.parent_pageid, self.markdown_dir, root=True)
Esempio n. 8
0
class ConfluenceRenderer(MarkdownRenderer):
    """Render report to comfluence via the atlassian API
    """
    def __init__(self, space="", url="", username="", token="", parent=None):
        self.parent = parent
        self.space = space

        # if not all([space, url, username, token]):
        #     raise ConfluenceAPIError("Must assigne a space, url, unername and token")

        # Define atlassian api connection
        self.conn = Confluence(url=url,
                               username=username,
                               password=token,
                               cloud=True)

    def set_parent(self, parent):
        """Set id of parent to write conluence page to
        """
        self.parent = parent

    def set_space(self, space):
        """Set id of space to write conluence page to
        """
        self.space = space

    def save(self, report):
        """Save report to Confluence
        """
        report, plots_to_upload = self._process_images(report)
        content = self._convert_to_jira_wiki(report)
        response = self.conn.create_page(self.space,
                                         report.title,
                                         content,
                                         type="page",
                                         representation="wiki",
                                         parent_id=self.parent)
        page_id = response['id']

        # upload images
        for filepath, name in plots_to_upload:
            self._upload_image(filepath, name, page_id)

    def _process_images(self, report):
        """Prepare report and images to be uploaded
        """
        out_report = deepcopy(report)
        out_report.components = []
        images_to_upload = []
        for component in report.get_components():
            if isinstance(component, Plot):
                # prepare upload
                filepath = component.get_path()
                name = os.path.basename(filepath)
                images_to_upload.append((filepath, name))

                # Change Plot to match
                new_plot = deepcopy(component)
                new_plot.set_path(name)
                out_report.add_component(new_plot)
            else:
                out_report.add_component(component)
        return out_report, images_to_upload

    def _upload_image(self, filepath, name, page_id):
        """Upload Iamges to Confluence
        """
        self.conn.attach_file(filepath,
                              space=self.space,
                              page_id=page_id,
                              name=name)

    def _test_connection(self):
        """Test initialized confluence connection
        """

    def _test_space_page_exists(self):
        """Test initialized confluence connection
        """

    def _convert_to_jira_wiki(self, report):
        """Convert Markdown content to Atlassian wiki markup
        """
        content = self._render_markdown(report.get_components())
        content = pypandoc.convert_text(content, "jira", "markdown")
        return content