Merge pull request #25772 from backstage/techdocs-common

techdocs: introduce common package for annotation constants
This commit is contained in:
MT Lewis
2024-07-30 08:38:41 +01:00
committed by GitHub
26 changed files with 214 additions and 44 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-techdocs-backend': patch
'@backstage/plugin-techdocs-node': patch
'@backstage/plugin-techdocs': patch
---
Use annotation constants from new techdocs-common package.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs-common': minor
---
Initial release of the techdocs-common package.
+2
View File
@@ -8,6 +8,7 @@
"pluginPackages": [
"@backstage/plugin-techdocs",
"@backstage/plugin-techdocs-backend",
"@backstage/plugin-techdocs-common",
"@backstage/plugin-techdocs-node",
"@backstage/plugin-techdocs-react"
]
@@ -67,6 +68,7 @@
"@backstage/plugin-catalog-common": "workspace:^",
"@backstage/plugin-permission-common": "workspace:^",
"@backstage/plugin-search-backend-module-techdocs": "workspace:^",
"@backstage/plugin-techdocs-common": "workspace:^",
"@backstage/plugin-techdocs-node": "workspace:^",
"@types/express": "^4.17.6",
"express": "^4.17.1",
@@ -28,6 +28,7 @@ import {
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { ConfigReader } from '@backstage/config';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
const logger = loggerToWinstonLogger(mockServices.logger.mock());
@@ -66,7 +67,7 @@ const expectedEntities: Entity[] = [
name: 'test-entity-with-docs',
description: 'Documented description',
annotations: {
'backstage.io/techdocs-ref': './',
[TECHDOCS_ANNOTATION]: './',
},
},
spec: {
@@ -37,6 +37,7 @@ import {
CATALOG_FILTER_EXISTS,
} from '@backstage/catalog-client';
import { TechDocsDocument } from '@backstage/plugin-techdocs-node';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
interface MkSearchIndexDoc {
title: string;
@@ -107,7 +108,7 @@ export class DefaultTechDocsCollator {
).getEntities(
{
filter: {
'metadata.annotations.backstage.io/techdocs-ref':
[`metadata.annotations.${TECHDOCS_ANNOTATION}`]:
CATALOG_FILTER_EXISTS,
},
fields: [
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+3
View File
@@ -0,0 +1,3 @@
# @backstage/plugin-techdocs-common
Common functionality for TechDocs
+11
View File
@@ -0,0 +1,11 @@
## API Report File for "@backstage/plugin-techdocs-common"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
// @public (undocumented)
export const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
// @public (undocumented)
export const TECHDOCS_EXTERNAL_ANNOTATION = 'backstage.io/techdocs-entity';
```
+11
View File
@@ -0,0 +1,11 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-plugin-techdocs-common
title: '@backstage/plugin-techdocs-common'
description: >-
Common functionality for TechDocs
spec:
lifecycle: production
type: backstage-common-library
owner: techdocs-maintainers
+52
View File
@@ -0,0 +1,52 @@
{
"name": "@backstage/plugin-techdocs-common",
"version": "0.0.0",
"description": "Common functionality for TechDocs",
"backstage": {
"role": "common-library",
"pluginId": "techdocs",
"pluginPackages": [
"@backstage/plugin-techdocs",
"@backstage/plugin-techdocs-backend",
"@backstage/plugin-techdocs-common",
"@backstage/plugin-techdocs-node",
"@backstage/plugin-techdocs-react"
]
},
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"keywords": [
"backstage",
"techdocs"
],
"homepage": "https://backstage.io",
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/techdocs-common"
},
"bugs": {
"url": "https://github.com/backstage/backstage/issues"
},
"license": "Apache-2.0",
"sideEffects": false,
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"test": "backstage-cli package test"
},
"devDependencies": {
"@backstage/cli": "workspace:^"
}
}
+20
View File
@@ -0,0 +1,20 @@
/*
* 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.
*/
/** @public */
export const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
/** @public */
export const TECHDOCS_EXTERNAL_ANNOTATION = 'backstage.io/techdocs-entity';
+24
View File
@@ -0,0 +1,24 @@
/*
* 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 * as anything from './';
describe('techdocs-common', () => {
// TODO: Test real things once they exist.
it('should exist', () => {
expect(anything).toBeTruthy();
});
});
+22
View File
@@ -0,0 +1,22 @@
/*
* 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.
*/
/**
* Common functionality for TechDocs
*
* @packageDocumentation
*/
export * from './constants';
+2
View File
@@ -8,6 +8,7 @@
"pluginPackages": [
"@backstage/plugin-techdocs",
"@backstage/plugin-techdocs-backend",
"@backstage/plugin-techdocs-common",
"@backstage/plugin-techdocs-node",
"@backstage/plugin-techdocs-react"
]
@@ -60,6 +61,7 @@
"@backstage/integration": "workspace:^",
"@backstage/integration-aws-node": "workspace:^",
"@backstage/plugin-search-common": "workspace:^",
"@backstage/plugin-techdocs-common": "workspace:^",
"@google-cloud/storage": "^7.0.0",
"@smithy/node-http-handler": "^2.1.7",
"@trendyol-js/openstack-swift-sdk": "^0.0.7",
+15 -14
View File
@@ -27,6 +27,7 @@ import { ScmIntegrations } from '@backstage/integration';
import os from 'os';
import path from 'path';
import { Readable } from 'stream';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import {
getDocFilesFromRepository,
getLocationForEntity,
@@ -64,7 +65,7 @@ const metadataBase = {
const goodAnnotation = {
annotations: {
'backstage.io/techdocs-ref':
[TECHDOCS_ANNOTATION]:
'url:https://github.com/backstage/backstage/blob/master/subfolder/',
},
};
@@ -81,7 +82,7 @@ const mockEntityWithAnnotation: Entity = {
const badAnnotation = {
annotations: {
'backstage.io/techdocs-ref': 'bad-annotation',
[TECHDOCS_ANNOTATION]: 'bad-annotation',
},
};
@@ -102,7 +103,7 @@ afterEach(() => jest.resetAllMocks());
describe('parseReferenceAnnotation', () => {
it('should parse annotation', () => {
const parsedLocationAnnotation = parseReferenceAnnotation(
'backstage.io/techdocs-ref',
TECHDOCS_ANNOTATION,
mockEntityWithAnnotation,
);
expect(parsedLocationAnnotation.type).toBe('url');
@@ -113,14 +114,14 @@ describe('parseReferenceAnnotation', () => {
it('should throw error without annotation', () => {
expect(() => {
parseReferenceAnnotation('backstage.io/techdocs-ref', entityBase);
parseReferenceAnnotation(TECHDOCS_ANNOTATION, entityBase);
}).toThrow(/No location annotation/);
});
it('should throw error with bad annotation', () => {
expect(() => {
parseReferenceAnnotation(
'backstage.io/techdocs-ref',
TECHDOCS_ANNOTATION,
mockEntityWithBadAnnotation,
);
}).toThrow(/Unable to parse/);
@@ -146,14 +147,14 @@ describe('transformDirLocation', () => {
metadata: {
name: 'test',
annotations: {
'backstage.io/techdocs-ref': techdocsRef,
[TECHDOCS_ANNOTATION]: techdocsRef,
},
},
};
const result = transformDirLocation(
entity,
parseReferenceAnnotation('backstage.io/techdocs-ref', entity),
parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity),
scmIntegrations,
);
@@ -179,14 +180,14 @@ describe('transformDirLocation', () => {
metadata: {
name: 'test',
annotations: {
'backstage.io/techdocs-ref': techdocsRef,
[TECHDOCS_ANNOTATION]: techdocsRef,
},
},
};
const result = transformDirLocation(
entity,
parseReferenceAnnotation('backstage.io/techdocs-ref', entity),
parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity),
scmIntegrations,
);
@@ -206,7 +207,7 @@ describe('transformDirLocation', () => {
metadata: {
name: 'test',
annotations: {
'backstage.io/techdocs-ref': 'dir:..',
[TECHDOCS_ANNOTATION]: 'dir:..',
},
},
};
@@ -214,7 +215,7 @@ describe('transformDirLocation', () => {
expect(() =>
transformDirLocation(
entity,
parseReferenceAnnotation('backstage.io/techdocs-ref', entity),
parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity),
scmIntegrations,
),
).toThrow(
@@ -234,7 +235,7 @@ describe('transformDirLocation', () => {
metadata: {
name: 'test',
annotations: {
'backstage.io/techdocs-ref': 'dir:.',
[TECHDOCS_ANNOTATION]: 'dir:.',
},
},
};
@@ -242,7 +243,7 @@ describe('transformDirLocation', () => {
expect(() =>
transformDirLocation(
entity,
parseReferenceAnnotation('backstage.io/techdocs-ref', entity),
parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity),
scmIntegrations,
),
).toThrow(/Unable to resolve location type other/);
@@ -262,7 +263,7 @@ describe('getLocationForEntity', () => {
metadata: {
name: 'test',
annotations: {
'backstage.io/techdocs-ref': 'dir:.',
[TECHDOCS_ANNOTATION]: 'dir:.',
},
},
};
+3 -8
View File
@@ -23,6 +23,7 @@ import {
} from '@backstage/catalog-model';
import { InputError } from '@backstage/errors';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import path from 'path';
import { Logger } from 'winston';
import { PreparerResponse, RemoteProtocol } from './stages/prepare/types';
@@ -122,10 +123,7 @@ export const getLocationForEntity = (
entity: Entity,
scmIntegration: ScmIntegrationRegistry,
): ParsedLocationAnnotation => {
const annotation = parseReferenceAnnotation(
'backstage.io/techdocs-ref',
entity,
);
const annotation = parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity);
switch (annotation.type) {
case 'url':
@@ -149,10 +147,7 @@ export const getDocFilesFromRepository = async (
entity: Entity,
opts?: { etag?: string; logger?: Logger },
): Promise<PreparerResponse> => {
const { target } = parseReferenceAnnotation(
'backstage.io/techdocs-ref',
entity,
);
const { target } = parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity);
opts?.logger?.debug(`Reading files from ${target}`);
// readTree will throw NotModifiedError if etag has not changed.
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { UrlReader, loggerToWinstonLogger } from '@backstage/backend-common';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { ConfigReader } from '@backstage/config';
import { DirectoryPreparer } from './dir';
import { mockServices } from '@backstage/backend-test-utils';
@@ -61,7 +62,7 @@ describe('directory preparer', () => {
const mockEntity = createMockEntity({
'backstage.io/managed-by-location':
'file:/directory/documented-component.yaml',
'backstage.io/techdocs-ref': 'dir:./our-documentation',
[TECHDOCS_ANNOTATION]: 'dir:./our-documentation',
});
const { preparedDir } = await directoryPreparer.prepare(mockEntity);
@@ -77,7 +78,7 @@ describe('directory preparer', () => {
const mockEntity = createMockEntity({
'backstage.io/managed-by-location':
'file:/directory/documented-component.yaml',
'backstage.io/techdocs-ref': 'dir:/our-documentation/techdocs',
[TECHDOCS_ANNOTATION]: 'dir:/our-documentation/techdocs',
});
await expect(directoryPreparer.prepare(mockEntity)).rejects.toThrow(
@@ -94,7 +95,7 @@ describe('directory preparer', () => {
const mockEntity = createMockEntity({
'backstage.io/managed-by-location':
'does-not-exist:https://github.com/backstage/backstage/blob/master/catalog-info.yaml',
'backstage.io/techdocs-ref': 'dir:./docs',
[TECHDOCS_ANNOTATION]: 'dir:./docs',
});
await expect(directoryPreparer.prepare(mockEntity)).rejects.toThrow(
@@ -22,6 +22,7 @@ import {
ScmIntegrationRegistry,
ScmIntegrations,
} from '@backstage/integration';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { Logger } from 'winston';
import { parseReferenceAnnotation, transformDirLocation } from '../../helpers';
import {
@@ -70,10 +71,7 @@ export class DirectoryPreparer implements PreparerBase {
entity: Entity,
options?: PreparerOptions,
): Promise<PreparerResponse> {
const annotation = parseReferenceAnnotation(
'backstage.io/techdocs-ref',
entity,
);
const annotation = parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity);
const { type, target } = transformDirLocation(
entity,
annotation,
@@ -15,6 +15,7 @@
*/
import { Entity } from '@backstage/catalog-model';
import { Config } from '@backstage/config';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
import { parseReferenceAnnotation } from '../../helpers';
import { DirectoryPreparer } from './dir';
import { UrlPreparer } from './url';
@@ -78,10 +79,7 @@ export class Preparers implements PreparerBuilder {
* @returns
*/
get(entity: Entity): PreparerBase {
const { type } = parseReferenceAnnotation(
'backstage.io/techdocs-ref',
entity,
);
const { type } = parseReferenceAnnotation(TECHDOCS_ANNOTATION, entity);
const preparer = this.preparerMap.get(type);
if (!preparer) {
+1
View File
@@ -8,6 +8,7 @@
"pluginPackages": [
"@backstage/plugin-techdocs",
"@backstage/plugin-techdocs-backend",
"@backstage/plugin-techdocs-common",
"@backstage/plugin-techdocs-node",
"@backstage/plugin-techdocs-react"
]
+2
View File
@@ -8,6 +8,7 @@
"pluginPackages": [
"@backstage/plugin-techdocs",
"@backstage/plugin-techdocs-backend",
"@backstage/plugin-techdocs-common",
"@backstage/plugin-techdocs-node",
"@backstage/plugin-techdocs-react"
]
@@ -71,6 +72,7 @@
"@backstage/plugin-catalog-react": "workspace:^",
"@backstage/plugin-search-common": "workspace:^",
"@backstage/plugin-search-react": "workspace:^",
"@backstage/plugin-techdocs-common": "workspace:^",
"@backstage/plugin-techdocs-react": "workspace:^",
"@backstage/theme": "workspace:^",
"@material-ui/core": "^4.12.2",
+1 -2
View File
@@ -19,14 +19,13 @@ import {
getCompoundEntityRef,
parseEntityRef,
} from '@backstage/catalog-model';
import { TECHDOCS_EXTERNAL_ANNOTATION } from '@backstage/plugin-techdocs-common';
import React from 'react';
import { TechDocsReaderPage } from './plugin';
import { TechDocsReaderPageContent } from './reader/components/TechDocsReaderPageContent';
import { TechDocsReaderPageSubheader } from './reader/components/TechDocsReaderPageSubheader';
const TECHDOCS_EXTERNAL_ANNOTATION = 'backstage.io/techdocs-entity';
type EntityPageDocsProps = { entity: Entity };
export const EntityPageDocs = ({ entity }: EntityPageDocsProps) => {
+4 -4
View File
@@ -25,10 +25,10 @@ import {
useEntity,
MissingAnnotationEmptyState,
} from '@backstage/plugin-catalog-react';
const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
const TECHDOCS_EXTERNAL_ANNOTATION = 'backstage.io/techdocs-entity';
import {
TECHDOCS_ANNOTATION,
TECHDOCS_EXTERNAL_ANNOTATION,
} from '@backstage/plugin-techdocs-common';
/**
* Helper that takes in entity and returns true/false if TechDocs is available for the entity
@@ -39,6 +39,7 @@ import {
ContentHeader,
} from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
const panels = {
DocsTable: DocsTable,
@@ -153,7 +154,7 @@ export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => {
} = useAsync(async () => {
const response = await catalogApi.getEntities({
filter: {
'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS,
[`metadata.annotations.${TECHDOCS_ANNOTATION}`]: CATALOG_FILTER_EXISTS,
},
fields: [
'apiVersion',
@@ -165,7 +166,7 @@ export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => {
],
});
return response.items.filter((entity: Entity) => {
return !!entity.metadata.annotations?.['backstage.io/techdocs-ref'];
return !!entity.metadata.annotations?.[TECHDOCS_ANNOTATION];
});
});
@@ -21,11 +21,12 @@ import {
EntityFilter,
useEntityList,
} from '@backstage/plugin-catalog-react';
import { TECHDOCS_ANNOTATION } from '@backstage/plugin-techdocs-common';
class TechDocsFilter implements EntityFilter {
getCatalogFilters(): Record<string, string | symbol | (string | symbol)[]> {
return {
'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS,
[`metadata.annotations.${TECHDOCS_ANNOTATION}`]: CATALOG_FILTER_EXISTS,
};
}
}
+11
View File
@@ -7743,6 +7743,7 @@ __metadata:
"@backstage/plugin-catalog-common": "workspace:^"
"@backstage/plugin-permission-common": "workspace:^"
"@backstage/plugin-search-backend-module-techdocs": "workspace:^"
"@backstage/plugin-techdocs-common": "workspace:^"
"@backstage/plugin-techdocs-node": "workspace:^"
"@types/express": ^4.17.6
express: ^4.17.1
@@ -7758,6 +7759,14 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/plugin-techdocs-common@workspace:^, @backstage/plugin-techdocs-common@workspace:plugins/techdocs-common":
version: 0.0.0-use.local
resolution: "@backstage/plugin-techdocs-common@workspace:plugins/techdocs-common"
dependencies:
"@backstage/cli": "workspace:^"
languageName: unknown
linkType: soft
"@backstage/plugin-techdocs-module-addons-contrib@workspace:^, @backstage/plugin-techdocs-module-addons-contrib@workspace:plugins/techdocs-module-addons-contrib":
version: 0.0.0-use.local
resolution: "@backstage/plugin-techdocs-module-addons-contrib@workspace:plugins/techdocs-module-addons-contrib"
@@ -7806,6 +7815,7 @@ __metadata:
"@backstage/integration": "workspace:^"
"@backstage/integration-aws-node": "workspace:^"
"@backstage/plugin-search-common": "workspace:^"
"@backstage/plugin-techdocs-common": "workspace:^"
"@google-cloud/storage": ^7.0.0
"@smithy/node-http-handler": ^2.1.7
"@trendyol-js/openstack-swift-sdk": ^0.0.7
@@ -7879,6 +7889,7 @@ __metadata:
"@backstage/plugin-catalog-react": "workspace:^"
"@backstage/plugin-search-common": "workspace:^"
"@backstage/plugin-search-react": "workspace:^"
"@backstage/plugin-techdocs-common": "workspace:^"
"@backstage/plugin-techdocs-module-addons-contrib": "workspace:^"
"@backstage/plugin-techdocs-react": "workspace:^"
"@backstage/test-utils": "workspace:^"