todo-backend: implement TodoScmReader

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-03-09 23:02:07 +01:00
parent 76e6336312
commit 2eeb884353
4 changed files with 123 additions and 10 deletions
+2 -1
View File
@@ -25,9 +25,10 @@ import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
reader,
config,
discovery,
}: PluginEnvironment): Promise<Router> {
const todoReader = new TodoScmReader({
const todoReader = TodoScmReader.fromConfig(config, {
logger,
reader,
});
+5 -3
View File
@@ -22,18 +22,20 @@
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.3",
"@backstage/config": "^0.1.3",
"@backstage/integration": "^0.5.0",
"@types/express": "^4.17.6",
"cross-fetch": "^3.0.6",
"express": "^4.17.1",
"express-promise-router": "^3.0.3",
"leasot": "^11.5.0",
"winston": "^3.2.1",
"cross-fetch": "^3.0.6",
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.6.3",
"@types/supertest": "^2.0.8",
"supertest": "^4.0.2",
"msw": "^0.21.2"
"msw": "^0.21.2",
"supertest": "^4.0.2"
},
"files": [
"dist"
@@ -14,27 +14,105 @@
* limitations under the License.
*/
import { extname } from 'path';
import { UrlReader } from '@backstage/backend-common';
import { ScmIntegrations } from '@backstage/integration';
import { Logger } from 'winston';
import { ReadTodosOptions, ReadTodosResult, TodoReader } from './types';
import { parse } from 'leasot';
import {
ReadTodosOptions,
ReadTodosResult,
TodoItem,
TodoReader,
} from './types';
import { Config } from '@backstage/config';
type TodoParser = (ctx: {
content: string;
path: string;
}) => (TodoItem & { line: number })[];
type Options = {
logger: Logger;
reader: UrlReader;
parser?: TodoParser;
};
const defaultTodoParser: TodoParser = ({ content, path }) => {
try {
const comments = parse(content, {
extension: extname(path),
});
return comments.map(comment => ({
text: comment.text,
author: comment.ref,
line: comment.line,
}));
} catch /* ignore unsupported extensions */ {
return [];
}
};
export class TodoScmReader implements TodoReader {
private readonly logger: Logger;
private readonly reader: UrlReader;
private readonly parser: TodoParser;
private readonly integrations: ScmIntegrations;
constructor(options: Options) {
static fromConfig(config: Config, options: Options) {
return new TodoScmReader(options, ScmIntegrations.fromConfig(config));
}
private constructor(options: Options, integrations: ScmIntegrations) {
this.logger = options.logger;
this.reader = options.reader;
this.parser = options.parser ?? defaultTodoParser;
this.integrations = integrations;
}
async readTodos({ url }: ReadTodosOptions): Promise<ReadTodosResult> {
return {
items: [{ text: 'My mock todo', viewUrl: url }],
};
const tree = await this.reader.readTree(url, {
filter(path) {
return !path.startsWith('.yarn');
},
});
const files = await tree.files();
this.logger.info(`Read ${files.length} files from ${url}`);
const todos = new Array<TodoItem>();
for (const file of files) {
const content = await file.content();
try {
const items = this.parser({
path: file.path,
content: content.toString('utf8'),
});
const viewUrl = this.integrations.resolveUrl({
url: file.path,
base: url,
});
let editUrl: string | undefined = this.integrations.resolveEditUrl(
viewUrl,
);
if (editUrl === viewUrl) {
editUrl = undefined;
}
todos.push(
...items.map(item => ({
...item,
editUrl,
viewUrl: item.line ? `${viewUrl}#L${item.line}` : viewUrl,
})),
);
} catch (error) {
this.logger.error(`Failed to parse TODO in ${url}, ${error}`);
}
}
return { items: todos };
}
}
+33 -1
View File
@@ -10092,7 +10092,7 @@ commander@^5.0.0, commander@^5.1.0:
resolved "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
commander@^6.1.0:
commander@^6.1.0, commander@^6.2.1:
version "6.2.1"
resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
@@ -12212,6 +12212,11 @@ envinfo@^7.7.4:
resolved "https://registry.npmjs.org/envinfo/-/envinfo-7.7.4.tgz#c6311cdd38a0e86808c1c9343f667e4267c4a320"
integrity sha512-TQXTYFVVwwluWSFis6K2XKxgrD22jEv0FTuLCQI+OjH7rn93+iY0fSSFM5lrSxFY+H1+B0/cvvlamr3UsBivdQ==
eol@^0.9.1:
version "0.9.1"
resolved "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd"
integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==
err-code@^2.0.2:
version "2.0.3"
resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9"
@@ -13890,6 +13895,11 @@ get-stdin@^6.0.0:
resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
get-stdin@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -16881,6 +16891,11 @@ json-to-pretty-yaml@^1.2.2:
remedial "^1.0.7"
remove-trailing-spaces "^1.0.6"
json2xml@^0.1.3:
version "0.1.3"
resolved "https://registry.npmjs.org/json2xml/-/json2xml-0.1.3.tgz#9ae7c220bedd7c66a668e26f7ac182f6704eca21"
integrity sha1-mufCIL7dfGamaOJvesGC9nBOyiE=
json3@^3.3.2:
version "3.3.3"
resolved "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
@@ -17295,6 +17310,23 @@ ldapjs@^2.2.0:
vasync "^2.2.0"
verror "^1.8.1"
leasot@^11.5.0:
version "11.5.0"
resolved "https://registry.npmjs.org/leasot/-/leasot-11.5.0.tgz#a99eb4479618c9d2ea442a32ee006e5b9da4844d"
integrity sha512-L08QKlmofYIRs5gfOmhOtbEJUu6U/zFGvYpboPq34yAHQ2Oc/QznOw62noe29yRJLiV/XnIDS8vO2um1e1sikA==
dependencies:
async "^3.2.0"
chalk "^4.1.0"
commander "^6.2.1"
eol "^0.9.1"
get-stdin "^8.0.0"
globby "^11.0.1"
json2xml "^0.1.3"
lodash "^4.17.20"
log-symbols "^4.0.0"
strip-ansi "^6.0.0"
text-table "^0.2.0"
left-pad@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"