Merge pull request #9742 from brexhq/sugupta/contrib-confluence-search

Contributing Confluence search functionality
This commit is contained in:
Emma Indal
2022-03-02 15:42:14 +01:00
committed by GitHub
4 changed files with 189 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
# Search
Contributions/extensions to the Search plugin
@@ -0,0 +1,95 @@
ConfluenceCollator.ts reference
```ts
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,59 @@
ConfluenceResultListItem.tsx reference
```tsx
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>
);
};
```
+32
View File
@@ -0,0 +1,32 @@
# 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.
Then, add the following code to your packages/app/src/components/search/SearchPage.tsx:
```tsx
import { ConfluenceResultListItem } from './ConfluenceResultListItem';
```
```tsx
case 'confluence':
return (
<ConfluenceResultListItem
key={document.location}
result={document}
/>
);
```
and the following to packages/backend/src/plugins/search.ts:
```ts
import { ConfluenceCollator } from './search/ConfluenceCollator';
```
```ts
indexBuilder.addCollator({
defaultRefreshIntervalSeconds: 600,
collator: new ConfluenceCollator(),
});
```