From ae08281331c96e526f82d9e33930179438511067 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Wed, 15 Feb 2023 14:31:50 -0500 Subject: [PATCH 01/11] expose the todos-backend plugin using the new plugin system Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- packages/backend-next/src/index.ts | 2 + plugins/todo-backend/src/lib/utils.ts | 107 ++++++++++++++++++ plugins/todo-backend/src/plugin.ts | 86 ++++++++++++++ .../src/service/TodoReaderService.ts | 35 +++++- plugins/todo-backend/src/service/index.ts | 2 + plugins/todo-backend/src/service/plugin.ts | 94 +++++++++++++++ plugins/todo-backend/src/service/router.ts | 106 ++--------------- plugins/todo-backend/src/service/types.ts | 8 ++ 8 files changed, 340 insertions(+), 100 deletions(-) create mode 100644 plugins/todo-backend/src/lib/utils.ts create mode 100644 plugins/todo-backend/src/plugin.ts create mode 100644 plugins/todo-backend/src/service/plugin.ts diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index 97b6eaf32b..a2f542afb1 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -18,10 +18,12 @@ import { catalogPlugin } from '@backstage/plugin-catalog-backend'; import { catalogModuleTemplateKind } from '@backstage/plugin-scaffolder-backend'; import { createBackend } from '@backstage/backend-defaults'; import { appPlugin } from '@backstage/plugin-app-backend'; +import { todosPlugin } from '@backstage/plugin-todo-backend'; const backend = createBackend(); backend.add(catalogPlugin()); backend.add(catalogModuleTemplateKind()); backend.add(appPlugin({ appPackageName: 'example-app' })); +backend.add(todosPlugin()); backend.start(); diff --git a/plugins/todo-backend/src/lib/utils.ts b/plugins/todo-backend/src/lib/utils.ts new file mode 100644 index 0000000000..cf0c2e4ea6 --- /dev/null +++ b/plugins/todo-backend/src/lib/utils.ts @@ -0,0 +1,107 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { InputError } from '@backstage/errors'; + +export const parseIntegerParam = ( + str: unknown, + ctx: string, +): number | undefined => { + if (str === undefined) { + return undefined; + } + if (typeof str !== 'string') { + throw new InputError(`invalid ${ctx}, must be a string`); + } + const parsed = parseInt(str, 10); + if (!Number.isInteger(parsed) || String(parsed) !== str) { + throw new InputError(`invalid ${ctx}, not an integer`); + } + return parsed; +}; + +export const parseOrderByParam = ( + str: unknown, + allowedFields: T, +): { field: T[number]; direction: 'asc' | 'desc' } | undefined => { + if (str === undefined) { + return undefined; + } + if (typeof str !== 'string') { + throw new InputError(`invalid orderBy query, must be a string`); + } + const [field, direction] = str.split('='); + if (!field) { + throw new InputError(`invalid orderBy query, field name is empty`); + } + if (direction !== 'asc' && direction !== 'desc') { + throw new InputError( + `invalid orderBy query, order direction must be 'asc' or 'desc'`, + ); + } + + if (field && !allowedFields.includes(field)) { + throw new InputError( + `invalid orderBy field, must be one of ${allowedFields.join(', ')}`, + ); + } + return { field, direction }; +}; + +export const parseFilterParam = ( + str: unknown, + allowedFields: T, +): { field: T[number]; value: string }[] | undefined => { + if (str === undefined) { + return undefined; + } + + const filters = new Array<{ field: T[number]; value: string }>(); + + const strs = [str].flat(); + for (const filterStr of strs) { + if (typeof filterStr !== 'string') { + throw new InputError( + `invalid filter query, must be a string or list of strings`, + ); + } + const splitIndex = filterStr.indexOf('='); + if (splitIndex <= 0) { + throw new InputError( + `invalid filter query, must separate field from value using '='`, + ); + } + + const field = filterStr.slice(0, splitIndex); + if (!allowedFields.includes(field)) { + throw new InputError( + `invalid filter field, must be one of ${allowedFields.join(', ')}`, + ); + } + + const value = filterStr.slice(splitIndex + 1); + if (!value) { + throw new InputError(`invalid filter query, value may not be empty`); + } + filters.push({ field, value }); + } + + return filters; +}; + +export const getBearerToken = (header?: string): string | undefined => { + return header?.match(/Bearer\s+(\S+)/i)?.[1]; +}; diff --git a/plugins/todo-backend/src/plugin.ts b/plugins/todo-backend/src/plugin.ts new file mode 100644 index 0000000000..49e8d354b1 --- /dev/null +++ b/plugins/todo-backend/src/plugin.ts @@ -0,0 +1,86 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + createBackendPlugin, + coreServices, +} from '@backstage/backend-plugin-api'; +import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model'; +import { InputError } from '@backstage/errors'; +import express, { Router } from 'express'; +import { + getBearerToken, + parseFilterParam, + parseIntegerParam, + parseOrderByParam, +} from './lib/utils'; +import { todoReaderServiceRef } from './service/TodoReaderService'; +import { TODO_FIELDS } from './service/types'; + +/** + * The Todos plugin is responsible for aggregating todo comments within source. + * @alpha + */ +export const todosPlugin = createBackendPlugin({ + pluginId: 'todo-backend', + register(env) { + env.registerInit({ + deps: { + todoReader: todoReaderServiceRef, + http: coreServices.httpRouter, + }, + async init({ http, todoReader }) { + const router = Router(); + router.use(express.json()); + + router.get('/v1/todos', async (req, res) => { + const offset = parseIntegerParam(req.query.offset, 'offset query'); + const limit = parseIntegerParam(req.query.limit, 'limit query'); + const orderBy = parseOrderByParam(req.query.orderBy, TODO_FIELDS); + const filters = parseFilterParam(req.query.filter, TODO_FIELDS); + + const entityRef = req.query.entity; + if (entityRef && typeof entityRef !== 'string') { + throw new InputError(`entity query must be a string`); + } + let entity: CompoundEntityRef | undefined = undefined; + if (entityRef) { + try { + entity = parseEntityRef(entityRef); + } catch (error) { + throw new InputError(`Invalid entity ref, ${error}`); + } + } + + const todos = await todoReader.listTodos( + { + entity, + offset, + limit, + orderBy, + filters, + }, + { + token: getBearerToken(req.headers.authorization), + }, + ); + res.json(todos); + }); + http.use(router); + }, + }); + }, +}); diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts index 35fd76b8be..93908c0f9f 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -20,8 +20,15 @@ import { getEntitySourceLocation, stringifyEntityRef, } from '@backstage/catalog-model'; -import { TodoReader } from '../lib'; +import { TodoReader, TodoScmReader } from '../lib'; import { ListTodosRequest, ListTodosResponse, TodoService } from './types'; +import { + coreServices, + createServiceFactory, + createServiceRef, +} from '@backstage/backend-plugin-api'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node'; +import { loggerToWinstonLogger } from '@backstage/backend-common'; const DEFAULT_DEFAULT_PAGE_SIZE = 10; const DEFAULT_MAX_PAGE_SIZE = 50; @@ -130,3 +137,29 @@ export class TodoReaderService implements TodoService { }; } } + +export const todoReaderServiceRef = createServiceRef({ + id: 'todo-client', + defaultFactory: async service => + createServiceFactory({ + service, + deps: { + catalogApi: catalogServiceRef, + logger: coreServices.logger, + reader: coreServices.urlReader, + config: coreServices.config, + }, + async factory({ catalogApi, logger, reader, config }) { + const winstonLogger = loggerToWinstonLogger(logger); + const todoReader = TodoScmReader.fromConfig(config, { + reader: reader, + logger: winstonLogger, + }); + return new TodoReaderService({ + catalogClient: catalogApi, + todoReader, + // TODO: override defaults here + }); + }, + }), +}); diff --git a/plugins/todo-backend/src/service/index.ts b/plugins/todo-backend/src/service/index.ts index 380d95dbe1..3dfff3f33e 100644 --- a/plugins/todo-backend/src/service/index.ts +++ b/plugins/todo-backend/src/service/index.ts @@ -19,3 +19,5 @@ export type { RouterOptions } from './router'; export type { TodoService, ListTodosRequest, ListTodosResponse } from './types'; export { TodoReaderService } from './TodoReaderService'; export type { TodoReaderServiceOptions } from './TodoReaderService'; +export { todosPlugin } from './plugin'; +export type { TodosPluginDependencies } from './plugin'; diff --git a/plugins/todo-backend/src/service/plugin.ts b/plugins/todo-backend/src/service/plugin.ts new file mode 100644 index 0000000000..54b089221f --- /dev/null +++ b/plugins/todo-backend/src/service/plugin.ts @@ -0,0 +1,94 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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 { + createBackendPlugin, + coreServices, + HttpRouterService, +} from '@backstage/backend-plugin-api'; +import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model'; +import { InputError } from '@backstage/errors'; +import express, { Router } from 'express'; +import { + getBearerToken, + parseFilterParam, + parseIntegerParam, + parseOrderByParam, +} from '../lib/utils'; +import { todoReaderServiceRef } from './TodoReaderService'; +import { TodoService, TODO_FIELDS } from './types'; + +export type TodosPluginDependencies = { + todoReader: TodoService; + http: HttpRouterService; +}; + +const todosPluginInit = async (params: TodosPluginDependencies) => { + const { http, todoReader } = params; + const router = Router(); + router.use(express.json()); + + router.get('/v1/todos', async (req, res) => { + const offset = parseIntegerParam(req.query.offset, 'offset query'); + const limit = parseIntegerParam(req.query.limit, 'limit query'); + const orderBy = parseOrderByParam(req.query.orderBy, TODO_FIELDS); + const filters = parseFilterParam(req.query.filter, TODO_FIELDS); + + const entityRef = req.query.entity; + if (entityRef && typeof entityRef !== 'string') { + throw new InputError(`entity query must be a string`); + } + let entity: CompoundEntityRef | undefined = undefined; + if (entityRef) { + try { + entity = parseEntityRef(entityRef); + } catch (error) { + throw new InputError(`Invalid entity ref, ${error}`); + } + } + + const todos = await todoReader.listTodos( + { + entity, + offset, + limit, + orderBy, + filters, + }, + { + token: getBearerToken(req.headers.authorization), + }, + ); + res.json(todos); + }); + http.use(router); +}; + +/** + * @alpha + */ +export const todosPlugin = createBackendPlugin({ + pluginId: 'todo-backend', + register(env) { + env.registerInit({ + deps: { + todoReader: todoReaderServiceRef, + http: coreServices.httpRouter, + }, + init: todosPluginInit, + }); + }, +}); diff --git a/plugins/todo-backend/src/service/router.ts b/plugins/todo-backend/src/service/router.ts index 33356a83d6..5954fab009 100644 --- a/plugins/todo-backend/src/service/router.ts +++ b/plugins/todo-backend/src/service/router.ts @@ -18,15 +18,13 @@ import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model'; import { InputError } from '@backstage/errors'; import express from 'express'; import Router from 'express-promise-router'; -import { TodoService } from './types'; - -const TODO_FIELDS = [ - 'text', - 'tag', - 'author', - 'viewUrl', - 'repoFilePath', -] as const; +import { type TodoService, TODO_FIELDS } from './types'; +import { + getBearerToken, + parseFilterParam, + parseIntegerParam, + parseOrderByParam, +} from '../lib/utils'; /** @public */ export interface RouterOptions { @@ -78,93 +76,3 @@ export async function createRouter( return router; } - -export function parseIntegerParam( - str: unknown, - ctx: string, -): number | undefined { - if (str === undefined) { - return undefined; - } - if (typeof str !== 'string') { - throw new InputError(`invalid ${ctx}, must be a string`); - } - const parsed = parseInt(str, 10); - if (!Number.isInteger(parsed) || String(parsed) !== str) { - throw new InputError(`invalid ${ctx}, not an integer`); - } - return parsed; -} - -export function parseOrderByParam( - str: unknown, - allowedFields: T, -): { field: T[number]; direction: 'asc' | 'desc' } | undefined { - if (str === undefined) { - return undefined; - } - if (typeof str !== 'string') { - throw new InputError(`invalid orderBy query, must be a string`); - } - const [field, direction] = str.split('='); - if (!field) { - throw new InputError(`invalid orderBy query, field name is empty`); - } - if (direction !== 'asc' && direction !== 'desc') { - throw new InputError( - `invalid orderBy query, order direction must be 'asc' or 'desc'`, - ); - } - - if (field && !allowedFields.includes(field)) { - throw new InputError( - `invalid orderBy field, must be one of ${allowedFields.join(', ')}`, - ); - } - return { field, direction }; -} - -export function parseFilterParam( - str: unknown, - allowedFields: T, -): { field: T[number]; value: string }[] | undefined { - if (str === undefined) { - return undefined; - } - - const filters = new Array<{ field: T[number]; value: string }>(); - - const strs = [str].flat(); - for (const filterStr of strs) { - if (typeof filterStr !== 'string') { - throw new InputError( - `invalid filter query, must be a string or list of strings`, - ); - } - const splitIndex = filterStr.indexOf('='); - if (splitIndex <= 0) { - throw new InputError( - `invalid filter query, must separate field from value using '='`, - ); - } - - const field = filterStr.slice(0, splitIndex); - if (!allowedFields.includes(field)) { - throw new InputError( - `invalid filter field, must be one of ${allowedFields.join(', ')}`, - ); - } - - const value = filterStr.slice(splitIndex + 1); - if (!value) { - throw new InputError(`invalid filter query, value may not be empty`); - } - filters.push({ field, value }); - } - - return filters; -} - -function getBearerToken(header?: string): string | undefined { - return header?.match(/Bearer\s+(\S+)/i)?.[1]; -} diff --git a/plugins/todo-backend/src/service/types.ts b/plugins/todo-backend/src/service/types.ts index 88d2f2b340..ab15e20027 100644 --- a/plugins/todo-backend/src/service/types.ts +++ b/plugins/todo-backend/src/service/types.ts @@ -48,3 +48,11 @@ export interface TodoService { options?: { token?: string }, ): Promise; } + +export const TODO_FIELDS = [ + 'text', + 'tag', + 'author', + 'viewUrl', + 'repoFilePath', +] as const; From 8c57bb780f5227710cbd352698fa7cf210e3877b Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Thu, 16 Feb 2023 08:24:42 -0500 Subject: [PATCH 02/11] create changeset for exposing the todos-backend plugin using the new system Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .changeset/new-waves-sing.md | 6 ++++++ plugins/todo-backend/src/service/router.test.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/new-waves-sing.md diff --git a/.changeset/new-waves-sing.md b/.changeset/new-waves-sing.md new file mode 100644 index 0000000000..124a508bde --- /dev/null +++ b/.changeset/new-waves-sing.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-todo-backend': minor +'example-backend-next': patch +--- + +Expose the todos-backend plugin using the new system, add the new entrypoint for todos-backend into the experimental backend diff --git a/plugins/todo-backend/src/service/router.test.ts b/plugins/todo-backend/src/service/router.test.ts index 0254147681..f2d9f2c7a3 100644 --- a/plugins/todo-backend/src/service/router.test.ts +++ b/plugins/todo-backend/src/service/router.test.ts @@ -18,12 +18,12 @@ import express from 'express'; import request from 'supertest'; import { errorHandler } from '@backstage/backend-common'; +import { createRouter } from './router'; import { - createRouter, parseFilterParam, parseIntegerParam, parseOrderByParam, -} from './router'; +} from '../lib/utils'; import { TodoService } from './types'; const mockListBody = { From 581b1f8a3a2fa45857401bfa03e4d83898791650 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:21:41 -0500 Subject: [PATCH 03/11] add api-report Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/todo-backend/api-report.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/plugins/todo-backend/api-report.md b/plugins/todo-backend/api-report.md index 277c87fea9..3e3c4cb58c 100644 --- a/plugins/todo-backend/api-report.md +++ b/plugins/todo-backend/api-report.md @@ -3,10 +3,12 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import express from 'express'; +import { HttpRouterService } from '@backstage/backend-plugin-api'; import { Logger } from 'winston'; import { ScmIntegrations } from '@backstage/integration'; import { UrlReader } from '@backstage/backend-common'; @@ -144,4 +146,15 @@ export interface TodoService { }, ): Promise; } + +// @alpha (undocumented) +export const todosPlugin: () => BackendFeature; + +// Warning: (ae-missing-release-tag) "TodosPluginDependencies" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type TodosPluginDependencies = { + todoReader: TodoService; + http: HttpRouterService; +}; ``` From fd28151c954271b9109fcd1315a791be6e4d72be Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Thu, 16 Feb 2023 12:39:47 -0500 Subject: [PATCH 04/11] separates concerns of type usage within todo-backend Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/todo-backend/api-report.md | 6 ++---- plugins/todo-backend/src/service/index.ts | 8 ++++++-- plugins/todo-backend/src/service/plugin.ts | 9 ++------- plugins/todo-backend/src/service/types.ts | 9 +++++++++ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/plugins/todo-backend/api-report.md b/plugins/todo-backend/api-report.md index 3e3c4cb58c..7492865ee4 100644 --- a/plugins/todo-backend/api-report.md +++ b/plugins/todo-backend/api-report.md @@ -8,7 +8,7 @@ import { CatalogApi } from '@backstage/catalog-client'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import express from 'express'; -import { HttpRouterService } from '@backstage/backend-plugin-api'; +import type { HttpRouterService } from '@backstage/backend-plugin-api'; import { Logger } from 'winston'; import { ScmIntegrations } from '@backstage/integration'; import { UrlReader } from '@backstage/backend-common'; @@ -150,9 +150,7 @@ export interface TodoService { // @alpha (undocumented) export const todosPlugin: () => BackendFeature; -// Warning: (ae-missing-release-tag) "TodosPluginDependencies" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @alpha (undocumented) export type TodosPluginDependencies = { todoReader: TodoService; http: HttpRouterService; diff --git a/plugins/todo-backend/src/service/index.ts b/plugins/todo-backend/src/service/index.ts index 3dfff3f33e..89a6939cda 100644 --- a/plugins/todo-backend/src/service/index.ts +++ b/plugins/todo-backend/src/service/index.ts @@ -16,8 +16,12 @@ export { createRouter } from './router'; export type { RouterOptions } from './router'; -export type { TodoService, ListTodosRequest, ListTodosResponse } from './types'; +export type { + TodoService, + ListTodosRequest, + ListTodosResponse, + TodosPluginDependencies, +} from './types'; export { TodoReaderService } from './TodoReaderService'; export type { TodoReaderServiceOptions } from './TodoReaderService'; export { todosPlugin } from './plugin'; -export type { TodosPluginDependencies } from './plugin'; diff --git a/plugins/todo-backend/src/service/plugin.ts b/plugins/todo-backend/src/service/plugin.ts index 54b089221f..6c5fd3bf6c 100644 --- a/plugins/todo-backend/src/service/plugin.ts +++ b/plugins/todo-backend/src/service/plugin.ts @@ -17,7 +17,6 @@ import { createBackendPlugin, coreServices, - HttpRouterService, } from '@backstage/backend-plugin-api'; import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model'; import { InputError } from '@backstage/errors'; @@ -29,12 +28,8 @@ import { parseOrderByParam, } from '../lib/utils'; import { todoReaderServiceRef } from './TodoReaderService'; -import { TodoService, TODO_FIELDS } from './types'; - -export type TodosPluginDependencies = { - todoReader: TodoService; - http: HttpRouterService; -}; +import { TODO_FIELDS } from './types'; +import type { TodosPluginDependencies } from './types'; const todosPluginInit = async (params: TodosPluginDependencies) => { const { http, todoReader } = params; diff --git a/plugins/todo-backend/src/service/types.ts b/plugins/todo-backend/src/service/types.ts index ab15e20027..6b56487605 100644 --- a/plugins/todo-backend/src/service/types.ts +++ b/plugins/todo-backend/src/service/types.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import type { HttpRouterService } from '@backstage/backend-plugin-api'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { TodoItem } from '../lib'; @@ -56,3 +57,11 @@ export const TODO_FIELDS = [ 'viewUrl', 'repoFilePath', ] as const; + +/** + * @alpha + */ +export type TodosPluginDependencies = { + todoReader: TodoService; + http: HttpRouterService; +}; From 4120513412ad0ee58242fd36c4092a756cae93fc Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Fri, 17 Feb 2023 10:07:21 -0500 Subject: [PATCH 05/11] adds @backstage/plugin-todo-backend to dependencies for plugin/todo-backend Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- .changeset/bright-kids-raise.md | 5 ++ .changeset/new-waves-sing.md | 6 -- packages/backend-next/src/index.ts | 4 +- plugins/todo-backend/api-report.md | 17 ++-- plugins/todo-backend/package.json | 1 + plugins/todo-backend/src/index.ts | 1 + .../src/lib/TodoReader/TodoScmReader.ts | 30 ++++++- .../todo-backend/src/lib/TodoReader/index.ts | 2 +- plugins/todo-backend/src/plugin.ts | 56 ++---------- .../src/service/TodoReaderService.ts | 52 +++++------ plugins/todo-backend/src/service/index.ts | 8 +- plugins/todo-backend/src/service/plugin.ts | 89 ------------------- 12 files changed, 78 insertions(+), 193 deletions(-) create mode 100644 .changeset/bright-kids-raise.md delete mode 100644 .changeset/new-waves-sing.md delete mode 100644 plugins/todo-backend/src/service/plugin.ts diff --git a/.changeset/bright-kids-raise.md b/.changeset/bright-kids-raise.md new file mode 100644 index 0000000000..e0d69e0c71 --- /dev/null +++ b/.changeset/bright-kids-raise.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-todo-backend': patch +--- + +todo-backend is now exposed as a plugin which uses the new plugin system diff --git a/.changeset/new-waves-sing.md b/.changeset/new-waves-sing.md deleted file mode 100644 index 124a508bde..0000000000 --- a/.changeset/new-waves-sing.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-todo-backend': minor -'example-backend-next': patch ---- - -Expose the todos-backend plugin using the new system, add the new entrypoint for todos-backend into the experimental backend diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index a2f542afb1..f4979760cf 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -18,12 +18,12 @@ import { catalogPlugin } from '@backstage/plugin-catalog-backend'; import { catalogModuleTemplateKind } from '@backstage/plugin-scaffolder-backend'; import { createBackend } from '@backstage/backend-defaults'; import { appPlugin } from '@backstage/plugin-app-backend'; -import { todosPlugin } from '@backstage/plugin-todo-backend'; +import { todoPlugin } from '@backstage/plugin-todo-backend'; const backend = createBackend(); backend.add(catalogPlugin()); backend.add(catalogModuleTemplateKind()); backend.add(appPlugin({ appPackageName: 'example-app' })); -backend.add(todosPlugin()); +backend.add(todoPlugin()); backend.start(); diff --git a/plugins/todo-backend/api-report.md b/plugins/todo-backend/api-report.md index 7492865ee4..3908d2475b 100644 --- a/plugins/todo-backend/api-report.md +++ b/plugins/todo-backend/api-report.md @@ -8,9 +8,9 @@ import { CatalogApi } from '@backstage/catalog-client'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import express from 'express'; -import type { HttpRouterService } from '@backstage/backend-plugin-api'; import { Logger } from 'winston'; import { ScmIntegrations } from '@backstage/integration'; +import { ServiceRef } from '@backstage/backend-plugin-api'; import { UrlReader } from '@backstage/backend-common'; // @public (undocumented) @@ -90,6 +90,9 @@ export type TodoParserResult = { lineNumber: number; }; +// @alpha +export const todoPlugin: () => BackendFeature; + // @public (undocumented) export interface TodoReader { readTodos(options: ReadTodosOptions): Promise; @@ -136,6 +139,9 @@ export type TodoScmReaderOptions = { filePathFilter?: (filePath: string) => boolean; }; +// @alpha (undocumented) +export const todoScmReaderRef: ServiceRef; + // @public (undocumented) export interface TodoService { // (undocumented) @@ -146,13 +152,4 @@ export interface TodoService { }, ): Promise; } - -// @alpha (undocumented) -export const todosPlugin: () => BackendFeature; - -// @alpha (undocumented) -export type TodosPluginDependencies = { - todoReader: TodoService; - http: HttpRouterService; -}; ``` diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 6a3b22aab4..51c3f8ede8 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -30,6 +30,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", diff --git a/plugins/todo-backend/src/index.ts b/plugins/todo-backend/src/index.ts index 973c91df44..b1654327d6 100644 --- a/plugins/todo-backend/src/index.ts +++ b/plugins/todo-backend/src/index.ts @@ -22,3 +22,4 @@ export * from './lib'; export * from './service'; +export * from './plugin'; diff --git a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts index 2696f0d54b..d364316ae1 100644 --- a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts +++ b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { UrlReader } from '@backstage/backend-common'; +import { loggerToWinstonLogger, UrlReader } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; import { Logger } from 'winston'; @@ -28,6 +28,11 @@ import { import { Config } from '@backstage/config'; import { createTodoParser } from './createTodoParser'; import path from 'path'; +import { + coreServices, + createServiceFactory, + createServiceRef, +} from '@backstage/backend-plugin-api'; const excludedExtensions = [ '.png', @@ -167,3 +172,26 @@ export class TodoScmReader implements TodoReader { return { result: { items: todos }, etag: tree.etag }; } } + +/** + * @alpha + */ +export const todoScmReaderRef = createServiceRef({ + id: 'todoScmReaderFactory', + defaultFactory: async service => + createServiceFactory({ + service, + deps: { + reader: coreServices.urlReader, + config: coreServices.config, + logger: coreServices.logger, + }, + factory: async ({ reader, config, logger }) => { + const winstonLogger = loggerToWinstonLogger(logger); + return TodoScmReader.fromConfig(config, { + logger: winstonLogger, + reader, + }); + }, + }), +}); diff --git a/plugins/todo-backend/src/lib/TodoReader/index.ts b/plugins/todo-backend/src/lib/TodoReader/index.ts index d0537861da..a56d5805d5 100644 --- a/plugins/todo-backend/src/lib/TodoReader/index.ts +++ b/plugins/todo-backend/src/lib/TodoReader/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export { TodoScmReader } from './TodoScmReader'; +export { TodoScmReader, todoScmReaderRef } from './TodoScmReader'; export type { TodoScmReaderOptions } from './TodoScmReader'; export { createTodoParser } from './createTodoParser'; export type { TodoParserOptions } from './createTodoParser'; diff --git a/plugins/todo-backend/src/plugin.ts b/plugins/todo-backend/src/plugin.ts index 49e8d354b1..cb25d3ddf0 100644 --- a/plugins/todo-backend/src/plugin.ts +++ b/plugins/todo-backend/src/plugin.ts @@ -18,23 +18,15 @@ import { createBackendPlugin, coreServices, } from '@backstage/backend-plugin-api'; -import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model'; -import { InputError } from '@backstage/errors'; -import express, { Router } from 'express'; -import { - getBearerToken, - parseFilterParam, - parseIntegerParam, - parseOrderByParam, -} from './lib/utils'; + import { todoReaderServiceRef } from './service/TodoReaderService'; -import { TODO_FIELDS } from './service/types'; +import { createRouter } from './service/router'; /** * The Todos plugin is responsible for aggregating todo comments within source. * @alpha */ -export const todosPlugin = createBackendPlugin({ +export const todoPlugin = createBackendPlugin({ pluginId: 'todo-backend', register(env) { env.registerInit({ @@ -43,43 +35,11 @@ export const todosPlugin = createBackendPlugin({ http: coreServices.httpRouter, }, async init({ http, todoReader }) { - const router = Router(); - router.use(express.json()); - - router.get('/v1/todos', async (req, res) => { - const offset = parseIntegerParam(req.query.offset, 'offset query'); - const limit = parseIntegerParam(req.query.limit, 'limit query'); - const orderBy = parseOrderByParam(req.query.orderBy, TODO_FIELDS); - const filters = parseFilterParam(req.query.filter, TODO_FIELDS); - - const entityRef = req.query.entity; - if (entityRef && typeof entityRef !== 'string') { - throw new InputError(`entity query must be a string`); - } - let entity: CompoundEntityRef | undefined = undefined; - if (entityRef) { - try { - entity = parseEntityRef(entityRef); - } catch (error) { - throw new InputError(`Invalid entity ref, ${error}`); - } - } - - const todos = await todoReader.listTodos( - { - entity, - offset, - limit, - orderBy, - filters, - }, - { - token: getBearerToken(req.headers.authorization), - }, - ); - res.json(todos); - }); - http.use(router); + http.use( + await createRouter({ + todoService: todoReader, + }), + ); }, }); }, diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts index 93908c0f9f..b3ed793d38 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -20,15 +20,15 @@ import { getEntitySourceLocation, stringifyEntityRef, } from '@backstage/catalog-model'; -import { TodoReader, TodoScmReader } from '../lib'; -import { ListTodosRequest, ListTodosResponse, TodoService } from './types'; import { - coreServices, createServiceFactory, createServiceRef, + ServiceRef, } from '@backstage/backend-plugin-api'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; -import { loggerToWinstonLogger } from '@backstage/backend-common'; + +import { TodoReader, todoScmReaderRef } from '../lib'; +import { ListTodosRequest, ListTodosResponse, TodoService } from './types'; const DEFAULT_DEFAULT_PAGE_SIZE = 10; const DEFAULT_MAX_PAGE_SIZE = 50; @@ -138,28 +138,22 @@ export class TodoReaderService implements TodoService { } } -export const todoReaderServiceRef = createServiceRef({ - id: 'todo-client', - defaultFactory: async service => - createServiceFactory({ - service, - deps: { - catalogApi: catalogServiceRef, - logger: coreServices.logger, - reader: coreServices.urlReader, - config: coreServices.config, - }, - async factory({ catalogApi, logger, reader, config }) { - const winstonLogger = loggerToWinstonLogger(logger); - const todoReader = TodoScmReader.fromConfig(config, { - reader: reader, - logger: winstonLogger, - }); - return new TodoReaderService({ - catalogClient: catalogApi, - todoReader, - // TODO: override defaults here - }); - }, - }), -}); +export const todoReaderServiceRef: ServiceRef = + createServiceRef({ + id: 'todo.client', + defaultFactory: async service => + createServiceFactory({ + service, + deps: { + catalogApi: catalogServiceRef, + todoReader: todoScmReaderRef, + }, + async factory({ catalogApi, todoReader }) { + const todoReaderService = new TodoReaderService({ + catalogClient: catalogApi, + todoReader, + }); + return todoReaderService; + }, + }), + }); diff --git a/plugins/todo-backend/src/service/index.ts b/plugins/todo-backend/src/service/index.ts index 89a6939cda..380d95dbe1 100644 --- a/plugins/todo-backend/src/service/index.ts +++ b/plugins/todo-backend/src/service/index.ts @@ -16,12 +16,6 @@ export { createRouter } from './router'; export type { RouterOptions } from './router'; -export type { - TodoService, - ListTodosRequest, - ListTodosResponse, - TodosPluginDependencies, -} from './types'; +export type { TodoService, ListTodosRequest, ListTodosResponse } from './types'; export { TodoReaderService } from './TodoReaderService'; export type { TodoReaderServiceOptions } from './TodoReaderService'; -export { todosPlugin } from './plugin'; diff --git a/plugins/todo-backend/src/service/plugin.ts b/plugins/todo-backend/src/service/plugin.ts deleted file mode 100644 index 6c5fd3bf6c..0000000000 --- a/plugins/todo-backend/src/service/plugin.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * 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 { - createBackendPlugin, - coreServices, -} from '@backstage/backend-plugin-api'; -import { CompoundEntityRef, parseEntityRef } from '@backstage/catalog-model'; -import { InputError } from '@backstage/errors'; -import express, { Router } from 'express'; -import { - getBearerToken, - parseFilterParam, - parseIntegerParam, - parseOrderByParam, -} from '../lib/utils'; -import { todoReaderServiceRef } from './TodoReaderService'; -import { TODO_FIELDS } from './types'; -import type { TodosPluginDependencies } from './types'; - -const todosPluginInit = async (params: TodosPluginDependencies) => { - const { http, todoReader } = params; - const router = Router(); - router.use(express.json()); - - router.get('/v1/todos', async (req, res) => { - const offset = parseIntegerParam(req.query.offset, 'offset query'); - const limit = parseIntegerParam(req.query.limit, 'limit query'); - const orderBy = parseOrderByParam(req.query.orderBy, TODO_FIELDS); - const filters = parseFilterParam(req.query.filter, TODO_FIELDS); - - const entityRef = req.query.entity; - if (entityRef && typeof entityRef !== 'string') { - throw new InputError(`entity query must be a string`); - } - let entity: CompoundEntityRef | undefined = undefined; - if (entityRef) { - try { - entity = parseEntityRef(entityRef); - } catch (error) { - throw new InputError(`Invalid entity ref, ${error}`); - } - } - - const todos = await todoReader.listTodos( - { - entity, - offset, - limit, - orderBy, - filters, - }, - { - token: getBearerToken(req.headers.authorization), - }, - ); - res.json(todos); - }); - http.use(router); -}; - -/** - * @alpha - */ -export const todosPlugin = createBackendPlugin({ - pluginId: 'todo-backend', - register(env) { - env.registerInit({ - deps: { - todoReader: todoReaderServiceRef, - http: coreServices.httpRouter, - }, - init: todosPluginInit, - }); - }, -}); From bfcac3907c036d740418d8e1aeb59e4cbc686406 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Fri, 17 Feb 2023 10:27:08 -0500 Subject: [PATCH 06/11] update yarn.lock Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- yarn.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/yarn.lock b/yarn.lock index 7d00ad8f19..8404d7958f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8376,6 +8376,7 @@ __metadata: resolution: "@backstage/plugin-todo-backend@workspace:plugins/todo-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" From 6a28b421df84f1d6a24f877dc0bb7ca89a1a8fd2 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Mon, 20 Feb 2023 08:40:00 -0500 Subject: [PATCH 07/11] update serviceRef names to adhere to naming convention Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/todo-backend/package.json | 1 + plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts | 4 ++-- plugins/todo-backend/src/lib/TodoReader/index.ts | 2 +- plugins/todo-backend/src/plugin.ts | 4 ++-- plugins/todo-backend/src/service/TodoReaderService.ts | 6 +++--- plugins/todo-backend/src/service/types.ts | 8 -------- yarn.lock | 1 + 7 files changed, 10 insertions(+), 16 deletions(-) diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 51c3f8ede8..e3e15e4f06 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -36,6 +36,7 @@ "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", diff --git a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts index d364316ae1..2804da98cc 100644 --- a/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts +++ b/plugins/todo-backend/src/lib/TodoReader/TodoScmReader.ts @@ -176,8 +176,8 @@ export class TodoScmReader implements TodoReader { /** * @alpha */ -export const todoScmReaderRef = createServiceRef({ - id: 'todoScmReaderFactory', +export const todoReaderServiceRef = createServiceRef({ + id: 'todo.todoReader', defaultFactory: async service => createServiceFactory({ service, diff --git a/plugins/todo-backend/src/lib/TodoReader/index.ts b/plugins/todo-backend/src/lib/TodoReader/index.ts index a56d5805d5..a325fcbe65 100644 --- a/plugins/todo-backend/src/lib/TodoReader/index.ts +++ b/plugins/todo-backend/src/lib/TodoReader/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export { TodoScmReader, todoScmReaderRef } from './TodoScmReader'; +export { TodoScmReader, todoReaderServiceRef } from './TodoScmReader'; export type { TodoScmReaderOptions } from './TodoScmReader'; export { createTodoParser } from './createTodoParser'; export type { TodoParserOptions } from './createTodoParser'; diff --git a/plugins/todo-backend/src/plugin.ts b/plugins/todo-backend/src/plugin.ts index cb25d3ddf0..cbb581f336 100644 --- a/plugins/todo-backend/src/plugin.ts +++ b/plugins/todo-backend/src/plugin.ts @@ -19,7 +19,7 @@ import { coreServices, } from '@backstage/backend-plugin-api'; -import { todoReaderServiceRef } from './service/TodoReaderService'; +import { todoServiceRef } from './service/TodoReaderService'; import { createRouter } from './service/router'; /** @@ -31,7 +31,7 @@ export const todoPlugin = createBackendPlugin({ register(env) { env.registerInit({ deps: { - todoReader: todoReaderServiceRef, + todoReader: todoServiceRef, http: coreServices.httpRouter, }, async init({ http, todoReader }) { diff --git a/plugins/todo-backend/src/service/TodoReaderService.ts b/plugins/todo-backend/src/service/TodoReaderService.ts index b3ed793d38..c5fa0aa70b 100644 --- a/plugins/todo-backend/src/service/TodoReaderService.ts +++ b/plugins/todo-backend/src/service/TodoReaderService.ts @@ -27,7 +27,7 @@ import { } from '@backstage/backend-plugin-api'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; -import { TodoReader, todoScmReaderRef } from '../lib'; +import { TodoReader, todoReaderServiceRef } from '../lib'; import { ListTodosRequest, ListTodosResponse, TodoService } from './types'; const DEFAULT_DEFAULT_PAGE_SIZE = 10; @@ -138,7 +138,7 @@ export class TodoReaderService implements TodoService { } } -export const todoReaderServiceRef: ServiceRef = +export const todoServiceRef: ServiceRef = createServiceRef({ id: 'todo.client', defaultFactory: async service => @@ -146,7 +146,7 @@ export const todoReaderServiceRef: ServiceRef = service, deps: { catalogApi: catalogServiceRef, - todoReader: todoScmReaderRef, + todoReader: todoReaderServiceRef, }, async factory({ catalogApi, todoReader }) { const todoReaderService = new TodoReaderService({ diff --git a/plugins/todo-backend/src/service/types.ts b/plugins/todo-backend/src/service/types.ts index 6b56487605..a6c54b9d87 100644 --- a/plugins/todo-backend/src/service/types.ts +++ b/plugins/todo-backend/src/service/types.ts @@ -57,11 +57,3 @@ export const TODO_FIELDS = [ 'viewUrl', 'repoFilePath', ] as const; - -/** - * @alpha - */ -export type TodosPluginDependencies = { - todoReader: TodoService; - http: HttpRouterService; -}; diff --git a/yarn.lock b/yarn.lock index 8404d7958f..b0ccfe6ec2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8383,6 +8383,7 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" "@types/express": ^4.17.6 "@types/supertest": ^2.0.8 express: ^4.17.1 From 604e17e09bb2cd5741d9372bf6dd5ca1cbda1e6b Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Mon, 20 Feb 2023 08:51:55 -0500 Subject: [PATCH 08/11] remove unused import Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/todo-backend/src/service/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/todo-backend/src/service/types.ts b/plugins/todo-backend/src/service/types.ts index a6c54b9d87..ab15e20027 100644 --- a/plugins/todo-backend/src/service/types.ts +++ b/plugins/todo-backend/src/service/types.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import type { HttpRouterService } from '@backstage/backend-plugin-api'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { TodoItem } from '../lib'; From fe259c1c01af2a934b50f1a938a63608c06af547 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Mon, 20 Feb 2023 08:58:08 -0500 Subject: [PATCH 09/11] update dependencies Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- packages/backend-next/package.json | 3 ++- yarn.lock | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 578cee8ad9..a76d7e1dd3 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -28,7 +28,8 @@ "@backstage/backend-defaults": "workspace:^", "@backstage/plugin-app-backend": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^" + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/plugin-todo-backend": "workspace:^" }, "devDependencies": { "@backstage/cli": "workspace:^" diff --git a/yarn.lock b/yarn.lock index b0ccfe6ec2..e078ec43d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22455,6 +22455,7 @@ __metadata: "@backstage/plugin-app-backend": "workspace:^" "@backstage/plugin-catalog-backend": "workspace:^" "@backstage/plugin-scaffolder-backend": "workspace:^" + "@backstage/plugin-todo-backend": "workspace:^" languageName: unknown linkType: soft From 2b0387915daf698d7f268224772140557b77e774 Mon Sep 17 00:00:00 2001 From: Oleg S <97077423+RobotSail@users.noreply.github.com> Date: Mon, 20 Feb 2023 09:13:52 -0500 Subject: [PATCH 10/11] update api report Signed-off-by: Oleg S <97077423+RobotSail@users.noreply.github.com> --- plugins/todo-backend/api-report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/todo-backend/api-report.md b/plugins/todo-backend/api-report.md index 3908d2475b..dabe2fbfd4 100644 --- a/plugins/todo-backend/api-report.md +++ b/plugins/todo-backend/api-report.md @@ -118,6 +118,9 @@ export type TodoReaderServiceOptions = { defaultPageSize?: number; }; +// @alpha (undocumented) +export const todoReaderServiceRef: ServiceRef; + // @public (undocumented) export class TodoScmReader implements TodoReader { constructor(options: TodoScmReaderOptions); @@ -139,9 +142,6 @@ export type TodoScmReaderOptions = { filePathFilter?: (filePath: string) => boolean; }; -// @alpha (undocumented) -export const todoScmReaderRef: ServiceRef; - // @public (undocumented) export interface TodoService { // (undocumented) From 048b4ab5f564657c2b94809b150a1d28b032a4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 21 Feb 2023 13:37:25 +0100 Subject: [PATCH 11/11] add alpha exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/todo/package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/todo/package.json b/plugins/todo/package.json index 3a0dd54fa8..cc988b3f45 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -8,7 +8,8 @@ "publishConfig": { "access": "public", "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "types": "dist/index.d.ts", + "alphaTypes": "dist/index.alpha.d.ts" }, "backstage": { "role": "frontend-plugin" @@ -20,7 +21,7 @@ "directory": "plugins/todo" }, "scripts": { - "build": "backstage-cli package build", + "build": "backstage-cli package build --experimental-type-build", "start": "backstage-cli package start", "lint": "backstage-cli package lint", "test": "backstage-cli package test", @@ -57,6 +58,7 @@ "msw": "^0.49.0" }, "files": [ - "dist" + "dist", + "alpha" ] }