Skip to content

Commit

Permalink
feat(marketplace): Add Plugin installation status in marketplace plug…
Browse files Browse the repository at this point in the history
…in (#250)

* add static and dynamic plugin processors to get the install status of a plugin

* add unit tests for catalog module

* add knip and api reports

* release marketplace plugin as 0.0.1

* address review comments
  • Loading branch information
karthikjeeyar authored Jan 8, 2025
1 parent 00f0cea commit 56f9484
Show file tree
Hide file tree
Showing 27 changed files with 1,284 additions and 34 deletions.
8 changes: 8 additions & 0 deletions workspaces/marketplace/.changeset/perfect-buses-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@red-hat-developer-hub/backstage-plugin-catalog-backend-module-marketplace': patch
'@red-hat-developer-hub/backstage-plugin-marketplace-backend': patch
'@red-hat-developer-hub/backstage-plugin-marketplace-common': patch
'@red-hat-developer-hub/backstage-plugin-marketplace': patch
---

release marketplace plugin
2 changes: 1 addition & 1 deletion workspaces/marketplace/app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ catalog:
entityFilename: catalog-info.yaml
pullRequestBranchName: backstage-integration
rules:
- allow: [Component, System, API, Resource, Location]
- allow: [Component, System, API, Resource, Plugin, PluginList, Location]
locations:
# Local example data, file locations are relative to the backend process, typically `packages/backend`
- type: file
Expand Down
1 change: 1 addition & 0 deletions workspaces/marketplace/examples/all-plugins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ spec:
- ./plugins/quay.yaml
- ./plugins/tekton.yaml
- ./plugins/topology.yaml
- ./plugins/search.yaml
14 changes: 14 additions & 0 deletions workspaces/marketplace/examples/plugins/search.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: marketplace.backstage.io/v1alpha1
kind: Plugin
metadata:
name: search
title: Backstage Search
description: Backstage Search lets you find the right information you are looking for in the Backstage ecosystem.
spec:
icon: https://janus-idp.io/images/plugins/3scale.svg
type: backend-plugin
lifecycle: production
owner: redhat
packages:
- name: '@backstage/plugin-search-backend'
version: '^1.0.0, ^1.1.1'
Original file line number Diff line number Diff line change
@@ -1,5 +1,116 @@
# @red-hat-developer-hub/backstage-plugin-catalog-backend-module-marketplace

The marketplace backend module for the catalog plugin.
This is an extension module to the plugin-catalog-backend plugin, providing new kinds of entity and processors that can be used to ingest marketplace plugin data.

_This plugin was created through the Backstage CLI_
## Getting Started

This marketplace catalog module introduces two new kinds to the software catalog called `Plugin` and `PluginList`.

## Installation

### Install the package

```shell
# From your Backstage root directory
yarn --cwd packages/backend add @red-hat-developer-hub/backstage-plugin-catalog-backend-module-marketplace
```

### Adding the plugin to your packages/backend

```typescript
backend.add(
import(
'@red-hat-developer-hub/backstage-plugin-catalog-backend-module-marketplace'
),
);
```

### Allow Plugin and PluginList to import via catalog

Add `Plugin` and `PluginList` kinds to the catalog rules in `app-config.yaml` to import this entities from external locations.

```yaml
catalog:
rules:
- allow: [Component, System, API, Resource, Location, Plugin, PluginList]
```
## Plugin configuration YAML Guide:
This YAML file is used to add marketplace plugin to the Software catalog in your backstage application.
```yaml
apiVersion: marketplace.backstage.io/v1alpha1
kind: Plugin
metadata:
name: tekton
title: Pipelines with Tekton
description: Easily view Tekton PipelineRun status for your services in Backstage.
spec:
type: frontend-plugin
lifecycle: production
owner: redhat
categories:
- CI/CD
developer: Red Hat
icon: https://janus-idp.io/images/plugins/tekton.svg
description: |
# Tekton plugin for Backstage
installation:
markdown: |
# Setting up the Tekton plugin
packages:
- name: '@backstage/plugin-search-backend'
version: '^1.0.0, ^1.1.1'
```
## Structure Overview
The YAML file is structured into the following sections:
- `spec.type`: Defines type of the plugin. Possible values are frontend-plugin, backend-plugin, backend-plugin-module
- `spec.categories`: Defines the categories for the plugin.
- `spec.description`: Description to show in the sidebar.
- `spec.installation.markdown`: Installation guide for the plugin.
- `spec.packages` - Defines all the package names to identify the installation status of the plugin. This field accepts both array of plugin names and JSON format. ie: name, version, backstage and distribution information.

**_NOTE:_** more examlpes of the plugins can be seen [here](https://github.com/redhat-developer/rhdh-plugins/tree/main/workspaces/marketplace/examples/plugins).

### PluginList configuration YAML Guide:

This entity allows you to create a curated list of plugins. This YAML file is used to add marketplace `PluginList` to the Software catalog in your backstage application.

```yaml
apiVersion: marketplace.backstage.io/v1alpha1
kind: PluginList
metadata:
name: featured-plugins
title: Featured Plugins
description: A set of great plugins! :)
spec:
type: curated-list
lifecycle: production
owner: redhat
plugins:
- quay
- 3scale
- tekton
- topology
```

## Structure Overview

The YAML file is structured into the following sections:

- `spec.plugins`: Defines a list of the plugins.

**_NOTE:_** more examlpes of the plugins can be seen [here](https://github.com/redhat-developer/rhdh-plugins/tree/main/workspaces/marketplace/examples/pluginlists).

## Processors

This module provides different process used to ingest the `plugin` and `pluginList` entity into the software catalog.

- **MarketplacePluginProcessors** - Ingests `Plugin` entity into the catalog.
- **MarketplacePluginListProcessors** - Ingests `PluginList` entity into the catalog.
- **LocalPluginInstallStatusProcessor** - Add and Update `entity.spec.installStatus` based on the packages installed in backstage workspaces.
- **DynamicPluginInstallStatusProcessor** - Add and Update `entity.spec.installStatus` based on the dynamic plugin installed in RHDH. This processor will work only when you have [scalprum-backend](https://github.com/janus-idp/backstage-showcase/tree/main/plugins/scalprum-backend) installed in your backstage instance.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Knip report
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": true,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
Expand Down Expand Up @@ -41,10 +40,9 @@
"@backstage/catalog-model": "^1.7.1",
"@backstage/plugin-catalog-common": "^1.1.0",
"@backstage/plugin-catalog-node": "^1.13.1",
"@backstage/types": "^1.2.0",
"@red-hat-developer-hub/backstage-plugin-marketplace-common": "workspace:^",
"express": "^4.17.1",
"glob": "^10.4.5",
"yaml": "^2.6.0"
"semver": "^7.6.3"
},
"devDependencies": {
"@backstage/backend-test-utils": "^1.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,87 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AuthService } from '@backstage/backend-plugin-api';
import { BackendFeature } from '@backstage/backend-plugin-api';
import { CatalogProcessor } from '@backstage/plugin-catalog-node';
import { CatalogProcessorCache } from '@backstage/plugin-catalog-node';
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node';
import { DiscoveryService } from '@backstage/backend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { LocationSpec } from '@backstage/plugin-catalog-common';
import { MarketplacePluginEntry } from '@red-hat-developer-hub/backstage-plugin-marketplace-common';

// @public (undocumented)
export type CachedData = {
[key: string]: number | string[];
plugins: any;
cachedTime: number;
};

// @public (undocumented)
const catalogModuleMarketplace: BackendFeature;
export default catalogModuleMarketplace;

// @public (undocumented)
export class DynamicPluginInstallStatusProcessor implements CatalogProcessor {
constructor(discovery: DiscoveryService, auth: AuthService);
// (undocumented)
getCachedPlugins(
cache: CatalogProcessorCache,
entityRef: string,
): Promise<CachedData>;
// (undocumented)
getInstalledPlugins(): Promise<any>;
// (undocumented)
getProcessorName(): string;
// (undocumented)
preProcessEntity(
entity: Entity,
_location: LocationSpec,
_emit: CatalogProcessorEmit,
_originLocation: LocationSpec,
cache: CatalogProcessorCache,
): Promise<MarketplacePluginEntry>;
}

// @public (undocumented)
export class LocalPluginInstallStatusProcessor implements CatalogProcessor {
constructor(paths?: string[]);
// (undocumented)
findWorkspacesPath(startPath?: string): string;
// (undocumented)
getProcessorName(): string;
// (undocumented)
isJSON(str: string): boolean;
// (undocumented)
preProcessEntity(entity: MarketplacePluginEntry): Promise<Entity>;
}

// @public (undocumented)
export class MarketplacePluginListProcessor implements CatalogProcessor {
// (undocumented)
getProcessorName(): string;
// (undocumented)
postProcessEntity(
entity: Entity,
_location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity>;
// (undocumented)
validateEntityKind(entity: Entity): Promise<boolean>;
}

// @public (undocumented)
export class MarketplacePluginProcessor implements CatalogProcessor {
// (undocumented)
getProcessorName(): string;
// (undocumented)
postProcessEntity(
entity: Entity,
_location: LocationSpec,
emit: CatalogProcessorEmit,
): Promise<Entity>;
// (undocumented)
validateEntityKind(entity: Entity): Promise<boolean>;
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
*/

export { catalogModuleMarketplace as default } from './module';

export * from './processors';
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2025 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 { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { catalogModuleMarketplace } from './module';
import { startTestBackend } from '@backstage/backend-test-utils';

describe('catalogModuleMarketplace', () => {
it('should register the extension point', async () => {
const extensionPoint = { addProcessor: jest.fn() };
await startTestBackend({
extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]],
features: [catalogModuleMarketplace],
});

expect(extensionPoint.addProcessor).toHaveBeenCalledTimes(4);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ import {
createBackendModule,
} from '@backstage/backend-plugin-api';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { MarketplacePluginProcessor } from './MarketplacePluginProcessor';
import { MarketplacePluginListProcessor } from './MarketplacePluginListProcessor';
import { MarketplacePluginProcessor } from './processors/MarketplacePluginProcessor';
import { MarketplacePluginListProcessor } from './processors/MarketplacePluginListProcessor';
import { DynamicPluginInstallStatusProcessor } from './processors/DynamicPluginInstallStatusProcessor';
import { LocalPluginInstallStatusProcessor } from './processors/LocalPluginInstallStatusProcessor';

/**
* @public
Expand All @@ -33,11 +35,17 @@ export const catalogModuleMarketplace = createBackendModule({
deps: {
logger: coreServices.logger,
catalog: catalogProcessingExtensionPoint,
discovery: coreServices.discovery,
auth: coreServices.auth,
},
async init({ logger, catalog }) {
logger.info('Marketplace provider initialized!');
async init({ logger, catalog, discovery, auth }) {
logger.info('Adding Marketplace processors to catalog...');
catalog.addProcessor(new MarketplacePluginProcessor());
catalog.addProcessor(new MarketplacePluginListProcessor());
catalog.addProcessor(new LocalPluginInstallStatusProcessor());
catalog.addProcessor(
new DynamicPluginInstallStatusProcessor(discovery, auth),
);
},
});
},
Expand Down
Loading

0 comments on commit 56f9484

Please sign in to comment.