diff --git a/packages/backend/src/plugins/todo.ts b/packages/backend/src/plugins/todo.ts index 9f90d82a05..38fb43f78e 100644 --- a/packages/backend/src/plugins/todo.ts +++ b/packages/backend/src/plugins/todo.ts @@ -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 { - 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 }); } diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index c47ff18ab8..6f3d88f56d 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -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", diff --git a/plugins/todo-backend/src/index.ts b/plugins/todo-backend/src/index.ts index 7612c392a2..d0406483c8 100644 --- a/plugins/todo-backend/src/index.ts +++ b/plugins/todo-backend/src/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export * from './service/router'; +export * from './lib'; +export * from './service'; diff --git a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts new file mode 100644 index 0000000000..bf309cd673 --- /dev/null +++ b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts @@ -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 { + return { + items: [{ text: 'My mock todo' }], + }; + } +} diff --git a/plugins/todo-backend/src/lib/TodoReader/index.ts b/plugins/todo-backend/src/lib/TodoReader/index.ts new file mode 100644 index 0000000000..22c47ebef2 --- /dev/null +++ b/plugins/todo-backend/src/lib/TodoReader/index.ts @@ -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'; diff --git a/plugins/todo-backend/src/lib/TodoReader/types.ts b/plugins/todo-backend/src/lib/TodoReader/types.ts new file mode 100644 index 0000000000..46e7f1dcfa --- /dev/null +++ b/plugins/todo-backend/src/lib/TodoReader/types.ts @@ -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; +} diff --git a/plugins/todo-backend/src/lib/index.ts b/plugins/todo-backend/src/lib/index.ts new file mode 100644 index 0000000000..85c100c959 --- /dev/null +++ b/plugins/todo-backend/src/lib/index.ts @@ -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'; diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts new file mode 100644 index 0000000000..4d9afc3099 --- /dev/null +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -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 { + 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', + }, + }; + } +} diff --git a/plugins/todo-backend/src/service/index.ts b/plugins/todo-backend/src/service/index.ts new file mode 100644 index 0000000000..cbcf591da5 --- /dev/null +++ b/plugins/todo-backend/src/service/index.ts @@ -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'; diff --git a/plugins/todo-backend/src/service/router.ts b/plugins/todo-backend/src/service/router.ts index 08d6440780..f5237d6c0a 100644 --- a/plugins/todo-backend/src/service/router.ts +++ b/plugins/todo-backend/src/service/router.ts @@ -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 { - 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; diff --git a/plugins/todo-backend/src/service/types.ts b/plugins/todo-backend/src/service/types.ts new file mode 100644 index 0000000000..bf417f0895 --- /dev/null +++ b/plugins/todo-backend/src/service/types.ts @@ -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; +}