From 348b36bf8593f3fcbed84fa4540bc0977af0b2cd Mon Sep 17 00:00:00 2001 From: Sean Hatfield <seanhatfield5@gmail.com> Date: Thu, 25 Apr 2024 17:53:38 -0700 Subject: [PATCH] [FEAT] Confluence data connector (#1181) * WIP Confluence data connector backend * confluence data connector complete * confluence citations * fix citation for confluence * Patch confulence integration * fix Citation Icon for confluence --------- Co-authored-by: timothycarambat <rambat1010@gmail.com> --- collector/extensions/index.js | 157 +++++++++++------ collector/package.json | 2 +- .../utils/extensions/Confluence/index.js | 110 ++++++++++++ .../DataConnectorOption/media/confluence.jpeg | Bin 0 -> 5659 bytes .../DataConnectorOption/media/index.js | 2 + .../Connectors/Confluence/index.jsx | 164 ++++++++++++++++++ .../MangeWorkspace/DataConnectors/index.jsx | 7 + .../ChatHistory/Citation/index.jsx | 39 +++-- .../src/media/dataConnectors/confluence.png | Bin 0 -> 9582 bytes frontend/src/models/dataConnector.js | 23 +++ server/endpoints/extensions/index.js | 22 +++ 11 files changed, 458 insertions(+), 68 deletions(-) create mode 100644 collector/utils/extensions/Confluence/index.js create mode 100644 frontend/src/components/DataConnectorOption/media/confluence.jpeg create mode 100644 frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Confluence/index.jsx create mode 100644 frontend/src/media/dataConnectors/confluence.png diff --git a/collector/extensions/index.js b/collector/extensions/index.js index 0e91d1731..6a3f3393e 100644 --- a/collector/extensions/index.js +++ b/collector/extensions/index.js @@ -4,69 +4,112 @@ const { reqBody } = require("../utils/http"); function extensions(app) { if (!app) return; - app.post("/ext/github-repo", [verifyPayloadIntegrity], async function (request, response) { - try { - const loadGithubRepo = require("../utils/extensions/GithubRepo"); - const { success, reason, data } = await loadGithubRepo(reqBody(request)); - response.status(200).json({ - success, - reason, - data - }); - } catch (e) { - console.error(e); - response.status(200).json({ - success: false, - reason: e.message || "A processing error occurred.", - data: {}, - }); + app.post( + "/ext/github-repo", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const loadGithubRepo = require("../utils/extensions/GithubRepo"); + const { success, reason, data } = await loadGithubRepo( + reqBody(request) + ); + response.status(200).json({ + success, + reason, + data, + }); + } catch (e) { + console.error(e); + response.status(200).json({ + success: false, + reason: e.message || "A processing error occurred.", + data: {}, + }); + } + return; } - return; - }); + ); // gets all branches for a specific repo - app.post("/ext/github-repo/branches", [verifyPayloadIntegrity], async function (request, response) { - try { - const GithubRepoLoader = require("../utils/extensions/GithubRepo/RepoLoader"); - const allBranches = await (new GithubRepoLoader(reqBody(request))).getRepoBranches() - response.status(200).json({ - success: true, - reason: null, - data: { - branches: allBranches - } - }); - } catch (e) { - console.error(e); - response.status(400).json({ - success: false, - reason: e.message, - data: { - branches: [] - } - }); + app.post( + "/ext/github-repo/branches", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const GithubRepoLoader = require("../utils/extensions/GithubRepo/RepoLoader"); + const allBranches = await new GithubRepoLoader( + reqBody(request) + ).getRepoBranches(); + response.status(200).json({ + success: true, + reason: null, + data: { + branches: allBranches, + }, + }); + } catch (e) { + console.error(e); + response.status(400).json({ + success: false, + reason: e.message, + data: { + branches: [], + }, + }); + } + return; } - return; - }); + ); - app.post("/ext/youtube-transcript", [verifyPayloadIntegrity], async function (request, response) { - try { - const loadYouTubeTranscript = require("../utils/extensions/YoutubeTranscript"); - const { success, reason, data } = await loadYouTubeTranscript(reqBody(request)); - response.status(200).json({ success, reason, data }); - } catch (e) { - console.error(e); - response.status(400).json({ - success: false, - reason: e.message, - data: { - title: null, - author: null - } - }); + app.post( + "/ext/youtube-transcript", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const loadYouTubeTranscript = require("../utils/extensions/YoutubeTranscript"); + const { success, reason, data } = await loadYouTubeTranscript( + reqBody(request) + ); + response.status(200).json({ success, reason, data }); + } catch (e) { + console.error(e); + response.status(400).json({ + success: false, + reason: e.message, + data: { + title: null, + author: null, + }, + }); + } + return; } - return; - }); + ); + + app.post( + "/ext/confluence", + [verifyPayloadIntegrity], + async function (request, response) { + try { + const loadConfluence = require("../utils/extensions/Confluence"); + const { success, reason, data } = await loadConfluence( + reqBody(request) + ); + response.status(200).json({ success, reason, data }); + } catch (e) { + console.error(e); + response.status(400).json({ + success: false, + reason: e.message, + data: { + title: null, + author: null, + }, + }); + } + return; + } + ); } module.exports = extensions; diff --git a/collector/package.json b/collector/package.json index 4a5a99fff..5d2e5f0f5 100644 --- a/collector/package.json +++ b/collector/package.json @@ -49,4 +49,4 @@ "nodemon": "^2.0.22", "prettier": "^2.4.1" } -} \ No newline at end of file +} diff --git a/collector/utils/extensions/Confluence/index.js b/collector/utils/extensions/Confluence/index.js new file mode 100644 index 000000000..1ea642e1a --- /dev/null +++ b/collector/utils/extensions/Confluence/index.js @@ -0,0 +1,110 @@ +const fs = require("fs"); +const path = require("path"); +const { default: slugify } = require("slugify"); +const { v4 } = require("uuid"); +const { writeToServerDocuments } = require("../../files"); +const { tokenizeString } = require("../../tokenizer"); +const { + ConfluencePagesLoader, +} = require("langchain/document_loaders/web/confluence"); + +function validSpaceUrl(spaceUrl = "") { + const UrlPattern = require("url-pattern"); + const pattern = new UrlPattern( + "https\\://(:subdomain).atlassian.net/wiki/spaces/(:spaceKey)*" + ); + const match = pattern.match(spaceUrl); + if (!match) return { valid: false, result: null }; + return { valid: true, result: match }; +} + +async function loadConfluence({ pageUrl, username, accessToken }) { + if (!pageUrl || !username || !accessToken) { + return { + success: false, + reason: + "You need either a username and access token, or a personal access token (PAT), to use the Confluence connector.", + }; + } + + const validSpace = validSpaceUrl(pageUrl); + if (!validSpace.result) { + return { + success: false, + reason: + "Confluence space URL is not in the expected format of https://domain.atlassian.net/wiki/space/~SPACEID/*", + }; + } + + const { subdomain, spaceKey } = validSpace.result; + console.log(`-- Working Confluence ${subdomain}.atlassian.net --`); + const loader = new ConfluencePagesLoader({ + baseUrl: `https://${subdomain}.atlassian.net/wiki`, + spaceKey, + username, + accessToken, + }); + + const { docs, error } = await loader + .load() + .then((docs) => { + return { docs, error: null }; + }) + .catch((e) => { + return { + docs: [], + error: e.message?.split("Error:")?.[1] || e.message, + }; + }); + + if (!docs.length || !!error) { + return { + success: false, + reason: error ?? "No pages found for that Confluence space.", + }; + } + const outFolder = slugify( + `${subdomain}-confluence-${v4().slice(0, 4)}` + ).toLowerCase(); + const outFolderPath = path.resolve( + __dirname, + `../../../../server/storage/documents/${outFolder}` + ); + fs.mkdirSync(outFolderPath); + + docs.forEach((doc) => { + const data = { + id: v4(), + url: doc.metadata.url + ".page", + title: doc.metadata.title || doc.metadata.source, + docAuthor: subdomain, + description: doc.metadata.title, + docSource: `${subdomain} Confluence`, + chunkSource: `confluence://${doc.metadata.url}`, + published: new Date().toLocaleString(), + wordCount: doc.pageContent.split(" ").length, + pageContent: doc.pageContent, + token_count_estimate: tokenizeString(doc.pageContent).length, + }; + + console.log( + `[Confluence Loader]: Saving ${doc.metadata.title} to ${outFolder}` + ); + writeToServerDocuments( + data, + `${slugify(doc.metadata.title)}-${data.id}`, + outFolderPath + ); + }); + + return { + success: true, + reason: null, + data: { + spaceKey, + destination: outFolder, + }, + }; +} + +module.exports = loadConfluence; diff --git a/frontend/src/components/DataConnectorOption/media/confluence.jpeg b/frontend/src/components/DataConnectorOption/media/confluence.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..7559663a68ac1dfdc3f5a64f4de891d3d449272c GIT binary patch literal 5659 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 DdbL6T literal 0 HcmV?d00001 diff --git a/frontend/src/components/DataConnectorOption/media/index.js b/frontend/src/components/DataConnectorOption/media/index.js index 543bed5f7..ac8105975 100644 --- a/frontend/src/components/DataConnectorOption/media/index.js +++ b/frontend/src/components/DataConnectorOption/media/index.js @@ -1,9 +1,11 @@ import Github from "./github.svg"; import YouTube from "./youtube.svg"; +import Confluence from "./confluence.jpeg"; const ConnectorImages = { github: Github, youtube: YouTube, + confluence: Confluence, }; export default ConnectorImages; diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Confluence/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Confluence/index.jsx new file mode 100644 index 000000000..52ca7e63d --- /dev/null +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/Connectors/Confluence/index.jsx @@ -0,0 +1,164 @@ +import { useState } from "react"; +import System from "@/models/system"; +import showToast from "@/utils/toast"; +import { Warning } from "@phosphor-icons/react"; +import { Tooltip } from "react-tooltip"; + +export default function ConfluenceOptions() { + const [loading, setLoading] = useState(false); + + const handleSubmit = async (e) => { + e.preventDefault(); + const form = new FormData(e.target); + + try { + setLoading(true); + showToast( + "Fetching all pages for Confluence space - this may take a while.", + "info", + { + clear: true, + autoClose: false, + } + ); + const { data, error } = await System.dataConnectors.confluence.collect({ + pageUrl: form.get("pageUrl"), + username: form.get("username"), + accessToken: form.get("accessToken"), + }); + + if (!!error) { + showToast(error, "error", { clear: true }); + setLoading(false); + return; + } + + showToast( + `Pages collected from Confluence space ${data.spaceKey}. Output folder is ${data.destination}.`, + "success", + { clear: true } + ); + e.target.reset(); + setLoading(false); + } catch (e) { + console.error(e); + showToast(e.message, "error", { clear: true }); + setLoading(false); + } + }; + + return ( + <div className="flex w-full"> + <div className="flex flex-col w-full px-1 md:pb-6 pb-16"> + <form className="w-full" onSubmit={handleSubmit}> + <div className="w-full flex flex-col py-2"> + <div className="w-full flex flex-col gap-4"> + <div className="flex flex-col pr-10"> + <div className="flex flex-col gap-y-1 mb-4"> + <label className="text-white text-sm font-bold flex gap-x-2 items-center"> + <p className="font-bold text-white">Confluence Page URL</p> + </label> + <p className="text-xs font-normal text-white/50"> + URL of a page in the Confluence space. + </p> + </div> + <input + type="url" + name="pageUrl" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5" + placeholder="https://example.atlassian.net/wiki/spaces/~7120208c08555d52224113949698b933a3bb56/pages/851969/Test+anythingLLM+page" + required={true} + autoComplete="off" + spellCheck={false} + /> + </div> + <div className="flex flex-col pr-10"> + <div className="flex flex-col gap-y-1 mb-4"> + <label className="text-white text-sm font-bold"> + Confluence Username + </label> + <p className="text-xs font-normal text-white/50"> + Your Confluence username. + </p> + </div> + <input + type="email" + name="username" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5" + placeholder="jdoe@example.com" + required={true} + autoComplete="off" + spellCheck={false} + /> + </div> + <div className="flex flex-col pr-10"> + <div className="flex flex-col gap-y-1 mb-4"> + <label className="text-white text-sm font-bold flex gap-x-2 items-center"> + <p className="font-bold text-white"> + Confluence Access Token + </p> + <Warning + size={14} + className="ml-1 text-orange-500 cursor-pointer" + data-tooltip-id="access-token-tooltip" + data-tooltip-place="right" + /> + <Tooltip + delayHide={300} + id="access-token-tooltip" + className="max-w-xs" + clickable={true} + > + <p className="text-sm"> + You need to provide an access token for authentication. + You can generate an access token{" "} + <a + href="https://id.atlassian.com/manage-profile/security/api-tokens" + target="_blank" + rel="noopener noreferrer" + className="underline" + onClick={(e) => e.stopPropagation()} + > + here + </a> + . + </p> + </Tooltip> + </label> + <p className="text-xs font-normal text-white/50"> + Access token for authentication. + </p> + </div> + <input + type="password" + name="accessToken" + className="bg-zinc-900 text-white placeholder:text-white/20 text-sm rounded-lg focus:border-white block w-full p-2.5" + placeholder="abcd1234" + required={true} + autoComplete="off" + spellCheck={false} + /> + </div> + </div> + </div> + + <div className="flex flex-col gap-y-2 w-full pr-10"> + <button + type="submit" + disabled={loading} + className="mt-2 w-full justify-center border border-slate-200 px-4 py-2 rounded-lg text-[#222628] text-sm font-bold items-center flex gap-x-2 bg-slate-200 hover:bg-slate-300 hover:text-slate-800 disabled:bg-slate-300 disabled:cursor-not-allowed" + > + {loading ? "Collecting pages..." : "Submit"} + </button> + {loading && ( + <p className="text-xs text-white/50"> + Once complete, all pages will be available for embedding into + workspaces. + </p> + )} + </div> + </form> + </div> + </div> + ); +} diff --git a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx index 419fc1fc9..69d30e281 100644 --- a/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx +++ b/frontend/src/components/Modals/MangeWorkspace/DataConnectors/index.jsx @@ -2,6 +2,7 @@ import ConnectorImages from "@/components/DataConnectorOption/media"; import { MagnifyingGlass } from "@phosphor-icons/react"; import GithubOptions from "./Connectors/Github"; import YoutubeOptions from "./Connectors/Youtube"; +import ConfluenceOptions from "./Connectors/Confluence"; import { useState } from "react"; import ConnectorOption from "./ConnectorOption"; @@ -20,6 +21,12 @@ export const DATA_CONNECTORS = { "Import the transcription of an entire YouTube video from a link.", options: <YoutubeOptions />, }, + confluence: { + name: "Confluence", + image: ConnectorImages.confluence, + description: "Import an entire Confluence page in a single click.", + options: <ConfluenceOptions />, + }, }; export default function DataConnectors() { diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx index 1dfeaaaf3..7105901d3 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/Citation/index.jsx @@ -1,4 +1,4 @@ -import { memo, useState } from "react"; +import React, { memo, useState } from "react"; import { v4 } from "uuid"; import { decode as HTMLDecode } from "he"; import truncate from "truncate"; @@ -14,6 +14,7 @@ import { X, YoutubeLogo, } from "@phosphor-icons/react"; +import ConfluenceLogo from "@/media/dataConnectors/confluence.png"; import { Tooltip } from "react-tooltip"; import { toPercentString } from "@/utils/numbers"; @@ -202,13 +203,6 @@ function CitationDetailModal({ source, onClose }) { ); } -const ICONS = { - file: FileText, - link: Link, - youtube: YoutubeLogo, - github: GithubLogo, -}; - // Show the correct title and/or display text for citations // which contain valid outbound links that can be clicked by the // user when viewing a citation. Optionally allows various icons @@ -221,10 +215,17 @@ function parseChunkSource({ title = "", chunks = [] }) { icon: "file", }; - if (!chunks.length || !chunks[0].chunkSource.startsWith("link://")) + if ( + !chunks.length || + (!chunks[0].chunkSource.startsWith("link://") && + !chunks[0].chunkSource.startsWith("confluence://")) + ) return nullResponse; try { - const url = new URL(chunks[0].chunkSource.split("link://")[1]); + const url = new URL( + chunks[0].chunkSource.split("link://")[1] || + chunks[0].chunkSource.split("confluence://")[1] + ); let text = url.host + url.pathname; let icon = "link"; @@ -238,6 +239,11 @@ function parseChunkSource({ title = "", chunks = [] }) { icon = "github"; } + if (url.host.includes("atlassian.net")) { + text = title; + icon = "confluence"; + } + return { isUrl: true, href: url.toString(), @@ -247,3 +253,16 @@ function parseChunkSource({ title = "", chunks = [] }) { } catch {} return nullResponse; } + +// Patch to render Confluence icon as a element like we do with Phosphor +const ConfluenceIcon = ({ ...props }) => ( + <img src={ConfluenceLogo} {...props} /> +); + +const ICONS = { + file: FileText, + link: Link, + youtube: YoutubeLogo, + github: GithubLogo, + confluence: ConfluenceIcon, +}; diff --git a/frontend/src/media/dataConnectors/confluence.png b/frontend/src/media/dataConnectors/confluence.png new file mode 100644 index 0000000000000000000000000000000000000000..27a5da07bd1402f48dc9cc39d26225505dd85242 GIT binary patch literal 9582 zcmXXs2UrtL(-Z?3Iud$US`ZO5s3_8VO-MjW=tZg0JE04arUD{}bVx1~L81auL<I$u z8W0eq_onokKfd38&)(eZl-u3e*<@y(OExvuVFvSosi>%!^>j56R8-WLDYpm%EroK+ zq2&%070r>UAyP}{p&_^1#0AeqF5h(y&m~UJMNXepcJ~E{&njQQ2DjfPr{5l@?>2|$ z$_0=43mywxf%_B|r_b62uVv1MTb!N?Tz;Dr-3y)z5U(YOF9qL#crVeTN6%Rf(U|u$ zyG&m2rpRw_dM)w<AJf?kgRDnrEJ(E06Xz@js8Ri_uH%rvLy8Jd;10L%294z?4XT$K z`Gdy1kKT5S+jkY>z0Bpa!tK9BkN!zxGex0u`7Beqe8D?h57+3dhj{`vx&7980{-v@ zY|>Z^G1!iBKV0Q{xWfH#g~xB5H}s4*aGN3qvYVwr^;1}s2{1ZMP~OZRe##s8m%`%m zUE}dxqu~4zWRT?`t>qAd-58JG8ehmEuirXfz$Ts55WUR^z1<YA|2nV#24C<V#W?33 zXXqUl7@dCe25bmn$n-YDe1V&SVTWLsrSlFG{DE8iL4O3o_8HJ4V8<!`pe@0$gG(`| z4CtTdZGSR4{5o$x!R#_E5cG#XWJfIKSRnY1V8}Lq=>DbHQ_2#+cB9NLGaMd^0>OVp zV#pU`PtV(pF}ch!IsFz4`3trmV{%*O4c-?H-xG}`3r8MZj5%SnA7^wJ=kQn%4BZwA z-4Vf@QnZ*KtO<qfh(w=Ia3;rJLSeh2QHNp)WG2T6W~T`@&kc&rg~N7*!*|8w&zKy> znO&zvA`h6ICYhZlSzKpD!uLcXC~cp@WpVz^>N?GSe^w-N|6<JPrMNQ|m*1k12N$D{ zFFhiQC!DdmOtHDmT<}_<uvlHEM57MGFh^`|(`;UU#G(!*lgX5qu-%`b*jWs7D3M5J zf3SEFbMzlFEA^Pn@nG&!%!$OKGY<DT4)=M91hQ1hKNX5WB;v`JVo$_l|E-AAdqq6% zj4}j;C5Abch&z)^B1<HkQOqcjcqX0pPem&A9~6%}{RjWoDDnvu3RyJjn4(KjGz>qz zKsgL&0SIF=s&hsqi{#BpbMO%PziZqvkW6mAMkXITVo~%fxU@^5!n-J-jhX`R|77gw z$mC4|nWFGbbBZ%EmuEj#=pTJHoxDo^bD2!u``06ri5mZV{y!xB@3~AS?~^ylBW9HD zVIi3yP9|>!lgaO&kjd&~a{K=aIgrU-6tsylkL3T6$V4)^lKgKv$CSJKKVtSjI724) z!2au!$z$Y<f5UJ5>z@1rG5^5i3E3c@GOxos+Z6t1GI5~v{u242)vg^NnnXn<Dz2xg zh76%z`}l^sO_hqO?cMrsOkW}E$`s270nHrKuJ;*J!L-s|mD@w{Ic;TjS2{M`tF+#+ z{5XC3_Qx??W}@dc!kSgT8l*;*=mQ=ZR(k6-{&s!twEK3XqWObG=RHjH6ruW63nnB& z$H&Bw>+zR}Fh+Avmi^m&(83uto3onqfT&hgDGm(|ysGw{Vfw>;S(2cB5fb=WuPl3+ zXjPIB;7By}5s7HPG>?}d3R5In4q4-}eV$uZD#@0rEe|>JZ$Gh@`BYOQ1Kj4+Em{kr z|Giv1yZXZXd+tg?_lj#+(wDzo`;V~23Pn*$5m6_{3L;~=%eZJ69<iV%VHSuMBu%rp z_U2xRjDvFhQ{77|&d`q%{!L_B0xi_+(o^G#i(NbmC9Cq{W6uWkM5el4*PZhYp8bZ` zFY#-zxo#H{^0}#&rOX`iOso6CgK>1IBhDOI=GX9&fK<VJ-rrT+eE`*~GS{%#rBgGn zQ#pB~fzpP-`^_20`^DK-jm^~B#H$$@#y0wup*+z08j#et<KK-TBmLqKyTX;?u+tXP zUJ;ijyO)5}J!ZO3A92|Er7%Fx98rNUbX1WZ!iYIE$rv4P{&q(d4I`4kFrh^qp0q33 z5Zo0#xKa6=)!+EUUi~j+MOJ#>J7<mEY%D)xTF=zNREa()8=}o##YQ2y@gC;jxjBj- zhr@?S3=7l?FcwSww~t<LD4Y4Or7b>MFgGQb2OA;ug!UxYUWQNV{bgd`+hei%l~z5Q zUM+0`$x-LYyQ!4iXtr<Dpa+DR0AdJn*gVtwpN;KHGY>Ofi5g0qx_GY9<=kv+jgYi$ z5`M4N+B#a~ryUX!k~1C_=6h3jR2>9R`3MW!>8N0y1g(646^J7B0Wg&r0(Ire6>4hX zdYZVKM}n(I>|7{sKNLe>ASxxl(P>>859|*ezsdMr(0m#B6?v7-k)KcTd7c_jRSaxx zG4#3cN`I)vEgo1iR{>v-HOfn?i(~8`<yhDHU)Sl3F9X^#-~HkgyYgd1{;t|oL4gmZ z`I`;uCuc?u4+n?iYT@D1*x|P%W#l~+NBfApWnX?{fLE89J1%Ka*c?Iy6xTvU<YBVP z8iQ&_*t-A@5PJ<3xhB(n<L;<Z_@jq9QU+N<)+jwgJ=FZ%+}y&#T-m(G^+usqn5!Y` z=3UhWXxqq*mZ={{O*G2q3;J70rW>pk&~pSDu-PCM3p?->ZWc4@2e2PhJoT%AbGla^ za?C6)(#s(xpP9u}0aOk6_JM(c_By{^osogt(^&O+BRsSLYG;~&!{JhD{L*J4hb=H6 za%Kqz6Ny650ZwxicxY&7xV+=@5dK!<l@IOZnRm!Z0L{wk6X{ZbgSwIL<nVO*cyimV z4p7LqME$@LzzG1GVGMxObP5gosTJ75WqYH@1oWAVoJOkQN~?2obMs~iR$SXRKBV<t zYsJqX+lpe5?10ty*!bVB+e19)Mu`Glvo%zLh#_@;J{mRgVSOmrq6h!morj?B4!-Z< zp|g{@zo%>(n$d!=K{?|T9u&E&0~z=qQH2EPt!WXt`IfclS5HkrQjCyjLs3*JEp42} zCumxA@MK3R^lI59<nE5Cs9D?BuV3$@NId&BdN#jG%WjkaYoi3PzThJ;euXz9LorjO zdO(C&OjP=G3P*s-CD_m?)bQ&234EuChc6s;6V>KyX*oKS0WDemIR4OCe;et5LZb%< z2TNpHtI*_V4a;p}PZA(zmavIM)0fnjC8GEx8+}k2V#~kol(FzUfD`{z|CQF9P zbI~*8bXeg!=@JtEMgvrQW+<9?rjS-$7HoKv8Db6!AUka`l@voqlw07tRMCaf!wEkW z=5|~@{GB%p8peWM(AD=n0<G)s^fc;KrNsm`Jn*y9w$#=Ve*)3Z%rAT_Wb{lSJrQty z`qVYgGp!!-LM@d>!2a4fsxhm5-pQcn^m|c1H+x)rqxZ>yEvLie)7IYY51UPrVrvC- z%naOPLI`<O8XwRKFIQ3$AdSAU6X5a)grG4I$S<gx3|t0*0ZGpqMLwO|`gU9L+~Ldh z^KJUyQL9Oa>5^&qE<fPIiFBjR(iy)bsP%k)7~)<n*2t#-T!r>E_&=GxX6(5q`{8Lx z45^RWkcAy(t+ujik_>F-5TZE;4?j7g&%T0|2TzWLc~MD24UC0HybjNXkNfB*PBK&9 zpd|KG>d!b4w0<}F&skx@IMadmB`Y$rX8C5Oq!E*RK5oTBlD(Wk^0HjnG)}#A33rsT zY=K~cWVDy_^?iBFEtlL7pmwyj%9K?L2l2&&IeB^6h3?9BFoug88FtU5Ld~N$%1XdR zjZBqM2Wp;|%+6k+aD<{_aE%I^*e+4s{WW0)f?TsAr!=T&n=HNz1<BP`{h+LG4ypuX z?$WoHwxNWODknS7-Xvev9l2|i4VD7-LfoKzhN__4YbuxB_0?aWJz1&Df52<0ioFf& zEvEzBVCjrEFO)3NUrgl@43LWnMp`-$7l7PE5aNcwq($$IZ2YWym@oV2g}ykTYvzD( zMoQyUEo@+#hkpCuELw&IK$bF!DXwLgK{xbV=^e*X9>8Jb$gL+ex6a@Hal(}<i9489 zS>?wKSU2FE32&Pe{)b^17wWkjYX-s;-57w+lG3$kK4Bt}wlqompKKt>HVGSDKTKZo zlbx2@m-iFy*F(JIi+JA#07$AoL9m=5QwucBtuc_%#F5kliH8$~#cwh94Tb*sqhdVX zAQECeFr_Rdh4<S2Tmkmvy`^apSFG<v<)0}DL}$`NzM}BxjOq{_QIMD+2*)PvPA)M@ zkSmmpbhbU2IZ!b<pK5$BL}DQkB=07%!PKlgL08HIgCr?ti8W|6iO3G50IYCi<PTxg zH8tAOK4Mg#4=&R~5TCsKg7<-;qP11W<6E!vsY_$AFIUzz%G==yi2N7eB1b>n+_<*U z-fCDSVLBL+q3%|e{?L$=ng-R{FkX+XPiiIHQVoW!92bD0Gu3PuBt97S`(-;$3SqdX zWaK2STpQbq+&Q~}O2==3Og|YDBT}G($-Bp5_6deWEqO>9P;<Qm`~rIo$d2g)@yt`j zCIRgGt<M;;!jPwT?vpj10**w+0cO7Hu}`<6r+p!jRbBDbbPeEd(?c(B-8(a_AI_m> zVn;1msi<cG94JkT*;A@SEc9}NLEjL+R!a`pxel*tn}bzPn0N0TygvM0=Iu2z>C&as z^e2dGs8TE*SwQSBZ|7G1Qw6;PsMBvbBl9atzy{tT5~VJ}{CA>@&{`j&w;ZVahaTbD zpEhcoTaOJ;nIJ{qZ2HfCh2X&ub`<jT{NG|^o1d=H=lJ@RgvP_;f)Q?e$}tmrK$%MH z`yldXx)iJ;1cU7F9g{+U?iev^%s<BG*bqM_?<^>Y%nu@2<4u_zh)Fes{$MI<+(T+` zDOJ`O^=ErCv;*E)Pa=&ePDn<<or;$48k+gZhWX68-e`sP8pi)aDpcf4s0Hf_>_v*7 zOZz1aeLLlM5e6oK_EJ@_bpAFsn_AaRv;JdY!4>G!aNO+H?D4Mg?hocvfS=Tudpom~ zMjp9Tt<+DNqRC7HCK7cZ&+ZxI!<65BTB?@VD#2#_GH6ylbpG@e&79<!rIwU=#{FIu zToU#q2^vAo`5f$)D)ObH<g?S#U_E{bSFAB%QeK?Lho~Orih=4r#?f$UK@=dbR;F={ z7Rta-dDvyVZ5!@-4Mccmot#uU<5j4Z7ktj%f>s^<-Ilf@tn_7{su}3PIYE>yi~T_X zQPiRzomvD>=#>J0+YfW0?-7RZJ3%vIR@3j6*oOtuH&OwPz>(;&FdF06+~%V9MG*D- zP=Qo(i4>n<aeC%^M}+=rUIbrTA8tuY_x|>SU!;sGg>|TimL{b0i2plYNV>sBWcSwt zba~^GPUPJ?9@~-R61^vW?XP2oHjUHSP)|<aW!si{VCWH5do-@NyWc$~bG~6v37yc$ zhm7W^p5bj{L#J-w8t38!JZE$ss2%VKwQY?Q#Lo>pDLBxMFnOUrk2Gk-tG3+rxcwj& zpHtex>2S$X`0?>@n>FgBTw$sDRZX2iLzrz$!btdy=xHg{<m$wW0F$QH^Nsi@?x1`o zdxqU6nW|W!gbzx=bzv4IK?H1qQs-GG1hw-<@uE?J_>8~0`tYtwqEU;zJMxc;$F)hM zvpsub=!~obdl#O|G8$TWR{4HKt&Sjims;bFjQA!Pep>Lvi>2sz*yQ}<60}wTOk}W; zZx*hW;KCl_%<kH0h|N{gg7lM^QlV#VUN?ahIf&FP{%0U>ak(7DV7f>6jtJy*c7{c5 zo<>Q#4}S!8=Ds<77y-q0wMs#>>5b+VLUhoi=W(u&EplB^1$ZrR^Vcnx^J&H}!{x-j zz@kHAlAxGC*iIGfcfiPxp@XvErE8?wj^Ct`0wOOI_e<lP9VvTD?oTqs`o2_Zb~0xj zL%8<JQh@U#)px6q?itNu!gL@67A;>vfIs<ZE6Y<d>gH{CCw;@c2Q%!7vk5ecb<nqb z?d&UIg%NRjMU=)Fzu-gNyMoueiqN}ZXwW@%a<1kGbX`RYBHDD@?&`hTL5)ELp`u2< z=7MN>43);4r~Kz;UGk&cw8u2c17OzyDyX(JNXPA`U!o3)aYwQ*B_hV-4#Cx7C}3p! z#TGO83|>n%a#51WD3dJ`x&5pG+}Z|fBAkyfuQYB}2`BK%J?x~GG3|cwyv~Aet1>kO z@NEZYa%>0f>1W{-W-ap^U>c9?v!8Nb{$|X7H~LWOA&bcUhL`iP$@+~)$RNdj(!my5 z^uj&8!`KGwI>^R4vXROu3R(NY&?DKtF}8Z*#p#%NDsG0d`s|lPqgWNr%<&iK56d?! z)|uCEj!!)R5C)ImIh~of8)flT6UowWO>3(J+%)-nZyzmj$K#snl?F-8t(vhg7O(Jv zqM2rt3lxab>)}4BSy`~7;tn*&wt6I2gt>m37`b#V-YOS!%e-f}K$!k&0BQ1t<T<SC z^VZ19p9NJ4vhbDHVCvG$#15x%<A2`h!oAiFp)ss&p6E;yEH44KM&Iz@Uejl%F&X|f z@VN8HNDgwpVY~)R4ZMA2_t|S97#3fpZyn!;vehV&+RJl;Dpk4HfMcpMo(>p1rOoS- ztGt94Ghv7z!bbERTkX&wnb)^1c=+`<*6W+rPo;n!Sq%?<#{_w?$6pYUrCrPQ$DpiF z=#iI_DdLv#rBAEPE&X!R^(%2)9fekWjp(|itK2AyqO01Fp<(#N7aiRjL0Md}_|9te z+_aoe-Qw@|>P3-G+0PBKwXbaoKD8nBi>0t8a0Ug8yHt2&c3iC^yt$#rqf>`md_Js{ zdLyTMy;5mFB1NTaPH4H>zlqu4Dt*QZ;eGa{6cfu>t1uaN0q=L4EN=|zkR*RuOP<a; z)mYTNV(>ftI<xw4)~<=MiT=z>Ho{%qUQ6ztS#3^Ngq&HLmI7{%H{}^v#jhHO$~G`V zvWOOKlE(H64^1-#uC!e;s6wIV8$!9Jk21yaN;g^?>YHjv;@|4{U+WbwAueH`o=b85 z2^sUy&#O_KPhcR|6qbH?`)e}sqB1%^3kHkCy1PSfH-#zv4bQWBszE2gH~SM7N-`2Z zoR6mBxf&Q0xGNytyZK$V$3SR?BO#uYo7t1;Oe_}etWf`(8+N-9dTb?{LC^Kv3^5nb zsJyE0hsIYeS4z?Men#Xi9%K{et#m9<GFfAnYuB&9X}ldh?A-jL%wD11|KLRA{~r9g zl9{inhk;GeyheB!X?+T;|306!bG^~|-Se_?w`TpS3FT%|ZF`7CpoSY^)2l#mxU3lv zYYh|NW{_ph3QT0?6ezZSJF=5GcYfCNTXR`eQl@;{uk|eEgxMbZgzrS}3g6O)?=CAU zrWe$z>*`e)bZncHFiY&BQW$N&9m2ei?)MeK?|W%|<5|oa=#w<iekK}{IvsQ0FI3hF zBe4azUlDkw6~*zs%jIl>r?#ocr3Z#-M!6QNcg9trM>-HOJ`aG*F9v7*Sp@=5hwj~V zT_Yhx_0Gh;I0vV(hg)usyF9{}GQU1Lr0@F@1^4v)%6Q<{j0id$L<u1Zw-;H*%k09y zw2iiM!M9FbDSzmu=({R~az|gh^2KYZJqX^TLR96BxI0P_YxkU@RI~Tgm;;SlU#z&z z0dhG%UMUB<=N;@lX&dL?R&r?~|BH#Kq3PK4Q&t;CMkzEQkX{?<fcX9KQ**2E-Jtgb z>1c2wiyl)B&T+N(BCB-Yw~~stkl+Q|bOSamh$2LNH@_35x^gM}74!RQ(D^e9v#e1A zwyM_004^$!M1`Mt#Pabu2lgE*`ydh=Ni_aTEB!B!?=+26EQ~B@e!GshZRN7fzUXby zmJ1HhRU>{Y8Nu?`9QkPH42j=9A(HbYP4k^`oQQCG)Ad-^7H96(<~EZ+cm1+8ISaG| zyl8jP7ve+=T%{NOV0RJ}inVV62*1v*K$dXr)nnkwGde5FlYtj^2J=2zxwRgyvVQge z&`mtjs-UpVW^Rj?y^(~4Ahm{d1g1@gvK$Rin7_-OUBQi7MkxNmF)g0ZZ-r?}ETS>@ z;o|iCKIzU^ZTk&kk7m{;KFA<s?p72+dHNXTTx(Li`Q$2>j-I7K+mR_ljH|=*bDTd1 zHkerW=qnHQTYKvbL5`)IX7b2e9_BSaYDYINpXXhzq{=dZ@3PNB6WzbN_stRpJ&P=y zzPq_ZA@PB*!lJVFXg29Y@I7Ug0=M4Rvb@B)v)nm_g3LeVj-{%Wmg%1LvdO$zb$8$W zfaR7Z!Q~Z6dIG4G-3qXD5}<~wqk`NIEnnY*ORX}Fe~H4F?D7C=t3Ja1q$@;(n%b9p zRfjO(Dlp$I9C39bh}>>Tw;xADV4?$H(by4`N8<aHRvzfVv)-X0m+k>>l}vl*{9MHR zny?DItWbZq(29=Skoo>*<RUupdku8rH0m+b&JcvqbugobYJ<;VmWWcYDBF7ZK!Awc zvqnai@S*Z{G*Og{l!4S-sAX_NJ3q%85@G)4PUC7XB`$A1GewrMXb_ivPqUscQBc2e zY*n$)oko?r;u`>y8iFH55z+1~I$Ocdw`VF2krh^cT1^;hmp(SL$kN})bk_5`^d!SC zx)XFqBcBiS?hfHe>yGx?kQlHQii;Q3g9^TCzICJd#>91}jm+Ks&<6J5n?^PmH=S0b zhz$s*>c8;$d$Y^T_sPrcS4Mv^UZrGI)-G&fAY6SDpBTkVw+g>KbNf*cKDc?nSmQU^ zT3%ez6=Fs$Z4cY-z_UB}_Fo8*WZr`-M<1v|Nbo9J1|zeolKobCcIm#t()My1L?NX@ zf;W?V)O+)zS;gBS>}&;rS{irXLMygBc4hU`CzB=H6wxg2?Iykgx~pOOfrj8FY>{K> zik!`RQ(>WCTAlfgUfh^-Je~(%7Ome(DXRefi$5ZT+3%wJN;oP$WuCG=8%wQ$vXw(k zb@A`IRQ27EM&|MqkH#J>dOA4IlNxDC38Pqq;JrVAFoQrH5^@g8N{FLxWezv)EL%5z zR}260o3u8w>5jvR>3&Z^29nh5cfIB$znws`Esuq*F!A^RZBp|I9L4}yI#`}!ciSkA z9IA$z!imo@XR$_K;1~qFEJG>kWOa}%(Wmd8sVz%d#!-^@7MxfUr9#05)U@7hwk;%G z%nPyj_@3Gj@P&UdjbTZdtMoALT*_OKf}nmPeK^p}nP3sl6$6+7sBi1&LcFd#QWYF9 z)fO{|;gk~JPnOUkiDI-!sbPko(w3u@DU~I4g8IgdD4^2<`a!3pxQVYr-^Zdv2qmHl z8v6t76uTY@?8o5I%<Is;=k#3?4=<~KYRvrzC-R$8d}|eHlBFhqX68k$lpp==mQzU& zxtuh4{(BS3Wd@o48yUT_-5c!5#W1hWQe)djt~{#TxqJ=chHOqYtx|)RU4lFYTo_1V zs<59(k?SN{b2H_?88p<=@%r*vI+P#mVoBVy&p)BxGOIhtv^)bSCeqnyz!Q=v|FL;I zCH60XUMOo9YpG5YDJvX%Gksx&t+w%p_n9@jh9RurHo)SDOv#LOS1BCZo@Q~sdjYvT z2ZP*=R)y~}-(LdtMKWY%<NMC&Up_=qOS4^rV4*Wq>asu!DGAD!L9?!6oG)yf#T-n6 zZ&3Fr!Tx1OI+_+2^a5e-a9G>Qph@?nNE-5)2YR-W@COR;;7wFc>epDElU-(TJb|!y zys24SF?IwQfOIg6WPpl+5e7wbQ&%Cc8x1ufP{0()Uew}!6MuPzrbi6D&HV%Ch*KT! zjxzbRwVKVe>b`_irxde>4-}(ce7YU|ah0`btYtd+(#mk|Ep#fG5(^^bk=AcBCp2D0 z5}Q&YWYqJ(6QqlKt@G~XR+=eTUnIIufkZ6^L~|C7)saU>hd8_A-#wM6@A_3frt%9} zNOZw)P>OuFKBxwo@1sXqm#CRA7N73PBSq{Q`O*^$t(qd^g4FmB7kN)>S!Op^cz=`Z zExgFgq^3<ScF==3&y!9i)n7hZOSqXES+Uqoq;Tu;Mw7?mdd7ilb8asvtvBk0Fp563 z1p5NK^CPjTDmGhxe!6t1c!iOOYk!A@Af<rGS)R6>5mNLJym{OFNswCD_jR7{)rmor zY$%5h<pDwZH17^at;b%+>nm(I35vu`V%gBlUmqc2u4n0Jje(ZgLw72|Kri8C#{dlp zim7r1lqnSwM`+)w2c?)XXU<L8)28ecPIgq&P%-#OrOK`B7Kl=ZQA{5gP*51WU`8wt z`wU-3%2~VE6BCM#c#6X2vruE82Q{8cok{`0uv@%})(fWw#%qQ-4EjGq7wgBLyIfBD zo%6mYubS@j1gQ|`SV~2b@u><?ZR+>syGbh15PZ^Q^Yme$y8c>W*UeD#t=RZJYsD** zIUzNF(9D7;Ftjhx8e~LzA3({Ax$Ti9Q2HYMcP{ZA_0lh2$GXyYYMPAlsQBR1aE((M z$l9X|dElMD-Grpa*HC*!1yjutqZQm+0Z8RcSZ@%f9c><yAub*=UH|zYbUPW>Zq^)! zM6QoOC@1AwZ{_>jntjL$+(n>ed)`SfE<Nkb)GC$R-+&XHBBHEBfOf=;pluMW@JSQj zhqE?hZ*)B)qL2OE3$&5d!e=hEir6v7@`T>hrf(GAz(V7}JI~358<IC}9o`-O=mrh2 zkdslqow@0oyIY;`thn7f55H{=@q%)qayTnnsg+OXfbetb!`e?8u!~N<;k?o<v`LjI zJ=a8OYMz$ska-}B{zxy$5`DGZxz*mu8|~fCqVj2TdLww-oLxq<4cR^nRPH`&Z<jp& z2_1xkN?%2B>?~o@4$ASr<D0+)uYPG7@iAM;nRc<HYDyi7RILqyEG@U*5$OZFbFFt^ zsOjve(67An?n5H_X)pMke=K4+zkAj>T%f~B-p5+&OjZ~GrBP{{R<TL%#8#13^YhCO z<YQ}c1+Ph8PHUi?+ZFs-kG+}l$rkx-Fn5pFMYup)!Pr%Q7DJ<at26;u2@Uy_4uzX8 zuCA_$POh#k{5Lz)FbVvLv*Uf9${zQ+p9&mB#*^=;=-obkHByaUy-ZEs5k5V9k<_h} zJ%1}#l`8dJi@t&X`7M;j$(boi)PTQOnyj+eq_X@x&{Uvpit#6|THu{3kKxeafsf4z ztNo8O?fJ7~4Y;VX@x?xew6nd@3YAbFlv)pVC%7#3;mSqy@An^6$Wl=lXNrunvfKEs z>gtq1!RfLORdR3ToS6Rw2K|=J=RP*J-l*n<Jp_?nGNDAw3Nmo*D?XlT?d0qYYup)9 zf=;C8yNFa;Tv}vJ#B4kKk2gvR`jYe%Ey%_&I-9P1%1NqYIYG_(SUx%oWr?p);@$f$ z9%Tp9B=Eg`{{>9l^<jU9^bTY*>|oLPRh3iXB9@UG(m#Z^COt$x4&P#wZeAUE6{U}} z)N6{=(xOi)lMkF~FxU)xfQrJpS6RcEt@K+m5X}J-%}0u@=2|`OB->YXGt<t}wCQ*L zZoCO7=yFEhlqoGc7jnyNe+TW59hf6b=d1O+r!js8yhKQ>gwEZIV>)n1vkgD$Dp9jr z-F*4{WAxAWYuk%Lp{ZwwI$RLP+IpUtcX9N_1szq`MceKV;0wARgJvG<*v{G^xt#Ty z&d0v??;Na|T<8b3&c&niX*g<8Eqh-*xP1vq#{IxUr0!#mSh4Vk?q$iid&BZ>ws&=P zT6{JyZrU<Up{R8dE^j>1$WYj<YUsvT>P$u`>*8nU-s()29RQ-m1MWlvf02-pMZ3tq zoG3Uuw|$r(Ttq(0ceV^gO@wMbzW39UExIN2H22sVa%w9Vcu6;Pc1OdvL5Daj3ssPg zuX0so+YR&>_F)aN)Cu)UIIms=m!Gl;OsPXX&_PP8iuBm&48I)PI3^_ik|{M#V0)6q z)G@z!_~@B>c72!XReIKdK#-yg;@J*_wy0fR{EhjoblE><$e#lsky4zXhk2#{<1Vin zabJs?cW;H%le_QM4mEVUnRQ;L%W5N@ZgFi(teiiv*$rTT<?U8(^JtlW`GV4Cvs&18 zrT>GPb+Od(ia)PzV%tCp?J+>y1RT9$1kQDq_1xH(D`_0AX#qLK+Kg`8Ss>u(YHx|q zMf%%vj{;vU*?&!FR6D7Kcgy=|!BW1{`3{Y|*j_u!4$SN%efsI3;O*(qtb@8|iUx~0 zAY>L~H+LFhgUh{@Qp9aaYN@}>O9Seq`qVSGaxjN=y_Qd|4^4FPyeJNn_7KIoXOt7! zKXXE^)dZ#O*D6lP@`U&Oxz^nXA-ygJKq6O0Ke6KBNFg~wMQ72*T`qa?TbEabAYJ+f zEg9IYf%&h5KOt4EOo98g&~dBinRZk{lI6X7T_>T<&dBNIMqQpyo>{q2*)#wLt>7#z z^r<!ybJMEpunCr&G-F2Pek^4(LK$DwEj^P79NVh98Bt~T9PA`8Qgr<_h%dD~+6$&2 z!Yct&M=Nfhsc<Eq0-POpv)ZjDOubM!stDlZ{mnZvPcsxRUg)5g$faI3dfe}l%A^|K zscjf1-D211x-`&!;X<9`pn_6cx^%fUZ)^M`$$}F>iT7mci?O`GZ_yHW%BLe#dRoSs J)#~=K{|7a%wvPY+ literal 0 HcmV?d00001 diff --git a/frontend/src/models/dataConnector.js b/frontend/src/models/dataConnector.js index e0b3c0c3e..19fa5f912 100644 --- a/frontend/src/models/dataConnector.js +++ b/frontend/src/models/dataConnector.js @@ -60,6 +60,29 @@ const DataConnector = { }); }, }, + + confluence: { + collect: async function ({ pageUrl, username, accessToken }) { + return await fetch(`${API_BASE}/ext/confluence`, { + method: "POST", + headers: baseHeaders(), + body: JSON.stringify({ + pageUrl, + username, + accessToken, + }), + }) + .then((res) => res.json()) + .then((res) => { + if (!res.success) throw new Error(res.reason); + return { data: res.data, error: null }; + }) + .catch((e) => { + console.error(e); + return { data: null, error: e.message }; + }); + }, + }, }; export default DataConnector; diff --git a/server/endpoints/extensions/index.js b/server/endpoints/extensions/index.js index bf07ec56c..07eb7130d 100644 --- a/server/endpoints/extensions/index.js +++ b/server/endpoints/extensions/index.js @@ -71,6 +71,28 @@ function extensionEndpoints(app) { } } ); + + app.post( + "/ext/confluence", + [validatedRequest, flexUserRoleValid([ROLES.admin, ROLES.manager])], + async (request, response) => { + try { + const responseFromProcessor = + await new CollectorApi().forwardExtensionRequest({ + endpoint: "/ext/confluence", + method: "POST", + body: request.body, + }); + await Telemetry.sendTelemetry("extension_invoked", { + type: "confluence", + }); + response.status(200).json(responseFromProcessor); + } catch (e) { + console.error(e); + response.sendStatus(500).end(); + } + } + ); } module.exports = { extensionEndpoints }; -- GitLab