Create announcements workspace and migrate search module (#2148)

* yarn create-workspace announcements

Signed-off-by: Kurt King <kurtaking@gmail.com>

* Migrate announcements search module

Signed-off-by: Kurt King <kurtaking@gmail.com>

* fix prettier

Signed-off-by: Kurt King <kurtaking@gmail.com>

* fix: yarn build:api-reports

Signed-off-by: Kurt King <kurtaking@gmail.com>

* fix: yarn backstage-cli repo fix --publish

Signed-off-by: Kurt King <kurtaking@gmail.com>

* chore: add myself to codeowners

Signed-off-by: Kurt King <kurtaking@gmail.com>

---------

Signed-off-by: Kurt King <kurtaking@gmail.com>
Co-authored-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com>
This commit is contained in:
Kurt King 2024-12-19 06:28:13 -07:00 committed by GitHub
parent 350250ce7b
commit 0e2ee41471
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 24539 additions and 0 deletions

1
.github/CODEOWNERS vendored
View File

@ -12,6 +12,7 @@ yarn.lock @backstage/community-plugins
/workspaces/acr @backstage/community-plugins-maintainers @christoph-jerolimov @ciiay @invincibleJai
/workspaces/adr @backstage/community-plugins-maintainers @kuangp
/workspaces/analytics @backstage/community-plugins-maintainers @jmezach
/workspaces/announcements @backstage/community-plugins-maintainers @kurtaking
/workspaces/azure-devops @backstage/community-plugins-maintainers @awanlin
/workspaces/azure-storage-explorer @backstage/community-plugins-maintainers @deepan10
/workspaces/bitrise @backstage/community-plugins-maintainers @backstage/sda-se-reviewers

View File

@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

View File

@ -0,0 +1,14 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"privatePackages": {
"tag": false,
"version": false
}
}

View File

@ -0,0 +1,8 @@
.git
.yarn/cache
.yarn/install-state.gz
node_modules
packages/*/src
packages/*/node_modules
plugins
*.local.yaml

View File

@ -0,0 +1 @@
playwright.config.ts

View File

@ -0,0 +1 @@
module.exports = require('../../.eslintrc.cjs');

54
workspaces/announcements/.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
# macOS
.DS_Store
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Coverage directory generated when running tests with coverage
coverage
# Dependencies
node_modules/
# Yarn 3 files
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Node version directives
.nvmrc
# dotenv environment variables file
.env
.env.test
# Build output
dist
dist-types
# Temporary change files created by Vim
*.swp
# MkDocs build output
site
# Local configuration files
*.local.yaml
# Sensitive credentials
*-credentials.yaml
# vscode database functionality support files
*.session.sql
# E2E test reports
e2e-test-report/

View File

@ -0,0 +1,5 @@
dist
dist-types
coverage
.vscode
.eslintrc.js

View File

@ -0,0 +1,16 @@
# [Backstage](https://backstage.io)
This is your newly scaffolded Backstage App, Good Luck!
To start the app, run:
```sh
yarn install
yarn dev
```
To generate knip reports for this app, run:
```sh
yarn backstage-repo-tools knip-reports
```

View File

@ -0,0 +1 @@
{ "version": "1.33.0" }

View File

@ -0,0 +1,13 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: announcements
description: An example of a Backstage application.
# Example for optional annotations
# annotations:
# github.com/project-slug: backstage/backstage
# backstage.io/techdocs-ref: dir:.
spec:
type: website
owner: john@example.com
lifecycle: experimental

View File

@ -0,0 +1,63 @@
{
"name": "@internal/announcements",
"version": "1.0.0",
"private": true,
"engines": {
"node": "18 || 20"
},
"scripts": {
"tsc": "tsc",
"tsc:full": "tsc --skipLibCheck false --incremental false",
"build:all": "backstage-cli repo build --all",
"build:api-reports": "yarn build:api-reports:only --tsc",
"build:api-reports:only": "backstage-repo-tools api-reports -o ae-wrong-input-file-type,ae-undocumented --validate-release-tags",
"clean": "backstage-cli repo clean",
"test": "backstage-cli repo test",
"test:all": "backstage-cli repo test --coverage",
"fix": "backstage-cli repo fix",
"lint": "backstage-cli repo lint --since origin/main",
"lint:all": "backstage-cli repo lint",
"prettier:check": "prettier --check .",
"prettier:fix": "prettier --ignore-unknown --write .",
"new": "backstage-cli new --scope @backstage-community",
"postinstall": "cd ../../ && yarn install"
},
"workspaces": {
"packages": [
"packages/*",
"plugins/*"
]
},
"repository": {
"type": "git",
"url": "https://github.com/backstage/community-plugins",
"directory": "workspaces/announcements"
},
"devDependencies": {
"@backstage/cli": "^0.29.0",
"@backstage/e2e-test-utils": "^0.1.1",
"@backstage/repo-tools": "^0.10.0",
"@changesets/cli": "^2.27.1",
"@spotify/prettier-config": "^12.0.0",
"node-gyp": "^9.0.0",
"prettier": "^2.3.2",
"typescript": "~5.3.0"
},
"dependencies": {
"knip": "^5.27.4"
},
"resolutions": {
"@types/react": "^18",
"@types/react-dom": "^18"
},
"prettier": "@spotify/prettier-config",
"lint-staged": {
"*.{js,jsx,ts,tsx,mjs,cjs}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
}
}

