todo-backend: create layered shell with types and mock

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2021-03-09 21:49:52 +01:00
parent f2bf479e0f
commit 117286b313
11 changed files with 281 additions and 15 deletions
+20 -2
View File
@@ -13,12 +13,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CatalogClient } from '@backstage/catalog-client';
import {
createRouter,
TodoReaderService,
TodoScmReader,
} from '@backstage/plugin-todo-backend';
import { Router } from 'express';
import { createRouter } from '@backstage/plugin-todo-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin({
logger,
reader,
discovery,
}: PluginEnvironment): Promise<Router> {
return await createRouter({ logger });
const todoReader = new TodoScmReader({
logger,
reader,
});
const catalogClient = new CatalogClient({ discoveryApi: discovery });
const todoService = new TodoReaderService({
logger,
todoReader,
catalogClient,
});
return await createRouter({ todoService });
}
+1
View File
@@ -20,6 +20,7 @@
"dependencies": {
"@backstage/backend-common": "^0.5.5",
"@backstage/catalog-client": "^0.3.6",
"@backstage/catalog-model": "^0.7.3",
"@backstage/config": "^0.1.3",
"@types/express": "^4.17.6",
"express": "^4.17.1",
+2 -1
View File
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export * from './service/router';
export * from './lib';
export * from './service';
@@ -0,0 +1,40 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { UrlReader } from '@backstage/backend-common';
import { Logger } from 'winston';
import { ReadTodosOptions, ReadTodosResult, TodoReader } from './types';
type Options = {
logger: Logger;
reader: UrlReader;
};
export class TodoScmReader implements TodoReader {
private readonly logger: Logger;
private readonly reader: UrlReader;
constructor(options: Options) {
this.logger = options.logger;
this.reader = options.reader;
}
async readTodos(_options: ReadTodosOptions): Promise<ReadTodosResult> {
return {
items: [{ text: 'My mock todo' }],
};
}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { TodoScmReader } from './TodoScmReader';
export type {
TodoItem,
TodoReader,
ReadTodosOptions,
ReadTodosResult,
} from './types';
@@ -0,0 +1,43 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type TodoItem = {
text: string;
author?: string;
viewUrl?: string;
editUrl?: string;
};
export type ReadTodosOptions = {
/**
* Base URLs defining the root at which to search for TODOs
*/
url: string;
};
export type ReadTodosResult = {
/**
* TODO items found at the given locations
*/
items: TodoItem[];
};
export interface TodoReader {
/**
* Searches for TODO items in code at a given location
*/
readTodos(options: ReadTodosOptions): Promise<ReadTodosResult>;
}
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './TodoReader';
@@ -0,0 +1,53 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CatalogClient } from '@backstage/catalog-client';
import { Logger } from 'winston';
import { TodoReader } from '../lib';
import { ListTodosRequest, ListTodosResponse, TodoService } from './types';
type Options = {
logger: Logger;
todoReader: TodoReader;
catalogClient: CatalogClient;
};
export class TodoReaderService implements TodoService {
private readonly logger: Logger;
private readonly todoReader: TodoReader;
private readonly catalogClient: CatalogClient;
constructor(options: Options) {
this.logger = options.logger;
this.todoReader = options.todoReader;
this.catalogClient = options.catalogClient;
}
async listTodos(_req: ListTodosRequest): Promise<ListTodosResponse> {
const todos = await this.todoReader.readTodos({
url: 'https://github.com/backstage/backstage',
});
return {
items: todos.items.slice(0, 10),
totalCount: todos.items.length,
cursors: {
prev: 'prev',
self: 'self',
next: 'next',
},
};
}
}
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createRouter } from './router';
export type { TodoService, ListTodosRequest, ListTodosResponse } from './types';
export { TodoReaderService } from './TodoReaderService';
+26 -12
View File
@@ -14,31 +14,45 @@
* limitations under the License.
*/
import { UrlReader } from '@backstage/backend-common';
import { CatalogClient } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { InputError } from '@backstage/backend-common';
import { EntityName, parseEntityName } from '@backstage/catalog-model';
import express from 'express';
import Router from 'express-promise-router';
import { Logger } from 'winston';
import { TodoService } from './types';
export interface RouterOptions {
logger: Logger;
config: Config;
reader: UrlReader;
catalogClient: CatalogClient;
todoService: TodoService;
}
export async function createRouter(
options: RouterOptions,
): Promise<express.Router> {
const { logger } = options;
const { todoService } = options;
const router = Router();
router.use(express.json());
router.get('/v1/todos', (req, res) => {
logger.debug('got todo request', req.query);
res.json({ items: [{ text: 'Test TODO' }], totalCount: 1 });
router.get('/v1/todos', async (req, res) => {
const { entity: entityRef, cursor } = req.query;
if (entityRef && typeof entityRef !== 'string') {
throw new InputError(`entity query must be a string`);
}
if (cursor && typeof cursor !== 'string') {
throw new InputError(`cursor query must be a string`);
}
let entity: EntityName | undefined = undefined;
if (entityRef) {
try {
entity = parseEntityName(entityRef);
} catch (error) {
throw new InputError(`Invalid entity ref, ${error}`);
}
}
const todos = await todoService.listTodos({ entity, cursor });
res.json(todos);
});
return router;
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { EntityName } from '@backstage/catalog-model';
import { TodoItem } from '../lib';
export type ListTodosRequest = {
entity?: EntityName;
cursor?: string;
};
export type ListTodosResponse = {
items: TodoItem[];
totalCount: number;
cursors: {
prev: string;
self: string;
next: string;
};
};
export interface TodoService {
listTodos(req: ListTodosRequest): Promise<ListTodosResponse>;
}