-
Notifications
You must be signed in to change notification settings - Fork 527
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Confluence storage and document plugin (#2940)
* Confluence storage and document plugin initial commit * Removed some debug code and fixed typo * Fixed PR comments for confluence plugin * Fix pr comments for confluence plugin --------- Co-authored-by: Cino Jose <[email protected]>
- Loading branch information
Showing
11 changed files
with
254 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ h11 | |
httpx | ||
jinja2 | ||
jira==2.0.0 | ||
atlassian-python-api==3.32.0 | ||
joblib | ||
numpy | ||
oauth2client | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from ._version import __version__ # noqa |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.0.1" |
28 changes: 28 additions & 0 deletions
28
src/dispatch/plugins/dispatch_atlassian_confluence/config.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from pydantic import Field, SecretStr, HttpUrl | ||
|
||
from enum import Enum | ||
from dispatch.config import BaseConfigurationModel | ||
|
||
|
||
class HostingType(str, Enum): | ||
"""Type of Atlassian Confluence deployment.""" | ||
|
||
cloud = "cloud" | ||
server = "server" | ||
|
||
|
||
class ConfluenceConfigurationBase(BaseConfigurationModel): | ||
"""Atlassian Confluence configuration description.""" | ||
|
||
api_url: HttpUrl = Field( | ||
title="API URL", description="This URL is used for communication with API." | ||
) | ||
hosting_type: HostingType = Field( | ||
"cloud", title="Hosting Type", description="Defines the type of deployment." | ||
) | ||
username: str = Field( | ||
title="Username", description="Username to use to authenticate to Confluence API." | ||
) | ||
password: SecretStr = Field( | ||
title="Password", description="Password to use to authenticate to Confluence API." | ||
) |
1 change: 1 addition & 0 deletions
1
src/dispatch/plugins/dispatch_atlassian_confluence/docs/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from ._version import __version__ # noqa |
1 change: 1 addition & 0 deletions
1
src/dispatch/plugins/dispatch_atlassian_confluence/docs/_version.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = "0.0.1" |
52 changes: 52 additions & 0 deletions
52
src/dispatch/plugins/dispatch_atlassian_confluence/docs/plugin.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from dispatch.plugins.dispatch_atlassian_confluence import docs as confluence_doc_plugin | ||
from dispatch.plugins.bases import DocumentPlugin | ||
from dispatch.plugins.dispatch_atlassian_confluence.config import ConfluenceConfigurationBase | ||
from atlassian import Confluence | ||
from typing import List | ||
|
||
|
||
def replace_content(client: Confluence, document_id: str, replacements: List[str]) -> dict(): | ||
# read content based on document_id | ||
current_content = client.get_page_by_id( | ||
document_id, expand="body.storage", status=None, version=None | ||
) | ||
current_content_body = current_content["body"]["storage"]["value"] | ||
for k, v in replacements.items(): | ||
if v: | ||
current_content_body = current_content_body.replace(k, v) | ||
|
||
updated_content = client.update_page( | ||
page_id=document_id, | ||
title=current_content["title"], | ||
body=current_content_body, | ||
representation="storage", | ||
type="page", | ||
parent_id=None, | ||
minor_edit=False, | ||
full_width=False, | ||
) | ||
return updated_content | ||
|
||
|
||
class ConfluencePageDocPlugin(DocumentPlugin): | ||
title = "Confluence pages plugin - Document Management" | ||
slug = "confluence-docs-document" | ||
description = "Use Confluence to update the contents." | ||
version = confluence_doc_plugin.__version__ | ||
|
||
author = "Cino Jose" | ||
author_url = "https://github.com/Netflix/dispatch" | ||
|
||
def __init__(self): | ||
self.configuration_schema = ConfluenceConfigurationBase | ||
|
||
def update(self, document_id: str, **kwargs): | ||
"""Replaces text in document.""" | ||
kwargs = {"{{" + k + "}}": v for k, v in kwargs.items()} | ||
confluence_client = Confluence( | ||
url=self.configuration.api_url, | ||
username=self.configuration.username, | ||
password=self.configuration.password.get_secret_value(), | ||
cloud=self.configuration.hosting_type, | ||
) | ||
return replace_content(confluence_client, document_id, kwargs) |
139 changes: 139 additions & 0 deletions
139
src/dispatch/plugins/dispatch_atlassian_confluence/plugin.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
from dispatch.plugins import dispatch_atlassian_confluence as confluence_plugin | ||
from dispatch.plugins.bases import StoragePlugin | ||
from dispatch.plugins.dispatch_atlassian_confluence.config import ConfluenceConfigurationBase | ||
|
||
from pydantic import Field | ||
|
||
from atlassian import Confluence | ||
import requests | ||
from requests.auth import HTTPBasicAuth | ||
import logging | ||
from typing import List | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
# TODO : Use the common config from the root directory. | ||
class ConfluenceConfiguration(ConfluenceConfigurationBase): | ||
"""Confluence configuration description.""" | ||
|
||
template_id: str = Field( | ||
title="Incident template ID", description="This is the page id of the template." | ||
) | ||
root_id: str = Field( | ||
title="Default Space ID", description="Defines the default Confluence Space to use." | ||
) | ||
parent_id: str = Field( | ||
title="Parent ID for the pages", | ||
description="Define the page id of a parent page where all the incident documents can be kept.", | ||
) | ||
open_on_close: bool = Field( | ||
title="Open On Close", | ||
default=False, | ||
description="Controls the visibility of resources on incident close. If enabled Dispatch will make all resources visible to the entire workspace.", | ||
) | ||
read_only: bool = Field( | ||
title="Readonly", | ||
default=False, | ||
description="The incident document will be marked as readonly on incident close. Participants will still be able to interact with the document but any other viewers will not.", | ||
) | ||
|
||
|
||
class ConfluencePagePlugin(StoragePlugin): | ||
title = "Confluence Plugin - Store your incident details" | ||
slug = "confluence" | ||
description = "Confluence plugin to create incident documents" | ||
version = confluence_plugin.__version__ | ||
|
||
author = "Cino Jose" | ||
author_url = "https://github.com/Netflix/dispatch" | ||
|
||
def __init__(self): | ||
self.configuration_schema = ConfluenceConfiguration | ||
|
||
def create_file( | ||
self, drive_id: str, name: str, participants: List[str] = [], file_type: str = "folder" | ||
): | ||
"""Creates a new Home page for the incident documents..""" | ||
try: | ||
if file_type not in ["document", "folder"]: | ||
return None | ||
confluence_client = Confluence( | ||
url=self.configuration.api_url, | ||
username=self.configuration.username, | ||
password=self.configuration.password.get_secret_value(), | ||
cloud=self.configuration.hosting_type, | ||
) | ||
child_display_body = """<h3>Incident Documents:</h3><ac:structured-macro ac:name="children" | ||
ac:schema-version="2" data-layout="default" ac:local-id="ec0e8d6d-3215-4328-b1f8-e96b03ccefb9" | ||
ac:macro-id="10235d28b48543519d4e2b06ca230142"><ac:parameter ac:name="sort">modified</ac:parameter> | ||
<ac:parameter ac:name="reverse">true</ac:parameter></ac:structured-macro>""" | ||
page_details = confluence_client.create_page( | ||
drive_id, | ||
name, | ||
body=child_display_body, | ||
parent_id=self.configuration.parent_id, | ||
type="page", | ||
representation="storage", | ||
editor="v2", | ||
full_width=False, | ||
) | ||
return { | ||
"weblink": f"{self.configuration.api_url}wiki/spaces/{drive_id}/pages/{page_details['id']}/{name}", | ||
"id": page_details["id"], | ||
"name": name, | ||
"description": "", | ||
} | ||
except Exception as e: | ||
logger.error(f"Exception happened while creating page: {e}") | ||
|
||
def copy_file(self, folder_id: str, file_id: str, name: str): | ||
# TODO : This is the function that is responsible for making the incident documents. | ||
try: | ||
confluence_client = Confluence( | ||
url=self.configuration.api_url, | ||
username=self.configuration.username, | ||
password=self.configuration.password.get_secret_value(), | ||
cloud=self.configuration.hosting_type, | ||
) | ||
logger.info(f"Copy_file function with args {folder_id}, {file_id}, {name}") | ||
template_content = confluence_client.get_page_by_id( | ||
self.configuration.template_id, expand="body.storage", status=None, version=None | ||
) | ||
page_details = confluence_client.create_page( | ||
space=self.configuration.root_id, | ||
parent_id=folder_id, | ||
title=name, | ||
type="page", | ||
body=template_content["body"], | ||
representation="storage", | ||
editor="v2", | ||
full_width=False, | ||
) | ||
if self.configuration.parent_id: | ||
"""TODO: Find and fix why the page is not created under the parent_id, folder_id""" | ||
self.move_file_confluence(page_id_to_move=page_details["id"], parent_id=folder_id) | ||
return { | ||
"weblink": f"{self.configuration.api_url}wiki/spaces/{folder_id}/pages/{page_details['id']}/{name}", | ||
"id": page_details["id"], | ||
"name": name, | ||
} | ||
except Exception as e: | ||
logger.error(f"Exception happened while creating page: {e}") | ||
|
||
def move_file(self, new_folder_id: str, file_id: str, **kwargs): | ||
"""Moves a file from one place to another. Not used in the plugin, | ||
keeping the body as the interface is needed to avoid exceptions.""" | ||
return {} | ||
|
||
def move_file_confluence(self, page_id_to_move: str, parent_id: str): | ||
try: | ||
url = f"{self.configuration.api_url}wiki/rest/api/content/{page_id_to_move}/move/append/{parent_id}" | ||
auth = HTTPBasicAuth( | ||
self.configuration.username, self.configuration.password.get_secret_value() | ||
) | ||
headers = {"Accept": "application/json"} | ||
response = requests.request("PUT", url, headers=headers, auth=auth) | ||
return response | ||
except Exception as e: | ||
logger.error(f"Exception happened while moving page: {e}") |