From 3b43b4f99f157e8a958fd9e78b8d2f39b165aaa8 Mon Sep 17 00:00:00 2001 From: cpl-jonathan Date: Wed, 2 Jul 2025 16:37:11 +1200 Subject: [PATCH 1/4] Working search component. --- .../confluence_search_pages_component.py | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 components/ClearPoint Confluence/confluence_search_pages_component.py diff --git a/components/ClearPoint Confluence/confluence_search_pages_component.py b/components/ClearPoint Confluence/confluence_search_pages_component.py new file mode 100644 index 0000000..90115e1 --- /dev/null +++ b/components/ClearPoint Confluence/confluence_search_pages_component.py @@ -0,0 +1,129 @@ +from langflow.custom import Component +from langflow.io import Output, SecretStrInput, MessageTextInput,StrInput,BoolInput +from atlassian import Confluence +from typing import List +from langflow.schema import Data, DataFrame + + +class ConfluenceSearchPagesComponent(Component): + display_name = "Confluence Search Pages" + description = ( + "Returns the matching pages in Confluence based on the provided CQL search query." + ) + icon = "file-search" + name = "ConfluenceSearchPagesComponent" + + inputs = [ + StrInput( + name="CONFLUENCE_SERVER_URL", + display_name="Confluence Server URL", + required=True, + ), + StrInput( + name="CONFLUENCE_USERNAME", + display_name="Confluence Username", + required=True, + ), + SecretStrInput( + name="CONFLUENCE_API_KEY", + display_name="Confluence API Key", + required=True, + ), + MessageTextInput( + name="cql_query", + display_name="CQL query ", + info="The CQL query to use to search for pages in Confluence.", + tool_mode=True, + ), + MessageTextInput( + name="expand_properties", + display_name="Properties to Expand", + info="A comma-separated list of properties to expand on in the search result.", + tool_mode=True, + ) + ] + + outputs = [ + Output( + name="pages", + display_name="List of Pages", + method="search_pages", + info="Matching pages as a list of Data objects." + ), + Output( + display_name="DataFrame of Pages", + name="dataframe", + method="build_dataframe", + info="Matching pages in a DataFrame.", + ), + ] + + def search_pages(self) -> List[Data]: + # HACK: make copy of inputs to avoid race condition. not guareenteed to work. See https://github.com/langflow-ai/langflow/issues/8791 + cql_query = self.cql_query + expand_properties = self.expand_properties + + + # create the confluence client + confluence = Confluence( url=self.CONFLUENCE_SERVER_URL, + username=self.CONFLUENCE_USERNAME, + password=self.CONFLUENCE_API_KEY) + + if expand_properties.strip() == "": + expand_properties = None + else: + # split the properties by comma and strip whitespace + expand_properties_list = [prop.strip() for prop in expand_properties.split(",") if prop.strip()] + + # see https://github.com/atlassian-api/atlassian-python-api/blob/705b26f8674334d663847774e21a38d718cf0dd3/atlassian/confluence/__init__.py#L2748 + response = confluence.cql(cql_query,expand=expand_properties) + print(response) + + + # build list of pages + pages_list = [] + for page in response["results"]: + + page_data = { + "id": page["content"]["id"], + "title": page["content"]["title"], + "excerpt": page.get("excerpt", ""), + } + + if expand_properties: + for prop in expand_properties_list: + prop_nodes = prop.split(".") + # traverse the nested properties + value = page + for node in prop_nodes: + if isinstance(value, dict) and node in value: + value = value[node] + else: + value = None + break + + # if value is not None, add it to the page data + if value: + page_data[prop.replace(".","_")] = value # can't have dots in key names, value get's lost in langflow + + pages_list.append(Data(data=page_data)) + + self.status = pages_list + return pages_list + + + + def build_dataframe(self) -> DataFrame: + # trigger the saerch + issues = self.search_pages() + + # convert results to list of dict + rows = [] + for issue in issues: + rows.append(dict(issue.data)) + + # convert list of dict to DataFrame + df_result = DataFrame(rows) + + self.status = df_result + return df_result From 7db5eb4f5223cbe5d34e5e5ad78ad9f1efe6f79a Mon Sep 17 00:00:00 2001 From: cpl-jonathan Date: Thu, 3 Jul 2025 13:24:39 +1200 Subject: [PATCH 2/4] Added get confluence page component. --- .../confluence_get_page_component.py | 68 +++++++++++++++++++ .../confluence_search_pages_component.py | 3 +- 2 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 components/ClearPoint Confluence/confluence_get_page_component.py diff --git a/components/ClearPoint Confluence/confluence_get_page_component.py b/components/ClearPoint Confluence/confluence_get_page_component.py new file mode 100644 index 0000000..f2d0068 --- /dev/null +++ b/components/ClearPoint Confluence/confluence_get_page_component.py @@ -0,0 +1,68 @@ +from langflow.custom import Component +from langflow.io import Output, SecretStrInput, MessageTextInput,StrInput,BoolInput +from atlassian import Confluence +from typing import List +from langflow.schema import Data, DataFrame + + +class ConfluenceGetPageComponent(Component): + display_name = "Confluence Get Page" + description = ( + "Fetches the content of a Confluence page by its page ID." + ) + icon = "file" + name = "ConfluenceGetPageComponent" + + inputs = [ + StrInput( + name="CONFLUENCE_SERVER_URL", + display_name="Confluence Server URL", + required=True, + ), + StrInput( + name="CONFLUENCE_USERNAME", + display_name="Confluence Username", + required=True, + ), + SecretStrInput( + name="CONFLUENCE_API_KEY", + display_name="Confluence API Key", + required=True, + ), + MessageTextInput( + name="page_id", + display_name="Page ID", + info="The ID of the page to fetch from Confluence.", + tool_mode=True, + ) + ] + + outputs = [ + Output( + name="page_content", + display_name="Page", + method="get_page", + info="The page content as a Data object." + ) + ] + + def get_page(self) -> List[Data]: + # HACK: make copy of inputs to avoid race condition. not guareenteed to work. See https://github.com/langflow-ai/langflow/issues/8791 + page_id = self.page_id + + # create the confluence client + confluence = Confluence( url=self.CONFLUENCE_SERVER_URL, + username=self.CONFLUENCE_USERNAME, + password=self.CONFLUENCE_API_KEY) + + # Get the page by ID and expand the body and children + page = confluence.get_page_by_id(page_id=page_id, expand='body.storage') + + page_content = { + 'id': page['id'], + 'title': page['title'], + 'content': page['body']['storage']['value'], + } + + self.status = page_content + return Data(data=page_content) diff --git a/components/ClearPoint Confluence/confluence_search_pages_component.py b/components/ClearPoint Confluence/confluence_search_pages_component.py index 90115e1..b81f3a8 100644 --- a/components/ClearPoint Confluence/confluence_search_pages_component.py +++ b/components/ClearPoint Confluence/confluence_search_pages_component.py @@ -77,8 +77,7 @@ def search_pages(self) -> List[Data]: # see https://github.com/atlassian-api/atlassian-python-api/blob/705b26f8674334d663847774e21a38d718cf0dd3/atlassian/confluence/__init__.py#L2748 response = confluence.cql(cql_query,expand=expand_properties) - print(response) - + # build list of pages pages_list = [] From 95890c064f1f2ae7b12d476f20d28972092b9ee8 Mon Sep 17 00:00:00 2001 From: cpl-jonathan Date: Thu, 3 Jul 2025 13:42:23 +1200 Subject: [PATCH 3/4] Working confluence update page. --- .../confluence_update_page_component.py | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 components/ClearPoint Confluence/confluence_update_page_component.py diff --git a/components/ClearPoint Confluence/confluence_update_page_component.py b/components/ClearPoint Confluence/confluence_update_page_component.py new file mode 100644 index 0000000..1469a95 --- /dev/null +++ b/components/ClearPoint Confluence/confluence_update_page_component.py @@ -0,0 +1,81 @@ +from langflow.custom import Component +from langflow.io import Output, SecretStrInput, MessageTextInput,StrInput,MultilineInput +from atlassian import Confluence +from typing import List +from langflow.schema import Data, DataFrame + + +class ConfluenceUpdatePageComponent(Component): + display_name = "Confluence Update Page" + description = ( + "Updates the content of a Confluence page." + ) + icon = "file-pen-line" + name = "ConfluenceUpdatePageComponent" + + inputs = [ + StrInput( + name="CONFLUENCE_SERVER_URL", + display_name="Confluence Server URL", + required=True, + ), + StrInput( + name="CONFLUENCE_USERNAME", + display_name="Confluence Username", + required=True, + ), + SecretStrInput( + name="CONFLUENCE_API_KEY", + display_name="Confluence API Key", + required=True, + ), + MessageTextInput( + name="page_id", + display_name="Page ID", + info="The ID of the page to update in Confluence.", + tool_mode=True, + ), + MultilineInput( + name="content", + display_name="Page Content", + info="The content to update the Confluence page with.", + tool_mode=True, + ) + ] + + outputs = [ + Output( + name="results", + display_name="Results", + method="update_page", + info="The results of the Confluence update page content operation." + ) + ] + + def update_page(self) -> List[Data]: + # HACK: make copy of inputs to avoid race condition. not guareenteed to work. See https://github.com/langflow-ai/langflow/issues/8791 + page_id = self.page_id + content = self.content + + # create the confluence client + confluence = Confluence( url=self.CONFLUENCE_SERVER_URL, + username=self.CONFLUENCE_USERNAME, + password=self.CONFLUENCE_API_KEY) + + # page title is required for update so fetch current page title + page = confluence.get_page_by_id(page_id=page_id) + page_title= page['title'] + + # Update the page + updated_page = confluence.update_page( + page_id=page_id, + title=page_title, + body=content, + representation='storage' + ) + + # build a results payload + results = {"page_id": page_id, "status": "Content updated successfully"} + + self.status = results + return Data(data=results) From 76dfc12345a2e5873c24bf0986ac020b8c289696 Mon Sep 17 00:00:00 2001 From: cpl-jonathan Date: Thu, 3 Jul 2025 14:20:39 +1200 Subject: [PATCH 4/4] Tweaks to update page. --- .../ClearPoint Confluence/confluence_update_page_component.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ClearPoint Confluence/confluence_update_page_component.py b/components/ClearPoint Confluence/confluence_update_page_component.py index 1469a95..8746706 100644 --- a/components/ClearPoint Confluence/confluence_update_page_component.py +++ b/components/ClearPoint Confluence/confluence_update_page_component.py @@ -38,7 +38,7 @@ class ConfluenceUpdatePageComponent(Component): MultilineInput( name="content", display_name="Page Content", - info="The content to update the Confluence page with.", + info="The content to update the Confluence page with. Content must be formatted using Confluence's storage format.", tool_mode=True, ) ]