adding Confluence search functionality upstream v1

Signed-off-by: su-gupta <sugupta@brex.com>
This commit is contained in:
su-gupta
2022-02-22 18:11:14 -05:00
parent f869d916a8
commit 0d1c461780
4 changed files with 146 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# Search
Contributions/extensions to the Search plugin
@@ -0,0 +1,87 @@
import { DocumentCollator } from '@backstage/search-common';
import fetch from 'cross-fetch'
export class ConfluenceCollator implements DocumentCollator {
public readonly type: string = 'confluence';
async execute() {
const ConfluenceUrlBase = 'https://{CONFLUENCE-ORG-NAME}.atlassian.net/wiki/rest/api'
async function getConfluenceData(requestUrl: string) {
var emptyJson = {}
try {
const res = await fetch(requestUrl, {
method: 'get',
headers: {
'Authorization': `Basic ${process.env.CONFLUENCE_TOKEN}`
},
});
if (res.ok) {
return await res.json();
}
} catch (err) {
console.error(err);
}
return emptyJson
}
async function getSpaces(): Promise<string[]> {
const data = await getConfluenceData(`${ConfluenceUrlBase}/space?&limit=1000&type=global&status=current`);
let spacesList = []
if (data["results"]) {
const results = data["results"];
for (const result of results) {
spacesList.push(result["key"])
}
}
return spacesList
}
async function getDocumentsFromSpaces(spaces: string[]): Promise<string[]> {
let documentsList = []
for (var space of spaces) {
let next = true
let requestUrl = `${ConfluenceUrlBase}/content?limit=1000&status=current&spaceKey=${space}`
while (next) {
const data = await getConfluenceData(requestUrl)
if (data["results"]) {
const results = data["results"]
for (const result of results) {
documentsList.push(result["_links"]["self"])
}
if (data["_links"]["next"]) {
requestUrl = data["_links"]["base"] + data["_links"]["next"]
} else {
next = false
}
} else {
break
}
}
}
return documentsList
}
async function getDocumentInfo(documents: string[]) {
let documentInfo = []
for (var documentUrl of documents) {
const data = await getConfluenceData(documentUrl + '?expand=body.storage')
if (data["status"] && data["status"]=="current") {
const documentMetaData = {
title: data["title"],
text: data["body"]["storage"]["value"],
location: data["_links"]["base"] + data["_links"]["webui"],
}
documentInfo.push(documentMetaData)
}
}
return documentInfo
}
const spacesList = await getSpaces();
const documentsList = await getDocumentsFromSpaces(spacesList);
const documentMetaDataList = await getDocumentInfo(documentsList);
return documentMetaDataList
}
}
@@ -0,0 +1,50 @@
import React from 'react';
import { Link } from '@backstage/core-components';
import { IndexableDocument } from '@backstage/search-common';
import {
Divider,
ListItem,
ListItemIcon,
ListItemText,
} from '@material-ui/core';
type Props = {
result: IndexableDocument;
};
export const ConfluenceResultListItem = ({ result }: Props) => {
// Remove html tags from document text before displaying
const chars = [];
let isTag = false;
for (const c of result.text.substring(0, 500)) {
if (c === "<") {
isTag = true;
continue;
}
if (c === ">") {
isTag = false;
chars.push(" ")
continue;
}
if (!isTag) {
chars.push(c);
}
}
const excerpt = chars.join("").substring(0, 80) + (result.text.length > 80 ? "..." : "");
return (
<Link to={result.location}>
<ListItem alignItems="center">
<ListItemIcon>
<img width="20" height="20" src="https://cdn.worldvectorlogo.com/logos/confluence-1.svg" />
</ListItemIcon>
<ListItemText
primaryTypographyProps={{ variant: 'h6' }}
primary={result.title}
secondary={excerpt}
/>
</ListItem>
<Divider />
</Link>
);
};
+6
View File
@@ -0,0 +1,6 @@
# Confluence
These files help you add Confluence as a source to the Backstage Search plugin.
To do so, add both files in this directory under the packages/backend/src/plugins/search/ pathway in your Backstage app as TypeScript files.
Then, update your packages/app/src/components/search/SearchPage.tsx and packages/backend/src/plugins/search.ts
to include the new Search source.