diff --git a/.changeset/serious-otters-call.md b/.changeset/serious-otters-call.md new file mode 100644 index 0000000..07d81a5 --- /dev/null +++ b/.changeset/serious-otters-call.md @@ -0,0 +1,7 @@ +--- +'@k-phoen/backstage-plugin-announcements-backend': minor +'@k-phoen/backstage-plugin-announcements-common': minor +'@k-phoen/backstage-plugin-announcements': minor +--- + +Correctly index announcements diff --git a/plugins/announcements-backend/src/search/AnnouncementCollatorFactory.ts b/plugins/announcements-backend/src/search/AnnouncementCollatorFactory.ts index 39f4c15..ba2d1e4 100644 --- a/plugins/announcements-backend/src/search/AnnouncementCollatorFactory.ts +++ b/plugins/announcements-backend/src/search/AnnouncementCollatorFactory.ts @@ -2,17 +2,17 @@ import { Readable } from 'stream'; import { Logger } from 'winston'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; -import { IndexableDocument } from '@backstage/plugin-search-common'; -import { Announcement, AnnouncementsClient } from './api'; - -export type IndexableAnnouncementDocument = IndexableDocument & { - excerpt: string; - createdAt: string; -}; +import { AnnouncementsClient } from './api'; +import { TokenManager } from '@backstage/backend-common'; +import { + Announcement, + IndexableAnnouncement, +} from '@k-phoen/backstage-plugin-announcements-common'; type AnnouncementCollatorOptions = { logger: Logger; discoveryApi: DiscoveryApi; + tokenManager?: TokenManager; }; export class AnnouncementCollatorFactory implements DocumentCollatorFactory { @@ -29,6 +29,7 @@ export class AnnouncementCollatorFactory implements DocumentCollatorFactory { this.logger = options.logger; this.announcementsClient = new AnnouncementsClient({ discoveryApi: options.discoveryApi, + tokenManager: options.tokenManager, }); } @@ -36,21 +37,32 @@ export class AnnouncementCollatorFactory implements DocumentCollatorFactory { return Readable.from(this.execute()); } - private async *execute(): AsyncGenerator { - this.logger.info('indexing announcements'); + private async *execute(): AsyncGenerator { + this.logger.info('started indexing announcements'); + + let results: Announcement[] = []; + let page = 1; + const maxPerPage = 50; + + do { + results = await this.announcementsClient.announcements({ + page, + maxPerPage, + }); + + this.logger.debug(`got ${results.length} announcements for page ${page}`); - const results = await this.announcementsClient.announcements(); + for (const result of results) { + yield this.getDocumentInfo(result); + } - this.logger.debug(`got ${results.length} announcements`); + page += 1; + } while (results.length !== 0); - for (const result of results) { - yield this.getDocumentInfo(result); - } + this.logger.info('finished indexing announcements'); } - private getDocumentInfo( - announcement: Announcement, - ): IndexableAnnouncementDocument { + private getDocumentInfo(announcement: Announcement): IndexableAnnouncement { this.logger.debug( `mapping announcement ${announcement.id} to indexable document`, ); diff --git a/plugins/announcements-backend/src/search/api.ts b/plugins/announcements-backend/src/search/api.ts index 4a8790a..588c69a 100644 --- a/plugins/announcements-backend/src/search/api.ts +++ b/plugins/announcements-backend/src/search/api.ts @@ -1,27 +1,36 @@ import fetch from 'node-fetch'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; - -export type Announcement = { - id: string; - publisher: string; - title: string; - excerpt: string; - body: string; - created_at: string; -}; +import { TokenManager } from '@backstage/backend-common'; +import { + Announcement, + AnnouncementsList, +} from '@k-phoen/backstage-plugin-announcements-common'; export class AnnouncementsClient { private readonly discoveryApi: DiscoveryApi; + private readonly tokenManager?: TokenManager; - constructor(opts: { discoveryApi: DiscoveryApi }) { + constructor(opts: { + discoveryApi: DiscoveryApi; + tokenManager?: TokenManager; + }) { this.discoveryApi = opts.discoveryApi; } private async fetch(input: string): Promise { const baseApiUrl = await this.discoveryApi.getBaseUrl('announcements'); - const response = await fetch(`${baseApiUrl}${input}`); + let headers = {}; + + if (this.tokenManager) { + const { token } = await this.tokenManager.getToken(); + headers = { + Authorization: `Bearer ${token}`, + }; + } + + const response = await fetch(`${baseApiUrl}${input}`, headers); if (!response.ok) { throw await ResponseError.fromResponse(response); } @@ -29,7 +38,17 @@ export class AnnouncementsClient { return response.json() as Promise; } - async announcements(): Promise { - return await this.fetch('/'); + async announcements({ + page, + maxPerPage, + }: { + page: number; + maxPerPage: number; + }): Promise { + return this.fetch( + `/announcements?page=${page}&max=${maxPerPage}`, + ).then(response => { + return response.results || []; + }); } } diff --git a/plugins/announcements-backend/src/service/router.ts b/plugins/announcements-backend/src/service/router.ts index 0543253..21a1392 100644 --- a/plugins/announcements-backend/src/service/router.ts +++ b/plugins/announcements-backend/src/service/router.ts @@ -11,24 +11,15 @@ import { BasicPermission, } from '@backstage/plugin-permission-common'; import { + CreateAnnouncementRequest, + CreateCategoryRequest, + UpdateAnnouncementRequest, announcementCreatePermission, announcementDeletePermission, announcementUpdatePermission, } from '@k-phoen/backstage-plugin-announcements-common'; import { AnnouncementsContext } from './announcementsContextBuilder'; -interface AnnouncementRequest { - publisher: string; - category?: string; - title: string; - excerpt: string; - body: string; -} - -interface CategoryRequest { - title: string; -} - export async function createRouter( options: AnnouncementsContext, ): Promise { @@ -112,7 +103,7 @@ export async function createRouter( router.post( '/announcements', - async (req: Request<{}, {}, AnnouncementRequest, {}>, res) => { + async (req: Request<{}, {}, CreateAnnouncementRequest, {}>, res) => { if (!(await isRequestAuthorized(req, announcementCreatePermission))) { throw new NotAllowedError('Unauthorized'); } @@ -132,7 +123,10 @@ export async function createRouter( router.put( '/announcements/:id', - async (req: Request<{ id: string }, {}, AnnouncementRequest, {}>, res) => { + async ( + req: Request<{ id: string }, {}, UpdateAnnouncementRequest, {}>, + res, + ) => { if (!(await isRequestAuthorized(req, announcementUpdatePermission))) { throw new NotAllowedError('Unauthorized'); } @@ -173,7 +167,7 @@ export async function createRouter( router.post( '/categories', - async (req: Request<{}, {}, CategoryRequest, {}>, res) => { + async (req: Request<{}, {}, CreateCategoryRequest, {}>, res) => { const category = { ...req.body, ...{ diff --git a/plugins/announcements-common/package.json b/plugins/announcements-common/package.json index a976ca4..9b46ad4 100644 --- a/plugins/announcements-common/package.json +++ b/plugins/announcements-common/package.json @@ -34,7 +34,8 @@ "@backstage/cli": "^0.25.0" }, "dependencies": { - "@backstage/plugin-permission-common": "^0.7.11" + "@backstage/plugin-permission-common": "^0.7.11", + "@backstage/plugin-search-common": "^1.2.9" }, "files": [ "dist" diff --git a/plugins/announcements-common/src/index.ts b/plugins/announcements-common/src/index.ts index c85954d..e92b376 100644 --- a/plugins/announcements-common/src/index.ts +++ b/plugins/announcements-common/src/index.ts @@ -1 +1,2 @@ export * from './permissions'; +export * from './types'; diff --git a/plugins/announcements-common/src/types.ts b/plugins/announcements-common/src/types.ts new file mode 100644 index 0000000..b04fbb7 --- /dev/null +++ b/plugins/announcements-common/src/types.ts @@ -0,0 +1,39 @@ +import { IndexableDocument } from '@backstage/plugin-search-common'; + +export type Category = { + slug: string; + title: string; +}; + +export type Announcement = { + id: string; + category?: Category; + publisher: string; + title: string; + excerpt: string; + body: string; + created_at: string; +}; + +export type AnnouncementsList = { + count: number; + results: Announcement[]; +}; + +export type IndexableAnnouncement = IndexableDocument & { + excerpt: string; + createdAt: string; +}; + +export type CreateAnnouncementRequest = Omit< + Announcement, + 'id' | 'category' | 'created_at' +> & { + category?: string; +}; + +export type UpdateAnnouncementRequest = CreateAnnouncementRequest; + +export type CreateCategoryRequest = { + title: string; +}; diff --git a/plugins/announcements/src/api.ts b/plugins/announcements/src/api.ts index 48c4033..25e64b8 100644 --- a/plugins/announcements/src/api.ts +++ b/plugins/announcements/src/api.ts @@ -8,39 +8,16 @@ import { } from '@backstage/core-plugin-api'; import { ResponseError } from '@backstage/errors'; import { FetchApi } from '@backstage/core-plugin-api'; - -const lastSeenKey = 'user_last_seen_date'; - -export type Category = { - slug: string; - title: string; -}; - -export type Announcement = { - id: string; - category?: Category; - publisher: string; - title: string; - excerpt: string; - body: string; - created_at: string; -}; - -export type AnnouncementsList = { - count: number; - results: Announcement[]; -}; - -export type CreateAnnouncementRequest = Omit< +import { Announcement, - 'id' | 'category' | 'created_at' -> & { - category?: string; -}; + AnnouncementsList, + Category, + CreateAnnouncementRequest, + CreateCategoryRequest, + UpdateAnnouncementRequest, +} from '@k-phoen/backstage-plugin-announcements-common'; -export type CreateCategoryRequest = { - title: string; -}; +const lastSeenKey = 'user_last_seen_date'; export interface AnnouncementsApi { announcements(opts: { @@ -53,7 +30,7 @@ export interface AnnouncementsApi { createAnnouncement(request: CreateAnnouncementRequest): Promise; updateAnnouncement( id: string, - request: CreateAnnouncementRequest, + request: UpdateAnnouncementRequest, ): Promise; deleteAnnouncementByID(id: string): Promise; @@ -171,7 +148,7 @@ export class DefaultAnnouncementsApi implements AnnouncementsApi { async updateAnnouncement( id: string, - request: CreateAnnouncementRequest, + request: UpdateAnnouncementRequest, ): Promise { return this.fetch(`/announcements/${id}`, { method: 'PUT', diff --git a/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx b/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx index 566de8e..0b0b586 100644 --- a/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx +++ b/plugins/announcements/src/components/AnnouncementForm/AnnouncementForm.tsx @@ -8,13 +8,13 @@ import { makeStyles, TextField, } from '@material-ui/core'; +import { announcementsApiRef } from '../../api'; +import { Autocomplete } from '@material-ui/lab'; +import { useAsync } from 'react-use'; import { Announcement, - announcementsApiRef, CreateAnnouncementRequest, -} from '../../api'; -import { Autocomplete } from '@material-ui/lab'; -import { useAsync } from 'react-use'; +} from '@k-phoen/backstage-plugin-announcements-common'; const useStyles = makeStyles(theme => ({ formRoot: { diff --git a/plugins/announcements/src/components/AnnouncementPage/AnnouncementPage.tsx b/plugins/announcements/src/components/AnnouncementPage/AnnouncementPage.tsx index df07ba6..0cf1238 100644 --- a/plugins/announcements/src/components/AnnouncementPage/AnnouncementPage.tsx +++ b/plugins/announcements/src/components/AnnouncementPage/AnnouncementPage.tsx @@ -22,8 +22,9 @@ import { } from '@backstage/plugin-catalog-react'; import Alert from '@material-ui/lab/Alert'; import { Grid } from '@material-ui/core'; -import { Announcement, announcementsApiRef } from '../../api'; +import { announcementsApiRef } from '../../api'; import { announcementViewRouteRef, rootRouteRef } from '../../routes'; +import { Announcement } from '@k-phoen/backstage-plugin-announcements-common'; const AnnouncementDetails = ({ announcement, diff --git a/plugins/announcements/src/components/AnnouncementSearchResultListItem/AnnouncementSearchResultListItem.tsx b/plugins/announcements/src/components/AnnouncementSearchResultListItem/AnnouncementSearchResultListItem.tsx index a7883d5..1eefa1b 100644 --- a/plugins/announcements/src/components/AnnouncementSearchResultListItem/AnnouncementSearchResultListItem.tsx +++ b/plugins/announcements/src/components/AnnouncementSearchResultListItem/AnnouncementSearchResultListItem.tsx @@ -14,6 +14,7 @@ import { makeStyles, } from '@material-ui/core'; import RecordVoiceOverIcon from '@material-ui/icons/RecordVoiceOver'; +import { IndexableAnnouncement } from '@k-phoen/backstage-plugin-announcements-common'; const useStyles = makeStyles({ createdAt: { @@ -30,10 +31,6 @@ const useStyles = makeStyles({ }, }); -export type IndexableAnnouncement = IndexableDocument & { - createdAt: string; -}; - export interface AnnouncementSearchResultProps { result?: IndexableDocument; highlight?: ResultHighlight; diff --git a/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx b/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx index 859070f..6b5f165 100644 --- a/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx +++ b/plugins/announcements/src/components/AnnouncementsPage/AnnouncementsPage.tsx @@ -6,6 +6,7 @@ import { announcementCreatePermission, announcementUpdatePermission, announcementDeletePermission, + Announcement, } from '@k-phoen/backstage-plugin-announcements-common'; import { DateTime } from 'luxon'; import { @@ -42,7 +43,7 @@ import { announcementViewRouteRef, rootRouteRef, } from '../../routes'; -import { Announcement, announcementsApiRef } from '../../api'; +import { announcementsApiRef } from '../../api'; import { DeleteAnnouncementDialog } from './DeleteAnnouncementDialog'; import { useDeleteAnnouncementDialogState } from './useDeleteAnnouncementDialogState'; import { Pagination } from '@material-ui/lab'; diff --git a/plugins/announcements/src/components/AnnouncementsPage/useDeleteAnnouncementDialogState.tsx b/plugins/announcements/src/components/AnnouncementsPage/useDeleteAnnouncementDialogState.tsx index f2035a9..a0b78c6 100644 --- a/plugins/announcements/src/components/AnnouncementsPage/useDeleteAnnouncementDialogState.tsx +++ b/plugins/announcements/src/components/AnnouncementsPage/useDeleteAnnouncementDialogState.tsx @@ -1,5 +1,5 @@ +import { Announcement } from '@k-phoen/backstage-plugin-announcements-common'; import { useCallback, useState } from 'react'; -import { Announcement } from '../../api'; export type DeleteAnnouncementDialogState = { open: (a: Announcement) => void; diff --git a/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx b/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx index a40da10..6a42a25 100644 --- a/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx +++ b/plugins/announcements/src/components/CategoriesPage/CategoriesPage.tsx @@ -11,8 +11,9 @@ import { import { useApi } from '@backstage/core-plugin-api'; import { Button, makeStyles } from '@material-ui/core'; import AddIcon from '@material-ui/icons/Add'; -import { announcementsApiRef, Category } from '../../api'; +import { announcementsApiRef } from '../../api'; import { NewCategoryDialog } from '../NewCategoryDialog'; +import { Category } from '@k-phoen/backstage-plugin-announcements-common'; const useStyles = makeStyles(theme => ({ container: { diff --git a/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx b/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx index 36eb2e9..1c02fd1 100644 --- a/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx +++ b/plugins/announcements/src/components/CreateAnnouncementPage/CreateAnnouncementPage.tsx @@ -2,13 +2,13 @@ import React, { ReactNode } from 'react'; import { useNavigate } from 'react-router-dom'; import { Page, Header, Content } from '@backstage/core-components'; import { alertApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { announcementsApiRef } from '../../api'; +import { rootRouteRef } from '../../routes'; +import { AnnouncementForm } from '../AnnouncementForm'; import { Announcement, - announcementsApiRef, CreateAnnouncementRequest, -} from '../../api'; -import { rootRouteRef } from '../../routes'; -import { AnnouncementForm } from '../AnnouncementForm'; +} from '@k-phoen/backstage-plugin-announcements-common'; type CreateAnnouncementPageProps = { themeId: string; diff --git a/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx b/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx index 5c7417a..c07b31e 100644 --- a/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx +++ b/plugins/announcements/src/components/EditAnnouncementPage/EditAnnouncementPage.tsx @@ -9,7 +9,8 @@ import { import { Alert } from '@material-ui/lab'; import { AnnouncementForm } from '../AnnouncementForm'; import { announcementEditRouteRef } from '../../routes'; -import { announcementsApiRef, CreateAnnouncementRequest } from '../../api'; +import { announcementsApiRef } from '../../api'; +import { UpdateAnnouncementRequest } from '@k-phoen/backstage-plugin-announcements-common'; type EditAnnouncementPageProps = { themeId: string; @@ -28,7 +29,7 @@ export const EditAnnouncementPage = (props: EditAnnouncementPageProps) => { let title = props.title; let content: React.ReactNode = ; - const onSubmit = async (request: CreateAnnouncementRequest) => { + const onSubmit = async (request: UpdateAnnouncementRequest) => { try { await announcementsApi.updateAnnouncement(id, request); alertApi.post({ message: 'Announcement updated.', severity: 'success' }); diff --git a/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx b/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx index 9240139..57cfc1c 100644 --- a/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx +++ b/plugins/announcements/src/components/NewAnnouncementBanner/NewAnnouncementBanner.tsx @@ -11,8 +11,9 @@ import { } from '@material-ui/core'; import { Alert } from '@material-ui/lab'; import Close from '@material-ui/icons/Close'; -import { Announcement, announcementsApiRef } from '../../api'; +import { announcementsApiRef } from '../../api'; import { announcementViewRouteRef } from '../../routes'; +import { Announcement } from '@k-phoen/backstage-plugin-announcements-common'; const useStyles = makeStyles(theme => ({ // showing on top, as a block