View File

@ -0,0 +1,9 @@
# The Plugins Folder
This is where your own plugins and their associated modules live, each in a
separate folder of its own.
If you want to create a new plugin here, go to your project root directory, run
the command `yarn new`, and follow the on-screen instructions.
You can also check out existing plugins on [the plugin marketplace](https://backstage.io/plugins)!

View File

@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);

View File

@ -0,0 +1,8 @@
# @backstage-community/plugin-search-backend-module-announcements
The announcements backend module for the search plugin.
## Previously maintained by
- [procore-oss](https://github.com/procore-oss/backstage-plugin-announcements/tree/main/plugins/search-backend-module-announcements)
- [K-Phoen](https://github.com/K-Phoen/backstage-plugin-announcements/tree/main/plugins/announcements-backend/src/search)

View File

@ -0,0 +1,32 @@
/*
* Copyright 2024 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 { SchedulerServiceTaskScheduleDefinitionConfig } from '@backstage/backend-plugin-api';
export interface Config {
search?: {
collators?: {
/**
* Configuration options for `@procore-oss/backstage-plugin-search-backend-module-announcements`
*/
announcements?: {
/**
* The schedule for how often to run the collation job.
*/
schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
};
};
};
}

View File

@ -0,0 +1,54 @@
{
"name": "@backstage-community/plugin-search-backend-module-announcements",
"description": "The announcements backend module for the search plugin.",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"repository": {
"type": "git",
"url": "https://github.com/backstage/community-plugins",
"directory": "workspaces/announcements/plugins/search-backend-module-announcements"
},
"backstage": {
"role": "backend-plugin-module",
"pluginId": "search",
"pluginPackage": "@backstage/plugin-search-backend"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/backend-plugin-api": "^1.0.2",
"@backstage/plugin-search-backend-node": "^1.3.5",
"@backstage/plugin-search-common": "^1.2.15",
"@procore-oss/backstage-plugin-announcements-common": "^0.2.8",
"@procore-oss/backstage-plugin-announcements-node": "^0.3.3"
},
"devDependencies": {
"@backstage/backend-test-utils": "^1.1.0",
"@backstage/cli": "^0.29.0",
"@backstage/test-utils": "^1.7.2",
"@testing-library/react": "^14.0.0",
"msw": "^1.3.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.3.0"
},
"files": [
"dist",
"config.d.ts"
]
}

View File

@ -0,0 +1,11 @@
## API Report File for "@backstage-community/plugin-search-backend-module-announcements"
> 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';
// @public
const searchModuleAnnouncementsCollator: BackendFeature;
export default searchModuleAnnouncementsCollator;
```

View File

@ -0,0 +1,96 @@
/*
* Copyright 2024 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 { AnnouncementCollatorFactory } from './AnnouncementCollatorFactory';
import { Readable } from 'stream';
import { TestPipeline } from '@backstage/plugin-search-backend-node';
import { registerMswTestHooks } from '@backstage/test-utils';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { mockServices } from '@backstage/backend-test-utils';
const mockAnnouncements = {
count: 3,
results: [
{
id: '1',
title: 'title',
publisher: 'publisher1',
body: 'body',
excerpt: 'excerpt',
created_at: 'created_at',
},
{
id: '2',
title: 'title',
publisher: 'publisher2',
body: 'body',
excerpt: 'excerpt',
created_at: 'created_at',
},
{
id: '3',
title: 'title',
publisher: 'publisher3',
body: 'body',
excerpt: 'excerpt',
created_at: 'created_at',
},
],
};
describe('AnnouncementCollatorFactory', () => {
const mockDiscovery = mockServices.discovery.mock({
getBaseUrl: jest.fn().mockReturnValue('http://localhost:7007/api'),
});
const factory = AnnouncementCollatorFactory.fromConfig({
logger: mockServices.logger.mock(),
discoveryApi: mockDiscovery,
auth: mockServices.auth(),
});
it('has expected type', () => {
expect(factory.type).toBe('announcements');
});
describe('getCollator', () => {
const worker = setupServer();
registerMswTestHooks(worker);
let collator: Readable;
beforeEach(async () => {
collator = await factory.getCollator();
worker.use(
rest.get('http://localhost:7007/api/announcements', (_, res, ctx) =>
res(ctx.status(200), ctx.json(mockAnnouncements)),
),
);
});
it('should return a Readable stream', async () => {
collator = await factory.getCollator();
expect(collator).toBeInstanceOf(Readable);
});
it('runs against announcements', async () => {
collator = await factory.getCollator();
const pipeline = TestPipeline.fromCollator(collator);
const { documents } = await pipeline.execute();
expect(mockDiscovery.getBaseUrl).toHaveBeenCalledWith('announcements');
expect(documents).toHaveLength(mockAnnouncements.results.length);
});
});
});

View File

@ -0,0 +1,110 @@
/*
* Copyright 2024 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 { Readable } from 'stream';
import {
DocumentCollatorFactory,
IndexableDocument,
} from '@backstage/plugin-search-common';
import { DefaultAnnouncementsService } from '@procore-oss/backstage-plugin-announcements-node';
import { Announcement } from '@procore-oss/backstage-plugin-announcements-common';
import {
AuthService,
DiscoveryService,
LoggerService,
} from '@backstage/backend-plugin-api';
/**
* Indexable document for announcements.
*
* @public
*/
type IndexableAnnouncementDocument = IndexableDocument & {
excerpt: string;
createdAt: string;
};
/**
* Options for {@link AnnouncementCollatorFactory}
*
* @public
*/
type AnnouncementCollatorOptions = {
logger: LoggerService;
discoveryApi: DiscoveryService;
auth: AuthService;
};
/**
* Search collator responsibile for indexing announcements.
*
* @public
*/
export class AnnouncementCollatorFactory implements DocumentCollatorFactory {
public readonly type: string = 'announcements';
private readonly logger: LoggerService;
private readonly announcementsClient: DefaultAnnouncementsService;
private readonly auth: AuthService;
static fromConfig(options: AnnouncementCollatorOptions) {
return new AnnouncementCollatorFactory(options);
}
private constructor(options: AnnouncementCollatorOptions) {
this.logger = options.logger;
this.announcementsClient = new DefaultAnnouncementsService({
discoveryApi: options.discoveryApi,
});
this.auth = options.auth;
}
async getCollator() {
return Readable.from(this.execute());
}
private async *execute(): AsyncGenerator<IndexableAnnouncementDocument> {
this.logger.info('indexing announcements');
const { token } = await this.auth.getPluginRequestToken({
onBehalfOf: await this.auth.getOwnServiceCredentials(),
targetPluginId: 'announcements',
});
const results = await this.announcementsClient.announcements({ token });
this.logger.debug(`got ${results.length} announcements`);
for (const result of results) {
yield this.getDocumentInfo(result);
}
}
private getDocumentInfo(
announcement: Announcement,
): IndexableAnnouncementDocument {
this.logger.debug(
`mapping announcement ${announcement.id} to indexable document`,
);
return {
title: announcement.title,
text: announcement.body,
excerpt: announcement.excerpt,
createdAt: announcement.created_at,
location: `/announcements/view/${announcement.id}`,
};
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2024 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.
*/
/**
* The announcements backend module for the search plugin.
*
* @packageDocumentation
*/
export { searchModuleAnnouncementsCollator as default } from './module';

View File

@ -0,0 +1,74 @@
/*
* Copyright 2024 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 {
coreServices,
createBackendModule,
readSchedulerServiceTaskScheduleDefinitionFromConfig,
} from '@backstage/backend-plugin-api';
import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha';
import { AnnouncementCollatorFactory } from './collators/AnnouncementCollatorFactory';
/**
* Search backend module for announcements.
*
* @public
*/
export const searchModuleAnnouncementsCollator = createBackendModule({
pluginId: 'search',
moduleId: 'announcements',
register(reg) {
reg.registerInit({
deps: {
logger: coreServices.logger,
config: coreServices.rootConfig,
permissions: coreServices.permissions,
discovery: coreServices.discovery,
scheduler: coreServices.scheduler,
auth: coreServices.auth,
indexRegistry: searchIndexRegistryExtensionPoint,
},
async init({
config,
logger,
discovery,
scheduler,
auth,
indexRegistry,
}) {
const defaultSchedule = {
frequency: { minutes: 10 },
timeout: { minutes: 15 },
initialDelay: { seconds: 3 },
};
const schedule = config.has('search.collators.announcements.schedule')
? readSchedulerServiceTaskScheduleDefinitionFromConfig(
config.getConfig('search.collators.announcements.schedule'),
)
: defaultSchedule;
indexRegistry.addCollator({
schedule: scheduler.createScheduledTaskRunner(schedule),
factory: AnnouncementCollatorFactory.fromConfig({
discoveryApi: discovery,
logger,
auth,
}),
});
},
});
},
});

View File

@ -0,0 +1,18 @@
{
"extends": "@backstage/cli/config/tsconfig.json",
"include": [
"packages/*/src",
"plugins/*/src",
"plugins/*/dev",
"plugins/*/migrations"
],
"files": ["node_modules/@backstage/cli/asset-types/asset-types.d.ts"],
"exclude": ["node_modules"],
"compilerOptions": {
"outDir": "dist-types",
"rootDir": ".",
"lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2022"],
"target": "ES2022",
"useUnknownInCatchVariables": false
}
}

File diff suppressed because it is too large Load Diff