From 5af3be07bb17cefaf72f8a3bfb49f3fe2841b901 Mon Sep 17 00:00:00 2001 From: Blake Stoddard Date: Mon, 28 Nov 2022 09:05:36 -0500 Subject: [PATCH 001/236] Add a title prop to DependsOnResourcesCard The 'Depends on X' naming scheme can be cumbersome to users and being able to change the component titles in the UI to be clearer is great. The sibling card (DependsOnComponentsCard) already supports a title prop. Signed-off-by: Blake Stoddard --- .../DependsOnResourcesCard/DependsOnResourcesCard.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx index a0f4d399f9..0ae6601a4a 100644 --- a/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx +++ b/plugins/catalog/src/components/DependsOnResourcesCard/DependsOnResourcesCard.tsx @@ -27,14 +27,15 @@ import { /** @public */ export interface DependsOnResourcesCardProps { variant?: InfoCardVariants; + title?: string; } export function DependsOnResourcesCard(props: DependsOnResourcesCardProps) { - const { variant = 'gridItem' } = props; + const { variant = 'gridItem', title = 'Depends on resources' } = props; return ( Date: Mon, 28 Nov 2022 09:41:17 -0500 Subject: [PATCH 002/236] Extend certain entity tables to accept a columns prop Prevents a user fromhaving to spin out a custom component that is essentially an exact duplicate of the Backstage-provided components just to remove or add columns. Our (Epic) implementation doesn't care about system or description for these cards, but it's annoying to maintain a seperate component for the sole purpose of different columns. Signed-off-by: Blake Stoddard --- .../src/components/ApisCards/ConsumedApisCard.tsx | 10 +++++++--- .../src/components/ApisCards/HasApisCard.tsx | 9 ++++++--- .../src/components/ApisCards/ProvidedApisCard.tsx | 10 +++++++--- .../DependsOnComponentsCard.tsx | 13 +++++++++---- .../DependsOnResourcesCard.tsx | 13 +++++++++---- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx index 588e62ceb7..f56bd3e1a7 100644 --- a/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ConsumedApisCard.tsx @@ -29,14 +29,18 @@ import { InfoCardVariants, Link, Progress, + TableColumn, WarningPanel, } from '@backstage/core-components'; /** * @public */ -export const ConsumedApisCard = (props: { variant?: InfoCardVariants }) => { - const { variant = 'gridItem' } = props; +export const ConsumedApisCard = (props: { + variant?: InfoCardVariants; + columns?: TableColumn[]; +}) => { + const { variant = 'gridItem', columns = apiEntityColumns } = props; const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities(entity, { type: RELATION_CONSUMES_API, @@ -79,7 +83,7 @@ export const ConsumedApisCard = (props: { variant?: InfoCardVariants }) => { } - columns={apiEntityColumns} + columns={columns} entities={entities as ApiEntity[]} /> ); diff --git a/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx b/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx index b09cafacda..b8aef1bca4 100644 --- a/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/HasApisCard.tsx @@ -33,7 +33,7 @@ import { WarningPanel, } from '@backstage/core-components'; -const columns: TableColumn[] = [ +const presetColumns: TableColumn[] = [ EntityTable.columns.createEntityRefColumn({ defaultKind: 'API' }), EntityTable.columns.createOwnerColumn(), createSpecApiTypeColumn(), @@ -44,8 +44,11 @@ const columns: TableColumn[] = [ /** * @public */ -export const HasApisCard = (props: { variant?: InfoCardVariants }) => { - const { variant = 'gridItem' } = props; +export const HasApisCard = (props: { + variant?: InfoCardVariants; + columns?: TableColumn[]; +}) => { + const { variant = 'gridItem', columns = presetColumns } = props; const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities(entity, { type: RELATION_HAS_PART, diff --git a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx index e9c63d9956..886ba17ba5 100644 --- a/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx +++ b/plugins/api-docs/src/components/ApisCards/ProvidedApisCard.tsx @@ -29,14 +29,18 @@ import { InfoCardVariants, Link, Progress, + TableColumn, WarningPanel, } from '@backstage/core-components'; /** * @public */ -export const ProvidedApisCard = (props: { variant?: InfoCardVariants }) => { - const { variant = 'gridItem' } = props; +export const ProvidedApisCard = (props: { + variant?: InfoCardVariants; + columns?: TableColumn[]; +}) => { + const { variant = 'gridItem', columns = apiEntityColumns } = props; const { entity } = useEntity(); const { entities, loading, error } = useRelatedEntities(entity, { type: RELATION_PROVIDES_API, @@ -79,7 +83,7 @@ export const ProvidedApisCard = (props: { variant?: InfoCardVariants }) => { } - columns={apiEntityColumns} + columns={columns} entities={entities as ApiEntity[]} /> ); diff --git a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx index 4f30c17a84..a7ff97ec73 100644 --- a/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx +++ b/plugins/catalog/src/components/DependsOnComponentsCard/DependsOnComponentsCard.tsx @@ -14,8 +14,8 @@ * limitations under the License. */ -import { RELATION_DEPENDS_ON } from '@backstage/catalog-model'; -import { InfoCardVariants } from '@backstage/core-components'; +import { RELATION_DEPENDS_ON, ComponentEntity } from '@backstage/catalog-model'; +import { InfoCardVariants, TableColumn } from '@backstage/core-components'; import React from 'react'; import { asComponentEntities, @@ -28,17 +28,22 @@ import { export interface DependsOnComponentsCardProps { variant?: InfoCardVariants; title?: string; + columns?: TableColumn[]; } export function DependsOnComponentsCard(props: DependsOnComponentsCardProps) { - const { variant = 'gridItem', title = 'Depends on components' } = props; + const { + variant = 'gridItem', + title = 'Depends on components', + columns = componentEntityColumns, + } = props; return ( []; } export function DependsOnResourcesCard(props: DependsOnResourcesCardProps) { - const { variant = 'gridItem', title = 'Depends on resources' } = props; + const { + variant = 'gridItem', + title = 'Depends on resources', + columns = resourceEntityColumns, + } = props; return ( Date: Mon, 28 Nov 2022 10:12:29 -0500 Subject: [PATCH 003/236] Add changeset Signed-off-by: Blake Stoddard --- .changeset/wild-ads-pull.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/wild-ads-pull.md diff --git a/.changeset/wild-ads-pull.md b/.changeset/wild-ads-pull.md new file mode 100644 index 0000000000..1fee3e52d8 --- /dev/null +++ b/.changeset/wild-ads-pull.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-api-docs': patch +'@backstage/plugin-catalog': patch +--- + +Add a columns prop to certain components that use the EntityTable for easier extensibility. From e4f1071e2a8a53b79e7316745b1722f5e81f5056 Mon Sep 17 00:00:00 2001 From: Blake Stoddard Date: Fri, 3 Feb 2023 13:12:02 -0500 Subject: [PATCH 004/236] update api-report files to show the new props Signed-off-by: Blake Stoddard --- plugins/api-docs/api-report.md | 6 ++++++ plugins/catalog/api-report.md | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index a4f61872f2..a44f09ed03 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -78,6 +78,7 @@ export type AsyncApiDefinitionWidgetProps = { // @public (undocumented) export const ConsumedApisCard: (props: { variant?: InfoCardVariants; + columns?: TableColumn[]; }) => JSX.Element; // @public (undocumented) @@ -106,6 +107,7 @@ export const EntityApiDefinitionCard: () => JSX.Element; // @public (undocumented) export const EntityConsumedApisCard: (props: { variant?: InfoCardVariants | undefined; + columns?: TableColumn[] | undefined; }) => JSX.Element; // @public (undocumented) @@ -116,11 +118,13 @@ export const EntityConsumingComponentsCard: (props: { // @public (undocumented) export const EntityHasApisCard: (props: { variant?: InfoCardVariants | undefined; + columns?: TableColumn[] | undefined; }) => JSX.Element; // @public (undocumented) export const EntityProvidedApisCard: (props: { variant?: InfoCardVariants | undefined; + columns?: TableColumn[] | undefined; }) => JSX.Element; // @public (undocumented) @@ -141,6 +145,7 @@ export type GraphQlDefinitionWidgetProps = { // @public (undocumented) export const HasApisCard: (props: { variant?: InfoCardVariants; + columns?: TableColumn[]; }) => JSX.Element; // @public (undocumented) @@ -167,6 +172,7 @@ export type PlainApiDefinitionWidgetProps = { // @public (undocumented) export const ProvidedApisCard: (props: { variant?: InfoCardVariants; + columns?: TableColumn[]; }) => JSX.Element; // @public (undocumented) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 6d746f6a7c..5762bbbb74 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -7,6 +7,7 @@ import { ApiHolder } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { ComponentEntity } from '@backstage/catalog-model'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; @@ -17,6 +18,7 @@ import { Observable } from '@backstage/types'; import { Overrides } from '@material-ui/core/styles/overrides'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; +import { ResourceEntity } from '@backstage/catalog-model'; import { ResultHighlight } from '@backstage/plugin-search-common'; import { RouteRef } from '@backstage/core-plugin-api'; import { SearchResultListItemExtensionProps } from '@backstage/plugin-search-react'; @@ -227,6 +229,8 @@ export interface DependencyOfComponentsCardProps { // @public (undocumented) export interface DependsOnComponentsCardProps { + // (undocumented) + columns?: TableColumn[]; // (undocumented) title?: string; // (undocumented) @@ -235,6 +239,10 @@ export interface DependsOnComponentsCardProps { // @public (undocumented) export interface DependsOnResourcesCardProps { + // (undocumented) + columns?: TableColumn[]; + // (undocumented) + title?: string; // (undocumented) variant?: InfoCardVariants; } From 42b2f6542199be17ce410003ac2d0255c47bca1b Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Thu, 9 Feb 2023 20:37:29 -0500 Subject: [PATCH 005/236] First try, need to figure out how to pass entity ref as form data. Signed-off-by: Aramis Sennyey --- .../src/components/EntityRefLink/humanize.ts | 9 ++++ .../fields/EntityPicker/EntityPicker.tsx | 45 +++++++++++-------- .../OwnedEntityPicker/OwnedEntityPicker.tsx | 15 +++---- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts index bfc8e2af84..9abf0d7978 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts +++ b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts @@ -18,6 +18,7 @@ import { Entity, CompoundEntityRef, DEFAULT_NAMESPACE, + UserEntity, } from '@backstage/catalog-model'; /** @@ -36,11 +37,19 @@ export function humanizeEntityRef( let kind; let namespace; let name; + console.log(entityRef); if ('metadata' in entityRef) { kind = entityRef.kind; namespace = entityRef.metadata.namespace; name = entityRef.metadata.name; + + // Escapes when we know more about an entity. + if (entityRef.metadata.title) { + return entityRef.metadata.title; + } else if ((entityRef as UserEntity).spec?.profile?.displayName) { + return (entityRef as UserEntity).spec?.profile?.displayName; + } } else { kind = entityRef.kind; namespace = entityRef.namespace; diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 48e60b60ad..0dc3e39fc4 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ import { type EntityFilterQuery } from '@backstage/catalog-client'; +import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef, @@ -22,7 +23,7 @@ import { import { TextField } from '@material-ui/core'; import FormControl from '@material-ui/core/FormControl'; import Autocomplete from '@material-ui/lab/Autocomplete'; -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback, useEffect, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { EntityPickerProps } from './schema'; @@ -45,6 +46,7 @@ export const EntityPicker = (props: EntityPickerProps) => { idSchema, } = props; const allowedKinds = uiSchema['ui:options']?.allowedKinds; + console.log(formData); const catalogFilter: EntityFilterQuery | undefined = uiSchema['ui:options']?.catalogFilter || @@ -54,29 +56,31 @@ export const EntityPicker = (props: EntityPickerProps) => { const defaultNamespace = uiSchema['ui:options']?.defaultNamespace; const catalogApi = useApi(catalogApiRef); + const [value, setValue] = useState(formData); - const { value: entities, loading } = useAsync(() => - catalogApi.getEntities( + const { value: entities, loading } = useAsync(async () => { + const { items } = await catalogApi.getEntities( catalogFilter ? { filter: catalogFilter } : undefined, - ), - ); - - const entityRefs = entities?.items.map(e => - humanizeEntityRef(e, { defaultKind, defaultNamespace }), - ); + ); + return items; + }); const onSelect = useCallback( - (_: any, value: string | null) => { - onChange(value ?? undefined); + (_: any, ref: string | Entity | null) => { + let entityRef: string = typeof ref === 'string' ? ref : ''; + if (typeof ref !== 'string') + entityRef = ref ? stringifyEntityRef(ref as Entity) : ''; + setValue(entityRef); + onChange(entityRef); }, - [onChange], + [onChange, setValue], ); useEffect(() => { - if (entityRefs?.length === 1) { - onChange(entityRefs[0]); + if (entities?.length === 1) { + onChange(stringifyEntityRef(entities[0])); } - }, [entityRefs, onChange]); + }, [entities, onChange]); return ( { error={rawErrors?.length > 0 && !formData} > e.metadata.name === formData)} loading={loading} onChange={onSelect} - options={entityRefs || []} + options={entities || []} + getOptionLabel={option => + typeof option === 'string' + ? option + : humanizeEntityRef(option, { defaultKind, defaultNamespace })! + } autoSelect freeSolo={uiSchema['ui:options']?.allowArbitraryValues ?? true} renderInput={params => ( diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx index 01a174a6a3..6d80972f1b 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { GetEntitiesResponse } from '@backstage/catalog-client'; -import { RELATION_OWNED_BY } from '@backstage/catalog-model'; +import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model'; import { identityApiRef, useApi } from '@backstage/core-plugin-api'; import { catalogApiRef, @@ -54,12 +54,9 @@ export const OwnedEntityPicker = (props: OwnedEntityPickerProps) => { uiSchema['ui:options']?.allowArbitraryValues ?? true; const { ownedEntities, loading } = useOwnedEntities(allowedKinds); - const entityRefs = ownedEntities?.items - .map(e => humanizeEntityRef(e, { defaultKind, defaultNamespace })) - .filter(n => n); - - const onSelect = (_: any, value: string | null) => { - onChange(value || ''); + const entities = ownedEntities?.items.filter(n => n); + const onSelect = (_: any, value: Entity | null) => { + onChange(value?.metadata.name || ''); }; return ( @@ -70,10 +67,10 @@ export const OwnedEntityPicker = (props: OwnedEntityPickerProps) => { > ( From f36840b4664ee1b061ad81479afcf5d4d2ffd6ba Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Fri, 10 Feb 2023 12:42:04 -0500 Subject: [PATCH 006/236] Update scaffolder filters to use humanizeEntityRef but stringifyEntityRef in the backend. Signed-off-by: Aramis Sennyey --- .../fields/EntityPicker/EntityPicker.tsx | 57 ++++++--- .../OwnedEntityPicker/OwnedEntityPicker.tsx | 117 ++++++------------ 2 files changed, 80 insertions(+), 94 deletions(-) diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 0dc3e39fc4..2a36b9f8ef 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -14,7 +14,11 @@ * limitations under the License. */ import { type EntityFilterQuery } from '@backstage/catalog-client'; -import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; +import { + Entity, + parseEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef, @@ -22,8 +26,10 @@ import { } from '@backstage/plugin-catalog-react'; import { TextField } from '@material-ui/core'; import FormControl from '@material-ui/core/FormControl'; -import Autocomplete from '@material-ui/lab/Autocomplete'; -import React, { useCallback, useEffect, useState } from 'react'; +import Autocomplete, { + AutocompleteChangeReason, +} from '@material-ui/lab/Autocomplete'; +import React, { useCallback, useEffect } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { EntityPickerProps } from './schema'; @@ -46,7 +52,6 @@ export const EntityPicker = (props: EntityPickerProps) => { idSchema, } = props; const allowedKinds = uiSchema['ui:options']?.allowedKinds; - console.log(formData); const catalogFilter: EntityFilterQuery | undefined = uiSchema['ui:options']?.catalogFilter || @@ -56,7 +61,6 @@ export const EntityPicker = (props: EntityPickerProps) => { const defaultNamespace = uiSchema['ui:options']?.defaultNamespace; const catalogApi = useApi(catalogApiRef); - const [value, setValue] = useState(formData); const { value: entities, loading } = useAsync(async () => { const { items } = await catalogApi.getEntities( @@ -64,16 +68,36 @@ export const EntityPicker = (props: EntityPickerProps) => { ); return items; }); + const allowArbitraryValues = + uiSchema['ui:options']?.allowArbitraryValues ?? true; const onSelect = useCallback( - (_: any, ref: string | Entity | null) => { - let entityRef: string = typeof ref === 'string' ? ref : ''; - if (typeof ref !== 'string') - entityRef = ref ? stringifyEntityRef(ref as Entity) : ''; - setValue(entityRef); - onChange(entityRef); + (_: any, ref: string | Entity | null, reason: AutocompleteChangeReason) => { + // if ref == string + if (typeof ref !== 'string') { + onChange(ref ? stringifyEntityRef(ref as Entity) : ''); + } else { + if (reason === 'blur') { + // Add in default namespace, etc. + let entityRef = ref; + try { + // Attempt to parse the entity ref into it's full form. + entityRef = stringifyEntityRef( + parseEntityRef(ref as string, { + defaultKind, + defaultNamespace: defaultNamespace || undefined, + }), + ); + } catch (err) { + // If the passed in value isn't an entity ref, do nothing. + } + if (formData !== entityRef) { + onChange(ref); + } + } + } }, - [onChange, setValue], + [onChange, formData, defaultKind, defaultNamespace], ); useEffect(() => { @@ -91,17 +115,22 @@ export const EntityPicker = (props: EntityPickerProps) => { e.metadata.name === formData)} + value={ + // Since free solo is usually enabled, attempt to + entities?.find(e => stringifyEntityRef(e) === formData) ?? + (allowArbitraryValues ? formData : '') + } loading={loading} onChange={onSelect} options={entities || []} getOptionLabel={option => + // option can be a string due to freeSolo. typeof option === 'string' ? option : humanizeEntityRef(option, { defaultKind, defaultNamespace })! } autoSelect - freeSolo={uiSchema['ui:options']?.allowArbitraryValues ?? true} + freeSolo={allowArbitraryValues} renderInput={params => ( { const { - onChange, schema: { title = 'Entity', description = 'An entity from the catalog' }, - required, uiSchema, - rawErrors, - formData, - idSchema, + required, } = props; + const identityApi = useApi(identityApiRef); + const { loading, value: identityRefs } = useAsync(async () => { + const identity = await identityApi.getBackstageIdentity(); + return identity.ownershipEntityRefs; + }); + const allowedKinds = uiSchema['ui:options']?.allowedKinds; - const defaultKind = uiSchema['ui:options']?.defaultKind; - const defaultNamespace = uiSchema['ui:options']?.defaultNamespace; - const allowArbitraryValues = - uiSchema['ui:options']?.allowArbitraryValues ?? true; - const { ownedEntities, loading } = useOwnedEntities(allowedKinds); - - const entities = ownedEntities?.items.filter(n => n); - const onSelect = (_: any, value: Entity | null) => { - onChange(value?.metadata.name || ''); - }; - - return ( - 0 && !formData} - > + if (loading) + return ( ( )} + options={[]} /> - + ); + + return ( + ); }; - -/** - * Takes the relevant parts of the Backstage identity, and translates them into - * a list of entities which are owned by the user. Takes an optional parameter - * to filter the entities based on allowedKinds - * - * - * @param allowedKinds - Array of allowed kinds to filter the entities - */ -function useOwnedEntities(allowedKinds?: string[]): { - loading: boolean; - ownedEntities: GetEntitiesResponse | undefined; -} { - const identityApi = useApi(identityApiRef); - const catalogApi = useApi(catalogApiRef); - - const { loading, value: refs } = useAsync(async () => { - const identity = await identityApi.getBackstageIdentity(); - const identityRefs = identity.ownershipEntityRefs; - const catalogs = await catalogApi.getEntities( - allowedKinds - ? { - filter: { - kind: allowedKinds, - [`relations.${RELATION_OWNED_BY}`]: identityRefs || [], - }, - } - : { - filter: { - [`relations.${RELATION_OWNED_BY}`]: identityRefs || [], - }, - }, - ); - return catalogs; - }, []); - - const ownedEntities = useMemo(() => { - return refs; - }, [refs]); - - return useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]); -} From 27a103ca07bdebaa34b6165d4bd2345178bfac96 Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Tue, 14 Feb 2023 10:23:58 +0000 Subject: [PATCH 007/236] Changed the createPermissionIntegrationRouter API to allow getResources to be optional Signed-off-by: Harry Hogg --- .changeset/clean-planes-join.md | 5 + plugins/permission-node/api-report.md | 7 +- .../createPermissionIntegrationRouter.test.ts | 94 +++++++++++-------- .../createPermissionIntegrationRouter.ts | 20 +++- 4 files changed, 81 insertions(+), 45 deletions(-) create mode 100644 .changeset/clean-planes-join.md diff --git a/.changeset/clean-planes-join.md b/.changeset/clean-planes-join.md new file mode 100644 index 0000000000..2ad009c133 --- /dev/null +++ b/.changeset/clean-planes-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-node': patch +--- + +Changed the createPermissionIntegrationRouter API to allow getResources to be optional diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 3f712fe362..3019812749 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -124,7 +124,7 @@ export const createPermissionIntegrationRouter: < NoInfer, PermissionRuleParams >[]; - getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>; + getResources?: GetResourcesFn | undefined; }) => express.Router; // @public @@ -137,6 +137,11 @@ export const createPermissionRule: < rule: PermissionRule, ) => PermissionRule; +// @public +export type GetResourcesFn = ( + resourceRefs: string[], +) => Promise>; + // @alpha export const isAndCriteria: ( criteria: PermissionCriteria, diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index 32d814465f..33b12f4fc2 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -19,18 +19,15 @@ import { createPermission, Permission, } from '@backstage/plugin-permission-common'; -import express, { Express, Router } from 'express'; +import express from 'express'; import request, { Response } from 'supertest'; import { z } from 'zod'; -import { createPermissionIntegrationRouter } from './createPermissionIntegrationRouter'; +import { + createPermissionIntegrationRouter, + GetResourcesFn, +} from './createPermissionIntegrationRouter'; import { createPermissionRule } from './createPermissionRule'; -const mockGetResources: jest.MockedFunction< - Parameters[0]['getResources'] -> = jest.fn(async resourceRefs => - resourceRefs.map(resourceRef => ({ id: resourceRef })), -); - const testPermission: Permission = createPermission({ name: 'test.permission', attributes: {}, @@ -56,29 +53,30 @@ const testRule2 = createPermissionRule({ toQuery: () => ({}), }); -describe('createPermissionIntegrationRouter', () => { - let app: Express; - let router: Router; +const defaultMockedGetResources: GetResourcesFn<{ id: string }> = jest.fn( + async resourceRefs => resourceRefs.map(resourceRef => ({ id: resourceRef })), +); - beforeAll(() => { - router = createPermissionIntegrationRouter({ - resourceType: 'test-resource', - permissions: [testPermission], - getResources: mockGetResources, - rules: [testRule1, testRule2], - }); - - app = express().use(router); +const createApp = ( + mockedGetResources: + | typeof defaultMockedGetResources + | null = defaultMockedGetResources, +) => { + const router = createPermissionIntegrationRouter({ + resourceType: 'test-resource', + permissions: [testPermission], + getResources: mockedGetResources || undefined, + rules: [testRule1, testRule2], }); + return express().use(router); +}; + +describe('createPermissionIntegrationRouter', () => { afterEach(() => { jest.clearAllMocks(); }); - it('works', async () => { - expect(router).toBeDefined(); - }); - describe('POST /.well-known/backstage/permissions/apply-conditions', () => { it.each([ { @@ -150,7 +148,7 @@ describe('createPermissionIntegrationRouter', () => { ], }, ])('returns 200/ALLOW when criteria match (case %#)', async conditions => { - const response = await request(app) + const response = await request(createApp()) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [ @@ -238,7 +236,7 @@ describe('createPermissionIntegrationRouter', () => { ])( 'returns 200/DENY when criteria do not match (case %#)', async conditions => { - const response = await request(app) + const response = await request(createApp()) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [ @@ -262,7 +260,7 @@ describe('createPermissionIntegrationRouter', () => { let response: Response; beforeEach(async () => { - response = await request(app) + response = await request(createApp()) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [ @@ -353,7 +351,7 @@ describe('createPermissionIntegrationRouter', () => { }); it('calls getResources for all required resources at once', () => { - expect(mockGetResources).toHaveBeenCalledWith([ + expect(defaultMockedGetResources).toHaveBeenCalledWith([ 'default:test/resource-1', 'default:test/resource-2', 'default:test/resource-3', @@ -363,7 +361,7 @@ describe('createPermissionIntegrationRouter', () => { }); it('returns 400 when called with incorrect resource type', async () => { - const response = await request(app) + const response = await request(createApp()) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [ @@ -413,11 +411,11 @@ describe('createPermissionIntegrationRouter', () => { }); it('returns 200/DENY when resource is not found', async () => { - mockGetResources.mockImplementationOnce(async resourceRefs => - resourceRefs.map(() => undefined), + const mockedGetResources: GetResourcesFn<{ id: string }> = jest.fn( + async resourceRefs => resourceRefs.map(() => undefined), ); - const response = await request(app) + const response = await request(createApp(mockedGetResources)) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [ @@ -446,15 +444,16 @@ describe('createPermissionIntegrationRouter', () => { }); it('interleaves responses for present and missing resources', async () => { - mockGetResources.mockImplementationOnce(async resourceRefs => - resourceRefs.map(resourceRef => - resourceRef === 'default:test/missing-resource' - ? undefined - : { id: resourceRef }, - ), + const mockedGetResources: GetResourcesFn<{ id: string }> = jest.fn( + async resourceRefs => + resourceRefs.map(resourceRef => + resourceRef === 'default:test/missing-resource' + ? undefined + : { id: resourceRef }, + ), ); - const response = await request(app) + const response = await request(createApp(mockedGetResources)) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [ @@ -548,18 +547,31 @@ describe('createPermissionIntegrationRouter', () => { ], }, ])(`returns 400 for invalid input %#`, async input => { - const response = await request(app) + const response = await request(createApp()) .post('/.well-known/backstage/permissions/apply-conditions') .send(input); expect(response.status).toEqual(400); expect(response.error && response.error.text).toMatch(/invalid/i); }); + + it('returns 400 with no getResources implementation', async () => { + const response = await request(createApp(null)) + .post('/.well-known/backstage/permissions/apply-conditions') + .send({ + items: [], + }); + + expect(response.status).toEqual(400); + expect(response.body.error.message).toEqual( + 'This plugin does not support the apply-conditions API.', + ); + }); }); describe('GET /.well-known/backstage/permissions/metadata', () => { it('returns a list of permissions and rules used by a given backend', async () => { - const response = await request(app).get( + const response = await request(createApp()).get( '/.well-known/backstage/permissions/metadata', ); diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index 23b59c9c43..49a89f0394 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -125,6 +125,16 @@ export type MetadataResponse = { rules: MetadataResponseSerializedRule[]; }; +/** + * Function type for returning an array of resources + * matching the given resourceRefs. + * + * @public + */ +export type GetResourcesFn = ( + resourceRefs: string[], +) => Promise>; + const applyConditions = ( criteria: PermissionCriteria>, resource: TResource | undefined, @@ -205,9 +215,7 @@ export const createPermissionIntegrationRouter = < // consider any rules whose resource type does not match // to be an error. rules: PermissionRule>[]; - getResources: ( - resourceRefs: string[], - ) => Promise>; + getResources?: GetResourcesFn; }): express.Router => { const { resourceType, permissions, rules, getResources } = options; const router = Router(); @@ -250,6 +258,12 @@ export const createPermissionIntegrationRouter = < router.post( '/.well-known/backstage/permissions/apply-conditions', async (req, res: Response) => { + if (!getResources) { + throw new InputError( + 'This plugin does not support the apply-conditions API.', + ); + } + const parseResult = applyConditionsRequestSchema.safeParse(req.body); if (!parseResult.success) { From 417ae9bb0867e559376a05e04454784f2757954d Mon Sep 17 00:00:00 2001 From: Harry Hogg Date: Tue, 14 Feb 2023 10:35:40 +0000 Subject: [PATCH 008/236] Backticks around changeset words to pass spelling check Signed-off-by: Harry Hogg --- .changeset/clean-planes-join.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/clean-planes-join.md b/.changeset/clean-planes-join.md index 2ad009c133..346f81f1b8 100644 --- a/.changeset/clean-planes-join.md +++ b/.changeset/clean-planes-join.md @@ -2,4 +2,4 @@ '@backstage/plugin-permission-node': patch --- -Changed the createPermissionIntegrationRouter API to allow getResources to be optional +Changed the `createPermissionIntegrationRouter` API to allow `getResources` to be optional From 3bf83a2aabf0ee578f51c12ac549aec6ae1a8aa2 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 14 Feb 2023 17:07:57 +0100 Subject: [PATCH 009/236] errors: add NotImplementedError Co-authored-by: Harry Hogg Signed-off-by: Vincenzo Scamporlino --- .changeset/polite-wombats-smash.md | 6 ++++++ packages/backend-app-api/src/http/MiddlewareFactory.ts | 3 +++ packages/errors/src/errors/common.ts | 7 +++++++ packages/errors/src/errors/index.ts | 1 + 4 files changed, 17 insertions(+) create mode 100644 .changeset/polite-wombats-smash.md diff --git a/.changeset/polite-wombats-smash.md b/.changeset/polite-wombats-smash.md new file mode 100644 index 0000000000..250ca215f5 --- /dev/null +++ b/.changeset/polite-wombats-smash.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-app-api': patch +'@backstage/errors': patch +--- + +Add NotImplementedError diff --git a/packages/backend-app-api/src/http/MiddlewareFactory.ts b/packages/backend-app-api/src/http/MiddlewareFactory.ts index dc9fe9aa5f..833bdba540 100644 --- a/packages/backend-app-api/src/http/MiddlewareFactory.ts +++ b/packages/backend-app-api/src/http/MiddlewareFactory.ts @@ -38,6 +38,7 @@ import { NotModifiedError, serializeError, } from '@backstage/errors'; +import { NotImplementedError } from '@backstage/errors'; /** * Options used to create a {@link MiddlewareFactory}. @@ -257,6 +258,8 @@ function getStatusCode(error: Error): number { return 404; case ConflictError.name: return 409; + case NotImplementedError.name: + return 501; default: break; } diff --git a/packages/errors/src/errors/common.ts b/packages/errors/src/errors/common.ts index 80a3d84d82..e1a292d7b5 100644 --- a/packages/errors/src/errors/common.ts +++ b/packages/errors/src/errors/common.ts @@ -74,6 +74,13 @@ export class ConflictError extends CustomErrorBase {} */ export class NotModifiedError extends CustomErrorBase {} +/** + * The server does not support the functionality required to fulfill the request. + * + * @public + */ +export class NotImplementedError extends CustomErrorBase {} + /** * An error that forwards an underlying cause with additional context in the message. * diff --git a/packages/errors/src/errors/index.ts b/packages/errors/src/errors/index.ts index 9e1c2408af..efd3257fbf 100644 --- a/packages/errors/src/errors/index.ts +++ b/packages/errors/src/errors/index.ts @@ -24,6 +24,7 @@ export { NotAllowedError, NotFoundError, NotModifiedError, + NotImplementedError, } from './common'; export { CustomErrorBase } from './CustomErrorBase'; export { ResponseError } from './ResponseError'; From 85194da56cc6fee618a0b37f63a11e49777c80e1 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 14 Feb 2023 17:12:28 +0100 Subject: [PATCH 010/236] permission-node: make resources and rules optional in createPermissionIntegrationRouter Co-authored-by: Harry Hogg Signed-off-by: Vincenzo Scamporlino --- .../createPermissionIntegrationRouter.test.ts | 22 +++-- .../createPermissionIntegrationRouter.ts | 86 +++++++++++++------ 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index 33b12f4fc2..af764b01e2 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -62,12 +62,16 @@ const createApp = ( | typeof defaultMockedGetResources | null = defaultMockedGetResources, ) => { - const router = createPermissionIntegrationRouter({ - resourceType: 'test-resource', - permissions: [testPermission], - getResources: mockedGetResources || undefined, - rules: [testRule1, testRule2], - }); + const router = createPermissionIntegrationRouter( + mockedGetResources + ? { + resourceType: 'test-resource', + permissions: [testPermission], + getResources: mockedGetResources, + rules: [testRule1, testRule2], + } + : { permissions: [testPermission] }, + ); return express().use(router); }; @@ -555,16 +559,16 @@ describe('createPermissionIntegrationRouter', () => { expect(response.error && response.error.text).toMatch(/invalid/i); }); - it('returns 400 with no getResources implementation', async () => { + it('returns 501 with no getResources implementation', async () => { const response = await request(createApp(null)) .post('/.well-known/backstage/permissions/apply-conditions') .send({ items: [], }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(501); expect(response.body.error.message).toEqual( - 'This plugin does not support the apply-conditions API.', + 'This plugin does not support the apply-conditions API', ); }); }); diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index 49a89f0394..d35bf06b24 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -36,6 +36,7 @@ import { isNotCriteria, isOrCriteria, } from './util'; +import { NotImplementedError } from '@backstage/errors'; const permissionCriteriaSchema: z.ZodSchema< PermissionCriteria @@ -204,10 +205,11 @@ const applyConditions = ( * * @public */ -export const createPermissionIntegrationRouter = < + +type CreatePermissionIntegrationRouterResourceOptions< TResourceType extends string, TResource, ->(options: { +> = { resourceType: TResourceType; permissions?: Array; // Do not infer value of TResourceType from supplied rules. @@ -215,12 +217,28 @@ export const createPermissionIntegrationRouter = < // consider any rules whose resource type does not match // to be an error. rules: PermissionRule>[]; - getResources?: GetResourcesFn; -}): express.Router => { - const { resourceType, permissions, rules, getResources } = options; + getResources: GetResourcesFn; +}; + +type CreatePermissionIntegrationRouterOptions< + TResourceType extends string, + TResource, +> = + | { + permissions: Array; + } + | CreatePermissionIntegrationRouterResourceOptions; + +export const createPermissionIntegrationRouter = < + TResourceType extends string, + TResource, +>( + options: CreatePermissionIntegrationRouterOptions, +): express.Router => { const router = Router(); router.use(express.json()); + const { permissions = [], rules = [] } = { rules: [], ...options }; router.get('/.well-known/backstage/permissions/metadata', (_, res) => { const serializedRules: MetadataResponseSerializedRule[] = rules.map( rule => ({ @@ -239,30 +257,31 @@ export const createPermissionIntegrationRouter = < return res.json(responseJson); }); - const getRule = createGetRule(rules); - - const assertValidResourceTypes = ( - requests: ApplyConditionsRequestEntry[], - ) => { - const invalidResourceTypes = requests - .filter(request => request.resourceType !== resourceType) - .map(request => request.resourceType); - - if (invalidResourceTypes.length) { - throw new InputError( - `Unexpected resource types: ${invalidResourceTypes.join(', ')}.`, - ); - } - }; - router.post( '/.well-known/backstage/permissions/apply-conditions', async (req, res: Response) => { - if (!getResources) { - throw new InputError( - 'This plugin does not support the apply-conditions API.', + if (!isCreatePermissionIntegrationRouterResourceOptions(options)) { + throw new NotImplementedError( + 'This plugin does not support the apply-conditions API', ); } + const { resourceType, getResources } = options; + + const getRule = createGetRule(rules); + + const assertValidResourceTypes = ( + requests: ApplyConditionsRequestEntry[], + ) => { + const invalidResourceTypes = requests + .filter(request => request.resourceType !== resourceType) + .map(request => request.resourceType); + + if (invalidResourceTypes.length) { + throw new InputError( + `Unexpected resource types: ${invalidResourceTypes.join(', ')}.`, + ); + } + }; const parseResult = applyConditionsRequestSchema.safeParse(req.body); @@ -303,3 +322,22 @@ export const createPermissionIntegrationRouter = < return router; }; + +function isCreatePermissionIntegrationRouterResourceOptions< + TResourceType extends string, + TResource, +>( + options: CreatePermissionIntegrationRouterOptions, +): options is CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource +> { + return ( + ( + options as CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource + > + ).resourceType !== undefined + ); +} From afdb225025e3375ef67f83a4e7c6c65587926802 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 14 Feb 2023 17:15:41 +0100 Subject: [PATCH 011/236] Improve changeset Co-authored-by: Harry Hogg Signed-off-by: Vincenzo Scamporlino --- .changeset/clean-planes-join.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/clean-planes-join.md b/.changeset/clean-planes-join.md index 346f81f1b8..dd8280f553 100644 --- a/.changeset/clean-planes-join.md +++ b/.changeset/clean-planes-join.md @@ -2,4 +2,4 @@ '@backstage/plugin-permission-node': patch --- -Changed the `createPermissionIntegrationRouter` API to allow `getResources` to be optional +Changed the `createPermissionIntegrationRouter` API to allow `getResources`, `resourceType` and `rules` to be optional From dbf36da3eb96c686a6aab23391e4cf516bcae0ae Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Tue, 14 Feb 2023 17:22:17 +0100 Subject: [PATCH 012/236] permission-node: improve api report for createPermissionIntegrationRouter Co-authored-by: Harry Hogg Signed-off-by: Vincenzo Scamporlino --- plugins/permission-node/api-report.md | 12 ++-- .../createPermissionIntegrationRouter.ts | 58 +++++++++++-------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 3019812749..b8d337819c 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -111,6 +111,11 @@ export const createConditionTransformer: < permissionRules: [...TRules], ) => ConditionTransformer; +// @public +export const createIsAuthorized: ( + rules: PermissionRule[], +) => (decision: PolicyDecision, resource: TResource | undefined) => boolean; + // @public export const createPermissionIntegrationRouter: < TResourceType extends string, @@ -124,7 +129,7 @@ export const createPermissionIntegrationRouter: < NoInfer, PermissionRuleParams >[]; - getResources?: GetResourcesFn | undefined; + getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>; }) => express.Router; // @public @@ -137,11 +142,6 @@ export const createPermissionRule: < rule: PermissionRule, ) => PermissionRule; -// @public -export type GetResourcesFn = ( - resourceRefs: string[], -) => Promise>; - // @alpha export const isAndCriteria: ( criteria: PermissionCriteria, diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index d35bf06b24..c8bd94f68e 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -170,6 +170,40 @@ const applyConditions = ( return rule.apply(resource, criteria.params ?? {}); }; +/** + * Options for creating a permission integration router specific + * for a particular resource type. + * + * @public + */ +export type CreatePermissionIntegrationRouterResourceOptions< + TResourceType extends string, + TResource, +> = { + resourceType: TResourceType; + permissions?: Array; + // Do not infer value of TResourceType from supplied rules. + // instead only consider the resourceType parameter, and + // consider any rules whose resource type does not match + // to be an error. + rules: PermissionRule>[]; + getResources: GetResourcesFn; +}; + +/** + * Options for creating a permission integration router. + * + * @public + */ +export type CreatePermissionIntegrationRouterOptions< + TResourceType extends string, + TResource, +> = + | { + permissions: Array; + } + | CreatePermissionIntegrationRouterResourceOptions; + /** * Create an express Router which provides an authorization route to allow * integration between the permission backend and other Backstage backend @@ -205,30 +239,6 @@ const applyConditions = ( * * @public */ - -type CreatePermissionIntegrationRouterResourceOptions< - TResourceType extends string, - TResource, -> = { - resourceType: TResourceType; - permissions?: Array; - // Do not infer value of TResourceType from supplied rules. - // instead only consider the resourceType parameter, and - // consider any rules whose resource type does not match - // to be an error. - rules: PermissionRule>[]; - getResources: GetResourcesFn; -}; - -type CreatePermissionIntegrationRouterOptions< - TResourceType extends string, - TResource, -> = - | { - permissions: Array; - } - | CreatePermissionIntegrationRouterResourceOptions; - export const createPermissionIntegrationRouter = < TResourceType extends string, TResource, From 5cee4f71caba40e2758f1f9a458178d5ec274ebb Mon Sep 17 00:00:00 2001 From: Claire Casey Date: Tue, 14 Feb 2023 14:45:56 -0500 Subject: [PATCH 013/236] add styling to handle overflow text on ownership and members card Signed-off-by: Claire Casey --- .../Cards/Group/MembersList/MembersListCard.tsx | 11 +++++++---- .../Cards/OwnershipCard/ComponentsGrid.tsx | 14 +++++++++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index 7e5b683bf1..65c3144d30 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -59,15 +59,14 @@ const useStyles = makeStyles((theme: Theme) => flex: '1', minWidth: '0px', }, - email: { + overflowingText: { overflow: 'hidden', - whiteSpace: 'nowrap', textOverflow: 'ellipsis', display: 'inline-block', maxWidth: '100%', '&:hover': { overflow: 'visible', - whiteSpace: 'normal', + wordBreak: 'break-word', }, }, }), @@ -108,6 +107,7 @@ const MemberComponent = (props: { member: UserEntity }) => { > { {profile?.email && ( - + {profile.email} )} diff --git a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx index 567c7ad3bc..edd8cc126f 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx @@ -44,8 +44,16 @@ const useStyles = makeStyles((theme: BackstageTheme) => }, height: '100%', }, - bold: { + title: { fontWeight: theme.typography.fontWeightBold, + textAlign: 'center', + overflow: 'hidden', + textOverflow: 'ellipsis', + maxWidth: '100%', + '&:hover': { + overflow: 'visible', + wordBreak: 'break-word', + }, }, entityTypeBox: { background: (props: { type: string }) => @@ -77,10 +85,10 @@ const EntityCountTile = ({ flexDirection="column" alignItems="center" > - + {counter} - + {pluralize(rawTitle.toLocaleUpperCase('en-US'), counter)} {type && {kind}} From a06fcac40405d66acdbd4d8e3c029f15e3864b21 Mon Sep 17 00:00:00 2001 From: Claire Casey Date: Tue, 14 Feb 2023 15:43:28 -0500 Subject: [PATCH 014/236] add changeset Signed-off-by: Claire Casey --- .changeset/selfish-hats-wait.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/selfish-hats-wait.md diff --git a/.changeset/selfish-hats-wait.md b/.changeset/selfish-hats-wait.md new file mode 100644 index 0000000000..40d0d194e7 --- /dev/null +++ b/.changeset/selfish-hats-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +Add styling to the MembersListCard and ComponentsGrid to handle overflow text. From 860de10fa67c2efa6ea3cf725e06792f68d7d381 Mon Sep 17 00:00:00 2001 From: Lucas Pires Date: Fri, 27 Jan 2023 17:58:28 -0300 Subject: [PATCH 015/236] feat: including authentication to server applications in scaffolder plugin Signed-off-by: Lucas Pires --- .changeset/backend-token-authentication.md | 5 ++++ packages/backend/src/plugins/scaffolder.ts | 1 + .../src/ScaffolderPlugin.ts | 3 +++ .../scaffolder-backend/src/service/router.ts | 26 ++++++++++++++----- 4 files changed, 28 insertions(+), 7 deletions(-) create mode 100644 .changeset/backend-token-authentication.md diff --git a/.changeset/backend-token-authentication.md b/.changeset/backend-token-authentication.md new file mode 100644 index 0000000000..597921822f --- /dev/null +++ b/.changeset/backend-token-authentication.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Make identity valid if subject of token is a github-server token diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index d079b64c28..e734408d42 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -34,5 +34,6 @@ export default async function createPlugin( reader: env.reader, identity: env.identity, scheduler: env.scheduler, + tokenManager: env.tokenManager, }); } diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 43b8d30f99..639f9b0806 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -82,6 +82,7 @@ export const scaffolderPlugin = createBackendPlugin( database: coreServices.database, httpRouter: coreServices.httpRouter, catalogClient: catalogServiceRef, + tokenManager: coreServices.tokenManager, }, async init({ logger, @@ -90,6 +91,7 @@ export const scaffolderPlugin = createBackendPlugin( database, httpRouter, catalogClient, + tokenManager, }) { const { additionalTemplateFilters, @@ -127,6 +129,7 @@ export const scaffolderPlugin = createBackendPlugin( taskWorkers, additionalTemplateFilters, additionalTemplateGlobals, + tokenManager, }); httpRouter.use(router); }, diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index c339de8d10..49cfa68306 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -14,7 +14,11 @@ * limitations under the License. */ -import { PluginDatabaseManager, UrlReader } from '@backstage/backend-common'; +import { + PluginDatabaseManager, + TokenManager, + UrlReader, +} from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { CatalogApi } from '@backstage/catalog-client'; import { @@ -82,6 +86,7 @@ export interface RouterOptions { additionalTemplateFilters?: Record; additionalTemplateGlobals?: Record; identity?: IdentityApi; + tokenManager?: TokenManager; } function isSupportedTemplate(entity: TemplateEntityV1beta3) { @@ -96,13 +101,14 @@ function isSupportedTemplate(entity: TemplateEntityV1beta3) { * are using the IdentityApi, we can remove this function. */ function buildDefaultIdentityClient({ - logger, + options, }: { - logger: Logger; + options: RouterOptions; }): IdentityApi { return { getIdentity: async ({ request }: IdentityApiGetIdentityRequest) => { const header = request.headers.authorization; + const { logger, tokenManager } = options; if (!header) { return undefined; @@ -132,8 +138,15 @@ function buildDefaultIdentityClient({ throw new TypeError('Expected string sub claim'); } - // Check that it's a valid ref, otherwise this will throw. - parseEntityRef(sub); + try { + // Check that it's a valid ref, otherwise this will throw. + parseEntityRef(sub); + } catch (e) { + if (sub !== 'backstage-server' || !options.tokenManager) { + throw e; + } + await tokenManager?.authenticate(token); + } return { identity: { @@ -179,8 +192,7 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); const identity: IdentityApi = - options.identity || buildDefaultIdentityClient({ logger }); - + options.identity || buildDefaultIdentityClient({ options }); const workingDirectory = await getWorkingDirectory(config, logger); const integrations = ScmIntegrations.fromConfig(config); From 9052ca8a2e37cf52268175b4d6096d460daa301d Mon Sep 17 00:00:00 2001 From: Lucas Pires Date: Fri, 27 Jan 2023 18:31:40 -0300 Subject: [PATCH 016/236] chore: including api changeset file Signed-off-by: Lucas Pires --- plugins/scaffolder-backend/api-report.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 0d745862a1..81546dd00a 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -32,6 +32,7 @@ import { TaskSecrets as TaskSecrets_2 } from '@backstage/plugin-scaffolder-node' import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node'; +import { TokenManager } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; @@ -642,6 +643,8 @@ export interface RouterOptions { taskBroker?: TaskBroker; // @deprecated (undocumented) taskWorkers?: number; + // (undocumented) + tokenManager?: TokenManager; } // @public (undocumented) From 770ecf61312cd89a042a10a3b83289cfe772c0db Mon Sep 17 00:00:00 2001 From: Lucas Pires Date: Wed, 15 Feb 2023 12:39:20 -0300 Subject: [PATCH 017/236] chore: removing token manager and adding backstage-server condition Signed-off-by: Lucas Pires --- .changeset/backend-token-authentication.md | 2 +- packages/backend/src/plugins/scaffolder.ts | 1 - plugins/scaffolder-backend/api-report.md | 3 --- .../src/ScaffolderPlugin.ts | 3 --- .../scaffolder-backend/src/service/router.ts | 24 +++++++------------ 5 files changed, 9 insertions(+), 24 deletions(-) diff --git a/.changeset/backend-token-authentication.md b/.changeset/backend-token-authentication.md index 597921822f..3b0f016800 100644 --- a/.changeset/backend-token-authentication.md +++ b/.changeset/backend-token-authentication.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -Make identity valid if subject of token is a github-server token +Make identity valid if subject of token is a backstage server-2-server auth token diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index e734408d42..d079b64c28 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -34,6 +34,5 @@ export default async function createPlugin( reader: env.reader, identity: env.identity, scheduler: env.scheduler, - tokenManager: env.tokenManager, }); } diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 81546dd00a..0d745862a1 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -32,7 +32,6 @@ import { TaskSecrets as TaskSecrets_2 } from '@backstage/plugin-scaffolder-node' import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskSpecV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node'; -import { TokenManager } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; @@ -643,8 +642,6 @@ export interface RouterOptions { taskBroker?: TaskBroker; // @deprecated (undocumented) taskWorkers?: number; - // (undocumented) - tokenManager?: TokenManager; } // @public (undocumented) diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 639f9b0806..43b8d30f99 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -82,7 +82,6 @@ export const scaffolderPlugin = createBackendPlugin( database: coreServices.database, httpRouter: coreServices.httpRouter, catalogClient: catalogServiceRef, - tokenManager: coreServices.tokenManager, }, async init({ logger, @@ -91,7 +90,6 @@ export const scaffolderPlugin = createBackendPlugin( database, httpRouter, catalogClient, - tokenManager, }) { const { additionalTemplateFilters, @@ -129,7 +127,6 @@ export const scaffolderPlugin = createBackendPlugin( taskWorkers, additionalTemplateFilters, additionalTemplateGlobals, - tokenManager, }); httpRouter.use(router); }, diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 49cfa68306..d082e3c268 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -86,7 +86,6 @@ export interface RouterOptions { additionalTemplateFilters?: Record; additionalTemplateGlobals?: Record; identity?: IdentityApi; - tokenManager?: TokenManager; } function isSupportedTemplate(entity: TemplateEntityV1beta3) { @@ -100,15 +99,11 @@ function isSupportedTemplate(entity: TemplateEntityV1beta3) { * until someone explicitly passes an IdentityApi. When we have reasonable confidence that most backstage deployments * are using the IdentityApi, we can remove this function. */ -function buildDefaultIdentityClient({ - options, -}: { - options: RouterOptions; -}): IdentityApi { +function buildDefaultIdentityClient(options: RouterOptions): IdentityApi { return { getIdentity: async ({ request }: IdentityApiGetIdentityRequest) => { const header = request.headers.authorization; - const { logger, tokenManager } = options; + const { logger } = options; if (!header) { return undefined; @@ -138,16 +133,13 @@ function buildDefaultIdentityClient({ throw new TypeError('Expected string sub claim'); } - try { - // Check that it's a valid ref, otherwise this will throw. - parseEntityRef(sub); - } catch (e) { - if (sub !== 'backstage-server' || !options.tokenManager) { - throw e; - } - await tokenManager?.authenticate(token); + if (sub === 'backstage-server') { + return undefined; } + // Check that it's a valid ref, otherwise this will throw. + parseEntityRef(sub); + return { identity: { userEntityRef: sub, @@ -192,7 +184,7 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); const identity: IdentityApi = - options.identity || buildDefaultIdentityClient({ options }); + options.identity || buildDefaultIdentityClient(options); const workingDirectory = await getWorkingDirectory(config, logger); const integrations = ScmIntegrations.fromConfig(config); From 5e2a3987aaf426f27ee16189e22488aec54890e2 Mon Sep 17 00:00:00 2001 From: Lucas Pires Date: Wed, 15 Feb 2023 14:23:43 -0300 Subject: [PATCH 018/236] fix: removing unused var Signed-off-by: Lucas Pires --- plugins/scaffolder-backend/src/service/router.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index d082e3c268..7e8ffc1109 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - PluginDatabaseManager, - TokenManager, - UrlReader, -} from '@backstage/backend-common'; +import { PluginDatabaseManager, UrlReader } from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { CatalogApi } from '@backstage/catalog-client'; import { From c915712447f4f356ce0905762fb78362bc3cc1ec Mon Sep 17 00:00:00 2001 From: Claire Casey Date: Thu, 16 Feb 2023 16:29:50 -0500 Subject: [PATCH 019/236] refactor to use OverflowTooltip component Signed-off-by: Claire Casey --- .changeset/selfish-hats-wait.md | 2 +- .../Group/MembersList/MembersListCard.tsx | 16 ++++----- .../Cards/OwnershipCard/ComponentsGrid.tsx | 36 +++++++++++-------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/.changeset/selfish-hats-wait.md b/.changeset/selfish-hats-wait.md index 40d0d194e7..d706d42e8e 100644 --- a/.changeset/selfish-hats-wait.md +++ b/.changeset/selfish-hats-wait.md @@ -2,4 +2,4 @@ '@backstage/plugin-org': patch --- -Add styling to the MembersListCard and ComponentsGrid to handle overflow text. +Add styling to the `MembersListCard` and `ComponentsGrid` to handle overflow text. diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index 65c3144d30..bf561a23a4 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -44,6 +44,7 @@ import { Progress, ResponseErrorPanel, Link, + OverflowTooltip, } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; @@ -59,14 +60,15 @@ const useStyles = makeStyles((theme: Theme) => flex: '1', minWidth: '0px', }, - overflowingText: { + email: { overflow: 'hidden', + whiteSpace: 'nowrap', textOverflow: 'ellipsis', display: 'inline-block', maxWidth: '100%', '&:hover': { overflow: 'visible', - wordBreak: 'break-word', + whiteSpace: 'normal', }, }, }), @@ -105,22 +107,18 @@ const MemberComponent = (props: { member: UserEntity }) => { }} textAlign="center" > - + - {displayName} + {profile?.email && ( - + {profile.email} )} diff --git a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx index edd8cc126f..b2714b5dd5 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx @@ -15,7 +15,12 @@ */ import { Entity } from '@backstage/catalog-model'; -import { Link, Progress, ResponseErrorPanel } from '@backstage/core-components'; +import { + Link, + OverflowTooltip, + Progress, + ResponseErrorPanel, +} from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { @@ -44,16 +49,11 @@ const useStyles = makeStyles((theme: BackstageTheme) => }, height: '100%', }, - title: { + bold: { fontWeight: theme.typography.fontWeightBold, - textAlign: 'center', - overflow: 'hidden', - textOverflow: 'ellipsis', - maxWidth: '100%', - '&:hover': { - overflow: 'visible', - wordBreak: 'break-word', - }, + }, + smallFont: { + fontSize: theme.typography.body2.fontSize, }, entityTypeBox: { background: (props: { type: string }) => @@ -76,6 +76,7 @@ const EntityCountTile = ({ const classes = useStyles({ type: type ?? kind }); const rawTitle = type ?? kind; + const isLongText = rawTitle.length > 10; return ( @@ -85,12 +86,19 @@ const EntityCountTile = ({ flexDirection="column" alignItems="center" > - + {counter} - - {pluralize(rawTitle.toLocaleUpperCase('en-US'), counter)} - + + + + + {type && {kind}} From 5632097f9258eb268d6387dfb2ff587826b87a48 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 17 Feb 2023 10:50:38 +0100 Subject: [PATCH 020/236] permission-node: make getResources optional Signed-off-by: Vincenzo Scamporlino --- .../src/integration/createPermissionIntegrationRouter.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index c8bd94f68e..f78fea6ea6 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -187,7 +187,7 @@ export type CreatePermissionIntegrationRouterResourceOptions< // consider any rules whose resource type does not match // to be an error. rules: PermissionRule>[]; - getResources: GetResourcesFn; + getResources?: GetResourcesFn; }; /** @@ -270,9 +270,12 @@ export const createPermissionIntegrationRouter = < router.post( '/.well-known/backstage/permissions/apply-conditions', async (req, res: Response) => { - if (!isCreatePermissionIntegrationRouterResourceOptions(options)) { + if ( + !isCreatePermissionIntegrationRouterResourceOptions(options) || + options.getResources === undefined + ) { throw new NotImplementedError( - 'This plugin does not support the apply-conditions API', + `This plugin does not expose any permission rule or can't evaluate conditional decisions`, ); } const { resourceType, getResources } = options; From d7ef962073699be1182c20ff270cf4f9b3d1e38d Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 17 Feb 2023 10:51:02 +0100 Subject: [PATCH 021/236] api reports Signed-off-by: Vincenzo Scamporlino --- packages/errors/api-report.md | 3 ++ plugins/permission-node/api-report.md | 43 +++++++++++++++++---------- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md index 1a65ae2315..650a826a30 100644 --- a/packages/errors/api-report.md +++ b/packages/errors/api-report.md @@ -84,6 +84,9 @@ export class NotAllowedError extends CustomErrorBase {} // @public export class NotFoundError extends CustomErrorBase {} +// @public +export class NotImplementedError extends CustomErrorBase {} + // @public export class NotModifiedError extends CustomErrorBase {} diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index b8d337819c..9fda5cfd96 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -111,26 +111,34 @@ export const createConditionTransformer: < permissionRules: [...TRules], ) => ConditionTransformer; -// @public -export const createIsAuthorized: ( - rules: PermissionRule[], -) => (decision: PolicyDecision, resource: TResource | undefined) => boolean; - // @public export const createPermissionIntegrationRouter: < TResourceType extends string, TResource, ->(options: { +>( + options: CreatePermissionIntegrationRouterOptions, +) => express.Router; + +// @public +export type CreatePermissionIntegrationRouterOptions< + TResourceType extends string, + TResource, +> = + | { + permissions: Array; + } + | CreatePermissionIntegrationRouterResourceOptions; + +// @public +export type CreatePermissionIntegrationRouterResourceOptions< + TResourceType extends string, + TResource, +> = { resourceType: TResourceType; - permissions?: Permission[] | undefined; - rules: PermissionRule< - TResource, - any, - NoInfer, - PermissionRuleParams - >[]; - getResources: (resourceRefs: string[]) => Promise<(TResource | undefined)[]>; -}) => express.Router; + permissions?: Array; + rules: PermissionRule>[]; + getResources?: GetResourcesFn; +}; // @public export const createPermissionRule: < @@ -142,6 +150,11 @@ export const createPermissionRule: < rule: PermissionRule, ) => PermissionRule; +// @public +export type GetResourcesFn = ( + resourceRefs: string[], +) => Promise>; + // @alpha export const isAndCriteria: ( criteria: PermissionCriteria, From 36e90ecdf1dd81b7142ff4a9571bc0c69ab1437f Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Fri, 17 Feb 2023 13:39:03 +0100 Subject: [PATCH 022/236] permission-node: fix error message Signed-off-by: Vincenzo Scamporlino --- .../src/integration/createPermissionIntegrationRouter.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index af764b01e2..577267b161 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -568,7 +568,7 @@ describe('createPermissionIntegrationRouter', () => { expect(response.status).toEqual(501); expect(response.body.error.message).toEqual( - 'This plugin does not support the apply-conditions API', + `This plugin does not expose any permission rule or can't evaluate conditional decisions`, ); }); }); From ad0f2169d697d8dd5ff0fb3a80bf7b3151e1dd24 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 10:10:21 +0100 Subject: [PATCH 023/236] extend definition with `setOwner` Signed-off-by: Joep Peeters --- packages/backend-common/config.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 6c473af3c0..ad83bde485 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -96,6 +96,8 @@ export interface Config { * @default database */ pluginDivisionMode?: 'database' | 'schema'; + /** Configures the ownership of newly created schemas in pg databases. */ + setOwner?: string; /** * Arbitrary config object to pass to knex when initializing * (https://knexjs.org/#Installation-client). Most notable is the debug @@ -125,6 +127,8 @@ export interface Config { * This is merged recursively into the base knexConfig */ knexConfig?: object; + /** Configures the ownership of newly created schemas in pg databases. */ + setOwner?: string; }; }; }; From 8f50a807328e05537d1482415b1286505a3eb855 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 10:11:18 +0100 Subject: [PATCH 024/236] add `setOwner` to pluginConfig Signed-off-by: Joep Peeters --- packages/backend-common/src/database/DatabaseManager.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 08979a92f8..7e16dd3787 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -186,6 +186,13 @@ export class DatabaseManager { }; } + private getSetOwnerConfig(pluginId: string): string | undefined { + return ( + this.config.getOptionalString(`${pluginPath(pluginId)}.setOwner`) ?? + this.config.getOptionalString('setOwner') + ); + } + /** * Provides the knexConfig which should be used for a given plugin. * @@ -283,6 +290,7 @@ export class DatabaseManager { ...this.getAdditionalKnexConfig(pluginId), client, connection: this.getConnectionConfig(pluginId), + setOwner: this.getSetOwnerConfig(pluginId), }; } From a9f4698a29d172f13e9c4716393af464a6f9b644 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 10:15:19 +0100 Subject: [PATCH 025/236] set ownership if configured Signed-off-by: Joep Peeters --- .../src/database/connectors/postgres.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index c780fb829b..fccd8ac48e 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -35,6 +35,14 @@ export function createPgDatabaseClient( ) { const knexConfig = buildPgDatabaseConfig(dbConfig, overrides); const database = knexFactory(knexConfig); + + const owner = dbConfig.getOptionalString('setOwner') + + if (owner) { + database.client.pool.on('createSuccess', (_event: any, pgClient: any) => { + pgClient.query(`SET ROLE ${owner}`, () => { }); + }); + } return database; } @@ -147,10 +155,18 @@ export async function ensurePgSchemaExists( ...schemas: Array ): Promise { const admin = createPgDatabaseClient(dbConfig); + const setOwner = dbConfig.getOptionalString('setOwner'); try { const ensureSchema = async (database: string) => { - await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [database]); + if (setOwner) { + await admin.raw(`CREATE SCHEMA IF NOT EXISTS ?? AUTHORIZATION ??`, [ + database, + setOwner, + ]); + } else { + await admin.raw(`CREATE SCHEMA IF NOT EXISTS ??`, [database]); + } }; await Promise.all(schemas.map(ensureSchema)); From 64560a55c2a6e730f755f20b569d5ee75e2e3783 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 14:57:09 +0100 Subject: [PATCH 026/236] test config handling Signed-off-by: Joep Peeters --- .../src/database/DatabaseManager.test.ts | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts index 95511d5aba..878fe62e4f 100644 --- a/packages/backend-common/src/database/DatabaseManager.test.ts +++ b/packages/backend-common/src/database/DatabaseManager.test.ts @@ -687,5 +687,59 @@ describe('DatabaseManager', () => { }), ); }); + + it('sets the owner config for plugin using default config', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'pg', + connection: { + host: 'localhost', + database: 'foodb', + }, + setOwner: 'backstage', + plugin: { + testowner: {}, + }, + }, + }, + }), + ); + await testManager.forPlugin('testowner').getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [baseConfig] = mockCalls[0]; + + expect(baseConfig.data.setOwner).toEqual('backstage'); + }); + + it('sets the owner config for plugin using plugin config', async () => { + const testManager = DatabaseManager.fromConfig( + new ConfigReader({ + backend: { + database: { + client: 'pg', + connection: { + host: 'localhost', + database: 'foodb', + }, + setOwner: 'backstage', + plugin: { + testowner: { + setOwner: 'backstage-plugin', + }, + }, + }, + }, + }), + ); + await testManager.forPlugin('testowner').getClient(); + + const mockCalls = mocked(createDatabaseClient).mock.calls.splice(-1); + const [baseConfig] = mockCalls[0]; + + expect(baseConfig.data.setOwner).toEqual('backstage-plugin'); + }); }); }); From f75097868a7fb252ccd142e629b8d4b814b5dfe6 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 14:57:36 +0100 Subject: [PATCH 027/236] add changeset Signed-off-by: Joep Peeters --- .changeset/mean-toys-itch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/mean-toys-itch.md diff --git a/.changeset/mean-toys-itch.md b/.changeset/mean-toys-itch.md new file mode 100644 index 0000000000..dc0d7b1155 --- /dev/null +++ b/.changeset/mean-toys-itch.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Adds config option to set ownership for newly created schema's and tables in Postgres From 53cd08bca29136cc1b117517dc68d453a214085d Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 15:01:47 +0100 Subject: [PATCH 028/236] fix: spelling Signed-off-by: Joep Peeters --- .changeset/mean-toys-itch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/mean-toys-itch.md b/.changeset/mean-toys-itch.md index dc0d7b1155..19f8c861df 100644 --- a/.changeset/mean-toys-itch.md +++ b/.changeset/mean-toys-itch.md @@ -2,4 +2,4 @@ '@backstage/backend-common': patch --- -Adds config option to set ownership for newly created schema's and tables in Postgres +Adds config option to set ownership for newly created schemas and tables in Postgres From be129f8f3cd15b4e10950450bb8ca3ec8a74daf2 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Mon, 20 Feb 2023 16:24:51 +0100 Subject: [PATCH 029/236] filter gitlab groups by prefix Signed-off-by: Matteo Silvestri --- .changeset/wild-bulldogs-suffer.md | 5 ++++ .../GitlabOrgDiscoveryEntityProvider.ts | 28 ++++++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 .changeset/wild-bulldogs-suffer.md diff --git a/.changeset/wild-bulldogs-suffer.md b/.changeset/wild-bulldogs-suffer.md new file mode 100644 index 0000000000..02f50927e9 --- /dev/null +++ b/.changeset/wild-bulldogs-suffer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +filter gitlab groups by prefix diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index f90a61f9e9..c507b19549 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -199,6 +199,10 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { continue; } + if (!group.full_path.startsWith(this.config.group)) { + continue; + } + groupRes.scanned++; groupRes.matches.push(group); @@ -253,7 +257,11 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { type: 'full', entities: [...userEntities, ...groupEntities].map(entity => ({ locationKey: this.getProviderName(), - entity: this.withLocations(this.integration.config.host, entity), + entity: this.withLocations( + this.integration.config.host, + this.integration.config.baseUrl, + entity, + ), })), }); } @@ -273,7 +281,9 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { const entity = this.createGroupEntity(group, host); if (group.parent_id && idMapped.hasOwnProperty(group.parent_id)) { - entity.spec.parent = idMapped[group.parent_id].full_path; + entity.spec.parent = this.groupName( + idMapped[group.parent_id].full_path, + ); } entities.push(entity); @@ -282,11 +292,11 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { return entities; } - private withLocations(host: string, entity: Entity): Entity { + private withLocations(host: string, baseUrl: string, entity: Entity): Entity { const location = entity.kind === 'Group' - ? `url:${host}/teams/${entity.metadata.name}` - : `url:${host}/${entity.metadata.name}`; + ? `url:${baseUrl}/${entity.metadata.annotations[`${host}/team-path`]}` + : `url:${baseUrl}/${entity.metadata.name}`; return merge( { metadata: { @@ -338,13 +348,17 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { if (!entity.spec.memberOf) { entity.spec.memberOf = []; } - entity.spec.memberOf.push(group.full_path.replace('/', '-')); + entity.spec.memberOf.push(this.groupName(group.full_path)); } } return entity; } + private groupName(full_path: string): string { + return full_path.replace(`${this.config.group}/`, '').replaceAll('/', '-'); + } + private createGroupEntity(group: GitLabGroup, host: string): GroupEntity { const annotations: { [annotationName: string]: string } = {}; @@ -354,7 +368,7 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { apiVersion: 'backstage.io/v1alpha1', kind: 'Group', metadata: { - name: group.full_path.replace('/', '-'), + name: this.groupName(group.full_path), annotations: annotations, }, spec: { From 4a1a720cd380d017b4dce8326d029e7161b1ddc2 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Mon, 20 Feb 2023 16:37:48 +0100 Subject: [PATCH 030/236] fix GitlabOrgDiscoveryEntityProvider.test.ts tests Signed-off-by: Matteo Silvestri --- .../providers/GitlabOrgDiscoveryEntityProvider.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts index 74649f41da..268b4c3ba4 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts @@ -350,9 +350,10 @@ describe('GitlabOrgDiscoveryEntityProvider', () => { kind: 'User', metadata: { annotations: { - 'backstage.io/managed-by-location': 'url:test-gitlab/test1', + 'backstage.io/managed-by-location': + 'url:https://test-gitlab/test1', 'backstage.io/managed-by-origin-location': - 'url:test-gitlab/test1', + 'url:https://test-gitlab/test1', 'test-gitlab/user-login': 'https://gitlab.example/test1', }, name: 'test1', @@ -375,9 +376,9 @@ describe('GitlabOrgDiscoveryEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:test-gitlab/teams/group1-group2', + 'url:https://test-gitlab/teams/group1-group2', 'backstage.io/managed-by-origin-location': - 'url:test-gitlab/teams/group1-group2', + 'url:https://test-gitlab/teams/group1-group2', 'test-gitlab/team-path': 'group1/group2', }, description: 'Group2', From e1dafc78dfc236a0fbe602a05ce521608974c692 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Tue, 21 Feb 2023 16:50:39 +0100 Subject: [PATCH 031/236] Adhere to typespec Signed-off-by: Joep Peeters --- packages/backend-common/src/database/DatabaseManager.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 7e16dd3787..07a9bd2934 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -285,12 +285,13 @@ export class DatabaseManager { */ private getConfigForPlugin(pluginId: string): Knex.Config { const { client } = this.getClientType(pluginId); + const setOwner = this.getSetOwnerConfig(pluginId); return { ...this.getAdditionalKnexConfig(pluginId), client, connection: this.getConnectionConfig(pluginId), - setOwner: this.getSetOwnerConfig(pluginId), + ...(setOwner && { setOwner }), }; } From 27c145a7b66d6c38d0ba2fd69a167c5a2de96f8e Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Tue, 21 Feb 2023 17:25:21 +0100 Subject: [PATCH 032/236] apply formatter Signed-off-by: Joep Peeters --- packages/backend-common/src/database/connectors/postgres.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index fccd8ac48e..682b2e6ceb 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -36,11 +36,11 @@ export function createPgDatabaseClient( const knexConfig = buildPgDatabaseConfig(dbConfig, overrides); const database = knexFactory(knexConfig); - const owner = dbConfig.getOptionalString('setOwner') + const owner = dbConfig.getOptionalString('setOwner'); if (owner) { database.client.pool.on('createSuccess', (_event: any, pgClient: any) => { - pgClient.query(`SET ROLE ${owner}`, () => { }); + pgClient.query(`SET ROLE ${owner}`, () => {}); }); } return database; From c79857ae25c8dba188413b7aa922d7e17e0980e3 Mon Sep 17 00:00:00 2001 From: Claire Casey Date: Tue, 21 Feb 2023 13:03:25 -0500 Subject: [PATCH 033/236] fix tests broken by OverflowToolTip changes Signed-off-by: Claire Casey --- .../MembersList/MembersListCard.test.tsx | 15 +++++++++- .../OwnershipCard/OwnershipCard.test.tsx | 29 ++++++++++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx index c98cc3eb32..cc524b60a2 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.test.tsx @@ -28,6 +28,19 @@ import { import React from 'react'; import { MembersListCard } from './MembersListCard'; +// Mock needed because jsdom doesn't correctly implement box-sizing +// https://github.com/ShinyChang/React-Text-Truncate/issues/70 +// https://stackoverflow.com/questions/71916701/how-to-mock-a-react-function-component-that-takes-a-ref-prop +jest.mock('react-text-truncate', () => { + const { forwardRef } = jest.requireActual('react'); + return { + __esModule: true, + default: forwardRef((props: any, ref: any) => ( +
{props.text}
+ )), + }; +}); + describe('MemberTab Test', () => { const groupEntity: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', @@ -95,7 +108,7 @@ describe('MemberTab Test', () => { expect( rendered.getByText('tara-macgovern@example.com'), ).toBeInTheDocument(); - expect(rendered.getByText('Tara MacGovern')).toHaveAttribute( + expect(rendered.getByText('Tara MacGovern').closest('a')).toHaveAttribute( 'href', '/catalog/foo-bar/user/tara.macgovern', ); diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx index 84f27fa183..30b7dee2de 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx @@ -120,6 +120,19 @@ const getEntitiesMock = ( } as GetEntitiesResponse); }; +// Mock needed because jsdom doesn't correctly implement box-sizing +// https://github.com/ShinyChang/React-Text-Truncate/issues/70 +// https://stackoverflow.com/questions/71916701/how-to-mock-a-react-function-component-that-takes-a-ref-prop +jest.mock('react-text-truncate', () => { + const { forwardRef } = jest.requireActual('react'); + return { + __esModule: true, + default: forwardRef((props: any, ref: any) => ( +
{props.text}
+ )), + }; +}); + describe('OwnershipCard', () => { const groupEntity: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', @@ -177,19 +190,19 @@ describe('OwnershipCard', () => { expect(getByText('OPENAPI')).toBeInTheDocument(); expect( - queryByText(getByText('OPENAPI').parentElement!, '1'), + queryByText(getByText('OPENAPI').closest('a')!, '1'), ).toBeInTheDocument(); expect(getByText('SERVICE')).toBeInTheDocument(); expect( - queryByText(getByText('SERVICE').parentElement!, '1'), + queryByText(getByText('SERVICE').closest('a')!, '1'), ).toBeInTheDocument(); expect(getByText('LIBRARY')).toBeInTheDocument(); expect( - queryByText(getByText('LIBRARY').parentElement!, '1'), + queryByText(getByText('LIBRARY').closest('a')!, '1'), ).toBeInTheDocument(); expect(getByText('SYSTEM')).toBeInTheDocument(); expect( - queryByText(getByText('SYSTEM').parentElement!, '1'), + queryByText(getByText('SYSTEM').closest('a')!, '1'), ).toBeInTheDocument(); }); @@ -215,17 +228,17 @@ describe('OwnershipCard', () => { expect(getByText('SYSTEM')).toBeInTheDocument(); expect( - queryByText(getByText('SYSTEM').parentElement!, '1'), + queryByText(getByText('SYSTEM').closest('a')!, '1'), ).toBeInTheDocument(); expect( - queryByText(getByText('SYSTEM').parentElement!, 'System'), + queryByText(getByText('SYSTEM').closest('a')!, 'System'), ).not.toBeInTheDocument(); expect(getByText('OPENAPI')).toBeInTheDocument(); expect( - queryByText(getByText('OPENAPI').parentElement!, '1'), + queryByText(getByText('OPENAPI').closest('a')!, '1'), ).toBeInTheDocument(); expect( - queryByText(getByText('OPENAPI').parentElement!, 'API'), + queryByText(getByText('OPENAPI').closest('a')!, 'API'), ).toBeInTheDocument(); expect(() => getByText('LIBRARY')).toThrow(); }); From ea2bbef1b16d95b87bbdb9a10d74cb5db7959270 Mon Sep 17 00:00:00 2001 From: Jos Craw Date: Wed, 22 Feb 2023 21:52:03 +1300 Subject: [PATCH 034/236] feat: add support for HTTPS proxy for AWS S3 requests in Techdocs Signed-off-by: Jos Craw --- .changeset/wicked-lions-repeat.md | 6 ++++++ packages/techdocs-cli/cli-report.md | 1 + packages/techdocs-cli/src/commands/index.ts | 4 ++++ packages/techdocs-cli/src/lib/PublisherConfig.ts | 1 + plugins/techdocs-node/package.json | 2 ++ plugins/techdocs-node/src/stages/publish/awsS3.ts | 12 ++++++++++++ yarn.lock | 4 +++- 7 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/wicked-lions-repeat.md diff --git a/.changeset/wicked-lions-repeat.md b/.changeset/wicked-lions-repeat.md new file mode 100644 index 0000000000..c48545c6bc --- /dev/null +++ b/.changeset/wicked-lions-repeat.md @@ -0,0 +1,6 @@ +--- +'@techdocs/cli': minor +'@backstage/plugin-techdocs-node': minor +--- + +Added support for an HTTPS proxy for techdocs AWS S3 requests diff --git a/packages/techdocs-cli/cli-report.md b/packages/techdocs-cli/cli-report.md index 846ccc88ca..556068de85 100644 --- a/packages/techdocs-cli/cli-report.md +++ b/packages/techdocs-cli/cli-report.md @@ -77,6 +77,7 @@ Options: --azureAccountKey --awsRoleArn --awsEndpoint + --awsProxy --awsS3sse --awsS3ForcePathStyle --awsBucketRootPath diff --git a/packages/techdocs-cli/src/commands/index.ts b/packages/techdocs-cli/src/commands/index.ts index 102e6d87de..98816a6b32 100644 --- a/packages/techdocs-cli/src/commands/index.ts +++ b/packages/techdocs-cli/src/commands/index.ts @@ -173,6 +173,10 @@ export function registerCommands(program: Command) { '--awsEndpoint ', 'Optional AWS endpoint to send requests to.', ) + .option( + '--awsProxy ', + 'Optional Proxy to use for AWS requests.', + ) .option('--awsS3sse ', 'Optional AWS S3 Server Side Encryption.') .option( '--awsS3ForcePathStyle', diff --git a/packages/techdocs-cli/src/lib/PublisherConfig.ts b/packages/techdocs-cli/src/lib/PublisherConfig.ts index 0ef6b66eaf..803e40308b 100644 --- a/packages/techdocs-cli/src/lib/PublisherConfig.ts +++ b/packages/techdocs-cli/src/lib/PublisherConfig.ts @@ -94,6 +94,7 @@ export class PublisherConfig { ...(opts.awsEndpoint && { endpoint: opts.awsEndpoint }), ...(opts.awsS3ForcePathStyle && { s3ForcePathStyle: true }), ...(opts.awsS3sse && { sse: opts.awsS3sse }), + ...(opts.awsProxy && { httpsProxy: opts.awsProxy }), }, }; } diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 4f458c4fff..2b9e74a359 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -42,6 +42,7 @@ "@aws-sdk/client-s3": "^3.208.0", "@aws-sdk/credential-providers": "^3.208.0", "@aws-sdk/lib-storage": "^3.208.0", + "@aws-sdk/node-http-handler": "^3.208.0", "@aws-sdk/types": "^3.208.0", "@azure/identity": "^2.1.0", "@azure/storage-blob": "^12.5.0", @@ -58,6 +59,7 @@ "express": "^4.17.1", "fs-extra": "10.1.0", "git-url-parse": "^13.0.0", + "hpagent": "^1.2.0", "js-yaml": "^4.0.0", "json5": "^2.1.3", "mime-types": "^2.1.27", diff --git a/plugins/techdocs-node/src/stages/publish/awsS3.ts b/plugins/techdocs-node/src/stages/publish/awsS3.ts index 6b528cfd30..679e9bf7c9 100644 --- a/plugins/techdocs-node/src/stages/publish/awsS3.ts +++ b/plugins/techdocs-node/src/stages/publish/awsS3.ts @@ -32,8 +32,10 @@ import { S3Client, } from '@aws-sdk/client-s3'; import { fromTemporaryCredentials } from '@aws-sdk/credential-providers'; +import { NodeHttpHandler } from '@aws-sdk/node-http-handler'; import { Upload } from '@aws-sdk/lib-storage'; import { AwsCredentialIdentityProvider } from '@aws-sdk/types'; +import { HttpsProxyAgent } from 'hpagent'; import express from 'express'; import fs from 'fs-extra'; import JSON5 from 'json5'; @@ -150,6 +152,11 @@ export class AwsS3Publish implements PublisherBase { 'techdocs.publisher.awsS3.endpoint', ); + // AWS HTTPS proxy is an optional config. If missing, no proxy is used + const httpsProxy = config.getOptionalString( + 'techdocs.publisher.awsS3.httpsProxy', + ); + // AWS forcePathStyle is an optional config. If missing, it defaults to false. Needs to be enabled for cases // where endpoint url points to locally hosted S3 compatible storage like Localstack const forcePathStyle = config.getOptionalBoolean( @@ -162,6 +169,11 @@ export class AwsS3Publish implements PublisherBase { ...(region && { region }), ...(endpoint && { endpoint }), ...(forcePathStyle && { forcePathStyle }), + ...(httpsProxy && { + requestHandler: new NodeHttpHandler({ + httpsAgent: new HttpsProxyAgent({ proxy: httpsProxy }), + }), + }), }); const legacyPathCasing = diff --git a/yarn.lock b/yarn.lock index 544b7beec4..934bf0d7e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1211,7 +1211,7 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/node-http-handler@npm:3.272.0": +"@aws-sdk/node-http-handler@npm:3.272.0, @aws-sdk/node-http-handler@npm:^3.208.0": version: 3.272.0 resolution: "@aws-sdk/node-http-handler@npm:3.272.0" dependencies: @@ -8516,6 +8516,7 @@ __metadata: "@aws-sdk/client-s3": ^3.208.0 "@aws-sdk/credential-providers": ^3.208.0 "@aws-sdk/lib-storage": ^3.208.0 + "@aws-sdk/node-http-handler": ^3.208.0 "@aws-sdk/types": ^3.208.0 "@azure/identity": ^2.1.0 "@azure/storage-blob": ^12.5.0 @@ -8540,6 +8541,7 @@ __metadata: express: ^4.17.1 fs-extra: 10.1.0 git-url-parse: ^13.0.0 + hpagent: ^1.2.0 js-yaml: ^4.0.0 json5: ^2.1.3 mime-types: ^2.1.27 From cee0cd96cced77db8ff6a302a6726f7e48188f84 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Sun, 19 Feb 2023 13:27:08 -0500 Subject: [PATCH 035/236] refactor: Replace white & black colors with theme aware ones Signed-off-by: Carlos Esteban Lopez --- .../src/components/Avatar/Avatar.tsx | 25 +++++++-------- .../MissingAnnotationEmptyState.tsx | 3 +- .../FeatureCalloutCircular.tsx | 8 ++--- .../src/components/Lifecycle/Lifecycle.tsx | 6 ++-- .../HeaderActionMenu/HeaderActionMenu.tsx | 8 ++++- .../src/layout/Sidebar/SidebarSubmenu.tsx | 2 +- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 8 +++-- .../airbrake/dev/components/ApiBar/ApiBar.tsx | 10 +++--- .../AzureSitesOverviewTable.tsx | 18 ++++++++--- .../EntityLabelsEmptyState.tsx | 3 +- .../EntityLinksCard/EntityLinksEmptyState.tsx | 3 +- .../CodeClimateTable/CodeClimateTable.tsx | 32 +++++++++---------- .../src/components/FileExplorer/CodeRow.tsx | 2 +- .../components/FileExplorer/FileContent.tsx | 2 +- .../GroupsExplorerContent/GroupsDiagram.tsx | 2 +- .../src/components/ToolCard/ToolCard.tsx | 2 +- .../ServiceDetailsCard/ServiceDetailsCard.tsx | 2 +- .../components/CalendarCard/AttendeeChip.tsx | 2 +- .../features/Stats/Info/InDepth/InDepth.tsx | 11 +++++-- .../src/components/AlertsPage/StatusChip.tsx | 18 +++++------ .../ILertCard/ILertCardEmptyState.tsx | 3 +- .../ILertCard/ILertCardHeaderStatus.tsx | 6 ++-- .../components/ServicesPage/StatusChip.tsx | 30 ++++++++--------- .../components/StatusPagePage/StatusChip.tsx | 30 ++++++++--------- .../StatusPagePage/VisibilityChip.tsx | 12 +++---- .../src/components/AttendeeChip.tsx | 2 +- .../DashboardSnapshot.tsx | 8 ++++- .../Cards/OwnershipCard/ComponentsGrid.tsx | 2 +- .../ScaffolderPageContextMenu.tsx | 6 ++-- .../components/TemplateCard/TemplateCard.tsx | 2 +- .../src/next/TemplateListPage/ContextMenu.tsx | 6 ++-- plugins/shortcuts/src/ShortcutItem.tsx | 6 ++-- .../components/RadarBubble/RadarBubble.tsx | 4 +-- .../src/components/RadarEntry/RadarEntry.tsx | 4 +-- .../components/RadarLegend/RadarLegend.tsx | 2 +- .../TechDocsReaderPageHeader.tsx | 8 ++++- 36 files changed, 169 insertions(+), 129 deletions(-) diff --git a/packages/core-components/src/components/Avatar/Avatar.tsx b/packages/core-components/src/components/Avatar/Avatar.tsx index 950f57668e..945cb20d2d 100644 --- a/packages/core-components/src/components/Avatar/Avatar.tsx +++ b/packages/core-components/src/components/Avatar/Avatar.tsx @@ -24,19 +24,18 @@ import { extractInitials, stringToColor } from './utils'; export type AvatarClassKey = 'avatar'; const useStyles = makeStyles( - (theme: Theme) => - createStyles({ - avatar: { - width: '4rem', - height: '4rem', - color: '#fff', - }, - avatarText: { - fontWeight: theme.typography.fontWeightBold, - letterSpacing: '1px', - textTransform: 'uppercase', - }, - }), + (theme: Theme) => ({ + avatar: { + width: '4rem', + height: '4rem', + color: theme.palette.common.white, + }, + avatarText: { + fontWeight: theme.typography.fontWeightBold, + letterSpacing: '1px', + textTransform: 'uppercase', + }, + }), { name: 'BackstageAvatar' }, ); diff --git a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx index 1910d6e7fd..6b336f5489 100644 --- a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx @@ -54,7 +54,8 @@ const useStyles = makeStyles( code: { borderRadius: 6, margin: `${theme.spacing(2)}px 0px`, - background: theme.palette.type === 'dark' ? '#444' : '#fff', + background: + theme.palette.type === 'dark' ? '#444' : theme.palette.common.white, }, }), { name: 'BackstageMissingAnnotationEmptyState' }, diff --git a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx index b967ee0a9f..92c10497f4 100644 --- a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx +++ b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx @@ -41,7 +41,7 @@ export type FeatureCalloutCircleClassKey = | 'text'; const useStyles = makeStyles( - { + theme => ({ '@keyframes pulsateSlightly': { '0%': { transform: 'scale(1.0)' }, '100%': { transform: 'scale(1.1)' }, @@ -78,7 +78,7 @@ const useStyles = makeStyles( height: '100%', backgroundColor: 'transparent', borderRadius: '100%', - border: '2px solid white', + border: `2px solid ${theme.palette.common.white}`, zIndex: 2001, transformOrigin: 'center center', animation: @@ -86,10 +86,10 @@ const useStyles = makeStyles( }, text: { position: 'absolute', - color: 'white', + color: theme.palette.common.white, zIndex: 2003, }, - }, + }), { name: 'BackstageFeatureCalloutCircular' }, ); diff --git a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx index f0bbeaa7bd..f6f4c3cd3e 100644 --- a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx +++ b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx @@ -26,9 +26,9 @@ type Props = CSS.Properties & { export type LifecycleClassKey = 'alpha' | 'beta'; const useStyles = makeStyles( - { + theme => ({ alpha: { - color: '#ffffff', + color: theme.palette.common.white, fontFamily: 'serif', fontWeight: 'normal', fontStyle: 'italic', @@ -39,7 +39,7 @@ const useStyles = makeStyles( fontWeight: 'normal', fontStyle: 'italic', }, - }, + }), { name: 'BackstageLifecycle' }, ); diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx index be6c0c6531..d2b11f9a3b 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx @@ -24,6 +24,7 @@ import ListItemText, { } from '@material-ui/core/ListItemText'; import Popover from '@material-ui/core/Popover'; import MoreVert from '@material-ui/icons/MoreVert'; +import { useTheme } from '@material-ui/core/styles'; /** * @public @@ -73,6 +74,11 @@ export type HeaderActionMenuProps = { * @public */ export function HeaderActionMenu(props: HeaderActionMenuProps) { + const { + palette: { + common: { white }, + }, + } = useTheme(); const { actionItems } = props; const [open, setOpen] = React.useState(false); const anchorElRef = React.useRef(null); @@ -84,7 +90,7 @@ export function HeaderActionMenu(props: HeaderActionMenuProps) { data-testid="header-action-menu" ref={anchorElRef} style={{ - color: 'white', + color: white, height: 56, width: 56, marginRight: -4, diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx index cc61a6a063..949148d7a1 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx @@ -80,7 +80,7 @@ const useStyles = makeStyles< title: { fontSize: theme.typography.h5.fontSize, fontWeight: theme.typography.fontWeightMedium, - color: '#FFF', + color: theme.palette.common.white, padding: theme.spacing(2.5), [theme.breakpoints.down('xs')]: { display: 'none', diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 5fdd29f203..b08647d335 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -35,7 +35,8 @@ const useStyles = makeStyles( height: 48, width: '100%', '&:hover': { - background: '#6f6f6f', + background: + theme.palette.navigation.navItem?.hoverBackground || '#6f6f6f', color: theme.palette.navigation.selectedColor, }, display: 'flex', @@ -52,7 +53,7 @@ const useStyles = makeStyles( }, selected: { background: '#6f6f6f', - color: '#FFF', + color: theme.palette.common.white, }, label: { margin: theme.spacing(1.75), @@ -82,7 +83,8 @@ const useStyles = makeStyles( width: '100%', padding: '10px 0 10px 0', '&:hover': { - background: '#6f6f6f', + background: + theme.palette.navigation.navItem?.hoverBackground || '#6f6f6f', color: theme.palette.navigation.selectedColor, }, }, diff --git a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx index da3bbb1e46..7c79dc8363 100644 --- a/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx +++ b/plugins/airbrake/dev/components/ApiBar/ApiBar.tsx @@ -17,20 +17,20 @@ import React from 'react'; import { makeStyles, TextField } from '@material-ui/core'; import { Context } from '../ContextProvider'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ root: { display: 'flex', gap: '1em', flexWrap: 'wrap', }, label: { - color: '#fff !important', + color: `${theme.palette.common.white} !important`, }, outline: { - color: '#fff !important', - borderColor: '#fff !important', + color: `${theme.palette.common.white} !important`, + borderColor: `${theme.palette.common.white} !important`, }, -}); +})); export const ApiBar = () => { const classes = useStyles(); diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx index 5b67fe844a..16321cd464 100644 --- a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx +++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.tsx @@ -28,6 +28,7 @@ import { import { default as MuiAlert } from '@material-ui/lab/Alert'; import { AzureSite } from '@backstage/plugin-azure-sites-common'; import { Table, TableColumn, Link } from '@backstage/core-components'; +import { useTheme } from '@material-ui/core/styles'; import FlashOnIcon from '@material-ui/icons/FlashOn'; import PublicIcon from '@material-ui/icons/Public'; import MoreVertIcon from '@material-ui/icons/MoreVert'; @@ -38,18 +39,27 @@ import OpenInNewIcon from '@material-ui/icons/OpenInNew'; import { DateTime } from 'luxon'; import { useApi } from '@backstage/core-plugin-api'; import { azureSiteApiRef } from '../../api'; +import { BackstageTheme } from '@backstage/theme'; type States = 'Waiting' | 'Running' | 'Paused' | 'Failed' | 'Stopped'; type Kinds = 'app' | 'functionapp'; const State = ({ value }: { value: States }) => { + const { + palette: { + common: { black }, + status: { ok, error }, + }, + } = useTheme(); + const colorMap = { Waiting: '#dcbc21', - Running: 'green', - Paused: 'black', - Failed: 'red', - Stopped: 'black', + Running: ok, + Paused: black, + Failed: error, + Stopped: black, }; + return ( ( code: { borderRadius: 6, margin: `${theme.spacing(2)}px 0px`, - background: theme.palette.type === 'dark' ? '#444' : '#fff', + background: + theme.palette.type === 'dark' ? '#444' : theme.palette.common.white, }, }), { name: 'PluginCatalogEntityLabelsEmptyState' }, diff --git a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx index 7f95a66700..c1d502dc78 100644 --- a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx @@ -34,7 +34,8 @@ const useStyles = makeStyles( code: { borderRadius: 6, margin: `${theme.spacing(2)}px 0px`, - background: theme.palette.type === 'dark' ? '#444' : '#fff', + background: + theme.palette.type === 'dark' ? '#444' : theme.palette.common.white, }, }), { name: 'PluginCatalogEntityLinksEmptyState' }, diff --git a/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx b/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx index e8e204ff9a..5aae7d4194 100644 --- a/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx +++ b/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx @@ -20,32 +20,32 @@ import { Link } from '@backstage/core-components'; import { Box, makeStyles, Typography } from '@material-ui/core'; import { BackstageTheme } from '@backstage/theme'; -const letterStyle = { - color: 'white', +const letterStyle = (theme: BackstageTheme) => ({ + color: theme.palette.common.white, border: 0, borderRadius: '3px', fontSize: '40px', padding: '5px 20px', -}; +}); const fontSize = { fontSize: '25px', }; -const letterColor = (letter: string) => { +const letterColor = (letter: string, theme: BackstageTheme) => { if (letter === 'A') { - return '#45d298'; + return theme.palette.success.main || '#45d298'; } else if (letter === 'B') { - return '#a5d86e'; + return theme.palette.success.light || '#a5d86e'; } else if (letter === 'C') { - return '#f1ce0c'; + return theme.palette.warning.light || '#f1ce0c'; } else if (letter === 'D') { - return '#f29141'; + return theme.palette.warning.main || '#f29141'; } else if (letter === 'F') { - return '#df5869'; + return theme.palette.error.light || '#df5869'; } - return '#45d298'; + return theme.palette.success.main || '#45d298'; }; const useStyles = makeStyles< @@ -54,7 +54,7 @@ const useStyles = makeStyles< maintainabilityLetter: string; testCoverageLetter: string; } ->({ +>(theme => ({ spaceAround: { display: 'flex', justifyContent: 'space-around', @@ -65,12 +65,12 @@ const useStyles = makeStyles< alignItems: 'center', }, maintainabilityLetterColor: { - ...letterStyle, - backgroundColor: props => letterColor(props.maintainabilityLetter), + ...letterStyle(theme), + backgroundColor: props => letterColor(props.maintainabilityLetter, theme), }, testCoverageLetterColor: { - ...letterStyle, - backgroundColor: props => letterColor(props.testCoverageLetter), + ...letterStyle(theme), + backgroundColor: props => letterColor(props.testCoverageLetter, theme), }, fontSize: { ...fontSize, @@ -82,7 +82,7 @@ const useStyles = makeStyles< paddingSides20: { padding: '0px 20px', }, -}); +})); export const CodeClimateTable = ({ codeClimateData, diff --git a/plugins/code-coverage/src/components/FileExplorer/CodeRow.tsx b/plugins/code-coverage/src/components/FileExplorer/CodeRow.tsx index 7eb8ce0f29..c7dee55f2b 100644 --- a/plugins/code-coverage/src/components/FileExplorer/CodeRow.tsx +++ b/plugins/code-coverage/src/components/FileExplorer/CodeRow.tsx @@ -29,7 +29,7 @@ const useStyles = makeStyles(theme => ({ width: '50px', borderRight: `1px solid ${theme.palette.grey[500]}`, textAlign: 'center', - color: 'white', // need to enforce this color since it needs to stand out against colored background + color: theme.palette.common.white, // need to enforce this color since it needs to stand out against colored background paddingLeft: theme.spacing(1), paddingRight: theme.spacing(1), }, diff --git a/plugins/code-coverage/src/components/FileExplorer/FileContent.tsx b/plugins/code-coverage/src/components/FileExplorer/FileContent.tsx index ac132c5a56..2ca6987be7 100644 --- a/plugins/code-coverage/src/components/FileExplorer/FileContent.tsx +++ b/plugins/code-coverage/src/components/FileExplorer/FileContent.tsx @@ -37,7 +37,7 @@ const useStyles = makeStyles(theme => ({ margin: 'auto', top: '2em', width: '80%', - border: '2px solid #000', + border: `2px solid ${theme.palette.common.black}`, boxShadow: theme.shadows[5], padding: theme.spacing(2, 4, 3), overflow: 'scroll', diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index fd1d5ba5fb..127bd193bf 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -64,7 +64,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ display: 'flex', alignItems: 'center', justifyContent: 'center', - color: 'black', + color: theme.palette.common.black, }, legend: { position: 'absolute', diff --git a/plugins/explore/src/components/ToolCard/ToolCard.tsx b/plugins/explore/src/components/ToolCard/ToolCard.tsx index 904db0c00b..dde8f97a54 100644 --- a/plugins/explore/src/components/ToolCard/ToolCard.tsx +++ b/plugins/explore/src/components/ToolCard/ToolCard.tsx @@ -41,7 +41,7 @@ const useStyles = makeStyles(theme => ({ }, lifecycle: { lineHeight: '0.8em', - color: 'white', + color: theme.palette.common.white, }, ga: { backgroundColor: theme.palette.status.ok, diff --git a/plugins/firehydrant/src/components/ServiceDetailsCard/ServiceDetailsCard.tsx b/plugins/firehydrant/src/components/ServiceDetailsCard/ServiceDetailsCard.tsx index ed8bbf6a81..bc2dd46fef 100644 --- a/plugins/firehydrant/src/components/ServiceDetailsCard/ServiceDetailsCard.tsx +++ b/plugins/firehydrant/src/components/ServiceDetailsCard/ServiceDetailsCard.tsx @@ -66,7 +66,7 @@ const useStyles = makeStyles(theme => ({ }, buttonLink: { backgroundColor: '#3b2492', - color: '#FFF', + color: theme.palette.common.white, textTransform: 'none', '&:hover': { backgroundColor: '#614ab6', diff --git a/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx b/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx index dd4d743572..4e36eff55a 100644 --- a/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx +++ b/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx @@ -44,7 +44,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => { '& svg': { height: 16, width: 16, - background: '#fff', + background: theme.palette.common.white, }, }, }; diff --git a/plugins/git-release-manager/src/features/Stats/Info/InDepth/InDepth.tsx b/plugins/git-release-manager/src/features/Stats/Info/InDepth/InDepth.tsx index b55b0a79b2..421b427470 100644 --- a/plugins/git-release-manager/src/features/Stats/Info/InDepth/InDepth.tsx +++ b/plugins/git-release-manager/src/features/Stats/Info/InDepth/InDepth.tsx @@ -21,6 +21,7 @@ import { Tooltip as MaterialTooltip, Typography, } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; import { BarChart, Bar, XAxis, YAxis, Legend, Tooltip } from 'recharts'; import { AverageReleaseTime } from './AverageReleaseTime'; @@ -30,6 +31,12 @@ import { useGetReleaseTimes } from '../hooks/useGetReleaseTimes'; import { useReleaseStatsContext } from '../../contexts/ReleaseStatsContext'; export function InDepth() { + const { + palette: { + success, + common: { black }, + }, + } = useTheme(); const { releaseStats } = useReleaseStatsContext(); const { averageReleaseTime, progress, releaseCommitPairs, run } = useGetReleaseTimes(); @@ -112,9 +119,9 @@ export function InDepth() { > - + - + {progress > 0 && progress < 100 && ( diff --git a/plugins/ilert/src/components/AlertsPage/StatusChip.tsx b/plugins/ilert/src/components/AlertsPage/StatusChip.tsx index 55fe46d810..ac7147b3b2 100644 --- a/plugins/ilert/src/components/AlertsPage/StatusChip.tsx +++ b/plugins/ilert/src/components/AlertsPage/StatusChip.tsx @@ -17,28 +17,28 @@ import { Chip, withStyles } from '@material-ui/core'; import React from 'react'; import { ACCEPTED, Alert, PENDING, RESOLVED } from '../../types'; -const ResolvedChip = withStyles({ +const ResolvedChip = withStyles(theme => ({ root: { backgroundColor: '#4caf50', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); -const AcceptedChip = withStyles({ +const AcceptedChip = withStyles(theme => ({ root: { backgroundColor: '#ffb74d', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const PendingChip = withStyles({ +}))(Chip); +const PendingChip = withStyles(theme => ({ root: { backgroundColor: '#d32f2f', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); export const alertStatusLabels = { [RESOLVED]: 'Resolved', diff --git a/plugins/ilert/src/components/ILertCard/ILertCardEmptyState.tsx b/plugins/ilert/src/components/ILertCard/ILertCardEmptyState.tsx index b386cb628b..966caea079 100644 --- a/plugins/ilert/src/components/ILertCard/ILertCardEmptyState.tsx +++ b/plugins/ilert/src/components/ILertCard/ILertCardEmptyState.tsx @@ -41,7 +41,8 @@ const useStyles = makeStyles(theme => ({ code: { borderRadius: 6, margin: theme.spacing(2, 0), - background: theme.palette.type === 'dark' ? '#444' : '#fff', + background: + theme.palette.type === 'dark' ? '#444' : theme.palette.common.white, }, header: { display: 'inline-block', diff --git a/plugins/ilert/src/components/ILertCard/ILertCardHeaderStatus.tsx b/plugins/ilert/src/components/ILertCard/ILertCardHeaderStatus.tsx index eb6ab91fd2..77db28a831 100644 --- a/plugins/ilert/src/components/ILertCard/ILertCardHeaderStatus.tsx +++ b/plugins/ilert/src/components/ILertCard/ILertCardHeaderStatus.tsx @@ -18,13 +18,13 @@ import Chip from '@material-ui/core/Chip'; import { withStyles } from '@material-ui/core/styles'; import { AlertSource } from '../../types'; -const MaintenanceChip = withStyles({ +const MaintenanceChip = withStyles(theme => ({ root: { backgroundColor: '#92949c', - color: 'white', + color: theme.palette.common.white, marginTop: 8, }, -})(Chip); +}))(Chip); export const ILertCardHeaderStatus = ({ alertSource, diff --git a/plugins/ilert/src/components/ServicesPage/StatusChip.tsx b/plugins/ilert/src/components/ServicesPage/StatusChip.tsx index 873bfbfb28..1d2c89ec6c 100644 --- a/plugins/ilert/src/components/ServicesPage/StatusChip.tsx +++ b/plugins/ilert/src/components/ServicesPage/StatusChip.tsx @@ -24,42 +24,42 @@ import { UNDER_MAINTENANCE, } from '../../types'; -const OperationalChip = withStyles({ +const OperationalChip = withStyles(theme => ({ root: { backgroundColor: '#388E3D', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); -const UnderMaintenanceChip = withStyles({ +const UnderMaintenanceChip = withStyles(theme => ({ root: { backgroundColor: '#616161', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const DegradedChip = withStyles({ +}))(Chip); +const DegradedChip = withStyles(theme => ({ root: { backgroundColor: '#FBC02D', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const PartialOutageChip = withStyles({ +}))(Chip); +const PartialOutageChip = withStyles(theme => ({ root: { backgroundColor: '#F57C02', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const MajorOutageChip = withStyles({ +}))(Chip); +const MajorOutageChip = withStyles(theme => ({ root: { backgroundColor: '#D22F2E', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); const serviceStatusLabels = { [OPERATIONAL]: 'Operational', diff --git a/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx b/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx index 5cd785c4cd..1eaffafd25 100644 --- a/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx +++ b/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx @@ -24,42 +24,42 @@ import { UNDER_MAINTENANCE, } from '../../types'; -const OperationalChip = withStyles({ +const OperationalChip = withStyles(theme => ({ root: { backgroundColor: '#388E3D', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); -const UnderMaintenanceChip = withStyles({ +const UnderMaintenanceChip = withStyles(theme => ({ root: { backgroundColor: '#616161', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const DegradedChip = withStyles({ +}))(Chip); +const DegradedChip = withStyles(theme => ({ root: { backgroundColor: '#FBC02D', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const PartialOutageChip = withStyles({ +}))(Chip); +const PartialOutageChip = withStyles(theme => ({ root: { backgroundColor: '#F57C02', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); -const MajorOutageChip = withStyles({ +}))(Chip); +const MajorOutageChip = withStyles(theme => ({ root: { backgroundColor: '#D22F2E', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); const statusPageStatusLabels = { [OPERATIONAL]: 'Operational', diff --git a/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx b/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx index 04e8019d3d..90967e5483 100644 --- a/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx +++ b/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx @@ -17,21 +17,21 @@ import { Chip, withStyles } from '@material-ui/core'; import React from 'react'; import { PRIVATE, PUBLIC, StatusPage } from '../../types'; -const PrivateChip = withStyles({ +const PrivateChip = withStyles(theme => ({ root: { backgroundColor: '#4caf50', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); -const PublicChip = withStyles({ +const PublicChip = withStyles(theme => ({ root: { backgroundColor: '#ffb74d', - color: 'white', + color: theme.palette.common.white, margin: 0, }, -})(Chip); +}))(Chip); const statusPageVisibilityLabels = { [PUBLIC]: 'Public', diff --git a/plugins/microsoft-calendar/src/components/AttendeeChip.tsx b/plugins/microsoft-calendar/src/components/AttendeeChip.tsx index 67dd601da7..b3b5b98301 100644 --- a/plugins/microsoft-calendar/src/components/AttendeeChip.tsx +++ b/plugins/microsoft-calendar/src/components/AttendeeChip.tsx @@ -44,7 +44,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => { '& svg': { height: 16, width: 16, - background: '#fff', + background: theme.palette.common.white, }, }, }; diff --git a/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx index dcbde9279a..cdac4196d4 100644 --- a/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx +++ b/plugins/newrelic-dashboard/src/components/NewRelicDashboard/DashboardSnapshotList/DashboardSnapshot.tsx @@ -16,6 +16,7 @@ import React from 'react'; import { Box, Grid, MenuItem, Select } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; import { useApi, storageApiRef } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; import { @@ -36,6 +37,11 @@ export const DashboardSnapshot = (props: { name: string; permalink: string; }) => { + const { + palette: { + common: { black }, + }, + } = useTheme(); const { guid, name, permalink } = props; const newRelicDashboardAPI = useApi(newRelicDashboardApiRef); const storageApi = useApi(storageApiRef).forBucket('newrelic-dashboard'); @@ -99,7 +105,7 @@ export const DashboardSnapshot = (props: { {url ? ( {`${name} ) : ( diff --git a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx index 567c7ad3bc..b130057fe3 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx @@ -37,7 +37,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => boxShadow: theme.shadows[2], borderRadius: '4px', padding: theme.spacing(2), - color: '#fff', + color: theme.palette.common.white, transition: `${theme.transitions.duration.standard}ms`, '&:hover': { boxShadow: theme.shadows[4], diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPageContextMenu.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPageContextMenu.tsx index 9e69fae68d..ca7648546e 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPageContextMenu.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPageContextMenu.tsx @@ -34,11 +34,11 @@ import { scaffolderListTaskRouteRef, } from '../../routes'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ button: { - color: 'white', + color: theme.palette.common.white, }, -}); +})); export type ScaffolderPageContextMenuProps = { editor?: boolean; diff --git a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx index 984e51f3b5..5e91e59679 100644 --- a/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx +++ b/plugins/scaffolder/src/components/TemplateCard/TemplateCard.tsx @@ -102,7 +102,7 @@ const useStyles = makeStyles(theme => ({ top: theme.spacing(0.5), right: theme.spacing(0.5), padding: '0.25rem', - color: '#fff', + color: theme.palette.common.white, }, })); diff --git a/plugins/scaffolder/src/next/TemplateListPage/ContextMenu.tsx b/plugins/scaffolder/src/next/TemplateListPage/ContextMenu.tsx index 3bca46af2a..6165d40e7c 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/ContextMenu.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/ContextMenu.tsx @@ -34,11 +34,11 @@ import { nextScaffolderListTaskRouteRef, } from '../routes'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ button: { - color: 'white', + color: theme.palette.common.white, }, -}); +})); export type ScaffolderPageContextMenuProps = { editor?: boolean; diff --git a/plugins/shortcuts/src/ShortcutItem.tsx b/plugins/shortcuts/src/ShortcutItem.tsx index 8ba1a96358..1d4e6a20ae 100644 --- a/plugins/shortcuts/src/ShortcutItem.tsx +++ b/plugins/shortcuts/src/ShortcutItem.tsx @@ -25,7 +25,7 @@ import { ShortcutApi } from './api'; import { Shortcut } from './types'; import { SidebarItem } from '@backstage/core-components'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ root: { '&:hover #edit': { visibility: 'visible', @@ -35,10 +35,10 @@ const useStyles = makeStyles({ visibility: 'hidden', }, icon: { - color: 'white', + color: theme.palette.common.white, fontSize: 16, }, -}); +})); const getIconText = (title: string) => title.split(' ').length === 1 diff --git a/plugins/tech-radar/src/components/RadarBubble/RadarBubble.tsx b/plugins/tech-radar/src/components/RadarBubble/RadarBubble.tsx index b2d7c1290f..c114744a40 100644 --- a/plugins/tech-radar/src/components/RadarBubble/RadarBubble.tsx +++ b/plugins/tech-radar/src/components/RadarBubble/RadarBubble.tsx @@ -24,7 +24,7 @@ export type Props = { y: number; }; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles(theme => ({ bubble: { pointerEvents: 'none', userSelect: 'none', @@ -42,7 +42,7 @@ const useStyles = makeStyles(() => ({ pointerEvents: 'none', userSelect: 'none', fontSize: '10px', - fill: '#fff', + fill: theme.palette.common.white, }, })); diff --git a/plugins/tech-radar/src/components/RadarEntry/RadarEntry.tsx b/plugins/tech-radar/src/components/RadarEntry/RadarEntry.tsx index 849b5157bd..829b9c7708 100644 --- a/plugins/tech-radar/src/components/RadarEntry/RadarEntry.tsx +++ b/plugins/tech-radar/src/components/RadarEntry/RadarEntry.tsx @@ -33,12 +33,12 @@ export type Props = { onClick?: (event: React.MouseEvent) => void; }; -const useStyles = makeStyles(() => ({ +const useStyles = makeStyles(theme => ({ text: { pointerEvents: 'none', userSelect: 'none', fontSize: '9px', - fill: '#fff', + fill: theme.palette.common.white, textAnchor: 'middle', }, diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx index 4ca0350e7b..42b87326c6 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx @@ -73,7 +73,7 @@ const useStyles = makeStyles(theme => ({ userSelect: 'none', fontSize: '11px', background: '#6f6f6f', - color: '#FFF', + color: theme.palette.common.white, }, entryLink: { pointerEvents: 'visiblePainted', diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx index dcb75f4cc4..f7b4f2eff2 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx @@ -19,6 +19,7 @@ import Helmet from 'react-helmet'; import { Grid } from '@material-ui/core'; import { Skeleton } from '@material-ui/lab'; +import { useTheme } from '@material-ui/core/styles'; import CodeIcon from '@material-ui/icons/Code'; import { @@ -64,6 +65,11 @@ export type TechDocsReaderPageHeaderProps = PropsWithChildren<{ export const TechDocsReaderPageHeader = ( props: TechDocsReaderPageHeaderProps, ) => { + const { + palette: { + common: { white }, + }, + } = useTheme(); const { children } = props; const addons = useTechDocsAddons(); const configApi = useApi(configApiRef); @@ -138,7 +144,7 @@ export const TechDocsReaderPageHeader = ( container direction="column" alignItems="center" - style={{ color: '#fff' }} + style={{ color: white }} > From cb8ec97cdeb772f52f0ad3c82d005843a0e16068 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Wed, 22 Feb 2023 04:25:43 -0500 Subject: [PATCH 036/236] refactor: Add changeset Signed-off-by: Carlos Esteban Lopez --- .changeset/odd-oranges-tease.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .changeset/odd-oranges-tease.md diff --git a/.changeset/odd-oranges-tease.md b/.changeset/odd-oranges-tease.md new file mode 100644 index 0000000000..f9d5e60d19 --- /dev/null +++ b/.changeset/odd-oranges-tease.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-git-release-manager': patch +'@backstage/plugin-microsoft-calendar': patch +'@backstage/plugin-newrelic-dashboard': patch +'@backstage/core-components': patch +'@backstage/plugin-code-coverage': patch +'@backstage/plugin-code-climate': patch +'@backstage/plugin-azure-sites': patch +'@backstage/plugin-firehydrant': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-gcalendar': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-ilert': patch +'@backstage/plugin-org': patch +--- + +Change black & white colors to be theme aware From fc2b7e885887e8497f57032eec2161b60cfe7bb8 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Wed, 22 Feb 2023 10:35:37 +0100 Subject: [PATCH 037/236] fix GitlabOrgDiscoveryEntityProvider.test.ts Signed-off-by: Matteo Silvestri --- .../src/providers/GitlabOrgDiscoveryEntityProvider.test.ts | 5 ++--- .../src/providers/GitlabOrgDiscoveryEntityProvider.ts | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts index 268b4c3ba4..cff215acd3 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.test.ts @@ -184,7 +184,6 @@ describe('GitlabOrgDiscoveryEntityProvider', () => { gitlab: { 'test-id': { host: 'test-gitlab', - group: 'test-group', orgEnabled: true, }, }, @@ -376,9 +375,9 @@ describe('GitlabOrgDiscoveryEntityProvider', () => { metadata: { annotations: { 'backstage.io/managed-by-location': - 'url:https://test-gitlab/teams/group1-group2', + 'url:https://test-gitlab/group1/group2', 'backstage.io/managed-by-origin-location': - 'url:https://test-gitlab/teams/group1-group2', + 'url:https://test-gitlab/group1/group2', 'test-gitlab/team-path': 'group1/group2', }, description: 'Group2', diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index c507b19549..77314ead17 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -356,7 +356,12 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { } private groupName(full_path: string): string { - return full_path.replace(`${this.config.group}/`, '').replaceAll('/', '-'); + if (this.config.group && full_path.startsWith(`${this.config.group}/`)) { + return full_path + .replace(`${this.config.group}/`, '') + .replaceAll('/', '-'); + } + return full_path.replaceAll('/', '-'); } private createGroupEntity(group: GitLabGroup, host: string): GroupEntity { From b6ec0886cdb3e6b8de3f1171c8c25cb959c4bd87 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Wed, 22 Feb 2023 04:50:33 -0500 Subject: [PATCH 038/236] fix: Remove unneded import Signed-off-by: Carlos Esteban Lopez --- packages/core-components/src/components/Avatar/Avatar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/src/components/Avatar/Avatar.tsx b/packages/core-components/src/components/Avatar/Avatar.tsx index 945cb20d2d..3727c892f9 100644 --- a/packages/core-components/src/components/Avatar/Avatar.tsx +++ b/packages/core-components/src/components/Avatar/Avatar.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import MaterialAvatar from '@material-ui/core/Avatar'; -import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import { makeStyles, Theme } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import React, { CSSProperties } from 'react'; From 3d96343887195b3838508492a1e967a769e497f5 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Wed, 22 Feb 2023 13:08:12 +0100 Subject: [PATCH 039/236] fix compilation issue GitlabOrgDiscoveryEntityProvider Signed-off-by: Matteo Silvestri --- .../src/providers/GitlabOrgDiscoveryEntityProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index 77314ead17..57a7210e24 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -295,7 +295,7 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { private withLocations(host: string, baseUrl: string, entity: Entity): Entity { const location = entity.kind === 'Group' - ? `url:${baseUrl}/${entity.metadata.annotations[`${host}/team-path`]}` + ? `url:${baseUrl}/${entity.metadata.annotations?.[`${host}/team-path`]}` : `url:${baseUrl}/${entity.metadata.name}`; return merge( { From 2de756939b7f0c36734e9f90e98df52aff26a130 Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Wed, 22 Feb 2023 15:26:29 +0100 Subject: [PATCH 040/236] fix GitlabOrgDiscoveryEntityProvider and tests Signed-off-by: Matteo Silvestri --- .../src/providers/GitlabOrgDiscoveryEntityProvider.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index 57a7210e24..43c070ecae 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -199,7 +199,10 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { continue; } - if (!group.full_path.startsWith(this.config.group)) { + if ( + this.config.group && + !group.full_path.startsWith(`${this.config.group}/`) + ) { continue; } From be3cddaab5f74b4150b68fa1635b85004f38ce87 Mon Sep 17 00:00:00 2001 From: Katharina Sick Date: Wed, 22 Feb 2023 15:36:35 +0100 Subject: [PATCH 041/236] fix: fix getting credentials for Bitbucket Server in the RepoUrlPicker Signed-off-by: Katharina Sick --- .changeset/famous-sloths-tie.md | 5 ++ .../src/providers/bitbucketServer/provider.ts | 2 +- .../RepoUrlPicker/RepoUrlPicker.test.tsx | 64 +++++++++++++++++++ .../fields/RepoUrlPicker/RepoUrlPicker.tsx | 11 ++-- 4 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 .changeset/famous-sloths-tie.md diff --git a/.changeset/famous-sloths-tie.md b/.changeset/famous-sloths-tie.md new file mode 100644 index 0000000000..53932afab6 --- /dev/null +++ b/.changeset/famous-sloths-tie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Getting credentials in the RepoUrlPicker now also works for targets without owner (e.g. Bitbucket Server). diff --git a/plugins/auth-backend/src/providers/bitbucketServer/provider.ts b/plugins/auth-backend/src/providers/bitbucketServer/provider.ts index fb8dd3d5b8..220f6db5cd 100644 --- a/plugins/auth-backend/src/providers/bitbucketServer/provider.ts +++ b/plugins/auth-backend/src/providers/bitbucketServer/provider.ts @@ -216,7 +216,7 @@ export class BitbucketServerAuthProvider implements OAuthHandlers { throw new Error(`Failed to retrieve the user '${username}'`); } - const user = (await userResponse.json()) as any; + const user = await userResponse.json(); const passportProfile = { provider: 'bitbucketServer', diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx index 1edb4fb940..c36374f939 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx @@ -38,6 +38,11 @@ describe('RepoUrlPicker', () => { integrations: [ { host: 'github.com', type: 'github', title: 'github.com' }, { host: 'dev.azure.com', type: 'azure', title: 'dev.azure.com' }, + { + host: 'server.bitbucket.org', + type: 'bitbucketServer', + title: 'server.bitbucket.org', + }, ], }), }; @@ -175,6 +180,65 @@ describe('RepoUrlPicker', () => { getByTestId('current-secrets').textContent!, ); + expect(currentSecrets).toEqual({ + secrets: { testKey: 'abc123' }, + }); + }); + it('should call the scmAuthApi with the correct params if only a project is set', async () => { + const SecretsComponent = () => { + const { secrets } = useTemplateSecrets(); + return ( +
{JSON.stringify({ secrets })}
+ ); + }; + const { getAllByRole, getByTestId } = await renderInTestApp( + + +
+ + + , + ); + + const [projectInput, repoInput] = getAllByRole('textbox'); + + await act(async () => { + fireEvent.change(projectInput, { target: { value: 'backstage' } }); + fireEvent.change(repoInput, { target: { value: 'repo123' } }); + + // need to wait for the debounce to finish + await new Promise(resolve => setTimeout(resolve, 600)); + }); + + expect(mockScmAuthApi.getCredentials).toHaveBeenCalledWith({ + url: 'https://server.bitbucket.org/backstage/repo123', + additionalScope: { + repoWrite: true, + }, + }); + + const currentSecrets = JSON.parse( + getByTestId('current-secrets').textContent!, + ); + expect(currentSecrets).toEqual({ secrets: { testKey: 'abc123' }, }); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx index 9cf61a5e40..9bd904de13 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx @@ -120,16 +120,17 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => { async () => { const { requestUserCredentials } = uiSchema?.['ui:options'] ?? {}; + const workspace = state.owner ? state.owner : state.project; if ( !requestUserCredentials || - !(state.host && state.owner && state.repoName) + !(state.host && workspace && state.repoName) ) { return; } - const [encodedHost, encodedOwner, encodedRepoName] = [ + const [encodedHost, encodedWorkspace, encodedRepoName] = [ state.host, - state.owner, + workspace, state.repoName, ].map(encodeURIComponent); @@ -137,14 +138,14 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => { // so lets grab them using the scmAuthApi and pass through // any additional scopes from the ui:options const { token } = await scmAuthApi.getCredentials({ - url: `https://${encodedHost}/${encodedOwner}/${encodedRepoName}`, + url: `https://${encodedHost}/${encodedWorkspace}/${encodedRepoName}`, additionalScope: { repoWrite: true, customScopes: requestUserCredentials.additionalScopes, }, }); - // set the secret using the key provided in the the ui:options for use + // set the secret using the key provided in the ui:options for use // in the templating the manifest with ${{ secrets[secretsKey] }} setSecrets({ [requestUserCredentials.secretsKey]: token }); }, From dc1746c533ba188cfa1f01f225b67ffed69a5ff9 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Wed, 22 Feb 2023 12:14:50 -0500 Subject: [PATCH 042/236] test: Fix missing theme provider in test Signed-off-by: Carlos Esteban Lopez --- .../AzureSitesOverviewTable.test.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx index a4d833fa15..6488b79301 100644 --- a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx +++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx @@ -24,6 +24,7 @@ import { } from '@backstage/core-plugin-api'; import { rest } from 'msw'; import { + renderInTestApp, setupRequestMockHandlers, TestApiProvider, } from '@backstage/test-utils'; @@ -65,7 +66,7 @@ describe('AzureSitesOverviewWidget', () => { }); it('should display an overview table with the data from the requests', async () => { - const rendered = render( + const rendered = await renderInTestApp( , From eb877bad7369d62befdfe51bb9e7c29fd077e9c7 Mon Sep 17 00:00:00 2001 From: Axel Hecht Date: Thu, 23 Feb 2023 11:42:36 +0100 Subject: [PATCH 043/236] Add an "Other Templates" group for scaffolder/next when groups are passed in. Also adding a number of options to the dev page to see this change in action. Signed-off-by: Axel Hecht --- .changeset/odd-waves-rescue.md | 5 +++ plugins/scaffolder/dev/index.tsx | 43 ++++++++++++++++++- .../TemplateListPage/TemplateListPage.tsx | 20 +++++++-- 3 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 .changeset/odd-waves-rescue.md diff --git a/.changeset/odd-waves-rescue.md b/.changeset/odd-waves-rescue.md new file mode 100644 index 0000000000..e435bc9962 --- /dev/null +++ b/.changeset/odd-waves-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Create an "Other Templates" group when groups are given to scaffolder/next. diff --git a/plugins/scaffolder/dev/index.tsx b/plugins/scaffolder/dev/index.tsx index 83496c719d..68c59eec2d 100644 --- a/plugins/scaffolder/dev/index.tsx +++ b/plugins/scaffolder/dev/index.tsx @@ -24,7 +24,7 @@ import { } from '@backstage/plugin-catalog-react'; import React from 'react'; import { scaffolderApiRef, ScaffolderClient } from '../src'; -import { ScaffolderPage } from '../src/plugin'; +import { NextScaffolderPage, ScaffolderPage } from '../src/plugin'; import { discoveryApiRef, fetchApiRef, @@ -68,4 +68,45 @@ createDevApp() title: 'Create', element: , }) + .addPage({ + path: '/next-create', + title: 'Create (next)', + element: , + }) + .addPage({ + path: '/create-groups', + title: 'Groups', + element: ( + e.metadata.tags?.includes('techdocs') || false, + title: 'Techdocs', + }, + { + filter: e => e.metadata.tags?.includes('react') || false, + title: 'React', + }, + ]} + /> + ), + }) + .addPage({ + path: '/next-create-groups', + title: 'Groups (next)', + element: ( + e.metadata.tags?.includes('techdocs') || false, + title: 'Techdocs', + }, + { + filter: e => e.metadata.tags?.includes('react') || false, + title: 'React', + }, + ]} + /> + ), + }) .render(); diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx index b701ceb3c4..bddc15a61b 100644 --- a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx @@ -52,13 +52,27 @@ export type TemplateListPageProps = { }; const defaultGroup: TemplateGroupFilter = { - title: 'All Templates', + title: 'Templates', filter: () => true, }; +const createGroupsWithOther = ( + groups: TemplateGroupFilter[], +): TemplateGroupFilter[] => [ + ...groups, + { + title: 'Other Templates', + filter: e => ![...groups].some(({ filter }) => filter(e)), + }, +]; + export const TemplateListPage = (props: TemplateListPageProps) => { const registerComponentLink = useRouteRef(registerComponentRouteRef); - const { TemplateCardComponent, groups = [] } = props; + const { TemplateCardComponent, groups: givenGroups = [] } = props; + + const groups = givenGroups.length + ? createGroupsWithOther(givenGroups) + : [defaultGroup]; return ( @@ -96,7 +110,7 @@ export const TemplateListPage = (props: TemplateListPageProps) => { From de9d28fb88c7a22f55ac73fb8882eab9004cfe55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 14:38:03 +0000 Subject: [PATCH 044/236] fix(deps): update dependency @roadiehq/backstage-plugin-github-pull-requests to v2.5.2 Signed-off-by: Renovate Bot --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 02058a26f0..de185bd470 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13185,8 +13185,8 @@ __metadata: linkType: hard "@roadiehq/backstage-plugin-github-pull-requests@npm:^2.2.7": - version: 2.5.1 - resolution: "@roadiehq/backstage-plugin-github-pull-requests@npm:2.5.1" + version: 2.5.2 + resolution: "@roadiehq/backstage-plugin-github-pull-requests@npm:2.5.2" dependencies: "@backstage/catalog-model": ^1.1.5 "@backstage/core-components": ^0.12.3 @@ -13209,7 +13209,7 @@ __metadata: react: ^16.13.1 || ^17.0.0 react-dom: ^16.13.1 || ^17.0.0 react-router: 6.0.0-beta.0 || ^6.3.0 - checksum: 769991ac46d4466261f2765eaab6eca903e751dc003d94a21dfb0376fca69af8edb2a8b9b12b375e903694278d32a95f8745d1f724a19429df34adeb3fb3e58c + checksum: dd8c3301867cd8ccaf094eb62c64b2923c4614e9ba384249c5ddb80b6375bc7f5e8828a7fc12f9e7785ca230948895e03a9c1521be45f69797f663b28cecc3c7 languageName: node linkType: hard From 26c27f4f808eaeab6ed7d6249ef25205d023174b Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Thu, 23 Feb 2023 10:44:10 -0500 Subject: [PATCH 045/236] Update EntityPicker to use stringifyEntityRef. Signed-off-by: Aramis Sennyey --- .../src/components/EntityRefLink/humanize.ts | 8 --- .../fields/EntityPicker/EntityPicker.test.tsx | 50 +++++++++++++++++++ .../fields/EntityPicker/EntityPicker.tsx | 38 ++++++++++---- 3 files changed, 79 insertions(+), 17 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts index 9abf0d7978..9bbfcbc1ed 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts +++ b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts @@ -37,19 +37,11 @@ export function humanizeEntityRef( let kind; let namespace; let name; - console.log(entityRef); if ('metadata' in entityRef) { kind = entityRef.kind; namespace = entityRef.metadata.namespace; name = entityRef.metadata.name; - - // Escapes when we know more about an entity. - if (entityRef.metadata.title) { - return entityRef.metadata.title; - } else if ((entityRef as UserEntity).spec?.profile?.displayName) { - return (entityRef as UserEntity).spec?.profile?.displayName; - } } else { kind = entityRef.kind; namespace = entityRef.namespace; diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx index b6d6688a09..3cc45e6412 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx @@ -236,4 +236,54 @@ describe('', () => { }); }); }); + + describe('uses full entity ref', () => { + beforeEach(() => { + uiSchema = { + 'ui:options': { + defaultKind: 'Group', + }, + }; + props = { + onChange, + schema, + required, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + catalogApi.getEntities.mockResolvedValue({ items: entities }); + }); + + it('returns the full entityRef when entity exists in the list', async () => { + const { getByRole } = await renderInTestApp( + + + , + ); + + const input = getByRole('textbox'); + + fireEvent.change(input, { target: { value: 'team-a' } }); + fireEvent.blur(input); + + expect(onChange).toHaveBeenCalledWith('group:default/team-a'); + }); + + it('returns the full entityRef when entity does not exist in the list', async () => { + const { getByRole } = await renderInTestApp( + + + , + ); + + const input = getByRole('textbox'); + + fireEvent.change(input, { target: { value: 'team-b' } }); + fireEvent.blur(input); + + expect(onChange).toHaveBeenCalledWith('group:default/team-b'); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 2a36b9f8ef..48d6453ab9 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -58,7 +58,8 @@ export const EntityPicker = (props: EntityPickerProps) => { (allowedKinds && { kind: allowedKinds }); const defaultKind = uiSchema['ui:options']?.defaultKind; - const defaultNamespace = uiSchema['ui:options']?.defaultNamespace; + const defaultNamespace = + uiSchema['ui:options']?.defaultNamespace || undefined; const catalogApi = useApi(catalogApiRef); @@ -71,13 +72,30 @@ export const EntityPicker = (props: EntityPickerProps) => { const allowArbitraryValues = uiSchema['ui:options']?.allowArbitraryValues ?? true; + const getLabel = useCallback( + (ref: string) => { + try { + return humanizeEntityRef( + parseEntityRef(ref, { defaultKind, defaultNamespace }), + { + defaultKind, + defaultNamespace, + }, + ); + } catch (err) { + return ref; + } + }, + [defaultKind, defaultNamespace], + ); + const onSelect = useCallback( (_: any, ref: string | Entity | null, reason: AutocompleteChangeReason) => { - // if ref == string + // ref can either be a string from free solo entry or if (typeof ref !== 'string') { onChange(ref ? stringifyEntityRef(ref as Entity) : ''); } else { - if (reason === 'blur') { + if (reason === 'blur' || reason === 'create-option') { // Add in default namespace, etc. let entityRef = ref; try { @@ -85,19 +103,20 @@ export const EntityPicker = (props: EntityPickerProps) => { entityRef = stringifyEntityRef( parseEntityRef(ref as string, { defaultKind, - defaultNamespace: defaultNamespace || undefined, + defaultNamespace, }), ); } catch (err) { // If the passed in value isn't an entity ref, do nothing. } - if (formData !== entityRef) { - onChange(ref); + // We need to check against formData here as that's the previous value for this field. + if (formData !== ref || allowArbitraryValues) { + onChange(entityRef); } } } }, - [onChange, formData, defaultKind, defaultNamespace], + [onChange, formData, defaultKind, defaultNamespace, allowArbitraryValues], ); useEffect(() => { @@ -116,9 +135,10 @@ export const EntityPicker = (props: EntityPickerProps) => { disabled={entities?.length === 1} id={idSchema?.$id} value={ - // Since free solo is usually enabled, attempt to + // Since free solo can be enabled, attempt to parse as a full entity ref first, then fall + // back to the given value. entities?.find(e => stringifyEntityRef(e) === formData) ?? - (allowArbitraryValues ? formData : '') + (allowArbitraryValues && formData ? getLabel(formData) : '') } loading={loading} onChange={onSelect} From 4e5891524ca57f0cb7a787e91aa7966a67f83782 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Thu, 23 Feb 2023 12:37:09 -0500 Subject: [PATCH 046/236] Update to match Zalando. Signed-off-by: Aramis Sennyey --- plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx | 2 ++ .../tech-radar/src/components/RadarLegend/RadarLegend.tsx | 1 + .../src/components/RadarLegend/RadarLegendRing.tsx | 4 +++- plugins/tech-radar/src/sample.ts | 8 ++++---- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx b/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx index ef62b8abd9..acf69e8cbb 100644 --- a/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx +++ b/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx @@ -40,6 +40,7 @@ const useStyles = makeStyles(theme => ({ fill: theme.palette.text.primary, fontSize: '25px', fontWeight: 800, + opacity: 0.35, }, })); @@ -62,6 +63,7 @@ const RadarGrid = (props: Props) => { y={ringRadius !== undefined ? -ringRadius + 42 : undefined} textAnchor="middle" className={classes.text} + style={{ fill: rings[ringIndex].color }} > {rings[ringIndex].name} , diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx index 4ca0350e7b..38d3757ff8 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx @@ -45,6 +45,7 @@ const useStyles = makeStyles(theme => ({ }, ringEmpty: { color: theme.palette.text.secondary, + fontSize: '12px', }, ringHeading: { pointerEvents: 'none', diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx index 68b42373f3..577e0bcab4 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx @@ -37,7 +37,9 @@ export const RadarLegendRing = ({ }: RadarLegendRingProps) => { return (
-

{ring.name}

+

+ {ring.name} +

{entries.length === 0 ? ( (empty) diff --git a/plugins/tech-radar/src/sample.ts b/plugins/tech-radar/src/sample.ts index 395b4f2ff5..a967d2cbe7 100644 --- a/plugins/tech-radar/src/sample.ts +++ b/plugins/tech-radar/src/sample.ts @@ -23,10 +23,10 @@ import { } from './api'; const rings = new Array(); -rings.push({ id: 'adopt', name: 'ADOPT', color: '#93c47d' }); -rings.push({ id: 'trial', name: 'TRIAL', color: '#93d2c2' }); -rings.push({ id: 'assess', name: 'ASSESS', color: '#fbdb84' }); -rings.push({ id: 'hold', name: 'HOLD', color: '#efafa9' }); +rings.push({ id: 'adopt', name: 'ADOPT', color: '#5BA300' }); +rings.push({ id: 'trial', name: 'TRIAL', color: '#009EB0' }); +rings.push({ id: 'assess', name: 'ASSESS', color: '#C7BA00' }); +rings.push({ id: 'hold', name: 'HOLD', color: '#E09B96' }); const quadrants = new Array(); quadrants.push({ id: 'infrastructure', name: 'Infrastructure' }); From 67b30654f442f3b9f4f94bb46e4046a28a70f002 Mon Sep 17 00:00:00 2001 From: Claire Casey Date: Thu, 23 Feb 2023 13:51:26 -0500 Subject: [PATCH 047/236] use OverflowTooltip component on email in MembershipCard Signed-off-by: Claire Casey --- .../Cards/Group/MembersList/MembersListCard.tsx | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index bf561a23a4..eb61989144 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -60,17 +60,6 @@ const useStyles = makeStyles((theme: Theme) => flex: '1', minWidth: '0px', }, - email: { - overflow: 'hidden', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - display: 'inline-block', - maxWidth: '100%', - '&:hover': { - overflow: 'visible', - whiteSpace: 'normal', - }, - }, }), ); @@ -103,7 +92,7 @@ const MemberComponent = (props: { member: UserEntity }) => { @@ -118,8 +107,8 @@ const MemberComponent = (props: { member: UserEntity }) => { {profile?.email && ( - - {profile.email} + + )} {description && ( From f773ea56ef459e53ff438b4034209597f97efd92 Mon Sep 17 00:00:00 2001 From: Jennysson Junior Date: Thu, 23 Feb 2023 16:47:23 -0300 Subject: [PATCH 048/236] docs: improving new relic plugin's docs Signed-off-by: Jennysson Junior --- plugins/newrelic/README.md | 89 ++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/plugins/newrelic/README.md b/plugins/newrelic/README.md index e7cdd0df4a..742e0da7c0 100644 --- a/plugins/newrelic/README.md +++ b/plugins/newrelic/README.md @@ -8,36 +8,77 @@ Website: [https://newrelic.com](https://newrelic.com) ## Getting Started This plugin uses the Backstage proxy to securely communicate with New Relic's -APIs. Add the following to your `app-config.yaml` to enable this configuration: +APIs. -```yaml -proxy: - '/newrelic/apm/api': - target: https://api.newrelic.com/v2 - headers: - X-Api-Key: ${NEW_RELIC_REST_API_KEY} -``` +1. Add the following to your `app-config.yaml` to enable this configuration: -In your production deployment of Backstage, you would also need to ensure that -you've set the `NEW_RELIC_REST_API_KEY` environment variable before starting -the backend. + ```yaml + proxy: + '/newrelic/apm/api': + target: https://api.newrelic.com/v2 + headers: + X-Api-Key: ${NEW_RELIC_REST_API_KEY} + ``` -While working locally, you may wish to hard-code your API key in your -`app-config.local.yaml` like this: + There is some types of api key on new relic, to this use must be `User` type of key, In your production deployment of Backstage, you would also need to ensure that + you've set the `NEW_RELIC_REST_API_KEY` environment variable before starting + the backend. -```yaml -# app-config.local.yaml -proxy: - '/newrelic/apm/api': - headers: - X-Api-Key: NRRA-YourActualApiKey -``` + While working locally, you may wish to hard-code your API key in your + `app-config.local.yaml` like this: -Read more about how to find or generate this key in -[New Relic's Documentation](https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys#rest-api-key). + ```yaml + # app-config.local.yaml + proxy: + '/newrelic/apm/api': + headers: + X-Api-Key: NRRA-YourActualApiKey + ``` -See if it's working by visiting the New Relic Plugin Path: -[/newrelic](http://localhost:3000/newrelic) + Read more about how to find or generate this key in + [New Relic's Documentation](https://docs.newrelic.com/docs/apis/get-started/intro-apis/types-new-relic-api-keys#rest-api-key). + + See if it's working by visiting the New Relic Plugin Path: + [/newrelic](http://localhost:3000/newrelic) + +2. Add a dependency to your `packages/app/package.json`: + ```sh + # From your Backstage root directory + yarn add --cwd packages/app @backstage/plugin-newrelic + ``` +3. Add the `NewRelicPage` to your `packages/app/src/App.tsx`: + + ```typescript + + … + } /> + + ``` + +4. Add link to New Relic to your sidebar + + ```typescript + // packages/app/src/components/Root/Root.tsx + import ExtensionIcon from '@material-ui/icons/ExtensionOutlined'; + + ... + + export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + ... + + ... + + + ); + + ``` + +5. Navigate to your.domain.com/newrelic. + + At this step you must be able to see a page like that + New Relic Plugin APM ## Features From 553f3c950113e1e975f2293e1e80d52a4633cec1 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Thu, 23 Feb 2023 15:53:30 -0500 Subject: [PATCH 049/236] fix(SearchPagination): disable next button on last page Signed-off-by: Phil Kuang --- .changeset/empty-books-occur.md | 5 +++++ plugins/search-react/api-report.md | 7 ++++++- .../SearchPagination/SearchPagination.tsx | 18 ++++++++++++++++-- 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 .changeset/empty-books-occur.md diff --git a/.changeset/empty-books-occur.md b/.changeset/empty-books-occur.md new file mode 100644 index 0000000000..69218c5cfa --- /dev/null +++ b/.changeset/empty-books-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': patch +--- + +Correctly disable next button in `SearchPagination` on last page diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index 375af20d5c..9be8d90395 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -234,6 +234,7 @@ export type SearchPaginationBaseProps = { className?: string; total?: number; cursor?: string; + hasNextPage?: boolean; onCursorChange?: (pageCursor: string) => void; limit?: number; limitLabel?: ReactNode; @@ -264,7 +265,11 @@ export type SearchPaginationLimitText = (params: { // @public export type SearchPaginationProps = Omit< SearchPaginationBaseProps, - 'pageLimit' | 'onPageLimitChange' | 'pageCursor' | 'onPageCursorChange' + | 'pageLimit' + | 'onPageLimitChange' + | 'pageCursor' + | 'onPageCursorChange' + | 'hasNextPage' >; // @public diff --git a/plugins/search-react/src/components/SearchPagination/SearchPagination.tsx b/plugins/search-react/src/components/SearchPagination/SearchPagination.tsx index 34b6deaff5..7ab9e8604c 100644 --- a/plugins/search-react/src/components/SearchPagination/SearchPagination.tsx +++ b/plugins/search-react/src/components/SearchPagination/SearchPagination.tsx @@ -77,6 +77,10 @@ export type SearchPaginationBaseProps = { * The cursor for the current page. */ cursor?: string; + /** + * Whether a next page exists + */ + hasNextPage?: boolean; /** * Callback fired when the current page cursor is changed. */ @@ -118,6 +122,7 @@ export const SearchPaginationBase = (props: SearchPaginationBaseProps) => { const { total: count = -1, cursor: pageCursor, + hasNextPage, onCursorChange: onPageCursorChange, limit: rowsPerPage = 25, limitLabel: labelRowsPerPage = 'Results per page:', @@ -151,6 +156,9 @@ export const SearchPaginationBase = (props: SearchPaginationBaseProps) => { component="div" count={count} page={page} + nextIconButtonProps={{ + ...(hasNextPage !== undefined && { disabled: !hasNextPage }), + }} onPageChange={handlePageChange} rowsPerPage={rowsPerPage} labelRowsPerPage={labelRowsPerPage} @@ -167,7 +175,11 @@ export const SearchPaginationBase = (props: SearchPaginationBaseProps) => { */ export type SearchPaginationProps = Omit< SearchPaginationBaseProps, - 'pageLimit' | 'onPageLimitChange' | 'pageCursor' | 'onPageCursorChange' + | 'pageLimit' + | 'onPageLimitChange' + | 'pageCursor' + | 'onPageCursorChange' + | 'hasNextPage' >; /** @@ -176,11 +188,13 @@ export type SearchPaginationProps = Omit< * @public */ export const SearchPagination = (props: SearchPaginationProps) => { - const { pageLimit, setPageLimit, pageCursor, setPageCursor } = useSearch(); + const { pageLimit, setPageLimit, pageCursor, setPageCursor, fetchNextPage } = + useSearch(); return ( Date: Thu, 23 Feb 2023 18:10:54 -0500 Subject: [PATCH 050/236] Add changeset. Signed-off-by: Aramis Sennyey --- .changeset/eighty-chairs-roll.md | 5 +++++ .../catalog-react/src/components/EntityRefLink/humanize.ts | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 .changeset/eighty-chairs-roll.md diff --git a/.changeset/eighty-chairs-roll.md b/.changeset/eighty-chairs-roll.md new file mode 100644 index 0000000000..c981229378 --- /dev/null +++ b/.changeset/eighty-chairs-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': minor +--- + +Update `EntityPicker` to use the fully qualified entity ref instead of the humanized version. diff --git a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts index 9bbfcbc1ed..bfc8e2af84 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/humanize.ts +++ b/plugins/catalog-react/src/components/EntityRefLink/humanize.ts @@ -18,7 +18,6 @@ import { Entity, CompoundEntityRef, DEFAULT_NAMESPACE, - UserEntity, } from '@backstage/catalog-model'; /** From 0ee83a57c75c01ef1ed4274952cec8050ff5ffcc Mon Sep 17 00:00:00 2001 From: Taras Date: Thu, 23 Feb 2023 20:04:34 -0500 Subject: [PATCH 051/236] Removed paths file Signed-off-by: Taras --- .../src/router/paths.ts | 19 ----- .../src/router/routes.ts | 79 ++++++++++--------- 2 files changed, 42 insertions(+), 56 deletions(-) delete mode 100644 plugins/catalog-backend-module-incremental-ingestion/src/router/paths.ts diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/router/paths.ts b/plugins/catalog-backend-module-incremental-ingestion/src/router/paths.ts deleted file mode 100644 index 2118177821..0000000000 --- a/plugins/catalog-backend-module-incremental-ingestion/src/router/paths.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2022 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. - */ - -export const PROVIDER_CLEANUP = '/incremental/cleanup'; -export const PROVIDER_HEALTH = '/incremental/health'; -export const PROVIDER_BASE_PATH = '/incremental/providers/:provider'; diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts index fc26abdf32..76fce4af75 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts @@ -19,7 +19,6 @@ import express from 'express'; import Router from 'express-promise-router'; import { Logger } from 'winston'; import { IncrementalIngestionDatabaseManager } from '../database/IncrementalIngestionDatabaseManager'; -import { PROVIDER_BASE_PATH, PROVIDER_CLEANUP, PROVIDER_HEALTH } from './paths'; export class IncrementalProviderRouter { private manager: IncrementalIngestionDatabaseManager; @@ -35,7 +34,7 @@ export class IncrementalProviderRouter { router.use(express.json()); // Get the overall health of all incremental providers - router.get(PROVIDER_HEALTH, async (_, res) => { + router.get('/incremental/health', async (_, res) => { const records = await this.manager.healthcheck(); const providers = records.map(record => record.provider_name); const duplicates = [ @@ -50,13 +49,13 @@ export class IncrementalProviderRouter { }); // Clean up and pause all providers - router.post(PROVIDER_CLEANUP, async (_, res) => { + router.post('/incremental/health', async (_, res) => { const result = await this.manager.cleanupProviders(); res.json(result); }); // Get basic status of the provider - router.get(PROVIDER_BASE_PATH, async (req, res) => { + router.get('/incremental/providers/:provider', async (req, res) => { const { provider } = req.params; const record = await this.manager.getCurrentIngestionRecord(provider); if (record) { @@ -91,35 +90,38 @@ export class IncrementalProviderRouter { }); // Trigger the provider's next action - router.post(`${PROVIDER_BASE_PATH}/trigger`, async (req, res) => { - const { provider } = req.params; - const record = await this.manager.getCurrentIngestionRecord(provider); - if (record) { - await this.manager.triggerNextProviderAction(provider); - res.json({ - success: true, - message: `${provider}: Next action triggered.`, - }); - } else { - const providers: string[] = await this.manager.listProviders(); - if (providers.includes(provider)) { - this.logger.debug(`${provider} - Ingestion record found`); + router.post( + `/incremental/providers/:provider/trigger`, + async (req, res) => { + const { provider } = req.params; + const record = await this.manager.getCurrentIngestionRecord(provider); + if (record) { + await this.manager.triggerNextProviderAction(provider); res.json({ success: true, - message: 'Unable to trigger next action (provider is restarting)', + message: `${provider}: Next action triggered.`, }); } else { - res.status(404).json({ - success: false, - message: `Provider '${provider}' not found`, - }); + const providers: string[] = await this.manager.listProviders(); + if (providers.includes(provider)) { + this.logger.debug(`${provider} - Ingestion record found`); + res.json({ + success: true, + message: 'Unable to trigger next action (provider is restarting)', + }); + } else { + res.status(404).json({ + success: false, + message: `Provider '${provider}' not found`, + }); + } } - } - }); + }, + ); // Start a brand-new ingestion cycle for the provider. // (Cancel's the current run if active, or marks it complete if resting) - router.post(`${PROVIDER_BASE_PATH}/start`, async (req, res) => { + router.post(`/incremental/providers/:provider/start`, async (req, res) => { const { provider } = req.params; const record = await this.manager.getCurrentIngestionRecord(provider); @@ -152,7 +154,7 @@ export class IncrementalProviderRouter { }); // Stop the provider and pause it for 24 hours - router.post(`${PROVIDER_BASE_PATH}/cancel`, async (req, res) => { + router.post(`/incremental/providers/:provider/cancel`, async (req, res) => { const { provider } = req.params; const record = await this.manager.getCurrentIngestionRecord(provider); if (record) { @@ -186,14 +188,14 @@ export class IncrementalProviderRouter { }); // Wipe out all ingestion records for the provider and pause for 24 hours - router.delete(PROVIDER_BASE_PATH, async (req, res) => { + router.delete('/incremental/providers/:provider', async (req, res) => { const { provider } = req.params; const result = await this.manager.purgeAndResetProvider(provider); res.json(result); }); // Get the ingestion marks for the current cycle - router.get(`${PROVIDER_BASE_PATH}/marks`, async (req, res) => { + router.get(`$/incremental/providers/:provider/marks`, async (req, res) => { const { provider } = req.params; const record = await this.manager.getCurrentIngestionRecord(provider); if (record) { @@ -221,16 +223,19 @@ export class IncrementalProviderRouter { } }); - router.delete(`${PROVIDER_BASE_PATH}/marks`, async (req, res) => { - const { provider } = req.params; - const deletions = await this.manager.clearFinishedIngestions(provider); + router.delete( + `/incremental/providers/:provider/marks`, + async (req, res) => { + const { provider } = req.params; + const deletions = await this.manager.clearFinishedIngestions(provider); - res.json({ - success: true, - message: `Expired marks for provider '${provider}' removed.`, - deletions, - }); - }); + res.json({ + success: true, + message: `Expired marks for provider '${provider}' removed.`, + deletions, + }); + }, + ); router.use(errorHandler()); From 655feeceb95ca7ff65842b29965905c28839b867 Mon Sep 17 00:00:00 2001 From: Taras Date: Thu, 23 Feb 2023 20:14:03 -0500 Subject: [PATCH 052/236] Added GET providers endpoint Signed-off-by: Taras --- .../src/router/routes.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts index 76fce4af75..7fd40db674 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts @@ -153,6 +153,15 @@ export class IncrementalProviderRouter { } }); + router.get(`/incremental/providers/`, async (_req, res) => { + const providers = await this.manager.listProviders(); + + res.json({ + success: true, + providers, + }); + }); + // Stop the provider and pause it for 24 hours router.post(`/incremental/providers/:provider/cancel`, async (req, res) => { const { provider } = req.params; From a811bd246c4b8f0fba4a218d3a2d8e01679c8043 Mon Sep 17 00:00:00 2001 From: Taras Date: Thu, 23 Feb 2023 20:15:58 -0500 Subject: [PATCH 053/236] Added changset Signed-off-by: Taras --- .changeset/forty-snails-clap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/forty-snails-clap.md diff --git a/.changeset/forty-snails-clap.md b/.changeset/forty-snails-clap.md new file mode 100644 index 0000000000..f5aef12d9d --- /dev/null +++ b/.changeset/forty-snails-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': minor +--- + +Added endpoint to get a list of known incremental entity providers From 754da685fb13f21cd8e49a6266654fa691c2526a Mon Sep 17 00:00:00 2001 From: Taras Date: Thu, 23 Feb 2023 20:19:44 -0500 Subject: [PATCH 054/236] Updated README Signed-off-by: Taras --- plugins/catalog-backend-module-incremental-ingestion/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/catalog-backend-module-incremental-ingestion/README.md b/plugins/catalog-backend-module-incremental-ingestion/README.md index 7fe36eeec7..f85321e613 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/README.md +++ b/plugins/catalog-backend-module-incremental-ingestion/README.md @@ -102,6 +102,7 @@ If you want to manage your incremental entity providers via REST endpoints, the | Method | Path | Description | | ------ | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | | GET | `/incremental/health` | Checks the health of all incremental providers. Returns array of any unhealthy ones. | +| GET | `/incremental/providers` | Get a list of all known incremental entity providers | | GET | `/incremental/providers/:provider` | Checks the status of an incremental provider (resting, interstitial, etc). | | POST | `/incremental/providers/:provider/trigger` | Triggers a provider's next action immediately. E.g., if it's currently interstitial, it will trigger the next burst. | | POST | `/incremental/providers/:provider/start` | Stop the current ingestion cycle and start a new one immediately. | From fb0a7577cbad6a2eb131a234333ab9e3c0d13fcb Mon Sep 17 00:00:00 2001 From: Taras Date: Thu, 23 Feb 2023 20:21:35 -0500 Subject: [PATCH 055/236] Correct paths Signed-off-by: Taras --- .../src/router/routes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts index 7fd40db674..0dd89468bd 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts @@ -49,7 +49,7 @@ export class IncrementalProviderRouter { }); // Clean up and pause all providers - router.post('/incremental/health', async (_, res) => { + router.post('/incremental/cleanup', async (_, res) => { const result = await this.manager.cleanupProviders(); res.json(result); }); @@ -204,7 +204,7 @@ export class IncrementalProviderRouter { }); // Get the ingestion marks for the current cycle - router.get(`$/incremental/providers/:provider/marks`, async (req, res) => { + router.get(`/incremental/providers/:provider/marks`, async (req, res) => { const { provider } = req.params; const record = await this.manager.getCurrentIngestionRecord(provider); if (record) { From e0ee35568b565c0a1e05723062435d48f88a056b Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Thu, 23 Feb 2023 22:30:43 -0500 Subject: [PATCH 056/236] test: Remove unused import Signed-off-by: Carlos Esteban Lopez --- .../AzureSitesOverviewTable.test.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx index 6488b79301..5d1a5ee73b 100644 --- a/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx +++ b/plugins/azure-sites/src/components/AzureSitesOverviewTableComponent/AzureSitesOverviewTable.test.tsx @@ -15,7 +15,6 @@ */ import React from 'react'; -import { render } from '@testing-library/react'; import { AnyApiRef, configApiRef, From 1578276708a6a036e736ef7c42f273fe94220e1d Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Wed, 22 Feb 2023 12:46:22 +0200 Subject: [PATCH 057/236] feat: add functionality to get scheduled tasks add new functionality to PluginTaskScheduler to return local and global tasks. this is to be able to trigger those tasks manually using the triggerTask functionality when there's no clear way to figure out the correct id to use as they in many cases come from a plugin. this is also usable for the upcoming debug plugin, see #9737 Signed-off-by: Heikki Hellgren --- .changeset/fresh-hairs-switch.md | 5 +++ packages/backend-tasks/api-report.md | 5 +++ .../src/tasks/PluginTaskSchedulerImpl.test.ts | 41 +++++++++++++++++++ .../src/tasks/PluginTaskSchedulerImpl.ts | 8 ++++ packages/backend-tasks/src/tasks/index.ts | 1 + packages/backend-tasks/src/tasks/types.ts | 21 ++++++++++ 6 files changed, 81 insertions(+) create mode 100644 .changeset/fresh-hairs-switch.md diff --git a/.changeset/fresh-hairs-switch.md b/.changeset/fresh-hairs-switch.md new file mode 100644 index 0000000000..16d3b119e5 --- /dev/null +++ b/.changeset/fresh-hairs-switch.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-tasks': minor +--- + +add functionality to get descriptions from the scheduler for triggering diff --git a/packages/backend-tasks/api-report.md b/packages/backend-tasks/api-report.md index 44ec4addbe..66f901adad 100644 --- a/packages/backend-tasks/api-report.md +++ b/packages/backend-tasks/api-report.md @@ -16,6 +16,7 @@ export type HumanDuration = HumanDuration_2; // @public export interface PluginTaskScheduler { createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner; + getScheduledTasks(): TaskDescriptor[]; scheduleTask( task: TaskScheduleDefinition & TaskInvocationDefinition, ): Promise; @@ -27,6 +28,10 @@ export function readTaskScheduleDefinitionFromConfig( config: Config, ): TaskScheduleDefinition; +// @public +export type TaskDescriptor = TaskScheduleDefinition & + Exclude; + // @public export type TaskFunction = | ((abortSignal: AbortSignal) => void | Promise) diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts index 1847370d2b..d64f353c27 100644 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts @@ -304,6 +304,47 @@ describe('PluginTaskManagerImpl', () => { ); }); + describe('can fetch task ids', () => { + it.each(databases.eachSupportedId())( + 'can fetch both global and local task ids, %p', + async databaseId => { + const { manager } = await init(databaseId); + + const fn = jest.fn(); + const promise = new Promise(resolve => fn.mockImplementation(resolve)); + await manager.scheduleTask({ + id: 'task1', + timeout: Duration.fromMillis(5000), + frequency: Duration.fromMillis(5000), + fn, + scope: 'global', + }); + + await manager.scheduleTask({ + id: 'task2', + timeout: Duration.fromMillis(5000), + frequency: Duration.fromMillis(5000), + fn, + scope: 'local', + }); + + await promise; + + const tasks = manager.getScheduledTasks(); + expect(tasks.length).toEqual(2); + expect(tasks[0].id).toEqual('task1'); + expect(tasks[1].id).toEqual('task2'); + expect(tasks[0].scope).toEqual('global'); + expect(tasks[1].scope).toEqual('local'); + expect(tasks[0].fn).toBeUndefined(); + expect(tasks[1].fn).toBeUndefined(); + expect(tasks[0].signal).toBeUndefined(); + expect(tasks[1].signal).toBeUndefined(); + }, + 60_000, + ); + }); + describe('parseDuration', () => { it('should parse durations', () => { expect(parseDuration({ milliseconds: 5000 })).toEqual('PT5S'); diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts index e57ec93c1c..bbc2b45718 100644 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts @@ -21,6 +21,7 @@ import { LocalTaskWorker } from './LocalTaskWorker'; import { TaskWorker } from './TaskWorker'; import { PluginTaskScheduler, + TaskDescriptor, TaskInvocationDefinition, TaskRunner, TaskScheduleDefinition, @@ -32,6 +33,7 @@ import { validateId } from './util'; */ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { private readonly localTasksById = new Map(); + private readonly allScheduledTasks: TaskDescriptor[] = []; constructor( private readonly databaseFactory: () => Promise, @@ -94,6 +96,8 @@ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { this.localTasksById.set(task.id, worker); } + const { fn: _, signal: __, ...descriptor } = task; + this.allScheduledTasks.push(descriptor as TaskDescriptor); } createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner { @@ -103,6 +107,10 @@ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { }, }; } + + getScheduledTasks(): TaskDescriptor[] { + return this.allScheduledTasks; + } } export function parseDuration( diff --git a/packages/backend-tasks/src/tasks/index.ts b/packages/backend-tasks/src/tasks/index.ts index f2037ac9dd..6f9ac6d859 100644 --- a/packages/backend-tasks/src/tasks/index.ts +++ b/packages/backend-tasks/src/tasks/index.ts @@ -19,6 +19,7 @@ export { TaskScheduler } from './TaskScheduler'; export type { PluginTaskScheduler, TaskFunction, + TaskDescriptor, TaskInvocationDefinition, TaskRunner, TaskScheduleDefinition, diff --git a/packages/backend-tasks/src/tasks/types.ts b/packages/backend-tasks/src/tasks/types.ts index 6ec5165aec..d8aa58bcb3 100644 --- a/packages/backend-tasks/src/tasks/types.ts +++ b/packages/backend-tasks/src/tasks/types.ts @@ -31,6 +31,14 @@ export type TaskFunction = | ((abortSignal: AbortSignal) => void | Promise) | (() => void | Promise); +/** + * A type to describe a scheduled task. + * + * @public + */ +export type TaskDescriptor = TaskScheduleDefinition & + Exclude; + /** * Options that control the scheduling of a task. * @@ -307,6 +315,19 @@ export interface PluginTaskScheduler { * @param schedule - The task schedule */ createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner; + + /** + * Returns all scheduled tasks registered to this scheduler. + * + * @remarks + * + * This method is useful for triggering tasks manually using the triggerTask + * functionality. Note that the returned tasks contain only tasks that have + * been initialized in this instance of the scheduler. + * + * @returns Scheduled tasks + */ + getScheduledTasks(): TaskDescriptor[]; } function isValidOptionalDurationString(d: string | undefined): boolean { From fb3d0b3af9144c6cece8ec23496885857a266039 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Fri, 24 Feb 2023 09:21:29 -0500 Subject: [PATCH 058/236] Update plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Taras Mankovski --- .../src/router/routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts index 0dd89468bd..4a89467a93 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/router/routes.ts @@ -153,7 +153,7 @@ export class IncrementalProviderRouter { } }); - router.get(`/incremental/providers/`, async (_req, res) => { + router.get(`/incremental/providers`, async (_req, res) => { const providers = await this.manager.listProviders(); res.json({ From 6421ccbfe9f2aebcb55b1fa9d5d73c79f6066b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jennysson=20J=C3=BAnior?= Date: Fri, 24 Feb 2023 11:45:50 -0300 Subject: [PATCH 059/236] docs: changing typescript to tsx highlights on example code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Johan Haals Signed-off-by: Jennysson Júnior --- plugins/newrelic/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/newrelic/README.md b/plugins/newrelic/README.md index 742e0da7c0..8ac7ed2c72 100644 --- a/plugins/newrelic/README.md +++ b/plugins/newrelic/README.md @@ -48,7 +48,7 @@ APIs. ``` 3. Add the `NewRelicPage` to your `packages/app/src/App.tsx`: - ```typescript + ```tsx } /> From 72b82dfadfcd0d83f45443a9f3a1d7182f692e9a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 24 Feb 2023 16:05:41 +0100 Subject: [PATCH 060/236] microsite: Add redirect for /docs Signed-off-by: Johan Haals --- docs/README.md | 3 --- microsite-next/docusaurus.config.js | 11 +++++++++++ microsite-next/package.json | 1 + microsite-next/yarn.lock | 21 +++++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) delete mode 100644 docs/README.md diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index c63a12b589..0000000000 --- a/docs/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Documentation - -The Backstage documentation is available at https://backstage.io/docs diff --git a/microsite-next/docusaurus.config.js b/microsite-next/docusaurus.config.js index c3956bb308..b3d1d16af1 100644 --- a/microsite-next/docusaurus.config.js +++ b/microsite-next/docusaurus.config.js @@ -96,6 +96,17 @@ module.exports = { }; }, }), + [ + '@docusaurus/plugin-client-redirects', + { + redirects: [ + { + from: '/docs', + to: '/docs/overview/what-is-backstage', + }, + ], + }, + ], ], themeConfig: /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ diff --git a/microsite-next/package.json b/microsite-next/package.json index c223e87584..cb71d5a25a 100644 --- a/microsite-next/package.json +++ b/microsite-next/package.json @@ -30,6 +30,7 @@ "prettier": "@spotify/prettier-config", "dependencies": { "@docusaurus/core": "2.3.1", + "@docusaurus/plugin-client-redirects": "^2.3.1", "@docusaurus/preset-classic": "2.3.1", "@swc/core": "^1.3.36", "clsx": "^1.1.1", diff --git a/microsite-next/yarn.lock b/microsite-next/yarn.lock index d633230dd6..517e3c2280 100644 --- a/microsite-next/yarn.lock +++ b/microsite-next/yarn.lock @@ -1813,6 +1813,26 @@ __metadata: languageName: node linkType: hard +"@docusaurus/plugin-client-redirects@npm:^2.3.1": + version: 2.3.1 + resolution: "@docusaurus/plugin-client-redirects@npm:2.3.1" + dependencies: + "@docusaurus/core": 2.3.1 + "@docusaurus/logger": 2.3.1 + "@docusaurus/utils": 2.3.1 + "@docusaurus/utils-common": 2.3.1 + "@docusaurus/utils-validation": 2.3.1 + eta: ^2.0.0 + fs-extra: ^10.1.0 + lodash: ^4.17.21 + tslib: ^2.4.0 + peerDependencies: + react: ^16.8.4 || ^17.0.0 + react-dom: ^16.8.4 || ^17.0.0 + checksum: bb730359c7f9015108a3d4ec8a570e9c73847349747bf71711f522a7dc40007786b7cd25cebc845eeb16b3505e8068242559a2eda790f4c07d4083d9014a6f84 + languageName: node + linkType: hard + "@docusaurus/plugin-content-blog@npm:2.3.1": version: 2.3.1 resolution: "@docusaurus/plugin-content-blog@npm:2.3.1" @@ -3708,6 +3728,7 @@ __metadata: dependencies: "@docusaurus/core": 2.3.1 "@docusaurus/module-type-aliases": 2.3.1 + "@docusaurus/plugin-client-redirects": ^2.3.1 "@docusaurus/preset-classic": 2.3.1 "@spotify/prettier-config": ^14.0.0 "@swc/core": ^1.3.36 From 66fdf05a1efe14dd395d45a54698dbf9ded3ca5e Mon Sep 17 00:00:00 2001 From: Jennysson Junior Date: Fri, 24 Feb 2023 12:32:30 -0300 Subject: [PATCH 061/236] memo: adding changeset of improving new relic plugin`s docs Signed-off-by: Jennysson Junior --- .changeset/wicked-spoons-call.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wicked-spoons-call.md diff --git a/.changeset/wicked-spoons-call.md b/.changeset/wicked-spoons-call.md new file mode 100644 index 0000000000..c57860349b --- /dev/null +++ b/.changeset/wicked-spoons-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-newrelic': patch +--- + +Updated installation instructions From 1a2fa2f4c7d16ee087922096fe3dda44d2240c11 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Fri, 24 Feb 2023 12:56:30 -0500 Subject: [PATCH 062/236] Support new option getRingColor to determine text and legend color. Signed-off-by: Aramis Sennyey --- .../src/components/Radar/Radar.test.tsx | 5 ++-- .../tech-radar/src/components/Radar/Radar.tsx | 16 ++++------- .../tech-radar/src/components/Radar/types.ts | 27 +++++++++++++++++++ .../src/components/RadarComponent.tsx | 7 ++++- .../src/components/RadarGrid/RadarGrid.tsx | 8 ++++-- .../components/RadarLegend/RadarLegend.tsx | 2 ++ .../RadarLegend/RadarLegendQuadrant.tsx | 3 +++ .../RadarLegend/RadarLegendRing.tsx | 7 ++++- .../src/components/RadarLegend/types.ts | 3 +++ .../src/components/RadarPlot/RadarPlot.tsx | 7 ++++- 10 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 plugins/tech-radar/src/components/Radar/types.ts diff --git a/plugins/tech-radar/src/components/Radar/Radar.test.tsx b/plugins/tech-radar/src/components/Radar/Radar.test.tsx index b76eb20a16..5260c298fa 100644 --- a/plugins/tech-radar/src/components/Radar/Radar.test.tsx +++ b/plugins/tech-radar/src/components/Radar/Radar.test.tsx @@ -18,9 +18,10 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import GetBBoxPolyfill from '../../utils/polyfills/getBBox'; -import Radar, { Props } from './Radar'; +import Radar from './Radar'; +import type { RadarProps } from './types'; -const minProps: Props = { +const minProps: RadarProps = { width: 500, height: 200, quadrants: [{ id: 'languages', name: 'Languages' }], diff --git a/plugins/tech-radar/src/components/Radar/Radar.tsx b/plugins/tech-radar/src/components/Radar/Radar.tsx index 8ba5af9811..a6dc045325 100644 --- a/plugins/tech-radar/src/components/Radar/Radar.tsx +++ b/plugins/tech-radar/src/components/Radar/Radar.tsx @@ -15,27 +15,20 @@ */ import React, { useMemo, useRef, useState } from 'react'; -import type { Entry, Quadrant, Ring } from '../../utils/types'; +import type { Entry } from '../../utils/types'; import RadarPlot from '../RadarPlot'; +import { RadarProps } from './types'; import { adjustEntries, adjustQuadrants, adjustRings } from './utils'; -export type Props = { - width: number; - height: number; - quadrants: Quadrant[]; - rings: Ring[]; - entries: Entry[]; - svgProps?: object; -}; - const Radar = ({ width, height, quadrants, rings, entries, + getRingColor, ...props -}: Props): JSX.Element => { +}: RadarProps): JSX.Element => { const radius = Math.min(width, height) / 2; // State @@ -66,6 +59,7 @@ const Radar = ({ return ( Color; +}; diff --git a/plugins/tech-radar/src/components/RadarComponent.tsx b/plugins/tech-radar/src/components/RadarComponent.tsx index 1f14e9d258..bf8fdd85da 100644 --- a/plugins/tech-radar/src/components/RadarComponent.tsx +++ b/plugins/tech-radar/src/components/RadarComponent.tsx @@ -20,7 +20,8 @@ import React, { useEffect } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { RadarEntry, techRadarApiRef, TechRadarLoaderResponse } from '../api'; import Radar from '../components/Radar'; -import { Entry } from '../utils/types'; +import { Entry, Ring } from '../utils/types'; +import { RadarProps } from './Radar/types'; const useTechRadarLoader = (id: string | undefined) => { const errorApi = useApi(errorApiRef); @@ -89,6 +90,8 @@ export interface TechRadarComponentProps { * Text to filter {@link RadarEntry} inside Tech Radar */ searchText?: string; + + getRingColor?: RadarProps['getRingColor']; } /** @@ -102,6 +105,7 @@ export interface TechRadarComponentProps { */ export function RadarComponent(props: TechRadarComponentProps) { const { loading, error, value: data } = useTechRadarLoader(props.id); + const { getRingColor = (ring: Ring) => ring.color } = props; const mapToEntries = ( loaderResponse: TechRadarLoaderResponse, @@ -136,6 +140,7 @@ export function RadarComponent(props: TechRadarComponentProps) { {!loading && !error && data && ( (theme => ({ @@ -47,7 +49,7 @@ const useStyles = makeStyles(theme => ({ // A component for the background grid of the radar, with axes, rings etc. It will render around the origin, i.e. // assume that (0, 0) is in the middle of the drawing. const RadarGrid = (props: Props) => { - const { radius, rings } = props; + const { radius, rings, getRingColor } = props; const classes = useStyles(props); const makeRingNode = (ringIndex: number, ringRadius?: number) => [ @@ -63,7 +65,9 @@ const RadarGrid = (props: Props) => { y={ringRadius !== undefined ? -ringRadius + 42 : undefined} textAnchor="middle" className={classes.text} - style={{ fill: rings[ringIndex].color }} + style={{ + fill: getRingColor ? getRingColor(rings[ringIndex]) : undefined, + }} > {rings[ringIndex].name} , diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx index 38d3757ff8..1db6c52517 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx @@ -88,6 +88,7 @@ const RadarLegend = ({ entries, onEntryMouseEnter, onEntryMouseLeave, + getRingColor, ...props }: RadarLegendProps): JSX.Element => { const classes = useStyles(props); @@ -96,6 +97,7 @@ const RadarLegend = ({ {quadrants.map(quadrant => ( ; onEntryMouseEnter: RadarLegendProps['onEntryMouseEnter']; onEntryMouseLeave: RadarLegendProps['onEntryMouseLeave']; + getRingColor: RadarLegendProps['getRingColor']; }; export const RadarLegendQuadrant = ({ @@ -37,6 +38,7 @@ export const RadarLegendQuadrant = ({ classes, onEntryMouseEnter, onEntryMouseLeave, + getRingColor, }: RadarLegendQuadrantProps) => { return ( ; onEntryMouseEnter?: RadarLegendProps['onEntryMouseEnter']; onEntryMouseLeave?: RadarLegendProps['onEntryMouseEnter']; + getRingColor?: RadarLegendProps['getRingColor']; }; export const RadarLegendRing = ({ @@ -34,10 +35,14 @@ export const RadarLegendRing = ({ classes, onEntryMouseEnter, onEntryMouseLeave, + getRingColor, }: RadarLegendRingProps) => { return (
-

+

{ring.name}

{entries.length === 0 ? ( diff --git a/plugins/tech-radar/src/components/RadarLegend/types.ts b/plugins/tech-radar/src/components/RadarLegend/types.ts index cdac1bfe9d..9974a6592b 100644 --- a/plugins/tech-radar/src/components/RadarLegend/types.ts +++ b/plugins/tech-radar/src/components/RadarLegend/types.ts @@ -15,6 +15,7 @@ */ import { Entry, Quadrant, Ring } from '../../utils/types'; +import { RadarProps } from '../Radar/types'; export type Segments = { [k: number]: { [k: number]: Entry[] }; @@ -26,4 +27,6 @@ export type RadarLegendProps = { entries: Entry[]; onEntryMouseEnter?: (entry: Entry) => void; onEntryMouseLeave?: (entry: Entry) => void; + + getRingColor?: RadarProps['getRingColor']; }; diff --git a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx index d9788be887..a51d1dacd9 100644 --- a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx +++ b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx @@ -22,6 +22,7 @@ import RadarEntry from '../RadarEntry'; import RadarBubble from '../RadarBubble'; import RadarFooter from '../RadarFooter'; import RadarLegend from '../RadarLegend'; +import { RadarProps } from '../Radar/types'; export type Props = { width: number; @@ -33,6 +34,8 @@ export type Props = { activeEntry?: Entry; onEntryMouseEnter?: (entry: Entry) => void; onEntryMouseLeave?: (entry: Entry) => void; + + getRingColor?: RadarProps['getRingColor']; }; // A component that draws the radar circle. @@ -47,6 +50,7 @@ const RadarPlot = (props: Props): JSX.Element => { activeEntry, onEntryMouseEnter, onEntryMouseLeave, + getRingColor, } = props; return ( @@ -61,9 +65,10 @@ const RadarPlot = (props: Props): JSX.Element => { onEntryMouseLeave={ onEntryMouseLeave && (entry => onEntryMouseLeave(entry)) } + getRingColor={getRingColor} /> - + {entries.map(entry => ( Date: Fri, 24 Feb 2023 12:58:19 -0500 Subject: [PATCH 063/236] Revert "Support new option getRingColor to determine text and legend color." This reverts commit 1a2fa2f4c7d16ee087922096fe3dda44d2240c11. Signed-off-by: Aramis Sennyey --- .../src/components/Radar/Radar.test.tsx | 5 ++-- .../tech-radar/src/components/Radar/Radar.tsx | 16 +++++++---- .../tech-radar/src/components/Radar/types.ts | 27 ------------------- .../src/components/RadarComponent.tsx | 7 +---- .../src/components/RadarGrid/RadarGrid.tsx | 8 ++---- .../components/RadarLegend/RadarLegend.tsx | 2 -- .../RadarLegend/RadarLegendQuadrant.tsx | 3 --- .../RadarLegend/RadarLegendRing.tsx | 7 +---- .../src/components/RadarLegend/types.ts | 3 --- .../src/components/RadarPlot/RadarPlot.tsx | 7 +---- 10 files changed, 18 insertions(+), 67 deletions(-) delete mode 100644 plugins/tech-radar/src/components/Radar/types.ts diff --git a/plugins/tech-radar/src/components/Radar/Radar.test.tsx b/plugins/tech-radar/src/components/Radar/Radar.test.tsx index 5260c298fa..b76eb20a16 100644 --- a/plugins/tech-radar/src/components/Radar/Radar.test.tsx +++ b/plugins/tech-radar/src/components/Radar/Radar.test.tsx @@ -18,10 +18,9 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import GetBBoxPolyfill from '../../utils/polyfills/getBBox'; -import Radar from './Radar'; -import type { RadarProps } from './types'; +import Radar, { Props } from './Radar'; -const minProps: RadarProps = { +const minProps: Props = { width: 500, height: 200, quadrants: [{ id: 'languages', name: 'Languages' }], diff --git a/plugins/tech-radar/src/components/Radar/Radar.tsx b/plugins/tech-radar/src/components/Radar/Radar.tsx index a6dc045325..8ba5af9811 100644 --- a/plugins/tech-radar/src/components/Radar/Radar.tsx +++ b/plugins/tech-radar/src/components/Radar/Radar.tsx @@ -15,20 +15,27 @@ */ import React, { useMemo, useRef, useState } from 'react'; -import type { Entry } from '../../utils/types'; +import type { Entry, Quadrant, Ring } from '../../utils/types'; import RadarPlot from '../RadarPlot'; -import { RadarProps } from './types'; import { adjustEntries, adjustQuadrants, adjustRings } from './utils'; +export type Props = { + width: number; + height: number; + quadrants: Quadrant[]; + rings: Ring[]; + entries: Entry[]; + svgProps?: object; +}; + const Radar = ({ width, height, quadrants, rings, entries, - getRingColor, ...props -}: RadarProps): JSX.Element => { +}: Props): JSX.Element => { const radius = Math.min(width, height) / 2; // State @@ -59,7 +66,6 @@ const Radar = ({ return ( Color; -}; diff --git a/plugins/tech-radar/src/components/RadarComponent.tsx b/plugins/tech-radar/src/components/RadarComponent.tsx index bf8fdd85da..1f14e9d258 100644 --- a/plugins/tech-radar/src/components/RadarComponent.tsx +++ b/plugins/tech-radar/src/components/RadarComponent.tsx @@ -20,8 +20,7 @@ import React, { useEffect } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { RadarEntry, techRadarApiRef, TechRadarLoaderResponse } from '../api'; import Radar from '../components/Radar'; -import { Entry, Ring } from '../utils/types'; -import { RadarProps } from './Radar/types'; +import { Entry } from '../utils/types'; const useTechRadarLoader = (id: string | undefined) => { const errorApi = useApi(errorApiRef); @@ -90,8 +89,6 @@ export interface TechRadarComponentProps { * Text to filter {@link RadarEntry} inside Tech Radar */ searchText?: string; - - getRingColor?: RadarProps['getRingColor']; } /** @@ -105,7 +102,6 @@ export interface TechRadarComponentProps { */ export function RadarComponent(props: TechRadarComponentProps) { const { loading, error, value: data } = useTechRadarLoader(props.id); - const { getRingColor = (ring: Ring) => ring.color } = props; const mapToEntries = ( loaderResponse: TechRadarLoaderResponse, @@ -140,7 +136,6 @@ export function RadarComponent(props: TechRadarComponentProps) { {!loading && !error && data && ( (theme => ({ @@ -49,7 +47,7 @@ const useStyles = makeStyles(theme => ({ // A component for the background grid of the radar, with axes, rings etc. It will render around the origin, i.e. // assume that (0, 0) is in the middle of the drawing. const RadarGrid = (props: Props) => { - const { radius, rings, getRingColor } = props; + const { radius, rings } = props; const classes = useStyles(props); const makeRingNode = (ringIndex: number, ringRadius?: number) => [ @@ -65,9 +63,7 @@ const RadarGrid = (props: Props) => { y={ringRadius !== undefined ? -ringRadius + 42 : undefined} textAnchor="middle" className={classes.text} - style={{ - fill: getRingColor ? getRingColor(rings[ringIndex]) : undefined, - }} + style={{ fill: rings[ringIndex].color }} > {rings[ringIndex].name} , diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx index 1db6c52517..38d3757ff8 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.tsx @@ -88,7 +88,6 @@ const RadarLegend = ({ entries, onEntryMouseEnter, onEntryMouseLeave, - getRingColor, ...props }: RadarLegendProps): JSX.Element => { const classes = useStyles(props); @@ -97,7 +96,6 @@ const RadarLegend = ({ {quadrants.map(quadrant => ( ; onEntryMouseEnter: RadarLegendProps['onEntryMouseEnter']; onEntryMouseLeave: RadarLegendProps['onEntryMouseLeave']; - getRingColor: RadarLegendProps['getRingColor']; }; export const RadarLegendQuadrant = ({ @@ -38,7 +37,6 @@ export const RadarLegendQuadrant = ({ classes, onEntryMouseEnter, onEntryMouseLeave, - getRingColor, }: RadarLegendQuadrantProps) => { return ( ; onEntryMouseEnter?: RadarLegendProps['onEntryMouseEnter']; onEntryMouseLeave?: RadarLegendProps['onEntryMouseEnter']; - getRingColor?: RadarLegendProps['getRingColor']; }; export const RadarLegendRing = ({ @@ -35,14 +34,10 @@ export const RadarLegendRing = ({ classes, onEntryMouseEnter, onEntryMouseLeave, - getRingColor, }: RadarLegendRingProps) => { return (
-

+

{ring.name}

{entries.length === 0 ? ( diff --git a/plugins/tech-radar/src/components/RadarLegend/types.ts b/plugins/tech-radar/src/components/RadarLegend/types.ts index 9974a6592b..cdac1bfe9d 100644 --- a/plugins/tech-radar/src/components/RadarLegend/types.ts +++ b/plugins/tech-radar/src/components/RadarLegend/types.ts @@ -15,7 +15,6 @@ */ import { Entry, Quadrant, Ring } from '../../utils/types'; -import { RadarProps } from '../Radar/types'; export type Segments = { [k: number]: { [k: number]: Entry[] }; @@ -27,6 +26,4 @@ export type RadarLegendProps = { entries: Entry[]; onEntryMouseEnter?: (entry: Entry) => void; onEntryMouseLeave?: (entry: Entry) => void; - - getRingColor?: RadarProps['getRingColor']; }; diff --git a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx index a51d1dacd9..d9788be887 100644 --- a/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx +++ b/plugins/tech-radar/src/components/RadarPlot/RadarPlot.tsx @@ -22,7 +22,6 @@ import RadarEntry from '../RadarEntry'; import RadarBubble from '../RadarBubble'; import RadarFooter from '../RadarFooter'; import RadarLegend from '../RadarLegend'; -import { RadarProps } from '../Radar/types'; export type Props = { width: number; @@ -34,8 +33,6 @@ export type Props = { activeEntry?: Entry; onEntryMouseEnter?: (entry: Entry) => void; onEntryMouseLeave?: (entry: Entry) => void; - - getRingColor?: RadarProps['getRingColor']; }; // A component that draws the radar circle. @@ -50,7 +47,6 @@ const RadarPlot = (props: Props): JSX.Element => { activeEntry, onEntryMouseEnter, onEntryMouseLeave, - getRingColor, } = props; return ( @@ -65,10 +61,9 @@ const RadarPlot = (props: Props): JSX.Element => { onEntryMouseLeave={ onEntryMouseLeave && (entry => onEntryMouseLeave(entry)) } - getRingColor={getRingColor} /> - + {entries.map(entry => ( Date: Fri, 24 Feb 2023 13:01:21 -0500 Subject: [PATCH 064/236] Add changeset. Signed-off-by: Aramis Sennyey --- .changeset/mighty-games-turn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/mighty-games-turn.md diff --git a/.changeset/mighty-games-turn.md b/.changeset/mighty-games-turn.md new file mode 100644 index 0000000000..4e9461e341 --- /dev/null +++ b/.changeset/mighty-games-turn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-tech-radar': minor +--- + +Update colors to match Zalando's tech radar, also add coloring on title and legend to match ring color. From 69002a336800559acb9943a8b227a0f1eccdef99 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Fri, 24 Feb 2023 13:14:21 -0500 Subject: [PATCH 065/236] Add test cases. Signed-off-by: Aramis Sennyey --- .../src/components/RadarGrid/RadarGrid.test.tsx | 12 ++++++++++++ .../src/components/RadarGrid/RadarGrid.tsx | 1 + .../components/RadarLegend/RadarLegend.test.tsx | 15 +++++++++++++++ .../components/RadarLegend/RadarLegendRing.tsx | 6 +++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/plugins/tech-radar/src/components/RadarGrid/RadarGrid.test.tsx b/plugins/tech-radar/src/components/RadarGrid/RadarGrid.test.tsx index 2e0a5a225c..e8896ec9e0 100644 --- a/plugins/tech-radar/src/components/RadarGrid/RadarGrid.test.tsx +++ b/plugins/tech-radar/src/components/RadarGrid/RadarGrid.test.tsx @@ -44,4 +44,16 @@ describe('RadarGrid', () => { expect(rendered.getByTestId('radar-grid-x-line')).toBeInTheDocument(); expect(rendered.getByTestId('radar-grid-y-line')).toBeInTheDocument(); }); + + it('should have the correct text color', async () => { + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByTestId('radar-ring-heading')).toHaveStyle({ + fill: '#93c47d', + }); + }); }); diff --git a/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx b/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx index acf69e8cbb..8dc1a62c56 100644 --- a/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx +++ b/plugins/tech-radar/src/components/RadarGrid/RadarGrid.tsx @@ -64,6 +64,7 @@ const RadarGrid = (props: Props) => { textAnchor="middle" className={classes.text} style={{ fill: rings[ringIndex].color }} + data-testid="radar-ring-heading" > {rings[ringIndex].name} , diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx index de8bfd15d7..70cd2d0244 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegend.test.tsx @@ -56,4 +56,19 @@ describe('RadarLegend', () => { expect(rendered.getAllByTestId('radar-quadrant')).toHaveLength(1); expect(rendered.getAllByTestId('radar-ring')).toHaveLength(1); }); + + it('should have the correct ring text color', async () => { + const rendered = await renderInTestApp( + + + , + ); + + expect(rendered.getByTestId('radar-legend')).toBeInTheDocument(); + + const legend = rendered.getByTestId('radar-legend-heading'); + expect(legend).toHaveStyle({ + color: '#93c47d', + }); + }); }); diff --git a/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx b/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx index 577e0bcab4..da5c10831e 100644 --- a/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx +++ b/plugins/tech-radar/src/components/RadarLegend/RadarLegendRing.tsx @@ -37,7 +37,11 @@ export const RadarLegendRing = ({ }: RadarLegendRingProps) => { return (
-

+

{ring.name}

{entries.length === 0 ? ( From cc77729c48d040dfe108a8c3599cac9fe4ceb946 Mon Sep 17 00:00:00 2001 From: Jos Craw Date: Sat, 25 Feb 2023 08:45:00 +1300 Subject: [PATCH 066/236] fix: update docs to include updated command reference Signed-off-by: Jos Craw --- docs/features/techdocs/cli.md | 45 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/docs/features/techdocs/cli.md b/docs/features/techdocs/cli.md index 682c6a8f8f..6b2e4383b8 100644 --- a/docs/features/techdocs/cli.md +++ b/docs/features/techdocs/cli.md @@ -176,29 +176,28 @@ Usage: techdocs-cli publish [options] Publish generated TechDocs site to an external storage AWS S3, Google GCS, etc. Options: - --publisher-type (Required always) awsS3 | googleGcs | azureBlobStorage - - same as techdocs.publisher.type in Backstage - app-config.yaml - --storage-name (Required always) In case of AWS/GCS, use the bucket - name. In case of Azure, use container name. Same as - techdocs.publisher.[TYPE].bucketName - --entity (Required always) Entity uid separated by / in - namespace/kind/name order (case-sensitive). Example: - default/Component/myEntity - --legacyUseCaseSensitiveTripletPaths Publishes objects with cased entity triplet prefix when set (e.g. namespace/Kind/name). - Only use if your TechDocs backend is configured the same way - --azureAccountName (Required for Azure) specify when --publisher-type - azureBlobStorage - --azureAccountKey Azure Storage Account key to use for authentication. - If not specified, you must set AZURE_TENANT_ID, - AZURE_CLIENT_ID & AZURE_CLIENT_SECRET as environment - variables. - --awsRoleArn Optional AWS ARN of role to be assumed. - --awsEndpoint Optional AWS endpoint to send requests to. - --awsS3ForcePathStyle Optional AWS S3 option to force path style. - --directory Path of the directory containing generated files to - publish (default: "./site/") - -h, --help display help for command + --publisher-type (Required always) awsS3 | googleGcs | azureBlobStorage | openStackSwift - same as techdocs.publisher.type in Backstage app-config.yaml + --storage-name (Required always) In case of AWS/GCS, use the bucket name. In case of Azure, use container name. Same as + techdocs.publisher.[TYPE].bucketName + --entity (Required always) Entity uid separated by / in namespace/kind/name order (case-sensitive). Example: default/Component/myEntity + --legacyUseCaseSensitiveTripletPaths Publishes objects with cased entity triplet prefix when set (e.g. namespace/Kind/name). Only use if your TechDocs backend is configured + the same way. (default: false) + --azureAccountName (Required for Azure) specify when --publisher-type azureBlobStorage + --azureAccountKey Azure Storage Account key to use for authentication. If not specified, you must set AZURE_TENANT_ID, AZURE_CLIENT_ID & + AZURE_CLIENT_SECRET as environment variables. + --awsRoleArn Optional AWS ARN of role to be assumed. + --awsEndpoint Optional AWS endpoint to send requests to. + --awsProxy Optional Proxy to use for AWS requests. + --awsS3sse Optional AWS S3 Server Side Encryption. + --awsS3ForcePathStyle Optional AWS S3 option to force path style. + --awsBucketRootPath Optional sub-directory to store files in Amazon S3 + --osCredentialId (Required for OpenStack) specify when --publisher-type openStackSwift + --osSecret (Required for OpenStack) specify when --publisher-type openStackSwift + --osAuthUrl (Required for OpenStack) specify when --publisher-type openStackSwift + --osSwiftUrl (Required for OpenStack) specify when --publisher-type openStackSwift + --gcsBucketRootPath Optional sub-directory to store files in Google cloud storage + --directory Path of the directory containing generated files to publish (default: "./site/") + -h, --help display help for command ``` ### Migrate content for case-insensitive access From f093ce83d58aae35abcb0eaebcec377c275e31ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 25 Feb 2023 11:43:07 +0100 Subject: [PATCH 067/236] fix batch fetch by ref with filtering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/slow-pens-compare.md | 5 ++ plugins/catalog-backend/src/catalog/types.ts | 4 +- .../service/DefaultEntitiesCatalog.test.ts | 48 +++++++++++++++++++ .../src/service/DefaultEntitiesCatalog.ts | 18 +++++-- 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 .changeset/slow-pens-compare.md diff --git a/.changeset/slow-pens-compare.md b/.changeset/slow-pens-compare.md new file mode 100644 index 0000000000..158cb6f760 --- /dev/null +++ b/.changeset/slow-pens-compare.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fix a bug where the batch fetch by ref endpoint did not work in conjunction with filtering (e.g. if authorization was enabled). diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 62c8fad605..404b816d4f 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -98,7 +98,9 @@ export interface EntitiesBatchRequest { */ entityRefs: string[]; /** - * Any additional filters to apply in the selection of the entities. + * Any additional filters to apply in the selection of the entities. Entities + * that do not match the filter result in a null entry in the response, as if + * they did not exist. */ filter?: EntityFilter; /** diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts index 740f5b2558..275bd1c42c 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.test.ts @@ -87,6 +87,9 @@ describe('DefaultEntitiesCatalog', () => { }); } + const search = await buildEntitySearch(id, entity); + await knex('search').insert(search); + return id; } @@ -721,6 +724,51 @@ describe('DefaultEntitiesCatalog', () => { }, 60_000, ); + + it.each(databases.eachSupportedId())( + 'queries for entities by ref, including filtering, %p', + async databaseId => { + await createDatabase(databaseId); + + await addEntity( + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + relations: [], + }, + [], + ); + await addEntity( + { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: { owner: 'me' }, + relations: [], + }, + [], + ); + + const catalog = new DefaultEntitiesCatalog({ + database: knex, + logger: getVoidLogger(), + stitcher, + }); + + const { items } = await catalog.entitiesBatch({ + entityRefs: ['k:default/two', 'k:default/one'], + filter: { key: 'spec.owner', values: ['me'] }, + }); + + expect(items.map(e => e && stringifyEntityRef(e))).toEqual([ + 'k:default/two', + null, + ]); + }, + 60_000, + ); }); describe('queryEntities', () => { diff --git a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts index f983d7496b..4e6109423e 100644 --- a/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/DefaultEntitiesCatalog.ts @@ -309,17 +309,27 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog { for (const chunk of lodashChunk(request.entityRefs, 200)) { let query = this.database('final_entities') - .innerJoin('refresh_state', { - 'refresh_state.entity_id': 'final_entities.entity_id', - }) + .innerJoin( + 'refresh_state', + 'refresh_state.entity_id', + 'final_entities.entity_id', + ) .select({ entityRef: 'refresh_state.entity_ref', entity: 'final_entities.final_entity', }) .whereIn('refresh_state.entity_ref', chunk); + if (request?.filter) { - query = parseFilter(request.filter, query, this.database); + query = parseFilter( + request.filter, + query, + this.database, + false, + 'refresh_state.entity_id', + ); } + for (const row of await query) { lookup.set(row.entityRef, row.entity ? JSON.parse(row.entity) : null); } From 9f23390bf33aed02a70b1c8c8ab4a26acc47884b Mon Sep 17 00:00:00 2001 From: Andreas Hubel <40266+saerdnaer@users.noreply.github.com> Date: Sat, 25 Feb 2023 16:56:48 +0100 Subject: [PATCH 068/236] fix display of placeholders on https://backstage.io/docs/local-dev/cli-commands/#create-github-app Signed-off-by: Andreas Hubel <40266+saerdnaer@users.noreply.github.com> --- docs/local-dev/cli-commands.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/local-dev/cli-commands.md b/docs/local-dev/cli-commands.md index e34dd63295..174fb3e348 100644 --- a/docs/local-dev/cli-commands.md +++ b/docs/local-dev/cli-commands.md @@ -354,7 +354,7 @@ package. This essentially calls `yarn pack` in each included package and unpacks the resulting archive in the target `workspace-dir`. ```text -Usage: backstage-cli build-workspace [options] <workspace-dir> +Usage: backstage-cli build-workspace [options] ``` ## create-github-app @@ -367,7 +367,7 @@ Launches a browser to create the App through GitHub and saves the result as a YAML file that can be referenced in the GitHub integration configuration. ```text -Usage: backstage-cli create-github-app <github-org> +Usage: backstage-cli create-github-app ``` ## info From 8b36144ab00804cd5f29523313c7405a0b812f10 Mon Sep 17 00:00:00 2001 From: Jos Craw Date: Sun, 26 Feb 2023 10:47:18 +1300 Subject: [PATCH 069/236] chore: added docs for overall config Signed-off-by: Jos Craw --- docs/features/techdocs/configuration.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/features/techdocs/configuration.md b/docs/features/techdocs/configuration.md index e95db2fad0..49ee9d72c2 100644 --- a/docs/features/techdocs/configuration.md +++ b/docs/features/techdocs/configuration.md @@ -136,6 +136,11 @@ techdocs: # https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/s3clientconfig.html#endpoint endpoint: ${AWS_ENDPOINT} + # (Optional) HTTPS proxy to use for S3 Requests + # Defaults to using no proxy + # This allows docs to be published and read from behind a proxy + httpsProxy: ${HTTPS_PROXY} + # (Optional) Whether to use path style URLs when communicating with S3. # Defaults to false. # This allows providers like LocalStack, Minio and Wasabi (and possibly others) to be used to host tech docs. From 20bfb411f25f00db0601b17d7c5448651301b38f Mon Sep 17 00:00:00 2001 From: tr-fteixeira <55003893+tr-fteixeira@users.noreply.github.com> Date: Sat, 25 Feb 2023 18:14:26 -0500 Subject: [PATCH 070/236] docs(azure-intgration): add note for projects with spaces on the name Signed-off-by: tr-fteixeira <55003893+tr-fteixeira@users.noreply.github.com> --- docs/integrations/azure/discovery.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/azure/discovery.md b/docs/integrations/azure/discovery.md index 7d53e21a89..6cb135900e 100644 --- a/docs/integrations/azure/discovery.md +++ b/docs/integrations/azure/discovery.md @@ -67,7 +67,7 @@ The parameters available are: - **`host:`** _(optional)_ Leave empty for Cloud hosted, otherwise set to your self-hosted instance host. - **`organization:`** Your Organization slug (or Collection for on-premise users). Required. -- **`project:`** _(optional)_ Your project slug. Wildcards are supported as show on the examples above. If not set, all projects will be searched. +- **`project:`** _(optional)_ Your project slug. Wildcards are supported as show on the examples above. If not set, all projects will be searched. For a project name containing spaces, use both single and double quotes as in `project: '"My Project Name"'`. - **`repository:`** _(optional)_ The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched. - **`path:`** _(optional)_ Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml. - **`schedule`** _(optional)_: From 2a73ded3861feac95c4e7cd5928fccc82de5d6be Mon Sep 17 00:00:00 2001 From: Kumar Date: Sun, 19 Feb 2023 00:04:41 +1100 Subject: [PATCH 071/236] feat: add support for MADR v3 Signed-off-by: Kumar --- .changeset/lovely-tigers-look.md | 5 ++ ADOPTERS.md | 1 + plugins/adr-backend/README.md | 2 +- plugins/adr-backend/src/search/madrParser.ts | 92 ++++++++++++++++---- 4 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 .changeset/lovely-tigers-look.md diff --git a/.changeset/lovely-tigers-look.md b/.changeset/lovely-tigers-look.md new file mode 100644 index 0000000000..5e7d230e6d --- /dev/null +++ b/.changeset/lovely-tigers-look.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-adr-backend': patch +--- + +Support MADR v3 format diff --git a/ADOPTERS.md b/ADOPTERS.md index 068928260d..8fa8d37c79 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -230,3 +230,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [OVO Energy](https://www.ovoenergy.com/) | [Michael Wizner](https://github.com/mwz), [Dan Laird](https://github.com/dlaird-ovo), [Samantha Betts](https://github.com/sammbetts) | Developer Experience Tool with an aim to to improve processes, boost productivity, finding of information/docs and building tech engagement throughout the business. | [MusicTribe](https://careers.musictribe.com/) | [Alex Ford](mailto:alex.j.ford@gmail.com), [Tiago Barbosa](https://github.com/t1agob) | We are starting to use Backstage as a developer portal to share API specifications and documentation, to quickly onboard new projects with the software templates, and to help developers discover software through the catalog. | [Cazoo](https://www.cazoo.co.uk/) | [Abz Mungul](https://www.linkedin.com/in/abzmungul/), [Scott Edwards](https://www.linkedin.com/in/scott-edwards-tech/) | We're assessing Backstage as our developer platform at Cazoo with a focus on reducing cognitive load for our engineers. We're currently aiming for 3 outcomes: creating visibility into service ownership across teams, improving the discoverability of event schemas and relationships, and improving the discoverability of technical documentation and best practices. | +| [Gumtree](https://www.gumtree.com.au) | [Kumar Gaurav](https://www.linkedin.com/in/kumargaurav517) | We are starting to use it as a single place to find all component information in a distributed architecture. | diff --git a/plugins/adr-backend/README.md b/plugins/adr-backend/README.md index 29fead7c84..4ffa9c63eb 100644 --- a/plugins/adr-backend/README.md +++ b/plugins/adr-backend/README.md @@ -89,7 +89,7 @@ indexBuilder.addCollator({ ### Parsing custom ADR document formats -By default, the `DefaultAdrCollatorFactory` will parse and index documents that follow the [MADR v2.x standard file name and template format](https://github.com/adr/madr/tree/2.1.2). If you use a different ADR format and file name convention, you can configure `DefaultAdrCollatorFactory` with custom `adrFilePathFilterFn` and `parser` options (see type definitions for details): +By default, the `DefaultAdrCollatorFactory` will parse and index documents that follow [MADR v3.0.0](https://github.com/adr/madr/tree/3.0.0) and [MADR v2.x](https://github.com/adr/madr/tree/2.1.2) standard file name and template format. If you use a different ADR format and file name convention, you can configure `DefaultAdrCollatorFactory` with custom `adrFilePathFilterFn` and `parser` options (see type definitions for details): ```ts DefaultAdrCollatorFactory.fromConfig({ diff --git a/plugins/adr-backend/src/search/madrParser.ts b/plugins/adr-backend/src/search/madrParser.ts index 30672028fe..c992ef125e 100644 --- a/plugins/adr-backend/src/search/madrParser.ts +++ b/plugins/adr-backend/src/search/madrParser.ts @@ -18,28 +18,71 @@ import { DateTime } from 'luxon'; import { marked } from 'marked'; import { MADR_DATE_FORMAT } from '@backstage/plugin-adr-common'; -export const madrParser = (content: string, dateFormat = MADR_DATE_FORMAT) => { - const tokens = marked.lexer(content); - if (!tokens.length) { - throw new Error('ADR has no content'); - } - - // First h1 header should contain ADR title - const adrTitle = ( +const getTitle = (tokens: marked.TokensList): string | undefined => { + return ( tokens.find( t => t.type === 'heading' && t.depth === 1, ) as marked.Tokens.Heading )?.text; +}; - // First list should contain status & date metadata (if defined) - const listTokens = (tokens.find(t => t.type === 'list') as marked.Tokens.List) - ?.items; - const adrStatus = listTokens +const getStatusForV3Format = (tokens: marked.TokensList): string | undefined => + ( + tokens.find( + t => + t.type === 'heading' && + t.depth === 2 && + t.text.toLowerCase().includes('status:'), + ) as marked.Tokens.Text + )?.text + .toLowerCase() + .split('\n') + .find(l => /^status:/.test(l)) + ?.replace(/^status:/i, '') + .trim() + .toLocaleLowerCase('en-US'); + +const getStatusForV2Format = (tokens: marked.TokensList): string | undefined => + (tokens.find(t => t.type === 'list') as marked.Tokens.List)?.items ?.find(t => /^status:/i.test(t.text)) ?.text.replace(/^status:/i, '') .trim() .toLocaleLowerCase('en-US'); +const getDateForV3Format = ( + tokens: marked.TokensList, + dateFormat: string, +): string | undefined => { + let adrDateTime: DateTime | undefined; + const dateLine = ( + tokens.find( + t => + t.type === 'heading' && + t.depth === 2 && + t.text.toLowerCase().includes('date:'), + ) as marked.Tokens.Text + )?.text + .toLowerCase() + .split('\n') + .find(l => /^date:/.test(l)); + + if (dateLine) { + adrDateTime = DateTime.fromFormat( + dateLine.replace(/^date:/i, '').trim(), + dateFormat, + ); + } + return adrDateTime?.isValid + ? adrDateTime.toFormat(MADR_DATE_FORMAT) + : undefined; +}; + +const getDateForV2Format = ( + tokens: marked.TokensList, + dateFormat: string, +): string | undefined => { + const listTokens = (tokens.find(t => t.type === 'list') as marked.Tokens.List) + ?.items; const adrDateTime = DateTime.fromFormat( listTokens ?.find(t => /^date:/i.test(t.text)) @@ -47,13 +90,30 @@ export const madrParser = (content: string, dateFormat = MADR_DATE_FORMAT) => { .trim() ?? '', dateFormat, ); - const adrDate = adrDateTime?.isValid + return adrDateTime?.isValid ? adrDateTime.toFormat(MADR_DATE_FORMAT) : undefined; +}; + +const getStatus = (tokens: marked.TokensList): string | undefined => { + return getStatusForV3Format(tokens) ?? getStatusForV2Format(tokens); +}; +const getDate = ( + tokens: marked.TokensList, + dateFormat: string, +): string | undefined => + getDateForV3Format(tokens, dateFormat) ?? + getDateForV2Format(tokens, dateFormat); + +export const madrParser = (content: string, dateFormat = MADR_DATE_FORMAT) => { + const tokens = marked.lexer(content); + if (!tokens.length) { + throw new Error('ADR has no content'); + } return { - title: adrTitle, - status: adrStatus, - date: adrDate, + title: getTitle(tokens), + status: getStatus(tokens), + date: getDate(tokens, dateFormat), }; }; From ca9229805e5f429e657787377dc3868024efd144 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Fri, 24 Feb 2023 12:48:39 +0000 Subject: [PATCH 072/236] move useEventStream into scaffolder-react Signed-off-by: Paul Cowan --- plugins/scaffolder-react/package.json | 2 ++ plugins/scaffolder-react/src/hooks/index.ts | 5 +++++ .../src}/hooks/useEventStream.ts | 15 +++++++++++++++ plugins/scaffolder/package.json | 1 - .../src/components/TaskPage/TaskPage.tsx | 2 +- .../src/next/OngoingTask/OngoingTask.tsx | 6 ++++-- .../src/next/OngoingTask/TaskSteps/TaskSteps.tsx | 2 +- yarn.lock | 3 ++- 8 files changed, 30 insertions(+), 6 deletions(-) rename plugins/{scaffolder/src/components => scaffolder-react/src}/hooks/useEventStream.ts (96%) diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 2d3e10ed9f..adf4a8b31a 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -66,11 +66,13 @@ "@rjsf/validator-ajv8": "5.1.0", "@types/json-schema": "^7.0.9", "classnames": "^2.2.6", + "immer": "^9.0.1", "json-schema": "^0.4.0", "json-schema-library": "^7.3.9", "lodash": "^4.17.21", "qs": "^6.9.4", "react-use": "^17.2.4", + "use-immer": "^0.8.0", "zen-observable": "^0.10.0", "zod": "~3.18.0", "zod-to-json-schema": "~3.18.0" diff --git a/plugins/scaffolder-react/src/hooks/index.ts b/plugins/scaffolder-react/src/hooks/index.ts index af64a9eebc..124950e83d 100644 --- a/plugins/scaffolder-react/src/hooks/index.ts +++ b/plugins/scaffolder-react/src/hooks/index.ts @@ -16,3 +16,8 @@ export { useCustomFieldExtensions } from './useCustomFieldExtensions'; export { useCustomLayouts } from './useCustomLayouts'; +export { + useTaskEventStream, + type TaskStream, + type Step, +} from './useEventStream'; diff --git a/plugins/scaffolder/src/components/hooks/useEventStream.ts b/plugins/scaffolder-react/src/hooks/useEventStream.ts similarity index 96% rename from plugins/scaffolder/src/components/hooks/useEventStream.ts rename to plugins/scaffolder-react/src/hooks/useEventStream.ts index be0ff922a4..101063d258 100644 --- a/plugins/scaffolder/src/components/hooks/useEventStream.ts +++ b/plugins/scaffolder-react/src/hooks/useEventStream.ts @@ -25,6 +25,11 @@ import { import { useApi } from '@backstage/core-plugin-api'; import { Subscription } from '@backstage/types'; +/** + * The status of the step being processed + * + * @public + */ export type Step = { id: string; status: ScaffolderTaskStatus; @@ -32,6 +37,11 @@ export type Step = { startedAt?: string; }; +/** + * A task event from the event stream + * + * @public + */ export type TaskStream = { loading: boolean; error?: Error; @@ -132,6 +142,11 @@ function reducer(draft: TaskStream, action: ReducerAction) { } } +/** + * A hook to stream the logs of a task being processed + * + * @public + */ export const useTaskEventStream = (taskId: string): TaskStream => { const scaffolderApi = useApi(scaffolderApiRef); const [state, dispatch] = useImmerReducer(reducer, { diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index b3d33298cb..8a0c1f8e26 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -82,7 +82,6 @@ "luxon": "^3.0.0", "qs": "^6.9.4", "react-use": "^17.2.4", - "use-immer": "^0.8.0", "yaml": "^2.0.0", "zen-observable": "^0.10.0", "zod": "~3.18.0", diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index 393bd495bf..a45f0cf413 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -50,8 +50,8 @@ import useInterval from 'react-use/lib/useInterval'; import { ScaffolderTaskStatus, ScaffolderTaskOutput, + useTaskEventStream, } from '@backstage/plugin-scaffolder-react'; -import { useTaskEventStream } from '../hooks/useEventStream'; import { TaskErrors } from './TaskErrors'; import { TaskPageLinks } from './TaskPageLinks'; import { diff --git a/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx index d0aaf4b838..0832d5a3ac 100644 --- a/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx @@ -15,7 +15,6 @@ */ import React, { useEffect, useMemo, useState, useCallback } from 'react'; import { Page, Header, Content, ErrorPanel } from '@backstage/core-components'; -import { useTaskEventStream } from '../../components/hooks/useEventStream'; import { useNavigate, useParams } from 'react-router-dom'; import { Box, makeStyles, Paper } from '@material-ui/core'; import { TaskSteps } from './TaskSteps'; @@ -25,7 +24,10 @@ import { nextSelectedTemplateRouteRef } from '../routes'; import { useRouteRef } from '@backstage/core-plugin-api'; import qs from 'qs'; import { ContextMenu } from './ContextMenu'; -import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; +import { + ScaffolderTaskOutput, + useTaskEventStream, +} from '@backstage/plugin-scaffolder-react'; import { DefaultTemplateOutputs } from '@backstage/plugin-scaffolder-react/alpha'; const useStyles = makeStyles({ diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx b/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx index 7cf5ed71bf..2fcbe941d1 100644 --- a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx +++ b/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx @@ -23,7 +23,7 @@ import { Box, } from '@material-ui/core'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; -import { Step } from '../../../components/hooks/useEventStream'; +import { type Step } from '@backstage/plugin-scaffolder-react'; import { StepIcon } from './StepIcon'; import { StepTime } from './StepTime'; diff --git a/yarn.lock b/yarn.lock index 6973779ac5..a39f14efe0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7777,11 +7777,13 @@ __metadata: "@testing-library/user-event": ^14.0.0 "@types/json-schema": ^7.0.9 classnames: ^2.2.6 + immer: ^9.0.1 json-schema: ^0.4.0 json-schema-library: ^7.3.9 lodash: ^4.17.21 qs: ^6.9.4 react-use: ^17.2.4 + use-immer: ^0.8.0 zen-observable: ^0.10.0 zod: ~3.18.0 zod-to-json-schema: ~3.18.0 @@ -7848,7 +7850,6 @@ __metadata: msw: ^1.0.0 qs: ^6.9.4 react-use: ^17.2.4 - use-immer: ^0.8.0 yaml: ^2.0.0 zen-observable: ^0.10.0 zod: ~3.18.0 From 8f4d13f21cf681a1e51a2e2aeb47e802cc76f4e4 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Fri, 24 Feb 2023 16:24:49 +0000 Subject: [PATCH 073/236] Move useTaskStream, TaskBorder, TaskLogStream and TaskSteps into scaffolder-react Signed-off-by: Paul Cowan --- .changeset/flat-kids-occur.md | 6 +++ .changeset/large-chefs-boil.md | 5 ++ plugins/scaffolder-react/api-report.md | 54 +++++++++++++++++++ plugins/scaffolder-react/package.json | 7 ++- .../TaskBorder}/TaskBorder.test.tsx | 0 .../src/components/TaskBorder}/TaskBorder.tsx | 5 ++ .../src/components/TaskBorder}/index.ts | 2 +- .../TaskLogStream}/TaskLogStream.test.tsx | 0 .../TaskLogStream}/TaskLogStream.tsx | 5 ++ .../src/components/TaskLogStream/index.ts | 16 ++++++ .../src/components}/TaskSteps/StepIcon.tsx | 0 .../components}/TaskSteps/StepTime.test.tsx | 0 .../src/components}/TaskSteps/StepTime.tsx | 0 .../components}/TaskSteps/TaskSteps.test.tsx | 0 .../src/components}/TaskSteps/TaskSteps.tsx | 11 +++- .../src/components/TaskSteps/index.ts | 16 ++++++ .../scaffolder-react/src/components/index.ts | 18 +++++++ plugins/scaffolder-react/src/index.ts | 1 + .../src/next/OngoingTask/OngoingTask.tsx | 14 ++--- yarn.lock | 5 ++ 20 files changed, 155 insertions(+), 10 deletions(-) create mode 100644 .changeset/flat-kids-occur.md create mode 100644 .changeset/large-chefs-boil.md rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components/TaskBorder}/TaskBorder.test.tsx (100%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components/TaskBorder}/TaskBorder.tsx (94%) rename plugins/{scaffolder/src/next/OngoingTask/TaskSteps => scaffolder-react/src/components/TaskBorder}/index.ts (93%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components/TaskLogStream}/TaskLogStream.test.tsx (100%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components/TaskLogStream}/TaskLogStream.tsx (95%) create mode 100644 plugins/scaffolder-react/src/components/TaskLogStream/index.ts rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components}/TaskSteps/StepIcon.tsx (100%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components}/TaskSteps/StepTime.test.tsx (100%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components}/TaskSteps/StepTime.tsx (100%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components}/TaskSteps/TaskSteps.test.tsx (100%) rename plugins/{scaffolder/src/next/OngoingTask => scaffolder-react/src/components}/TaskSteps/TaskSteps.tsx (94%) create mode 100644 plugins/scaffolder-react/src/components/TaskSteps/index.ts create mode 100644 plugins/scaffolder-react/src/components/index.ts diff --git a/.changeset/flat-kids-occur.md b/.changeset/flat-kids-occur.md new file mode 100644 index 0000000000..4e4c002928 --- /dev/null +++ b/.changeset/flat-kids-occur.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-react': minor +'@backstage/plugin-scaffolder': minor +--- + +Move `useTaskStream`, `TaskBorder`, `TaskLogStream` and `TaskSteps` into `scaffolder-react`. diff --git a/.changeset/large-chefs-boil.md b/.changeset/large-chefs-boil.md new file mode 100644 index 0000000000..016ca197c8 --- /dev/null +++ b/.changeset/large-chefs-boil.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': minor +--- + +move useEventStream, TaskSteps, TaskBorder and TaskLogStream into scaffolder-react diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index 9b50eef884..384c4b80f2 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -17,6 +17,10 @@ import { JsonValue } from '@backstage/types'; import { Observable } from '@backstage/types'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; +import { ScaffolderTask as ScaffolderTask_2 } from '@backstage/plugin-scaffolder-react'; +import { ScaffolderTaskOutput as ScaffolderTaskOutput_2 } from '@backstage/plugin-scaffolder-react'; +import { ScaffolderTaskStatus as ScaffolderTaskStatus_2 } from '@backstage/plugin-scaffolder-react'; +import { Step as Step_2 } from '@backstage/plugin-scaffolder-react'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; @@ -277,6 +281,53 @@ export const SecretsContextProvider: ({ children, }: PropsWithChildren<{}>) => JSX.Element; +// @public +export type Step = { + id: string; + status: ScaffolderTaskStatus_2; + endedAt?: string; + startedAt?: string; +}; + +// @public (undocumented) +export interface StepperProps { + // (undocumented) + activeStep?: number; + // (undocumented) + steps: (TaskStep & Step_2)[]; +} + +// @public +export const TaskBorder: (props: { + isComplete: boolean; + isError: boolean; +}) => JSX.Element; + +// @public +export const TaskLogStream: (props: { + logs: { + [k: string]: string[]; + }; +}) => JSX.Element; + +// @public +export const TaskSteps: (props: StepperProps) => JSX.Element; + +// @public +export type TaskStream = { + loading: boolean; + error?: Error; + stepLogs: { + [stepId in string]: string[]; + }; + completed: boolean; + task?: ScaffolderTask_2; + steps: { + [stepId in string]: Step; + }; + output?: ScaffolderTaskOutput_2; +}; + // @public export type TemplateParameterSchema = { title: string; @@ -300,6 +351,9 @@ export const useCustomLayouts: >( outlet: React.ReactNode, ) => TComponentDataType[]; +// @public +export const useTaskEventStream: (taskId: string) => TaskStream; + // @public export const useTemplateSecrets: () => ScaffolderUseTemplateSecrets; diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index adf4a8b31a..070a92ee53 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -58,6 +58,7 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", + "@react-hookz/web": "^20.0.0", "@rjsf/core": "^3.2.1", "@rjsf/core-v5": "npm:@rjsf/core@5.1.0", "@rjsf/material-ui": "^3.2.1", @@ -66,10 +67,12 @@ "@rjsf/validator-ajv8": "5.1.0", "@types/json-schema": "^7.0.9", "classnames": "^2.2.6", + "humanize-duration": "^3.25.1", "immer": "^9.0.1", "json-schema": "^0.4.0", "json-schema-library": "^7.3.9", "lodash": "^4.17.21", + "luxon": "^3.0.0", "qs": "^6.9.4", "react-use": "^17.2.4", "use-immer": "^0.8.0", @@ -91,7 +94,9 @@ "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^8.0.0", - "@testing-library/user-event": "^14.0.0" + "@testing-library/user-event": "^14.0.0", + "@types/humanize-duration": "^3.18.1", + "@types/luxon": "^3.0.0" }, "files": [ "dist" diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskBorder.test.tsx b/plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.test.tsx similarity index 100% rename from plugins/scaffolder/src/next/OngoingTask/TaskBorder.test.tsx rename to plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.test.tsx diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskBorder.tsx b/plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.tsx similarity index 94% rename from plugins/scaffolder/src/next/OngoingTask/TaskBorder.tsx rename to plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.tsx index fdfab17367..17eea3997a 100644 --- a/plugins/scaffolder/src/next/OngoingTask/TaskBorder.tsx +++ b/plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.tsx @@ -26,6 +26,11 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ }, })); +/** + * The visual progress of the task event stream + * + * @public + */ export const TaskBorder = (props: { isComplete: boolean; isError: boolean; diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/index.ts b/plugins/scaffolder-react/src/components/TaskBorder/index.ts similarity index 93% rename from plugins/scaffolder/src/next/OngoingTask/TaskSteps/index.ts rename to plugins/scaffolder-react/src/components/TaskBorder/index.ts index 28724ba512..e1559957d1 100644 --- a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/index.ts +++ b/plugins/scaffolder-react/src/components/TaskBorder/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { TaskSteps } from './TaskSteps'; +export { TaskBorder } from './TaskBorder'; diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskLogStream.test.tsx b/plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.test.tsx similarity index 100% rename from plugins/scaffolder/src/next/OngoingTask/TaskLogStream.test.tsx rename to plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.test.tsx diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskLogStream.tsx b/plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.tsx similarity index 95% rename from plugins/scaffolder/src/next/OngoingTask/TaskLogStream.tsx rename to plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.tsx index 00e8452b42..973627aa02 100644 --- a/plugins/scaffolder/src/next/OngoingTask/TaskLogStream.tsx +++ b/plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.tsx @@ -25,6 +25,11 @@ const useStyles = makeStyles({ }, }); +/** + * The text of the event stream + * + * @public + */ export const TaskLogStream = (props: { logs: { [k: string]: string[] } }) => { const styles = useStyles(); return ( diff --git a/plugins/scaffolder-react/src/components/TaskLogStream/index.ts b/plugins/scaffolder-react/src/components/TaskLogStream/index.ts new file mode 100644 index 0000000000..a6570f996b --- /dev/null +++ b/plugins/scaffolder-react/src/components/TaskLogStream/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export { TaskLogStream } from './TaskLogStream'; diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/StepIcon.tsx b/plugins/scaffolder-react/src/components/TaskSteps/StepIcon.tsx similarity index 100% rename from plugins/scaffolder/src/next/OngoingTask/TaskSteps/StepIcon.tsx rename to plugins/scaffolder-react/src/components/TaskSteps/StepIcon.tsx diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/StepTime.test.tsx b/plugins/scaffolder-react/src/components/TaskSteps/StepTime.test.tsx similarity index 100% rename from plugins/scaffolder/src/next/OngoingTask/TaskSteps/StepTime.test.tsx rename to plugins/scaffolder-react/src/components/TaskSteps/StepTime.test.tsx diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/StepTime.tsx b/plugins/scaffolder-react/src/components/TaskSteps/StepTime.tsx similarity index 100% rename from plugins/scaffolder/src/next/OngoingTask/TaskSteps/StepTime.tsx rename to plugins/scaffolder-react/src/components/TaskSteps/StepTime.tsx diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.test.tsx b/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.test.tsx similarity index 100% rename from plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.test.tsx rename to plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.test.tsx diff --git a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx b/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx similarity index 94% rename from plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx rename to plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx index 2fcbe941d1..8b43d5e17b 100644 --- a/plugins/scaffolder/src/next/OngoingTask/TaskSteps/TaskSteps.tsx +++ b/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx @@ -27,11 +27,20 @@ import { type Step } from '@backstage/plugin-scaffolder-react'; import { StepIcon } from './StepIcon'; import { StepTime } from './StepTime'; -interface StepperProps { +/** + * + * @public + */ +export interface StepperProps { steps: (TaskStep & Step)[]; activeStep?: number; } +/** + * The visual stepper of the task event stream + * + * @public + */ export const TaskSteps = (props: StepperProps) => { return ( Date: Fri, 24 Feb 2023 16:26:52 +0000 Subject: [PATCH 074/236] remove bogus changeset Signed-off-by: Paul Cowan --- .changeset/large-chefs-boil.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/large-chefs-boil.md diff --git a/.changeset/large-chefs-boil.md b/.changeset/large-chefs-boil.md deleted file mode 100644 index 016ca197c8..0000000000 --- a/.changeset/large-chefs-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-react': minor ---- - -move useEventStream, TaskSteps, TaskBorder and TaskLogStream into scaffolder-react From e2e7cb207cfe07cd74642c4e4eed184a7f96d952 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 14:41:17 +0200 Subject: [PATCH 075/236] Update CODEOWNERS Signed-off-by: Tomas Dabasinskas --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 10f1ac188c..010af451af 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -30,6 +30,7 @@ yarn.lock @backstage/maintainers @back /plugins/catalog-backend-module-aws @backstage/maintainers @backstage/catalog-core @pjungermann /plugins/catalog-backend-module-bitbucket-cloud @backstage/maintainers @backstage/catalog-core @pjungermann /plugins/catalog-backend-module-msgraph @backstage/maintainers @backstage/catalog-core @pjungermann +/plugins/catalog-backend-module-puppetdb @backstage/maintainers @backstage/catalog-core @tdabasinskas /plugins/catalog-graph @backstage/maintainers @backstage/catalog-core @backstage/sda-se-reviewers /plugins/circleci @backstage/maintainers @adamdmharvey /plugins/cloudbuild @backstage/maintainers @trivago/ebarrios From 0c48cb2137f4deb863677b2707df4427eca7888b Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 14:58:42 +0200 Subject: [PATCH 076/236] Generate new plugin Signed-off-by: Tomas Dabasinskas --- .../.eslintrc.js | 1 + .../catalog-backend-module-puppetdb/README.md | 14 +++++ .../package.json | 54 +++++++++++++++++++ .../src/setupTests.ts | 16 ++++++ yarn.lock | 16 ++++++ 5 files changed, 101 insertions(+) create mode 100644 plugins/catalog-backend-module-puppetdb/.eslintrc.js create mode 100644 plugins/catalog-backend-module-puppetdb/README.md create mode 100644 plugins/catalog-backend-module-puppetdb/package.json create mode 100644 plugins/catalog-backend-module-puppetdb/src/setupTests.ts diff --git a/plugins/catalog-backend-module-puppetdb/.eslintrc.js b/plugins/catalog-backend-module-puppetdb/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-backend-module-puppetdb/README.md b/plugins/catalog-backend-module-puppetdb/README.md new file mode 100644 index 0000000000..c66627fa8b --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/README.md @@ -0,0 +1,14 @@ +# catalog-backend-module-puppetdb + +Welcome to the catalog-backend-module-puppetdb backend plugin! + +_This plugin was created through the Backstage CLI_ + +## Getting started + +Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn +start` in the root directory, and then navigating to [/catalog-backend-module-puppetdb](http://localhost:3000/catalog-backend-module-puppetdb). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json new file mode 100644 index 0000000000..5f6a39f73d --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -0,0 +1,54 @@ +{ + "name": "@backstage/plugin-catalog-backend-module-puppetdb-backend", + "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", + "version": "0.0.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "catalog-backend-module-puppetdb" + }, + "keywords": [ + "backstage", + "puppetdb", + "puppet" + ], + "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-common": "workspace:^", + "@backstage/config": "workspace:^", + "lodash": "^4.17.21", + "node-fetch": "^2.6.7", + "uuid": "^8.0.0", + "winston": "^3.2.1" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@types/lodash": "^4.14.151", + "msw": "^0.49.0" + }, + "files": [ + "dist", + "config.d.ts" + ], + "configSchema": "config.d.ts" +} diff --git a/plugins/catalog-backend-module-puppetdb/src/setupTests.ts b/plugins/catalog-backend-module-puppetdb/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export {}; diff --git a/yarn.lock b/yarn.lock index 6973779ac5..33261fc7da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5206,6 +5206,22 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-catalog-backend-module-puppetdb-backend@workspace:plugins/catalog-backend-module-puppetdb": + version: 0.0.0-use.local + resolution: "@backstage/plugin-catalog-backend-module-puppetdb-backend@workspace:plugins/catalog-backend-module-puppetdb" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@types/lodash": ^4.14.151 + lodash: ^4.17.21 + msw: ^0.49.0 + node-fetch: ^2.6.7 + uuid: ^8.0.0 + winston: ^3.2.1 + languageName: unknown + linkType: soft + "@backstage/plugin-catalog-backend@workspace:^, @backstage/plugin-catalog-backend@workspace:plugins/catalog-backend": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend@workspace:plugins/catalog-backend" From 9bb20d2b0ddd0cd1a4c041634d86ef97c8d6c0d4 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 19:49:08 +0200 Subject: [PATCH 077/236] Add initial code Signed-off-by: Tomas Dabasinskas --- .../config.d.ts | 58 +++++ .../package.json | 6 + .../src/index.ts | 24 ++ .../src/providers/PuppetDBEntityProvider.ts | 236 ++++++++++++++++++ .../providers/PuppetDBEntityProviderConfig.ts | 99 ++++++++ .../src/providers/constants.ts | 29 +++ .../src/providers/index.ts | 19 ++ .../src/puppet/constants.ts | 32 +++ .../src/puppet/index.ts | 24 ++ .../src/puppet/read.ts | 75 ++++++ .../src/puppet/transformers.ts | 65 +++++ .../src/puppet/types.ts | 102 ++++++++ yarn.lock | 6 + 13 files changed, 775 insertions(+) create mode 100644 plugins/catalog-backend-module-puppetdb/config.d.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/index.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/constants.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/index.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/index.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/read.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/types.ts diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts new file mode 100644 index 0000000000..6514629251 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2020 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 { + TaskScheduleDefinition, + TaskScheduleDefinitionConfig, +} from '@backstage/backend-tasks'; + +/** + * Represents the configuration for the Backstage. + */ +export interface Config { + /** + * Configuration for the catalog. + */ + catalog?: { + /** + * Configuration for the providers. + */ + providers?: { + /** + * Puppet entity provider configuration. Uses "default" as default ID for the single config variant. + */ + puppet?: ProviderConfig | Record; + }; + }; +} + +/** + * Configuration of {@link PuppetDBEntityProvider}. + */ +interface ProviderConfig { + /** + * (Required) The host of PuppetDB API instance. + */ + host: string; + /** + * (Optional) PQL query to filter PuppetDB nodes. + */ + query?: string; + /** + * (Optional) Task schedule definition for the refresh. + */ + schedule?: TaskScheduleDefinition; +} diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 5f6a39f73d..fa7f08da1a 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -35,8 +35,14 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/types": "workspace:^", "lodash": "^4.17.21", + "luxon": "^3.0.0", "node-fetch": "^2.6.7", "uuid": "^8.0.0", "winston": "^3.2.1" diff --git a/plugins/catalog-backend-module-puppetdb/src/index.ts b/plugins/catalog-backend-module-puppetdb/src/index.ts new file mode 100644 index 0000000000..b5c5f9d209 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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. + */ + +/** + * A Backstage catalog backend module that helps integrate towards PuppetDB + * + * @packageDocumentation + */ + +export * from './providers'; +export * from './puppet'; diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts new file mode 100644 index 0000000000..a887d4778c --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts @@ -0,0 +1,236 @@ +/* + * Copyright 2021 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 { + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-backend'; +import { Logger } from 'winston'; +import { + PuppetDBEntityProviderConfig, + readProviderConfigs, +} from './PuppetDBEntityProviderConfig'; +import { Config } from '@backstage/config'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; +import * as uuid from 'uuid'; +import { ResourceTransformer, defaultResourceTransformer } from '../puppet'; +import { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, + Entity, +} from '@backstage/catalog-model/'; +import { merge } from 'lodash'; +import { readPuppetNodes } from '../puppet/read'; +import { ENDPOINT_NODES } from '../puppet/constants'; + +/** + * Reads nodes from [PuppetDB](https://www.puppet.com/docs/puppet/6/puppetdb_overview.html) + * based on the provided query and registers them as Resource entities in the catalog. + * + * @public + */ +export class PuppetDBEntityProvider implements EntityProvider { + private readonly config: PuppetDBEntityProviderConfig; + private readonly logger: Logger; + private readonly scheduleFn: () => Promise; + private readonly transformer: ResourceTransformer; + private connection?: EntityProviderConnection; + + /** + * Creates instances of {@link PuppetDBEntityProvider} from a configuration. + * + * @param config - The configuration to read provider information from. + * @param deps - The dependencies for {@link PuppetDBEntityProvider}. + * + * @returns A list of {@link PuppetDBEntityProvider} instances. + */ + static fromConfig( + config: Config, + deps: { + logger: Logger; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + transformer?: ResourceTransformer; + }, + ): PuppetDBEntityProvider[] { + if (!deps.schedule && !deps.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + + return readProviderConfigs(config).map(providerConfig => { + if (!deps.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for puppet-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + deps.schedule ?? + deps.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + + const transformer = deps.transformer ?? defaultResourceTransformer; + + return new PuppetDBEntityProvider( + providerConfig, + deps.logger, + taskRunner, + transformer, + ); + }); + } + + /** + * Creates an instance of {@link PuppetDBEntityProvider}. + * + * @param config - Configuration of the provider. + * @param logger - The instance of a {@link Logger}. + * @param taskRunner - The instance of {@link TaskRunner}. + * @param transformer - A {@link ResourceTransformer} function. + * + * @private + */ + private constructor( + config: PuppetDBEntityProviderConfig, + logger: Logger, + taskRunner: TaskRunner, + transformer: ResourceTransformer, + ) { + this.config = config; + this.logger = logger.child({ + target: this.getProviderName(), + }); + this.scheduleFn = this.createScheduleFn(taskRunner); + this.transformer = transformer; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + getProviderName(): string { + return `puppetdb-provider:${this.config.id}`; + } + + /** + * Creates a function that can be used to schedule a refresh of the catalog. + * + * @param taskRunner - The instance of {@link TaskRunner}. + * + * @private + */ + private createScheduleFn(taskRunner: TaskRunner): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return taskRunner.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: PuppetDBEntityProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + try { + await this.refresh(logger); + } catch (error) { + logger.error(`${this.getProviderName()} refresh failed`, error); + } + }, + }); + }; + } + + /** + * Refreshes the catalog by reading nodes from PuppetDB and registering them as Resource Entities. + * + * @param logger - The instance of a {@link Logger}. + */ + async refresh(logger: Logger) { + if (!this.connection) { + throw new Error('Not initialized'); + } + + const { markReadComplete } = trackProgress(logger); + const entities = await readPuppetNodes(this.config, { + logger, + transformer: this.transformer, + }); + const { markCommitComplete } = markReadComplete(entities); + + await this.connection.applyMutation({ + type: 'full', + entities: [...entities].map(entity => ({ + locationKey: this.getProviderName(), + entity: withLocations(this.config.host, entity), + })), + }); + markCommitComplete(entities); + } +} + +/** + * Ensures the entities have required annotation data. + * + * @param host - The host of the PuppetDB instance. + * @param entity - The entity to add the annotations to. + * + * @returns Entity with @{@link ANNOTATION_LOCATION} and @{@link ANNOTATION_ORIGIN_LOCATION} annotations. + */ +function withLocations(host: string, entity: Entity): Entity { + const location = new URL(host); + location.pathname = `${ENDPOINT_NODES}/${entity.metadata?.name}`; + + return merge( + { + metadata: { + annotations: { + [ANNOTATION_LOCATION]: `url:${location.toString()}`, + [ANNOTATION_ORIGIN_LOCATION]: `url:${location.toString()}`, + }, + }, + }, + entity, + ) as Entity; +} + +/** + * Tracks the progress of the PuppetDB read and commit operations. + * + * @param logger - The instance of a {@link Logger}. + */ +function trackProgress(logger: Logger) { + let timestamp = Date.now(); + + function markReadComplete(entities: Entity[]) { + const readDuration = ((Date.now() - timestamp) / 1000).toFixed(1); + timestamp = Date.now(); + logger.info( + `Read ${entities?.length ?? 0} in ${readDuration} seconds. Committing...`, + ); + return { markCommitComplete }; + } + + function markCommitComplete(entities: Entity[]) { + const commitDuration = ((Date.now() - timestamp) / 1000).toFixed(1); + logger.info( + `Committed ${entities?.length ?? 0} in ${commitDuration} seconds.`, + ); + } + + return { markReadComplete }; +} diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts new file mode 100644 index 0000000000..e96a4a11e9 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2021 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 { + readTaskScheduleDefinitionFromConfig, + TaskScheduleDefinition, +} from '@backstage/backend-tasks'; +import { Config } from '@backstage/config'; +import { DEFAULT_PROVIDER_ID } from './constants'; + +/** + * Configuration of {@link PuppetDBEntityProvider}. + * + * @public + */ +export type PuppetDBEntityProviderConfig = { + /** + * ID of the provider. + */ + id: string; + /** + * (Required) The host of PuppetDB API instance. + */ + host: string; + /** + * (Optional) PQL query to filter PuppetDB nodes. + */ + query?: string; + /** + * (Optional) Task schedule definition for the refresh. + */ + schedule?: TaskScheduleDefinition; +}; + +/** + * Reads the configuration of the PuppetDB Entity Providers. + * + * @param config - The application configuration. + * + * @returns PuppetDB Entity Provider configurations list. + */ +export function readProviderConfigs( + config: Config, +): PuppetDBEntityProviderConfig[] { + const providersConfig = config.getOptionalConfig( + 'catalog.providers.puppetdb', + ); + if (!providersConfig) { + return []; + } + + if (providersConfig.has('host')) { + return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)]; + } + + return providersConfig.keys().map(id => { + return readProviderConfig(id, providersConfig.getConfig(id)); + }); +} + +/** + * Reads the configuration for the PuppetDB Entity Provider. + * + * @param id - ID of the provider. + * @param config - The application configuration. + * + * @returns The PuppetDB Entity Provider configuration. + */ +function readProviderConfig( + id: string, + config: Config, +): PuppetDBEntityProviderConfig { + const host = config.getString('host').replace(/\/+$/, ''); + const query = config.getOptionalString('query'); + + const schedule = config.has('schedule') + ? readTaskScheduleDefinitionFromConfig(config.getConfig('schedule')) + : undefined; + + return { + id, + host, + query, + schedule, + }; +} diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts b/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts new file mode 100644 index 0000000000..02b333c613 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts @@ -0,0 +1,29 @@ +/* + * Copyright 2020 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. + */ + +/** + * Name of the default provider when a using a simple configuration. + * + * @public + */ +export const DEFAULT_PROVIDER_ID = 'default'; + +/** + * Default owner for entities created by the PuppetDB provider. + * + * @public + */ +export const DEFAULT_OWNER = 'unknown'; diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/index.ts b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts new file mode 100644 index 0000000000..4e220395ba --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2021 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. + */ + +export { PuppetDBEntityProvider } from './PuppetDBEntityProvider'; +export type { PuppetDBEntityProviderConfig } from './PuppetDBEntityProviderConfig'; +export { DEFAULT_PROVIDER_ID, DEFAULT_OWNER } from './constants'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts new file mode 100644 index 0000000000..ef355f22d3 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2020 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. + */ + +/** + * Annotation for specifying the certificate name of a node in PuppetDB. + * + * @public + */ +export const ANNOTATION_PUPPET_CERTNAME = 'puppet.com/certname'; + +/** + * Path of PuppetDB FactSets endpoint. + */ +export const ENDPOINT_FACTSETS = '/pdb/query/v4/factsets'; + +/** + * Path of PuppetDB Nodes endpoint. + */ +export const ENDPOINT_NODES = '/pdb/query/v4/nodes'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/index.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/index.ts new file mode 100644 index 0000000000..644d111ac1 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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. + */ + +export { ANNOTATION_PUPPET_CERTNAME } from './constants'; +export { defaultResourceTransformer } from './transformers'; +export type { + PuppetNode, + PuppetFactSet, + PuppetFact, + ResourceTransformer, +} from './types'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts new file mode 100644 index 0000000000..edd33bc76a --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2020 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 { PuppetDBEntityProviderConfig } from '../providers'; +import { PuppetNode, ResourceTransformer } from './types'; +import { ResourceEntity } from '@backstage/catalog-model/'; +import { defaultResourceTransformer } from './transformers'; +import { ResponseError } from '@backstage/errors'; +import { ENDPOINT_FACTSETS } from './constants'; +import { Logger } from 'winston'; + +/** + * Reads nodes and their facts from PuppetDB. + * + * @param config - The provider configuration. + * @param opts - Additional options. + */ +export async function readPuppetNodes( + config: PuppetDBEntityProviderConfig, + opts?: { + transformer?: ResourceTransformer; + logger?: Logger; + }, +): Promise { + const transformFn = opts?.transformer ?? defaultResourceTransformer; + const url = new URL(config.host); + url.pathname = ENDPOINT_FACTSETS; + + if (config.query) { + url.searchParams.set('query', config.query); + } + + if (opts?.logger) { + opts.logger + .child({ url: url.toString() }) + .debug('Reading nodes from PuppetDB'); + } + + const response = await fetch(url.toString(), { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + const nodes = (await response.json()) as PuppetNode[]; + const entities: ResourceEntity[] = []; + + for (const node of nodes) { + const entity = await transformFn(node, config); + if (entity) { + entities.push(entity); + } + } + + return entities; +} diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts new file mode 100644 index 0000000000..ca0d2a4ccc --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2020 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 { ResourceTransformer } from './types'; +import { DEFAULT_NAMESPACE, ResourceEntity } from '@backstage/catalog-model'; +import { DEFAULT_OWNER } from '../providers'; +import { ANNOTATION_PUPPET_CERTNAME } from './constants'; + +/** + * A default implementation of the {@link ResourceTransformer}. + * + * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform. + * @param _config - The configuration for the entity provider. + * + * @returns A `ResourceEntity`. + * + * @public + */ +export const defaultResourceTransformer: ResourceTransformer = async ( + node, + _config, +) => { + const certName = node.certname.toLowerCase(); + const type = node.facts?.data?.find(e => e.name === 'is_virtual')?.value + ? 'virtual-machine' + : 'physical-server'; + const kernel = node.facts?.data?.find(e => e.name === 'kernel')?.value; + + const entity: ResourceEntity = { + apiVersion: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: certName, + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: certName, + }, + namespace: DEFAULT_NAMESPACE, + description: node.facts?.data + ?.find(e => e.name === 'ipaddress') + ?.value?.toString(), + tags: kernel ? [kernel.toString().toLowerCase()] : [], + }, + spec: { + type: type, + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }; + + return entity; +}; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts new file mode 100644 index 0000000000..7dc951f49f --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts @@ -0,0 +1,102 @@ +/* + * Copyright 2021 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 { ResourceEntity } from '@backstage/catalog-model'; +import { JsonValue } from '@backstage/types'; +import { PuppetDBEntityProviderConfig } from '../providers/PuppetDBEntityProviderConfig'; + +/** + * Customize the ingested Resource entity. + * + * @param node - The found PuppetDB node entry in its source format. This is the entry that you want to transform. + * @param config - The configuration for the entity provider. + * + * @returns A `ResourceEntity` or `undefined` if you want to ignore the found group for being ingested by the catalog. + * + * @public + */ +export type ResourceTransformer = ( + node: PuppetNode, + config: PuppetDBEntityProviderConfig, +) => Promise; + +/** + * A node in PuppetDB. + * + * @public + */ +export type PuppetNode = { + /** + * The most recent time of fact submission from the associated certname. + */ + timestamp: string; + /** + * The certname associated with the factset. + */ + certname: string; + /** + * A hash of the factset's certname, environment, timestamp, facts, and producer_timestamp. + */ + hash: string; + /** + * The most recent time of fact submission for the relevant certname from the Puppet Server. + */ + producer_timestamp: string; + /** + * The certname of the Puppet Server that sent the factset to PuppetDB. + */ + producer: string; + /** + * The environment associated with the fact. + */ + environment: string; + /** + * The facts associated with the factset. + */ + facts: PuppetFactSet; +}; + +/** + * The set of all facts for a single certname in PuppetDB. + * + * @public + */ +export type PuppetFactSet = { + /** + * The array of facts. + */ + data: PuppetFact[]; + /** + * The URL to retrieve more information about the facts. + */ + href: string; +}; + +/** + * A fact in PuppetDB. + * + * @public + */ +export type PuppetFact = { + /** + * The name of the fact. + */ + name: string; + /** + * The value of the fact, in JSON format. + */ + value: JsonValue; +}; diff --git a/yarn.lock b/yarn.lock index 33261fc7da..eb17085b2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5211,10 +5211,16 @@ __metadata: resolution: "@backstage/plugin-catalog-backend-module-puppetdb-backend@workspace:plugins/catalog-backend-module-puppetdb" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-tasks": "workspace:^" + "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-catalog-backend": "workspace:^" + "@backstage/types": "workspace:^" "@types/lodash": ^4.14.151 lodash: ^4.17.21 + luxon: ^3.0.0 msw: ^0.49.0 node-fetch: ^2.6.7 uuid: ^8.0.0 From 30483f692dab8dc0de33dd108b1b82643e5b9ef3 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 19:49:23 +0200 Subject: [PATCH 078/236] Add tests Signed-off-by: Tomas Dabasinskas --- .../providers/PuppetDBEntityProvider.test.ts | 218 ++++++++++++++++ .../PuppetDBEntityProviderConfig.test.ts | 129 +++++++++ .../src/puppet/read.test.ts | 244 ++++++++++++++++++ .../src/puppet/transformers.test.ts | 85 ++++++ 4 files changed, 676 insertions(+) create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts create mode 100644 plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts new file mode 100644 index 0000000000..3b665b94b4 --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts @@ -0,0 +1,218 @@ +/* + * Copyright 2021 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 { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PuppetDBEntityProvider } from './PuppetDBEntityProvider'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; +import * as p from '../puppet/read'; +import { DEFAULT_OWNER } from './constants'; +import { ANNOTATION_PUPPET_CERTNAME } from '../puppet'; +import { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, +} from '@backstage/catalog-model/'; +import { ENDPOINT_NODES } from '../puppet/constants'; + +const logger = getVoidLogger(); + +jest.mock('../puppet/read', () => ({ + __esModule: true, + default: jest.fn(), + readPuppetNodes: null, +})); + +class PersistingTaskRunner implements TaskRunner { + private tasks: TaskInvocationDefinition[] = []; + + getTasks() { + return this.tasks; + } + + run(task: TaskInvocationDefinition): Promise { + this.tasks.push(task); + return Promise.resolve(undefined); + } +} + +describe('PuppetEntityProvider', () => { + const config = new ConfigReader({ + catalog: { + providers: { + puppetdb: { + host: 'http://puppetdb:8080', + schedule: { + frequency: { + minutes: 10, + }, + timeout: { + minutes: 10, + }, + }, + }, + }, + }, + }); + + describe('where there are no nodes', () => { + beforeEach(() => { + // @ts-ignore + p.readPuppetNodes = jest.fn().mockResolvedValue([]); + }); + + it('creates no entities', async () => { + const connection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + const providers = PuppetDBEntityProvider.fromConfig(config, { + logger, + schedule: new PersistingTaskRunner(), + }); + + await providers[0].connect(connection); + await providers[0].refresh(logger); + + expect(connection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [], + }); + }); + }); + + describe('where there are nodes', () => { + beforeEach(() => { + // @ts-ignore + p.readPuppetNodes = jest.fn().mockResolvedValue([ + { + api_version: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'node1', + namespace: 'default', + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: 'node1', + }, + tags: ['windows'], + description: 'Description 1', + spec: { + type: 'virtual-machine', + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }, + }, + { + api_version: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'node2', + namespace: 'default', + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: 'node2', + }, + tags: ['linux'], + description: 'Description 2', + spec: { + type: 'physical-server', + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }, + }, + ]); + }); + + it('creates entities', async () => { + const connection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + const providers = PuppetDBEntityProvider.fromConfig(config, { + logger, + schedule: new PersistingTaskRunner(), + }); + + await providers[0].connect(connection); + await providers[0].refresh(logger); + + expect(connection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [ + { + locationKey: providers[0].getProviderName(), + entity: { + api_version: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'node1', + namespace: 'default', + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: 'node1', + [ANNOTATION_LOCATION]: `url:${config.getString( + 'catalog.providers.puppetdb.host', + )}${ENDPOINT_NODES}/node1`, + [ANNOTATION_ORIGIN_LOCATION]: `url:${config.getString( + 'catalog.providers.puppetdb.host', + )}${ENDPOINT_NODES}/node1`, + }, + tags: ['windows'], + description: 'Description 1', + spec: { + type: 'virtual-machine', + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }, + }, + }, + { + locationKey: providers[0].getProviderName(), + entity: { + api_version: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'node2', + namespace: 'default', + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: 'node2', + [ANNOTATION_LOCATION]: `url:${config.getString( + 'catalog.providers.puppetdb.host', + )}${ENDPOINT_NODES}/node2`, + [ANNOTATION_ORIGIN_LOCATION]: `url:${config.getString( + 'catalog.providers.puppetdb.host', + )}${ENDPOINT_NODES}/node2`, + }, + tags: ['linux'], + description: 'Description 2', + spec: { + type: 'physical-server', + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }, + }, + }, + ], + }); + }); + }); +}); diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts new file mode 100644 index 0000000000..07fe54bbca --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts @@ -0,0 +1,129 @@ +/* + * Copyright 2021 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 { ConfigReader } from '@backstage/config'; +import { readProviderConfigs } from './PuppetDBEntityProviderConfig'; +import { Duration } from 'luxon'; + +describe('readProviderConfigs', () => { + afterEach(() => jest.resetAllMocks()); + + it('no provider config', () => { + const config = new ConfigReader({}); + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(0); + }); + + it('single simple provider config', () => { + const config = new ConfigReader({ + catalog: { + providers: { + puppetdb: { + host: 'https://puppetdb', + }, + }, + }, + }); + + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(1); + expect(providerConfigs[0].id).toEqual('default'); + expect(providerConfigs[0].host).toEqual('https://puppetdb'); + }); + + it('single specific provider config', () => { + const config = new ConfigReader({ + catalog: { + providers: { + puppetdb: { + 'my-provider': { + host: 'https://puppetdb', + }, + }, + }, + }, + }); + + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(1); + expect(providerConfigs[0].id).toEqual('my-provider'); + expect(providerConfigs[0].host).toEqual('https://puppetdb'); + }); + + it('multiple provider configs', () => { + const config = new ConfigReader({ + catalog: { + providers: { + puppetdb: { + 'my-provider': { + host: 'https://my-puppet/', + query: 'my-query', + }, + 'your-provider': { + host: 'https://your-puppet', + query: 'your-query', + }, + }, + }, + }, + }); + + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(2); + expect(providerConfigs[0]).toEqual({ + id: 'my-provider', + host: 'https://my-puppet', + query: 'my-query', + }); + expect(providerConfigs[1]).toEqual({ + id: 'your-provider', + host: 'https://your-puppet', + query: 'your-query', + }); + }); + + it('provider config with schedule', () => { + const config = new ConfigReader({ + catalog: { + providers: { + puppetdb: { + host: 'https://puppetdb', + schedule: { + frequency: 'PT30M', + timeout: { + minutes: 10, + }, + }, + }, + }, + }, + }); + + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(1); + expect(providerConfigs[0].schedule).toEqual({ + frequency: Duration.fromISO('PT30M'), + timeout: { + minutes: 10, + }, + }); + }); +}); diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts new file mode 100644 index 0000000000..573be365dd --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts @@ -0,0 +1,244 @@ +/* + * Copyright 2020 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 { readPuppetNodes } from './read'; +import { + DEFAULT_PROVIDER_ID, + PuppetDBEntityProviderConfig, +} from '../providers'; +import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; +import fetch from 'node-fetch'; +import { ANNOTATION_PUPPET_CERTNAME, ENDPOINT_FACTSETS } from './constants'; + +jest.mock('node-fetch', () => { + const original = jest.requireActual('node-fetch'); + return { + __esModule: true, + default: jest.fn(), + Headers: original.Headers, + }; +}); +(global as any).fetch = fetch; + +describe('readPuppetNodes', () => { + const mockFetch = fetch as unknown as jest.Mocked; + + describe('where no query is specified', () => { + const config: PuppetDBEntityProviderConfig = { + host: 'https://puppetdb', + id: DEFAULT_PROVIDER_ID, + }; + + beforeEach(async () => { + mockFetch.mockReturnValueOnce( + Promise.resolve( + new Response( + JSON.stringify([ + { + certname: 'node1', + timestamp: 'time1', + hash: 'hash1', + producer_timestamp: 'producer_time1', + producer: 'producer1', + environment: 'environment1', + facts: { + data: [ + { + name: 'is_virtual', + value: true, + }, + { + name: 'kernel', + value: 'Linux', + }, + { + name: 'ipaddress', + value: 'ipaddress1', + }, + { + name: 'clientnoop', + value: true, + }, + { + name: 'clientversion', + value: 'clientversion1', + }, + ], + }, + }, + { + certname: 'node2', + timestamp: 'time2', + hash: 'hash2', + producer_timestamp: 'producer_time2', + producer: 'producer2', + environment: 'environment2', + facts: { + data: [ + { + name: 'is_virtual', + value: false, + }, + { + name: 'kernel', + value: 'Windows', + }, + { + name: 'ipaddress', + value: 'ipaddress2', + }, + { + name: 'clientnoop', + value: false, + }, + { + name: 'clientversion', + value: 'clientversion2', + }, + ], + }, + }, + ]), + ), + ), + ); + }); + + describe('where custom transformer is used', () => { + it('should use it for transforming puppet nodes', async () => { + const entities = await readPuppetNodes(config, { + transformer: async (node, _config) => { + return { + apiVersion: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: `custom-${node.certname}`, + namespace: DEFAULT_NAMESPACE, + }, + spec: { + type: 'Custom', + owner: 'Custom', + dependsOn: [], + dependencyOf: [], + }, + }; + }, + }); + + expect(entities).toHaveLength(2); + expect(entities[0]).toEqual({ + apiVersion: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'custom-node1', + namespace: DEFAULT_NAMESPACE, + }, + spec: { + type: 'Custom', + owner: 'Custom', + dependsOn: [], + dependencyOf: [], + }, + }); + expect(entities[1]).toEqual({ + apiVersion: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'custom-node2', + namespace: DEFAULT_NAMESPACE, + }, + spec: { + type: 'Custom', + owner: 'Custom', + dependsOn: [], + dependencyOf: [], + }, + }); + }); + }); + + describe('where default transformer is used', () => { + it('should use it for transforming puppet nodes', async () => { + const entities = await readPuppetNodes(config); + + expect(entities).toHaveLength(2); + expect(entities[0].metadata.annotations).toEqual({ + [ANNOTATION_PUPPET_CERTNAME]: 'node1', + }); + expect(entities[1].metadata.annotations).toEqual({ + [ANNOTATION_PUPPET_CERTNAME]: 'node2', + }); + }); + }); + }); + + describe('where query is specified', () => { + const config: PuppetDBEntityProviderConfig = { + host: 'https://puppetdb', + id: DEFAULT_PROVIDER_ID, + query: '["=", "certname", "node1"]', + }; + + describe('where no results are matched', () => { + beforeEach(async () => { + mockFetch.mockReturnValueOnce( + Promise.resolve(new Response(JSON.stringify([]))), + ); + }); + + it('should return empty array', async () => { + const entities = await readPuppetNodes(config); + expect(entities).toHaveLength(0); + }); + }); + + describe('where results are matched', () => { + beforeEach(async () => { + mockFetch.mockReturnValueOnce( + Promise.resolve( + new Response( + JSON.stringify([ + { + certname: 'node1', + timestamp: 'time1', + hash: 'hash1', + producer_timestamp: 'producer_time1', + producer: 'producer1', + environment: 'environment1', + }, + ]), + ), + ), + ); + }); + + it('should return matched results', async () => { + const entities = await readPuppetNodes(config); + expect(mockFetch).toHaveBeenCalledWith( + `${config.host}${ENDPOINT_FACTSETS}?query=%5B%22%3D%22%2C+%22certname%22%2C+%22node1%22%5D`, + { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + method: 'GET', + }, + ); + expect(entities).toHaveLength(1); + }); + }); + }); +}); diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts new file mode 100644 index 0000000000..30ae79e87b --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright 2020 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 { PuppetDBEntityProviderConfig } from '../providers'; +import { PuppetNode } from './types'; +import { defaultResourceTransformer } from './transformers'; +import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; +import { DEFAULT_OWNER } from '../providers'; +import { ANNOTATION_PUPPET_CERTNAME } from './constants'; + +describe('defaultResourceTransformer', () => { + it('should transform a puppet node to a resource entity', async () => { + const config: PuppetDBEntityProviderConfig = { + host: '', + id: '', + }; + const node: PuppetNode = { + certname: 'node1', + timestamp: 'time1', + hash: 'hash1', + producer_timestamp: 'producer_time1', + producer: 'producer1', + environment: 'environment1', + facts: { + href: 'facts1', + data: [ + { + name: 'kernel', + value: 'Linux', + }, + { + name: 'ipaddress', + value: 'ipaddress1', + }, + { + name: 'is_virtual', + value: true, + }, + { + name: 'clientnoop', + value: true, + }, + { + name: 'clientversion', + value: 'clientversion1', + }, + ], + }, + }; + + const entity = await defaultResourceTransformer(node, config); + expect(entity).toEqual({ + apiVersion: 'backstage.io/v1beta1', + kind: 'Resource', + metadata: { + name: 'node1', + namespace: DEFAULT_NAMESPACE, + annotations: { + [ANNOTATION_PUPPET_CERTNAME]: 'node1', + }, + description: 'ipaddress1', + tags: ['linux'], + }, + spec: { + type: 'virtual-machine', + owner: DEFAULT_OWNER, + dependsOn: [], + dependencyOf: [], + }, + }); + }); +}); From 6746020caf741a540b55bcdf9d436d663d15582e Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 19:59:30 +0200 Subject: [PATCH 079/236] Update naming Signed-off-by: Tomas Dabasinskas --- .../config.d.ts | 2 +- ...test.ts => PuppetDbEntityProvider.test.ts} | 6 ++--- ...yProvider.ts => PuppetDbEntityProvider.ts} | 24 +++++++++---------- ...s => PuppetDbEntityProviderConfig.test.ts} | 2 +- ...fig.ts => PuppetDbEntityProviderConfig.ts} | 8 +++---- .../src/providers/index.ts | 4 ++-- .../src/puppet/read.test.ts | 6 ++--- .../src/puppet/read.ts | 4 ++-- .../src/puppet/transformers.test.ts | 4 ++-- .../src/puppet/types.ts | 4 ++-- 10 files changed, 32 insertions(+), 32 deletions(-) rename plugins/catalog-backend-module-puppetdb/src/providers/{PuppetDBEntityProvider.test.ts => PuppetDbEntityProvider.test.ts} (97%) rename plugins/catalog-backend-module-puppetdb/src/providers/{PuppetDBEntityProvider.ts => PuppetDbEntityProvider.ts} (91%) rename plugins/catalog-backend-module-puppetdb/src/providers/{PuppetDBEntityProviderConfig.test.ts => PuppetDbEntityProviderConfig.test.ts} (98%) rename plugins/catalog-backend-module-puppetdb/src/providers/{PuppetDBEntityProviderConfig.ts => PuppetDbEntityProviderConfig.ts} (93%) diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts index 6514629251..a79438944e 100644 --- a/plugins/catalog-backend-module-puppetdb/config.d.ts +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -40,7 +40,7 @@ export interface Config { } /** - * Configuration of {@link PuppetDBEntityProvider}. + * Configuration of {@link PuppetDbEntityProvider}. */ interface ProviderConfig { /** diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts similarity index 97% rename from plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts rename to plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts index 3b665b94b4..7ea191190b 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts @@ -17,7 +17,7 @@ import { TaskInvocationDefinition, TaskRunner } from '@backstage/backend-tasks'; import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; -import { PuppetDBEntityProvider } from './PuppetDBEntityProvider'; +import { PuppetDbEntityProvider } from './PuppetDbEntityProvider'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import * as p from '../puppet/read'; import { DEFAULT_OWNER } from './constants'; @@ -79,7 +79,7 @@ describe('PuppetEntityProvider', () => { applyMutation: jest.fn(), refresh: jest.fn(), }; - const providers = PuppetDBEntityProvider.fromConfig(config, { + const providers = PuppetDbEntityProvider.fromConfig(config, { logger, schedule: new PersistingTaskRunner(), }); @@ -144,7 +144,7 @@ describe('PuppetEntityProvider', () => { applyMutation: jest.fn(), refresh: jest.fn(), }; - const providers = PuppetDBEntityProvider.fromConfig(config, { + const providers = PuppetDbEntityProvider.fromConfig(config, { logger, schedule: new PersistingTaskRunner(), }); diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts similarity index 91% rename from plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts rename to plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts index a887d4778c..e6c19686a3 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProvider.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts @@ -20,9 +20,9 @@ import { } from '@backstage/plugin-catalog-backend'; import { Logger } from 'winston'; import { - PuppetDBEntityProviderConfig, + PuppetDbEntityProviderConfig, readProviderConfigs, -} from './PuppetDBEntityProviderConfig'; +} from './PuppetDbEntityProviderConfig'; import { Config } from '@backstage/config'; import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import * as uuid from 'uuid'; @@ -42,20 +42,20 @@ import { ENDPOINT_NODES } from '../puppet/constants'; * * @public */ -export class PuppetDBEntityProvider implements EntityProvider { - private readonly config: PuppetDBEntityProviderConfig; +export class PuppetDbEntityProvider implements EntityProvider { + private readonly config: PuppetDbEntityProviderConfig; private readonly logger: Logger; private readonly scheduleFn: () => Promise; private readonly transformer: ResourceTransformer; private connection?: EntityProviderConnection; /** - * Creates instances of {@link PuppetDBEntityProvider} from a configuration. + * Creates instances of {@link PuppetDbEntityProvider} from a configuration. * * @param config - The configuration to read provider information from. - * @param deps - The dependencies for {@link PuppetDBEntityProvider}. + * @param deps - The dependencies for {@link PuppetDbEntityProvider}. * - * @returns A list of {@link PuppetDBEntityProvider} instances. + * @returns A list of {@link PuppetDbEntityProvider} instances. */ static fromConfig( config: Config, @@ -65,7 +65,7 @@ export class PuppetDBEntityProvider implements EntityProvider { scheduler?: PluginTaskScheduler; transformer?: ResourceTransformer; }, - ): PuppetDBEntityProvider[] { + ): PuppetDbEntityProvider[] { if (!deps.schedule && !deps.scheduler) { throw new Error('Either schedule or scheduler must be provided.'); } @@ -83,7 +83,7 @@ export class PuppetDBEntityProvider implements EntityProvider { const transformer = deps.transformer ?? defaultResourceTransformer; - return new PuppetDBEntityProvider( + return new PuppetDbEntityProvider( providerConfig, deps.logger, taskRunner, @@ -93,7 +93,7 @@ export class PuppetDBEntityProvider implements EntityProvider { } /** - * Creates an instance of {@link PuppetDBEntityProvider}. + * Creates an instance of {@link PuppetDbEntityProvider}. * * @param config - Configuration of the provider. * @param logger - The instance of a {@link Logger}. @@ -103,7 +103,7 @@ export class PuppetDBEntityProvider implements EntityProvider { * @private */ private constructor( - config: PuppetDBEntityProviderConfig, + config: PuppetDbEntityProviderConfig, logger: Logger, taskRunner: TaskRunner, transformer: ResourceTransformer, @@ -141,7 +141,7 @@ export class PuppetDBEntityProvider implements EntityProvider { id: taskId, fn: async () => { const logger = this.logger.child({ - class: PuppetDBEntityProvider.prototype.constructor.name, + class: PuppetDbEntityProvider.prototype.constructor.name, taskId, taskInstanceId: uuid.v4(), }); diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProviderConfig.test.ts similarity index 98% rename from plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts rename to plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProviderConfig.test.ts index 07fe54bbca..517247fd6d 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProviderConfig.test.ts @@ -15,7 +15,7 @@ */ import { ConfigReader } from '@backstage/config'; -import { readProviderConfigs } from './PuppetDBEntityProviderConfig'; +import { readProviderConfigs } from './PuppetDbEntityProviderConfig'; import { Duration } from 'luxon'; describe('readProviderConfigs', () => { diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProviderConfig.ts similarity index 93% rename from plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts rename to plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProviderConfig.ts index e96a4a11e9..159a1e5504 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDBEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProviderConfig.ts @@ -22,11 +22,11 @@ import { Config } from '@backstage/config'; import { DEFAULT_PROVIDER_ID } from './constants'; /** - * Configuration of {@link PuppetDBEntityProvider}. + * Configuration of {@link PuppetDbEntityProvider}. * * @public */ -export type PuppetDBEntityProviderConfig = { +export type PuppetDbEntityProviderConfig = { /** * ID of the provider. */ @@ -54,7 +54,7 @@ export type PuppetDBEntityProviderConfig = { */ export function readProviderConfigs( config: Config, -): PuppetDBEntityProviderConfig[] { +): PuppetDbEntityProviderConfig[] { const providersConfig = config.getOptionalConfig( 'catalog.providers.puppetdb', ); @@ -82,7 +82,7 @@ export function readProviderConfigs( function readProviderConfig( id: string, config: Config, -): PuppetDBEntityProviderConfig { +): PuppetDbEntityProviderConfig { const host = config.getString('host').replace(/\/+$/, ''); const query = config.getOptionalString('query'); diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/index.ts b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts index 4e220395ba..aae8f0cd44 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/index.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export { PuppetDBEntityProvider } from './PuppetDBEntityProvider'; -export type { PuppetDBEntityProviderConfig } from './PuppetDBEntityProviderConfig'; +export { PuppetDbEntityProvider } from './PuppetDbEntityProvider'; +export type { PuppetDbEntityProviderConfig } from './PuppetDbEntityProviderConfig'; export { DEFAULT_PROVIDER_ID, DEFAULT_OWNER } from './constants'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts index 573be365dd..afa18c0ba5 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts @@ -17,7 +17,7 @@ import { readPuppetNodes } from './read'; import { DEFAULT_PROVIDER_ID, - PuppetDBEntityProviderConfig, + PuppetDbEntityProviderConfig, } from '../providers'; import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; import fetch from 'node-fetch'; @@ -37,7 +37,7 @@ describe('readPuppetNodes', () => { const mockFetch = fetch as unknown as jest.Mocked; describe('where no query is specified', () => { - const config: PuppetDBEntityProviderConfig = { + const config: PuppetDbEntityProviderConfig = { host: 'https://puppetdb', id: DEFAULT_PROVIDER_ID, }; @@ -186,7 +186,7 @@ describe('readPuppetNodes', () => { }); describe('where query is specified', () => { - const config: PuppetDBEntityProviderConfig = { + const config: PuppetDbEntityProviderConfig = { host: 'https://puppetdb', id: DEFAULT_PROVIDER_ID, query: '["=", "certname", "node1"]', diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts index edd33bc76a..8ecbb63d91 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { PuppetDBEntityProviderConfig } from '../providers'; +import { PuppetDbEntityProviderConfig } from '../providers'; import { PuppetNode, ResourceTransformer } from './types'; import { ResourceEntity } from '@backstage/catalog-model/'; import { defaultResourceTransformer } from './transformers'; @@ -29,7 +29,7 @@ import { Logger } from 'winston'; * @param opts - Additional options. */ export async function readPuppetNodes( - config: PuppetDBEntityProviderConfig, + config: PuppetDbEntityProviderConfig, opts?: { transformer?: ResourceTransformer; logger?: Logger; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts index 30ae79e87b..e5c5698d99 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { PuppetDBEntityProviderConfig } from '../providers'; +import { PuppetDbEntityProviderConfig } from '../providers'; import { PuppetNode } from './types'; import { defaultResourceTransformer } from './transformers'; import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; @@ -23,7 +23,7 @@ import { ANNOTATION_PUPPET_CERTNAME } from './constants'; describe('defaultResourceTransformer', () => { it('should transform a puppet node to a resource entity', async () => { - const config: PuppetDBEntityProviderConfig = { + const config: PuppetDbEntityProviderConfig = { host: '', id: '', }; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts index 7dc951f49f..f71ba299bd 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts @@ -16,7 +16,7 @@ import { ResourceEntity } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/types'; -import { PuppetDBEntityProviderConfig } from '../providers/PuppetDBEntityProviderConfig'; +import { PuppetDbEntityProviderConfig } from '../providers/PuppetDbEntityProviderConfig'; /** * Customize the ingested Resource entity. @@ -30,7 +30,7 @@ import { PuppetDBEntityProviderConfig } from '../providers/PuppetDBEntityProvide */ export type ResourceTransformer = ( node: PuppetNode, - config: PuppetDBEntityProviderConfig, + config: PuppetDbEntityProviderConfig, ) => Promise; /** From cb9341399a3ab7e61118cfa2b465977bff12392f Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 20:19:56 +0200 Subject: [PATCH 080/236] Generate API Report Signed-off-by: Tomas Dabasinskas --- .../api-report.md | 79 +++++++++++++++++++ .../config.d.ts | 5 +- .../providers/PuppetDbEntityProvider.test.ts | 3 +- .../src/providers/PuppetDbEntityProvider.ts | 2 +- .../src/providers/constants.ts | 7 -- .../src/providers/index.ts | 2 +- .../src/puppet/constants.ts | 7 ++ .../src/puppet/transformers.test.ts | 3 +- .../src/puppet/transformers.ts | 3 +- 9 files changed, 92 insertions(+), 19 deletions(-) create mode 100644 plugins/catalog-backend-module-puppetdb/api-report.md diff --git a/plugins/catalog-backend-module-puppetdb/api-report.md b/plugins/catalog-backend-module-puppetdb/api-report.md new file mode 100644 index 0000000000..798e672f4c --- /dev/null +++ b/plugins/catalog-backend-module-puppetdb/api-report.md @@ -0,0 +1,79 @@ +## API Report File for "@backstage/plugin-catalog-backend-module-puppetdb-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import { EntityProvider } from '@backstage/plugin-catalog-backend'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; +import { JsonValue } from '@backstage/types'; +import { Logger } from 'winston'; +import { PluginTaskScheduler } from '@backstage/backend-tasks'; +import { ResourceEntity } from '@backstage/catalog-model'; +import { TaskRunner } from '@backstage/backend-tasks'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; + +// @public +export const ANNOTATION_PUPPET_CERTNAME = 'puppet.com/certname'; + +// @public +export const DEFAULT_PROVIDER_ID = 'default'; + +// @public +export const defaultResourceTransformer: ResourceTransformer; + +// @public +export class PuppetDbEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + static fromConfig( + config: Config, + deps: { + logger: Logger; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + transformer?: ResourceTransformer; + }, + ): PuppetDbEntityProvider[]; + // (undocumented) + getProviderName(): string; + refresh(logger: Logger): Promise; +} + +// @public +export type PuppetDbEntityProviderConfig = { + id: string; + host: string; + query?: string; + schedule?: TaskScheduleDefinition; +}; + +// @public +export type PuppetFact = { + name: string; + value: JsonValue; +}; + +// @public +export type PuppetFactSet = { + data: PuppetFact[]; + href: string; +}; + +// @public +export type PuppetNode = { + timestamp: string; + certname: string; + hash: string; + producer_timestamp: string; + producer: string; + environment: string; + facts: PuppetFactSet; +}; + +// @public +export type ResourceTransformer = ( + node: PuppetNode, + config: PuppetDbEntityProviderConfig, +) => Promise; +``` diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts index a79438944e..6b6c393d6b 100644 --- a/plugins/catalog-backend-module-puppetdb/config.d.ts +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -14,10 +14,7 @@ * limitations under the License. */ -import { - TaskScheduleDefinition, - TaskScheduleDefinitionConfig, -} from '@backstage/backend-tasks'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; /** * Represents the configuration for the Backstage. diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts index 7ea191190b..b049c16856 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts @@ -20,13 +20,12 @@ import { getVoidLogger } from '@backstage/backend-common'; import { PuppetDbEntityProvider } from './PuppetDbEntityProvider'; import { EntityProviderConnection } from '@backstage/plugin-catalog-backend'; import * as p from '../puppet/read'; -import { DEFAULT_OWNER } from './constants'; import { ANNOTATION_PUPPET_CERTNAME } from '../puppet'; import { ANNOTATION_LOCATION, ANNOTATION_ORIGIN_LOCATION, } from '@backstage/catalog-model/'; -import { ENDPOINT_NODES } from '../puppet/constants'; +import { DEFAULT_OWNER, ENDPOINT_NODES } from '../puppet/constants'; const logger = getVoidLogger(); diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts index e6c19686a3..5246c3373d 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts @@ -158,7 +158,7 @@ export class PuppetDbEntityProvider implements EntityProvider { /** * Refreshes the catalog by reading nodes from PuppetDB and registering them as Resource Entities. * - * @param logger - The instance of a {@link Logger}. + * @param logger - The instance of a Logger. */ async refresh(logger: Logger) { if (!this.connection) { diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts b/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts index 02b333c613..ca8a48b374 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/constants.ts @@ -20,10 +20,3 @@ * @public */ export const DEFAULT_PROVIDER_ID = 'default'; - -/** - * Default owner for entities created by the PuppetDB provider. - * - * @public - */ -export const DEFAULT_OWNER = 'unknown'; diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/index.ts b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts index aae8f0cd44..43d673c30c 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/index.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/index.ts @@ -16,4 +16,4 @@ export { PuppetDbEntityProvider } from './PuppetDbEntityProvider'; export type { PuppetDbEntityProviderConfig } from './PuppetDbEntityProviderConfig'; -export { DEFAULT_PROVIDER_ID, DEFAULT_OWNER } from './constants'; +export { DEFAULT_PROVIDER_ID } from './constants'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts index ef355f22d3..672bc48932 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts @@ -30,3 +30,10 @@ export const ENDPOINT_FACTSETS = '/pdb/query/v4/factsets'; * Path of PuppetDB Nodes endpoint. */ export const ENDPOINT_NODES = '/pdb/query/v4/nodes'; + +/** + * Default owner for entities created by the PuppetDB provider. + * + * @public + */ +export const DEFAULT_OWNER = 'unknown'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts index e5c5698d99..f7d3813bf6 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts @@ -18,8 +18,7 @@ import { PuppetDbEntityProviderConfig } from '../providers'; import { PuppetNode } from './types'; import { defaultResourceTransformer } from './transformers'; import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; -import { DEFAULT_OWNER } from '../providers'; -import { ANNOTATION_PUPPET_CERTNAME } from './constants'; +import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_OWNER } from './constants'; describe('defaultResourceTransformer', () => { it('should transform a puppet node to a resource entity', async () => { diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts index ca0d2a4ccc..ec4c4a2af3 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts @@ -16,8 +16,7 @@ import { ResourceTransformer } from './types'; import { DEFAULT_NAMESPACE, ResourceEntity } from '@backstage/catalog-model'; -import { DEFAULT_OWNER } from '../providers'; -import { ANNOTATION_PUPPET_CERTNAME } from './constants'; +import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_OWNER } from './constants'; /** * A default implementation of the {@link ResourceTransformer}. From a866a0a6b63f4f85cd59c7b18a18dc49314cce32 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 20:32:28 +0200 Subject: [PATCH 081/236] Fix package name Signed-off-by: Tomas Dabasinskas --- plugins/catalog-backend-module-puppetdb/api-report.md | 2 +- plugins/catalog-backend-module-puppetdb/package.json | 2 +- yarn.lock | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/api-report.md b/plugins/catalog-backend-module-puppetdb/api-report.md index 798e672f4c..84227af5d3 100644 --- a/plugins/catalog-backend-module-puppetdb/api-report.md +++ b/plugins/catalog-backend-module-puppetdb/api-report.md @@ -1,4 +1,4 @@ -## API Report File for "@backstage/plugin-catalog-backend-module-puppetdb-backend" +## API Report File for "@backstage/plugin-catalog-backend-module-puppetdb" > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index fa7f08da1a..440c7e48af 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,5 +1,5 @@ { - "name": "@backstage/plugin-catalog-backend-module-puppetdb-backend", + "name": "@backstage/plugin-catalog-backend-module-puppetdb", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "version": "0.0.1", "main": "src/index.ts", diff --git a/yarn.lock b/yarn.lock index eb17085b2d..7b71cdff35 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5206,9 +5206,9 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-backend-module-puppetdb-backend@workspace:plugins/catalog-backend-module-puppetdb": +"@backstage/plugin-catalog-backend-module-puppetdb@workspace:plugins/catalog-backend-module-puppetdb": version: 0.0.0-use.local - resolution: "@backstage/plugin-catalog-backend-module-puppetdb-backend@workspace:plugins/catalog-backend-module-puppetdb" + resolution: "@backstage/plugin-catalog-backend-module-puppetdb@workspace:plugins/catalog-backend-module-puppetdb" dependencies: "@backstage/backend-common": "workspace:^" "@backstage/backend-tasks": "workspace:^" From 3b8f0fbf01f7c52022ac0c41d02997538d38cd78 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 21:14:46 +0200 Subject: [PATCH 082/236] Rename default owner constant Signed-off-by: Tomas Dabasinskas --- plugins/catalog-backend-module-puppetdb/package.json | 2 +- .../src/providers/PuppetDbEntityProvider.test.ts | 10 +++++----- .../src/puppet/constants.ts | 2 +- .../src/puppet/transformers.test.ts | 4 ++-- .../src/puppet/transformers.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 440c7e48af..0ccb2fc24d 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -17,7 +17,7 @@ "repository": { "type": "git", "url": "https://github.com/backstage/backstage", - "directory": "catalog-backend-module-puppetdb" + "directory": "plugins/catalog-backend-module-puppetdb" }, "keywords": [ "backstage", diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts index b049c16856..b72e0b8401 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts @@ -25,7 +25,7 @@ import { ANNOTATION_LOCATION, ANNOTATION_ORIGIN_LOCATION, } from '@backstage/catalog-model/'; -import { DEFAULT_OWNER, ENDPOINT_NODES } from '../puppet/constants'; +import { DEFAULT_ENTITY_OWNER, ENDPOINT_NODES } from '../puppet/constants'; const logger = getVoidLogger(); @@ -110,7 +110,7 @@ describe('PuppetEntityProvider', () => { description: 'Description 1', spec: { type: 'virtual-machine', - owner: DEFAULT_OWNER, + owner: DEFAULT_ENTITY_OWNER, dependsOn: [], dependencyOf: [], }, @@ -129,7 +129,7 @@ describe('PuppetEntityProvider', () => { description: 'Description 2', spec: { type: 'physical-server', - owner: DEFAULT_OWNER, + owner: DEFAULT_ENTITY_OWNER, dependsOn: [], dependencyOf: [], }, @@ -175,7 +175,7 @@ describe('PuppetEntityProvider', () => { description: 'Description 1', spec: { type: 'virtual-machine', - owner: DEFAULT_OWNER, + owner: DEFAULT_ENTITY_OWNER, dependsOn: [], dependencyOf: [], }, @@ -203,7 +203,7 @@ describe('PuppetEntityProvider', () => { description: 'Description 2', spec: { type: 'physical-server', - owner: DEFAULT_OWNER, + owner: DEFAULT_ENTITY_OWNER, dependsOn: [], dependencyOf: [], }, diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts index 672bc48932..bf093182f2 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts @@ -36,4 +36,4 @@ export const ENDPOINT_NODES = '/pdb/query/v4/nodes'; * * @public */ -export const DEFAULT_OWNER = 'unknown'; +export const DEFAULT_ENTITY_OWNER = 'unknown'; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts index f7d3813bf6..15f3bc1419 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.test.ts @@ -18,7 +18,7 @@ import { PuppetDbEntityProviderConfig } from '../providers'; import { PuppetNode } from './types'; import { defaultResourceTransformer } from './transformers'; import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; -import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_OWNER } from './constants'; +import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_ENTITY_OWNER } from './constants'; describe('defaultResourceTransformer', () => { it('should transform a puppet node to a resource entity', async () => { @@ -75,7 +75,7 @@ describe('defaultResourceTransformer', () => { }, spec: { type: 'virtual-machine', - owner: DEFAULT_OWNER, + owner: DEFAULT_ENTITY_OWNER, dependsOn: [], dependencyOf: [], }, diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts index ec4c4a2af3..d99d89b1cf 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts @@ -16,7 +16,7 @@ import { ResourceTransformer } from './types'; import { DEFAULT_NAMESPACE, ResourceEntity } from '@backstage/catalog-model'; -import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_OWNER } from './constants'; +import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_ENTITY_OWNER } from './constants'; /** * A default implementation of the {@link ResourceTransformer}. @@ -54,7 +54,7 @@ export const defaultResourceTransformer: ResourceTransformer = async ( }, spec: { type: type, - owner: DEFAULT_OWNER, + owner: DEFAULT_ENTITY_OWNER, dependsOn: [], dependencyOf: [], }, From d2882d147fc83933b88e2adbac121ea255fffb02 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 22:26:25 +0200 Subject: [PATCH 083/236] Add README Signed-off-by: Tomas Dabasinskas --- .../catalog-backend-module-puppetdb/README.md | 95 +++++++++++++++++-- .../src/puppet/transformers.ts | 2 +- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/README.md b/plugins/catalog-backend-module-puppetdb/README.md index c66627fa8b..9fea95d61e 100644 --- a/plugins/catalog-backend-module-puppetdb/README.md +++ b/plugins/catalog-backend-module-puppetdb/README.md @@ -1,14 +1,91 @@ -# catalog-backend-module-puppetdb +# Catalog Backend Module for Puppet -Welcome to the catalog-backend-module-puppetdb backend plugin! +This is an extension module to the `plugin-catalog-backend` plugin, providing an `PuppetDbEntityProvider` that can be used to ingest +[Resource entities](https://backstage.io/docs/features/software-catalog/descriptor-format#kind-resource) from a +[PuppetDB](https://www.puppet.com/docs/puppet/6/puppetdb_overview.html) instance(s). This provider is useful if you want to import nodes +from your PuppetDB into Backstage. -_This plugin was created through the Backstage CLI_ +## Installation -## Getting started +The provider is not installed by default, therefore you have to add a dependency to `@backstage/plugin-catalog-backend-module-puppetdb` +to your backend package: -Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn -start` in the root directory, and then navigating to [/catalog-backend-module-puppetdb](http://localhost:3000/catalog-backend-module-puppetdb). +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-catalog-backend-module-puppetdb +``` -You can also serve the plugin in isolation by running `yarn start` in the plugin directory. -This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. -It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. +Update the catalog plugin initialization in your backend to add the provider and schedule it: + +```diff ++ import { PuppetDbEntityProvider } from '@backstage/plugin-catalog-backend-module-puppetdb'; + + export default async function createPlugin( + env: PluginEnvironment, + ): Promise { + const builder = await CatalogBuilder.create(env); + ++ builder.addEntityProvider( ++ PuppetDbEntityProvider.fromConfig(env.config, { ++ logger: env.logger, ++ schedule: env.scheduler.createScheduledTaskRunner({ ++ frequency: { minutes: 10 }, ++ timeout: { minutes: 50 }, ++ initialDelay: { seconds: 15} ++ }), ++ }); ++ ); +``` + +After this, you also have to add some configuration in your app-config that describes what you want to import for that target. + +## Configuration + +The following configuration is an example of how a setup could look for importing nodes from an internal PuppetDB instance: + +```yaml +catalog: + providers: + puppet: + default: + # (Required) The host of PuppetDB API instance: + host: https://puppetdb.example.com + + # (Optional) Query to filter PuppetDB nodes: + #query: '["=","certname","example.com"]' +``` + +## Customize the Provider + +The default ingestion behaviour will likely not work for all use cases - you will want to set proper `Owner`, `System` and other fields for the +ingested resources. In case you want to customize the ingested entities, the provider allows to pass a transformer for resources. Here we will show an example +of overriding the default transformer. + +1. Create a transformer: +2. + +```ts +export const customResourceTransformer: ResourceTransformer = async ( + node, + config, +): Promise => { + // Transofrmations may change namespace, owner, change entity naming pattern, add labels, annotations, etc. + + // Create the Resource Entity on your own, or wrap the default transformer + return await defaultResourceTransformer(node, config); +}; +``` + +2. Configure the provider with the transformer: + +```ts +const puppetDbEntityProvider = PuppetDbEntityProvider.fromConfig(env.config, { + logger: env.logger, + schedule: env.scheduler.createScheduledTaskRunner({ + frequency: { minutes: 10 }, + timeout: { minutes: 50 }, + initialDelay: { seconds: 15 }, + }), + transformer: customResourceTransformer, +}); +``` diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts index d99d89b1cf..2062f8e828 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts @@ -31,7 +31,7 @@ import { ANNOTATION_PUPPET_CERTNAME, DEFAULT_ENTITY_OWNER } from './constants'; export const defaultResourceTransformer: ResourceTransformer = async ( node, _config, -) => { +): Promise => { const certName = node.certname.toLowerCase(); const type = node.facts?.data?.find(e => e.name === 'is_virtual')?.value ? 'virtual-machine' From 87263449c6a314c8478199c6da636d4ccbf2799d Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 22:34:06 +0200 Subject: [PATCH 084/236] Update plugins list in MicroSite Signed-off-by: Tomas Dabasinskas --- .../plugins/catalog-backend-module-puppetdb.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 microsite/data/plugins/catalog-backend-module-puppetdb.yaml diff --git a/microsite/data/plugins/catalog-backend-module-puppetdb.yaml b/microsite/data/plugins/catalog-backend-module-puppetdb.yaml new file mode 100644 index 0000000000..3eac117e19 --- /dev/null +++ b/microsite/data/plugins/catalog-backend-module-puppetdb.yaml @@ -0,0 +1,13 @@ +--- +title: PuppetDB Entity Provider +author: TDabasinskas +authorUrl: https://github.com/tdabasinskas +category: Configuration Management +description: Import nodes from PuppetDB into Backstage as Resource Entities +documentation: https://github.com/backstage/backstage/blob/master/plugins/catalog-backend-module-puppetdb/README.md +iconUrl: https://avatars.githubusercontent.com/u/234268?s=200&v=4 +npmPackageName: '@backstage/plugin-catalog-backend-module-puppetdb' +tags: + - puppet + - puppetdb +addedDate: '2023-02-06' From a1efcf9a6581385f7dae344d06d0416991686551 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 22:41:03 +0200 Subject: [PATCH 085/236] Add changeset Signed-off-by: Tomas Dabasinskas --- .changeset/fifty-beds-dress.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fifty-beds-dress.md diff --git a/.changeset/fifty-beds-dress.md b/.changeset/fifty-beds-dress.md new file mode 100644 index 0000000000..19049732cc --- /dev/null +++ b/.changeset/fifty-beds-dress.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-puppetdb': minor +--- + +Initial version of the plugin. From e3048fc810f0b96af93eb0317aa91c438b922572 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 23:02:05 +0200 Subject: [PATCH 086/236] Update configuration properties Signed-off-by: Tomas Dabasinskas --- plugins/catalog-backend-module-puppetdb/README.md | 2 +- plugins/catalog-backend-module-puppetdb/config.d.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/README.md b/plugins/catalog-backend-module-puppetdb/README.md index 9fea95d61e..3610c3e0fd 100644 --- a/plugins/catalog-backend-module-puppetdb/README.md +++ b/plugins/catalog-backend-module-puppetdb/README.md @@ -46,7 +46,7 @@ The following configuration is an example of how a setup could look for importin ```yaml catalog: providers: - puppet: + puppetdb: default: # (Required) The host of PuppetDB API instance: host: https://puppetdb.example.com diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts index 6b6c393d6b..dc48695f04 100644 --- a/plugins/catalog-backend-module-puppetdb/config.d.ts +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -29,9 +29,9 @@ export interface Config { */ providers?: { /** - * Puppet entity provider configuration. Uses "default" as default ID for the single config variant. + * PuppetDB Entity Provider configuration. Uses "default" as default ID for the single config variant. */ - puppet?: ProviderConfig | Record; + puppetdb?: ProviderConfig | Record; }; }; } From 29f496d2d7d3c7e1a10a739c87a0e962ff689be7 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Tue, 7 Feb 2023 07:58:41 +0200 Subject: [PATCH 087/236] Fix tests Signed-off-by: Tomas Dabasinskas --- .../src/puppet/read.test.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts index afa18c0ba5..8dcbff1a4c 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts @@ -23,15 +23,9 @@ import { DEFAULT_NAMESPACE } from '@backstage/catalog-model'; import fetch from 'node-fetch'; import { ANNOTATION_PUPPET_CERTNAME, ENDPOINT_FACTSETS } from './constants'; -jest.mock('node-fetch', () => { - const original = jest.requireActual('node-fetch'); - return { - __esModule: true, - default: jest.fn(), - Headers: original.Headers, - }; -}); +jest.mock('node-fetch'); (global as any).fetch = fetch; +const { Response } = jest.requireActual('node-fetch'); describe('readPuppetNodes', () => { const mockFetch = fetch as unknown as jest.Mocked; From 968a62954f5ef3653a61bfeff268730b78497913 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Mon, 27 Feb 2023 07:55:04 +0200 Subject: [PATCH 088/236] Update plugins/catalog-backend-module-puppetdb/README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Tomas Dabasinskas --- plugins/catalog-backend-module-puppetdb/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-puppetdb/README.md b/plugins/catalog-backend-module-puppetdb/README.md index 3610c3e0fd..3cc2783561 100644 --- a/plugins/catalog-backend-module-puppetdb/README.md +++ b/plugins/catalog-backend-module-puppetdb/README.md @@ -69,7 +69,7 @@ export const customResourceTransformer: ResourceTransformer = async ( node, config, ): Promise => { - // Transofrmations may change namespace, owner, change entity naming pattern, add labels, annotations, etc. + // Transformations may change namespace, owner, change entity naming pattern, add labels, annotations, etc. // Create the Resource Entity on your own, or wrap the default transformer return await defaultResourceTransformer(node, config); From 0b05465b5e714d3ca88a66826bcfc579d3025610 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Mon, 27 Feb 2023 07:57:44 +0200 Subject: [PATCH 089/236] Update README Signed-off-by: Tomas Dabasinskas --- plugins/catalog-backend-module-puppetdb/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/catalog-backend-module-puppetdb/README.md b/plugins/catalog-backend-module-puppetdb/README.md index 3cc2783561..eeb363bd32 100644 --- a/plugins/catalog-backend-module-puppetdb/README.md +++ b/plugins/catalog-backend-module-puppetdb/README.md @@ -62,7 +62,6 @@ ingested resources. In case you want to customize the ingested entities, the pro of overriding the default transformer. 1. Create a transformer: -2. ```ts export const customResourceTransformer: ResourceTransformer = async ( From c4bdec7d49277f15aa75f5362a51dc058fc8fc1f Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Mon, 27 Feb 2023 08:34:29 +0200 Subject: [PATCH 090/236] Address review Signed-off-by: Tomas Dabasinskas --- .../config.d.ts | 51 ++++++++++++------- .../src/providers/PuppetDbEntityProvider.ts | 7 ++- .../src/puppet/read.ts | 4 +- .../src/puppet/transformers.ts | 8 ++- .../src/puppet/types.ts | 2 +- yarn.lock | 34 +++++++++++++ 6 files changed, 74 insertions(+), 32 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/config.d.ts b/plugins/catalog-backend-module-puppetdb/config.d.ts index dc48695f04..e890584f41 100644 --- a/plugins/catalog-backend-module-puppetdb/config.d.ts +++ b/plugins/catalog-backend-module-puppetdb/config.d.ts @@ -31,25 +31,38 @@ export interface Config { /** * PuppetDB Entity Provider configuration. Uses "default" as default ID for the single config variant. */ - puppetdb?: ProviderConfig | Record; + puppetdb?: + | { + /** + * (Required) The host of PuppetDB API instance. + */ + host: string; + /** + * (Optional) PQL query to filter PuppetDB nodes. + */ + query?: string; + /** + * (Optional) Task schedule definition for the refresh. + */ + schedule?: TaskScheduleDefinition; + } + | Record< + string, + { + /** + * (Required) The host of PuppetDB API instance. + */ + host: string; + /** + * (Optional) PQL query to filter PuppetDB nodes. + */ + query?: string; + /** + * (Optional) Task schedule definition for the refresh. + */ + schedule?: TaskScheduleDefinition; + } + >; }; }; } - -/** - * Configuration of {@link PuppetDbEntityProvider}. - */ -interface ProviderConfig { - /** - * (Required) The host of PuppetDB API instance. - */ - host: string; - /** - * (Optional) PQL query to filter PuppetDB nodes. - */ - query?: string; - /** - * (Optional) Task schedule definition for the refresh. - */ - schedule?: TaskScheduleDefinition; -} diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts index 5246c3373d..a3ac4b140e 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.ts @@ -192,15 +192,14 @@ export class PuppetDbEntityProvider implements EntityProvider { * @returns Entity with @{@link ANNOTATION_LOCATION} and @{@link ANNOTATION_ORIGIN_LOCATION} annotations. */ function withLocations(host: string, entity: Entity): Entity { - const location = new URL(host); - location.pathname = `${ENDPOINT_NODES}/${entity.metadata?.name}`; + const location = `${host}/${ENDPOINT_NODES}/${entity.metadata?.name}`; return merge( { metadata: { annotations: { - [ANNOTATION_LOCATION]: `url:${location.toString()}`, - [ANNOTATION_ORIGIN_LOCATION]: `url:${location.toString()}`, + [ANNOTATION_LOCATION]: `url:${location}`, + [ANNOTATION_ORIGIN_LOCATION]: `url:${location}`, }, }, }, diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts index 8ecbb63d91..bbd7843ecc 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts @@ -44,9 +44,7 @@ export async function readPuppetNodes( } if (opts?.logger) { - opts.logger - .child({ url: url.toString() }) - .debug('Reading nodes from PuppetDB'); + opts.logger.debug('Reading nodes from PuppetDB', { url: url.toString() }); } const response = await fetch(url.toString(), { diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts index 2062f8e828..9ec607b83e 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/transformers.ts @@ -32,13 +32,13 @@ export const defaultResourceTransformer: ResourceTransformer = async ( node, _config, ): Promise => { - const certName = node.certname.toLowerCase(); + const certName = node.certname.toLocaleLowerCase('en-US'); const type = node.facts?.data?.find(e => e.name === 'is_virtual')?.value ? 'virtual-machine' : 'physical-server'; const kernel = node.facts?.data?.find(e => e.name === 'kernel')?.value; - const entity: ResourceEntity = { + return { apiVersion: 'backstage.io/v1beta1', kind: 'Resource', metadata: { @@ -50,7 +50,7 @@ export const defaultResourceTransformer: ResourceTransformer = async ( description: node.facts?.data ?.find(e => e.name === 'ipaddress') ?.value?.toString(), - tags: kernel ? [kernel.toString().toLowerCase()] : [], + tags: kernel ? [kernel.toString().toLocaleLowerCase('en-US')] : [], }, spec: { type: type, @@ -59,6 +59,4 @@ export const defaultResourceTransformer: ResourceTransformer = async ( dependencyOf: [], }, }; - - return entity; }; diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts index f71ba299bd..5bfac5c998 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/types.ts @@ -96,7 +96,7 @@ export type PuppetFact = { */ name: string; /** - * The value of the fact, in JSON format. + * The value of the fact. */ value: JsonValue; }; diff --git a/yarn.lock b/yarn.lock index 7b71cdff35..2b88e6ce8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30043,6 +30043,40 @@ __metadata: languageName: node linkType: hard +"msw@npm:^0.49.0": + version: 0.49.3 + resolution: "msw@npm:0.49.3" + dependencies: + "@mswjs/cookies": ^0.2.2 + "@mswjs/interceptors": ^0.17.5 + "@open-draft/until": ^1.0.3 + "@types/cookie": ^0.4.1 + "@types/js-levenshtein": ^1.1.1 + chalk: 4.1.1 + chokidar: ^3.4.2 + cookie: ^0.4.2 + graphql: ^15.0.0 || ^16.0.0 + headers-polyfill: ^3.1.0 + inquirer: ^8.2.0 + is-node-process: ^1.0.1 + js-levenshtein: ^1.1.6 + node-fetch: ^2.6.7 + outvariant: ^1.3.0 + path-to-regexp: ^6.2.0 + strict-event-emitter: ^0.4.3 + type-fest: ^2.19.0 + yargs: ^17.3.1 + peerDependencies: + typescript: ">= 4.4.x <= 4.9.x" + peerDependenciesMeta: + typescript: + optional: true + bin: + msw: cli/index.js + checksum: 8322cd42cd69f289c05517d02bde22fc2f10e86fc2d0d209d9df54bd03d10b8123723c5587a2654dcd2cd0f314a016f9eccac88cffa30fafd1f9fead16db639e + languageName: node + linkType: hard + "msw@npm:^1.0.0, msw@npm:^1.0.1": version: 1.0.1 resolution: "msw@npm:1.0.1" From b9838936379f9d6b6e3c7e6ccc3ca7980267c53d Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Mon, 27 Feb 2023 09:11:44 +0200 Subject: [PATCH 091/236] Fix tests Signed-off-by: Tomas Dabasinskas --- .../src/providers/PuppetDbEntityProvider.test.ts | 8 ++++---- .../src/puppet/constants.ts | 4 ++-- .../src/puppet/read.test.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts index b72e0b8401..e9dc5e0fae 100644 --- a/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/providers/PuppetDbEntityProvider.test.ts @@ -166,10 +166,10 @@ describe('PuppetEntityProvider', () => { [ANNOTATION_PUPPET_CERTNAME]: 'node1', [ANNOTATION_LOCATION]: `url:${config.getString( 'catalog.providers.puppetdb.host', - )}${ENDPOINT_NODES}/node1`, + )}/${ENDPOINT_NODES}/node1`, [ANNOTATION_ORIGIN_LOCATION]: `url:${config.getString( 'catalog.providers.puppetdb.host', - )}${ENDPOINT_NODES}/node1`, + )}/${ENDPOINT_NODES}/node1`, }, tags: ['windows'], description: 'Description 1', @@ -194,10 +194,10 @@ describe('PuppetEntityProvider', () => { [ANNOTATION_PUPPET_CERTNAME]: 'node2', [ANNOTATION_LOCATION]: `url:${config.getString( 'catalog.providers.puppetdb.host', - )}${ENDPOINT_NODES}/node2`, + )}/${ENDPOINT_NODES}/node2`, [ANNOTATION_ORIGIN_LOCATION]: `url:${config.getString( 'catalog.providers.puppetdb.host', - )}${ENDPOINT_NODES}/node2`, + )}/${ENDPOINT_NODES}/node2`, }, tags: ['linux'], description: 'Description 2', diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts index bf093182f2..730f21ec16 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/constants.ts @@ -24,12 +24,12 @@ export const ANNOTATION_PUPPET_CERTNAME = 'puppet.com/certname'; /** * Path of PuppetDB FactSets endpoint. */ -export const ENDPOINT_FACTSETS = '/pdb/query/v4/factsets'; +export const ENDPOINT_FACTSETS = 'pdb/query/v4/factsets'; /** * Path of PuppetDB Nodes endpoint. */ -export const ENDPOINT_NODES = '/pdb/query/v4/nodes'; +export const ENDPOINT_NODES = 'pdb/query/v4/nodes'; /** * Default owner for entities created by the PuppetDB provider. diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts index 8dcbff1a4c..e0bf5c6d43 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.test.ts @@ -222,7 +222,7 @@ describe('readPuppetNodes', () => { it('should return matched results', async () => { const entities = await readPuppetNodes(config); expect(mockFetch).toHaveBeenCalledWith( - `${config.host}${ENDPOINT_FACTSETS}?query=%5B%22%3D%22%2C+%22certname%22%2C+%22node1%22%5D`, + `${config.host}/${ENDPOINT_FACTSETS}?query=%5B%22%3D%22%2C+%22certname%22%2C+%22node1%22%5D`, { headers: { Accept: 'application/json', From 238cf657c0949bcc949cf9ecd677d1dd99cf8791 Mon Sep 17 00:00:00 2001 From: Antonio Musolino Date: Fri, 17 Feb 2023 08:33:42 +0100 Subject: [PATCH 092/236] fix(techdocs): copy to clipboard works in usecure context Signed-off-by: Antonio Musolino --- .changeset/spotty-turtles-reply.md | 5 +++++ .../reader/transformers/copyToClipboard.test.ts | 16 +++++++++++++++- .../src/reader/transformers/copyToClipboard.tsx | 6 ++++-- 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 .changeset/spotty-turtles-reply.md diff --git a/.changeset/spotty-turtles-reply.md b/.changeset/spotty-turtles-reply.md new file mode 100644 index 0000000000..2dfc12a176 --- /dev/null +++ b/.changeset/spotty-turtles-reply.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Copy to clipboard now works in a not secure context. diff --git a/plugins/techdocs/src/reader/transformers/copyToClipboard.test.ts b/plugins/techdocs/src/reader/transformers/copyToClipboard.test.ts index 9c4ee08346..cac7f76a0e 100644 --- a/plugins/techdocs/src/reader/transformers/copyToClipboard.test.ts +++ b/plugins/techdocs/src/reader/transformers/copyToClipboard.test.ts @@ -18,6 +18,7 @@ import { createTestShadowDom } from '../../test-utils'; import { copyToClipboard } from './copyToClipboard'; import { lightTheme } from '@backstage/theme'; import { waitFor } from '@testing-library/react'; +import useCopyToClipboard from 'react-use/lib/useCopyToClipboard'; const clipboardSpy = jest.fn(); Object.defineProperty(window.navigator, 'clipboard', { @@ -26,8 +27,21 @@ Object.defineProperty(window.navigator, 'clipboard', { }, }); +jest.mock('react-use/lib/useCopyToClipboard', () => { + const original = jest.requireActual('react-use/lib/useCopyToClipboard'); + + return { + __esModule: true, + default: jest.fn().mockImplementation(original.default), + }; +}); + describe('copyToClipboard', () => { it('calls navigator.clipboard.writeText when clipboard button has been clicked', async () => { + const spy = useCopyToClipboard as jest.Mock; + const copy = jest.fn(); + spy.mockReturnValue([{}, copy]); + const expectedClipboard = 'function foo() {return "bar";}'; const shadowDom = await createTestShadowDom( ` @@ -51,7 +65,7 @@ describe('copyToClipboard', () => { expect(tooltip).toHaveTextContent('Copied to clipboard'); }); - expect(clipboardSpy).toHaveBeenCalledWith(expectedClipboard); + expect(copy).toHaveBeenCalledWith(expectedClipboard); }); it('only gets applied to code blocks', async () => { diff --git a/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx b/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx index a90c4ca116..422bbb10ce 100644 --- a/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx +++ b/plugins/techdocs/src/reader/transformers/copyToClipboard.tsx @@ -25,6 +25,7 @@ import { } from '@material-ui/core'; import Button from '@material-ui/core/Button'; import type { Transformer } from './transformer'; +import useCopyToClipboard from 'react-use/lib/useCopyToClipboard'; const CopyToClipboardTooltip = withStyles(theme => ({ tooltip: { @@ -49,11 +50,12 @@ type CopyToClipboardButtonProps = { const CopyToClipboardButton = ({ text }: CopyToClipboardButtonProps) => { const [open, setOpen] = useState(false); + const [, copyToClipboard] = useCopyToClipboard(); const handleClick = useCallback(() => { - window.navigator.clipboard.writeText(text); + copyToClipboard(text); setOpen(true); - }, [text]); + }, [text, copyToClipboard]); const handleClose = useCallback(() => { setOpen(false); From 2128963db1dd4986c87902ca6fbc38dbcf4510d1 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Mon, 27 Feb 2023 09:48:06 +0200 Subject: [PATCH 093/236] Improve nodes URL initialization Signed-off-by: Tomas Dabasinskas --- plugins/catalog-backend-module-puppetdb/src/puppet/read.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts index bbd7843ecc..67247d73a5 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts @@ -36,8 +36,7 @@ export async function readPuppetNodes( }, ): Promise { const transformFn = opts?.transformer ?? defaultResourceTransformer; - const url = new URL(config.host); - url.pathname = ENDPOINT_FACTSETS; + const url = new URL(ENDPOINT_FACTSETS, config.host); if (config.query) { url.searchParams.set('query', config.query); From 3c38b7cd4d18a4f5711ad716188e1e10d724f47a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 27 Feb 2023 10:07:05 +0100 Subject: [PATCH 094/236] Update link to docs Signed-off-by: Johan Haals --- docs/openapi/definitions/auth.yaml | 2 +- packages/backend-app-api/README.md | 2 +- packages/backend-common/README.md | 2 +- packages/backend-defaults/README.md | 2 +- packages/backend-dev-utils/README.md | 2 +- packages/backend-plugin-api/README.md | 2 +- packages/backend-tasks/README.md | 2 +- packages/backend-test-utils/README.md | 2 +- packages/backend/README.md | 2 +- packages/cli-common/README.md | 2 +- packages/cli/README.md | 2 +- packages/codemods/README.md | 2 +- packages/config-loader/README.md | 2 +- packages/config/README.md | 2 +- .../create-app/templates/default-app/packages/backend/README.md | 2 +- packages/dev-utils/README.md | 2 +- packages/e2e-test/README.md | 2 +- packages/test-utils/README.md | 2 +- packages/theme/README.md | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/openapi/definitions/auth.yaml b/docs/openapi/definitions/auth.yaml index 863544c71a..1ed649da9c 100644 --- a/docs/openapi/definitions/auth.yaml +++ b/docs/openapi/definitions/auth.yaml @@ -19,7 +19,7 @@ info: version: 0.1.1-alpha.8 externalDocs: description: Backstage official documentation - url: https://github.com/backstage/backstage/blob/master/docs/README.md + url: https://backstage.io/docs servers: - url: http://localhost:7007/api/auth/ tags: diff --git a/packages/backend-app-api/README.md b/packages/backend-app-api/README.md index 44705cde52..e5b80abeaf 100644 --- a/packages/backend-app-api/README.md +++ b/packages/backend-app-api/README.md @@ -16,5 +16,5 @@ yarn add --cwd packages/backend @backstage/backend-app-api ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) - [Backstage Backend System](https://backstage.io/docs/backend-system/) diff --git a/packages/backend-common/README.md b/packages/backend-common/README.md index a1ebf18841..578fbc0830 100644 --- a/packages/backend-common/README.md +++ b/packages/backend-common/README.md @@ -36,4 +36,4 @@ app.listen(PORT, () => { ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/backend-defaults/README.md b/packages/backend-defaults/README.md index 826994da23..2dcc5766df 100644 --- a/packages/backend-defaults/README.md +++ b/packages/backend-defaults/README.md @@ -16,5 +16,5 @@ yarn add --cwd packages/backend @backstage/backend-defaults ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) - [Backstage Backend System](https://backstage.io/docs/backend-system/) diff --git a/packages/backend-dev-utils/README.md b/packages/backend-dev-utils/README.md index c60f5a4ad7..cebe591d50 100644 --- a/packages/backend-dev-utils/README.md +++ b/packages/backend-dev-utils/README.md @@ -16,5 +16,5 @@ yarn add --cwd plugins/-backend @backstage/backend-dev-utils ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) - [Backstage Backend System](https://backstage.io/docs/backend-system/) diff --git a/packages/backend-plugin-api/README.md b/packages/backend-plugin-api/README.md index 02b19a0c40..a17c8715a7 100644 --- a/packages/backend-plugin-api/README.md +++ b/packages/backend-plugin-api/README.md @@ -16,5 +16,5 @@ yarn add --cwd plugins/-backend @backstage/backend-plugin-api ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) - [Backstage Backend System](https://backstage.io/docs/backend-system/) diff --git a/packages/backend-tasks/README.md b/packages/backend-tasks/README.md index b3c304eb34..afaca06e55 100644 --- a/packages/backend-tasks/README.md +++ b/packages/backend-tasks/README.md @@ -35,4 +35,4 @@ When working with the `@backstage/backend-tasks` library you may run into your t ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/backend-test-utils/README.md b/packages/backend-test-utils/README.md index 7ccf4e2798..d0f52388bb 100644 --- a/packages/backend-test-utils/README.md +++ b/packages/backend-test-utils/README.md @@ -15,4 +15,4 @@ yarn add --dev @backstage/backend-test-utils ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/backend/README.md b/packages/backend/README.md index e4fd81749d..f86d09842a 100644 --- a/packages/backend/README.md +++ b/packages/backend/README.md @@ -58,4 +58,4 @@ Read more about the [auth-backend](https://github.com/backstage/backstage/blob/m ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/cli-common/README.md b/packages/cli-common/README.md index 91b774145e..e16398e81a 100644 --- a/packages/cli-common/README.md +++ b/packages/cli-common/README.md @@ -9,4 +9,4 @@ Do not install this package directly, it is an internal package used by [@backst ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/cli/README.md b/packages/cli/README.md index 1aa4233032..2b0db93cea 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -25,4 +25,4 @@ To try out the command locally, you can execute the following from the parent di ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/codemods/README.md b/packages/codemods/README.md index c40e9a8dcc..791612c06b 100644 --- a/packages/codemods/README.md +++ b/packages/codemods/README.md @@ -35,4 +35,4 @@ npx jscodeshift --parser=tsx --extensions=tsx,js,ts,tsx --transform=node_modules ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/config-loader/README.md b/packages/config-loader/README.md index 261394a6a5..ccb792404d 100644 --- a/packages/config-loader/README.md +++ b/packages/config-loader/README.md @@ -9,4 +9,4 @@ Do not install this package directly, it is an internal package used by [@backst ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/config/README.md b/packages/config/README.md index 9866b1bce9..4c130ef6a3 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -9,4 +9,4 @@ Do not install this package directly, it is an internal package used by [@backst ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/create-app/templates/default-app/packages/backend/README.md b/packages/create-app/templates/default-app/packages/backend/README.md index aa042db030..867487ba0a 100644 --- a/packages/create-app/templates/default-app/packages/backend/README.md +++ b/packages/create-app/templates/default-app/packages/backend/README.md @@ -56,4 +56,4 @@ and ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/dev-utils/README.md b/packages/dev-utils/README.md index 78fa3bf7f6..9f71ef1b03 100644 --- a/packages/dev-utils/README.md +++ b/packages/dev-utils/README.md @@ -16,4 +16,4 @@ yarn add -D @backstage/dev-utils ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/e2e-test/README.md b/packages/e2e-test/README.md index f3692de2a7..19f37a600e 100644 --- a/packages/e2e-test/README.md +++ b/packages/e2e-test/README.md @@ -21,4 +21,4 @@ If you make changes to other packages you will need to rerun `yarn tsc && yarn b ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/test-utils/README.md b/packages/test-utils/README.md index d32ebcaf02..74e6d823d3 100644 --- a/packages/test-utils/README.md +++ b/packages/test-utils/README.md @@ -14,4 +14,4 @@ yarn add -D @backstage/test-utils ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) diff --git a/packages/theme/README.md b/packages/theme/README.md index 0dc4059745..f50ed1cea6 100644 --- a/packages/theme/README.md +++ b/packages/theme/README.md @@ -14,4 +14,4 @@ yarn add @backstage/theme ## Documentation - [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) -- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) +- [Backstage Documentation](https://backstage.io/docs) From 482dae5de1c10cb86fb45b0895a41f159a8f9402 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 27 Feb 2023 10:21:28 +0100 Subject: [PATCH 095/236] Add changeset Signed-off-by: Johan Haals --- .changeset/twelve-cars-push.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .changeset/twelve-cars-push.md diff --git a/.changeset/twelve-cars-push.md b/.changeset/twelve-cars-push.md new file mode 100644 index 0000000000..632688c933 --- /dev/null +++ b/.changeset/twelve-cars-push.md @@ -0,0 +1,19 @@ +--- +'@backstage/backend-plugin-api': patch +'@backstage/backend-test-utils': patch +'@backstage/backend-dev-utils': patch +'@backstage/backend-defaults': patch +'@backstage/backend-app-api': patch +'@backstage/backend-common': patch +'@backstage/backend-tasks': patch +'@backstage/config-loader': patch +'@backstage/cli-common': patch +'@backstage/create-app': patch +'@backstage/test-utils': patch +'@backstage/dev-utils': patch +'@backstage/codemods': patch +'@backstage/config': patch +'@backstage/theme': patch +--- + +Updated link to docs. From c47a5ede5ca3e9c92376576c6f8e9c96aa9f1a3d Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Mon, 27 Feb 2023 11:04:20 +0100 Subject: [PATCH 096/236] add GitlabOrgDiscoveryEntityProvider documentation Signed-off-by: Matteo Silvestri --- docs/integrations/gitlab/org.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/integrations/gitlab/org.md b/docs/integrations/gitlab/org.md index 2ab72b4bd6..83dfd0f77d 100644 --- a/docs/integrations/gitlab/org.md +++ b/docs/integrations/gitlab/org.md @@ -31,4 +31,10 @@ catalog: yourProviderId: host: gitlab.com orgEnabled: true + group: org/teams # Optional. Must not end with slash. Accepts only groups under the provided path (excluded) + groupPattern: '[\s\S]*' # Optional. Filters found groups based on provided patter. Defaults to `[\s\S]*`, which means to not filter anything ``` + +When the `group` parameter is provided, the corresponding path prefix will be stripped out from each matching group +when computing the unique entity name. e.g. If `group` is `org/teams`, the name for `org/teams/avengers/gotg` will +be `avengers-gotg`. From 09d68599aa7502637a771c9c0893c56a6f80eb25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 27 Feb 2023 11:24:16 +0100 Subject: [PATCH 097/236] use absolute img path for plugins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- microsite/data/plugins/adr.yaml | 2 +- microsite/data/plugins/announcements.yaml | 2 +- microsite/data/plugins/apollo-explorer.yaml | 2 +- microsite/data/plugins/azure-pipelines.yaml | 2 +- microsite/data/plugins/azure-spring-apps.yml | 2 +- microsite/data/plugins/backstage-analytics-module-ga.yaml | 2 +- microsite/data/plugins/backstage-kubernetes.yaml | 2 +- microsite/data/plugins/backstage-search-platform.yaml | 2 +- microsite/data/plugins/backstage-software-catalog.yaml | 2 +- microsite/data/plugins/backstage-software-templates.yaml | 2 +- microsite/data/plugins/backstage-techdocs.yaml | 2 +- microsite/data/plugins/badges.yaml | 2 +- microsite/data/plugins/bazaar.yaml | 2 +- microsite/data/plugins/catalog-graph.yaml | 2 +- microsite/data/plugins/cicd-statistics.yaml | 2 +- microsite/data/plugins/circleci.yaml | 2 +- microsite/data/plugins/codescene.yaml | 2 +- microsite/data/plugins/cortex.yaml | 2 +- microsite/data/plugins/cost-insights.yaml | 2 +- microsite/data/plugins/dora-metrics.yaml | 2 +- microsite/data/plugins/dynatrace.yaml | 2 +- microsite/data/plugins/entity-feedback.yaml | 2 +- microsite/data/plugins/git-release-manager.yaml | 2 +- microsite/data/plugins/github-pull-requests-board.yaml | 2 +- microsite/data/plugins/home.yaml | 2 +- microsite/data/plugins/humanitec.yaml | 4 ++-- microsite/data/plugins/octopus-deploy.yaml | 2 +- microsite/data/plugins/playlist.yaml | 2 +- microsite/data/plugins/qeta.yaml | 2 +- microsite/data/plugins/s3-viewer.yaml | 2 +- microsite/data/plugins/scaffolder-backend-dotnet.yaml | 2 +- microsite/data/plugins/scaffolder-backend-module-rails.yaml | 2 +- .../data/plugins/scaffolder-backend-roadie-http-request.yaml | 2 +- microsite/data/plugins/scaffolder-backend-roadie-utils.yaml | 2 +- microsite/data/plugins/score-card.yaml | 2 +- microsite/data/plugins/shortcuts.yaml | 2 +- microsite/data/plugins/sonarqube.yaml | 2 +- microsite/data/plugins/splunk-on-call.yaml | 2 +- microsite/data/plugins/stack-overflow.yaml | 2 +- microsite/data/plugins/stackstorm.yaml | 2 +- microsite/data/plugins/toolbox.yaml | 2 +- microsite/data/plugins/vault.yaml | 2 +- microsite/data/plugins/xcmetrics.yaml | 2 +- 43 files changed, 44 insertions(+), 44 deletions(-) diff --git a/microsite/data/plugins/adr.yaml b/microsite/data/plugins/adr.yaml index fabb26e685..e6b7b0cccb 100644 --- a/microsite/data/plugins/adr.yaml +++ b/microsite/data/plugins/adr.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/kuangp category: Discovery description: Browse your project's ADRs. documentation: https://github.com/backstage/backstage/tree/master/plugins/adr -iconUrl: img/adr-logo.png +iconUrl: /img/adr-logo.png npmPackageName: '@backstage/plugin-adr' addedDate: '2022-04-13' diff --git a/microsite/data/plugins/announcements.yaml b/microsite/data/plugins/announcements.yaml index de80dcf6fe..d03c1a1bd7 100644 --- a/microsite/data/plugins/announcements.yaml +++ b/microsite/data/plugins/announcements.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/K-Phoen category: Discovery description: Write and share announcements within Backstage. documentation: https://github.com/K-Phoen/backstage-plugin-announcements/ -iconUrl: img/plugin-announcements-logo.png +iconUrl: /img/plugin-announcements-logo.png npmPackageName: '@k-phoen/backstage-plugin-announcements' addedDate: '2022-11-09' diff --git a/microsite/data/plugins/apollo-explorer.yaml b/microsite/data/plugins/apollo-explorer.yaml index 1a95eaa035..98008e3fc0 100644 --- a/microsite/data/plugins/apollo-explorer.yaml +++ b/microsite/data/plugins/apollo-explorer.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/unredundant category: Debugging description: Integrates Apollo Explorer graphs as a tool to browse GraphQL API endpoints inside Backstage. documentation: https://github.com/backstage/backstage/blob/master/plugins/apollo-explorer/README.md -iconUrl: img/apollo-explorer.png +iconUrl: /img/apollo-explorer.png npmPackageName: '@backstage/plugin-apollo-explorer' addedDate: '2022-07-20' diff --git a/microsite/data/plugins/azure-pipelines.yaml b/microsite/data/plugins/azure-pipelines.yaml index d4caa90507..5b719a4248 100644 --- a/microsite/data/plugins/azure-pipelines.yaml +++ b/microsite/data/plugins/azure-pipelines.yaml @@ -5,6 +5,6 @@ authorUrl: https://keyloop.com/ category: CI/CD description: Easily view your Azure Pipelines within the Software Catalog documentation: https://github.com/backstage/backstage/blob/master/plugins/azure-devops/README.md -iconUrl: img/azure-pipelines.svg +iconUrl: /img/azure-pipelines.svg npmPackageName: '@backstage/plugin-azure-devops' addedDate: '2021-12-22' diff --git a/microsite/data/plugins/azure-spring-apps.yml b/microsite/data/plugins/azure-spring-apps.yml index 5b41885ac4..e0b77a42c8 100644 --- a/microsite/data/plugins/azure-spring-apps.yml +++ b/microsite/data/plugins/azure-spring-apps.yml @@ -5,6 +5,6 @@ authorUrl: https://enfuse.io/ category: Discovery description: Easily view your Azure Spring Apps service resources documentation: https://github.com/enfuse/asae-backstage-plugin/blob/main/README.md -iconUrl: img/enfuse.png +iconUrl: /img/enfuse.png npmPackageName: '@enfuse/plugin-azure-spring-apps' addedDate: '2022-11-21' diff --git a/microsite/data/plugins/backstage-analytics-module-ga.yaml b/microsite/data/plugins/backstage-analytics-module-ga.yaml index 051345d43b..d58ca322eb 100644 --- a/microsite/data/plugins/backstage-analytics-module-ga.yaml +++ b/microsite/data/plugins/backstage-analytics-module-ga.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/spotify category: Analytics description: Track usage of your Backstage instance using Google Analytics. documentation: https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md -iconUrl: img/ga-icon.png +iconUrl: /img/ga-icon.png npmPackageName: '@backstage/plugin-analytics-module-ga' addedDate: '2021-10-07' diff --git a/microsite/data/plugins/backstage-kubernetes.yaml b/microsite/data/plugins/backstage-kubernetes.yaml index 603211eb21..f9514880b0 100644 --- a/microsite/data/plugins/backstage-kubernetes.yaml +++ b/microsite/data/plugins/backstage-kubernetes.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: Core Feature description: Monitor all your service's deployments at a glance, even across clusters. documentation: https://backstage.io/docs/features/kubernetes/ -iconUrl: img/backstage-k8s.svg +iconUrl: /img/backstage-k8s.svg npmPackageName: '@backstage/plugin-kubernetes' order: 4 addedDate: '2021-03-29' diff --git a/microsite/data/plugins/backstage-search-platform.yaml b/microsite/data/plugins/backstage-search-platform.yaml index 1488b8a283..fa069d8875 100644 --- a/microsite/data/plugins/backstage-search-platform.yaml +++ b/microsite/data/plugins/backstage-search-platform.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: Core Feature description: A composable and extensible search platform built to fit your organization’s needs and find information quickly. documentation: https://backstage.io/docs/features/search/ -iconUrl: img/backstage-search-platform.svg +iconUrl: /img/backstage-search-platform.svg npmPackageName: '@backstage/plugin-search' order: 5 addedDate: '2022-09-07' diff --git a/microsite/data/plugins/backstage-software-catalog.yaml b/microsite/data/plugins/backstage-software-catalog.yaml index 13dac42e0c..3d47acabc7 100644 --- a/microsite/data/plugins/backstage-software-catalog.yaml +++ b/microsite/data/plugins/backstage-software-catalog.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: Core Feature description: Manage all your services and software components, all in one place. documentation: https://backstage.io/docs/features/software-catalog/ -iconUrl: img/backstage-software-catalog.svg +iconUrl: /img/backstage-software-catalog.svg npmPackageName: '@backstage/plugin-catalog' order: 1 addedDate: '2021-06-17' diff --git a/microsite/data/plugins/backstage-software-templates.yaml b/microsite/data/plugins/backstage-software-templates.yaml index 492f26f97a..916f754a2a 100644 --- a/microsite/data/plugins/backstage-software-templates.yaml +++ b/microsite/data/plugins/backstage-software-templates.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: Core Feature description: Create new software components in just a few steps, with your standards built-in (Scaffolder). documentation: https://backstage.io/docs/features/software-templates/ -iconUrl: img/backstage-software-templates.svg +iconUrl: /img/backstage-software-templates.svg npmPackageName: '@backstage/plugin-scaffolder' order: 2 addedDate: '2021-03-29' diff --git a/microsite/data/plugins/backstage-techdocs.yaml b/microsite/data/plugins/backstage-techdocs.yaml index f49fe98811..ea462a6c94 100644 --- a/microsite/data/plugins/backstage-techdocs.yaml +++ b/microsite/data/plugins/backstage-techdocs.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: Core Feature description: A docs-like-code solution to technical documentation. Write your docs right alongside your code. documentation: https://backstage.io/docs/features/techdocs/ -iconUrl: img/backstage-techdocs.svg +iconUrl: /img/backstage-techdocs.svg npmPackageName: '@backstage/plugin-techdocs' order: 3 addedDate: '2021-03-29' diff --git a/microsite/data/plugins/badges.yaml b/microsite/data/plugins/badges.yaml index a5f397843d..86d16ab547 100644 --- a/microsite/data/plugins/badges.yaml +++ b/microsite/data/plugins/badges.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/backstage/community category: Discovery description: The badges plugin offers a set of badges that can be used outside of Backstage, showing information related to data from the catalog. documentation: https://github.com/backstage/backstage/blob/master/plugins/badges/README.md -iconUrl: img/badges.svg +iconUrl: /img/badges.svg npmPackageName: '@backstage/plugin-badges' addedDate: '2021-09-29' diff --git a/microsite/data/plugins/bazaar.yaml b/microsite/data/plugins/bazaar.yaml index c408229be2..ce3a4d5dfc 100644 --- a/microsite/data/plugins/bazaar.yaml +++ b/microsite/data/plugins/bazaar.yaml @@ -4,6 +4,6 @@ authorUrl: https://www.axis.com category: Discovery description: A marketplace where engineers can propose projects suitable for inner sourcing documentation: https://github.com/backstage/backstage/blob/master/plugins/bazaar/README.md -iconUrl: img/bazaar.svg +iconUrl: /img/bazaar.svg npmPackageName: '@backstage/plugin-bazaar' addedDate: '2022-01-11' diff --git a/microsite/data/plugins/catalog-graph.yaml b/microsite/data/plugins/catalog-graph.yaml index 6fd7214c4c..46ee7b2f17 100644 --- a/microsite/data/plugins/catalog-graph.yaml +++ b/microsite/data/plugins/catalog-graph.yaml @@ -5,6 +5,6 @@ authorUrl: https://sda.se/ category: Discovery description: Extend the Backstage Software Catalog with a graph that shows all entities and their relationships providing an easier way to discover the ecosystem. documentation: https://github.com/backstage/backstage/blob/master/plugins/catalog-graph/README.md -iconUrl: img/catalog-graph.svg +iconUrl: /img/catalog-graph.svg npmPackageName: '@backstage/plugin-catalog-graph' addedDate: '2021-09-15' diff --git a/microsite/data/plugins/cicd-statistics.yaml b/microsite/data/plugins/cicd-statistics.yaml index a43b56b0d9..ff5c2cccf3 100644 --- a/microsite/data/plugins/cicd-statistics.yaml +++ b/microsite/data/plugins/cicd-statistics.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: CI/CD description: Visualize CI/CD pipeline statistics such as build time or success and error rates. documentation: https://github.com/backstage/backstage/tree/master/plugins/cicd-statistics -iconUrl: img/cicd-statistics.svg +iconUrl: /img/cicd-statistics.svg npmPackageName: '@backstage/plugin-cicd-statistics' tags: - ci diff --git a/microsite/data/plugins/circleci.yaml b/microsite/data/plugins/circleci.yaml index 64582440ec..5d7978624b 100644 --- a/microsite/data/plugins/circleci.yaml +++ b/microsite/data/plugins/circleci.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: CI/CD description: Automate your development process with CI hosted in the cloud or on a private server. documentation: https://github.com/backstage/backstage/tree/master/plugins/circleci -iconUrl: img/circleci.png +iconUrl: /img/circleci.png npmPackageName: '@backstage/plugin-circleci' tags: - ci diff --git a/microsite/data/plugins/codescene.yaml b/microsite/data/plugins/codescene.yaml index bca23fc1fa..734e575282 100644 --- a/microsite/data/plugins/codescene.yaml +++ b/microsite/data/plugins/codescene.yaml @@ -5,6 +5,6 @@ authorUrl: https://codescene.com/ category: Quality description: CodeScene is a multi-purpose tool bridging code, business and people. See hidden risks and social patterns in your code. Prioritize and reduce technical debt. documentation: https://github.com/backstage/backstage/tree/master/plugins/codescene -iconUrl: img/codescene_logo.svg +iconUrl: /img/codescene_logo.svg npmPackageName: '@backstage/plugin-codescene' addedDate: '2022-04-12' diff --git a/microsite/data/plugins/cortex.yaml b/microsite/data/plugins/cortex.yaml index bf480ccfcd..77b01ddceb 100644 --- a/microsite/data/plugins/cortex.yaml +++ b/microsite/data/plugins/cortex.yaml @@ -5,7 +5,7 @@ authorUrl: https://www.getcortexapp.com category: Monitoring description: Grade the quality of your Backstage services using Scorecards. Automate production readiness, migrations, security audits, and more with CQL (Cortex Query Language). documentation: https://github.com/cortexapps/backstage-plugin -iconUrl: img/cortex.png +iconUrl: /img/cortex.png npmPackageName: '@cortexapps/backstage-plugin' tags: - web diff --git a/microsite/data/plugins/cost-insights.yaml b/microsite/data/plugins/cost-insights.yaml index 824769c174..603cd18416 100644 --- a/microsite/data/plugins/cost-insights.yaml +++ b/microsite/data/plugins/cost-insights.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spotify category: Discovery description: Visualize, understand and optimize your team's cloud costs. documentation: https://github.com/backstage/backstage/tree/master/plugins/cost-insights -iconUrl: img/cost-insights.png +iconUrl: /img/cost-insights.png npmPackageName: '@backstage/plugin-cost-insights' tags: - web diff --git a/microsite/data/plugins/dora-metrics.yaml b/microsite/data/plugins/dora-metrics.yaml index 65c2e4fe6e..3f59902f46 100644 --- a/microsite/data/plugins/dora-metrics.yaml +++ b/microsite/data/plugins/dora-metrics.yaml @@ -5,6 +5,6 @@ authorUrl: https://www.okayhq.com category: Metrics description: Embed dashboards (like DORA metrics) in your team or service pages from any dev tools (including Github, Gitlab, Jira, Argo, CircleCI, Buildkite, Pagerduty, Rollbar, Sentry, etc). documentation: https://github.com/OkayHQ/backstage-plugin -iconUrl: img/okay.png +iconUrl: /img/okay.png npmPackageName: '@okayhq/backstage-plugin' addedDate: '2022-06-16' diff --git a/microsite/data/plugins/dynatrace.yaml b/microsite/data/plugins/dynatrace.yaml index b68d993e4b..9ec1fee7b3 100644 --- a/microsite/data/plugins/dynatrace.yaml +++ b/microsite/data/plugins/dynatrace.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/telus category: Monitoring description: View monitoring info from Dynatrace for services in your software catalog. documentation: https://github.com/backstage/backstage/tree/master/plugins/dynatrace -iconUrl: img/dynatrace.svg +iconUrl: /img/dynatrace.svg npmPackageName: '@backstage/plugin-dynatrace' tags: - dynatrace diff --git a/microsite/data/plugins/entity-feedback.yaml b/microsite/data/plugins/entity-feedback.yaml index c40d0450ca..3630eddb73 100644 --- a/microsite/data/plugins/entity-feedback.yaml +++ b/microsite/data/plugins/entity-feedback.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/kuangp category: Quality description: Give and view feedback on entities available in the Backstage catalog. documentation: https://github.com/backstage/backstage/tree/master/plugins/entity-feedback -iconUrl: img/entity-feedback-logo.png +iconUrl: /img/entity-feedback-logo.png npmPackageName: '@backstage/plugin-entity-feedback' addedDate: '2023-01-20' diff --git a/microsite/data/plugins/git-release-manager.yaml b/microsite/data/plugins/git-release-manager.yaml index e7000be694..6d357a23f6 100644 --- a/microsite/data/plugins/git-release-manager.yaml +++ b/microsite/data/plugins/git-release-manager.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/spotify category: Release management description: Manage releases without having to juggle git commands. documentation: https://github.com/backstage/backstage/tree/master/plugins/git-release-manager -iconUrl: img/git-release-manager-logo.svg +iconUrl: /img/git-release-manager-logo.svg npmPackageName: '@backstage/plugin-git-release-manager' addedDate: '2021-10-04' diff --git a/microsite/data/plugins/github-pull-requests-board.yaml b/microsite/data/plugins/github-pull-requests-board.yaml index 0c3435b581..09776eb468 100644 --- a/microsite/data/plugins/github-pull-requests-board.yaml +++ b/microsite/data/plugins/github-pull-requests-board.yaml @@ -5,6 +5,6 @@ authorUrl: https://engineering.dazn.com category: Source Control Mgmt description: View all open GitHub pull requests owned by your team in Backstage. documentation: https://github.com/backstage/backstage/tree/master/plugins/github-pull-requests-board -iconUrl: img/github-pull-requests-board-logo.svg +iconUrl: /img/github-pull-requests-board-logo.svg npmPackageName: '@backstage/plugin-github-pull-requests-board' addedDate: '2022-05-10' diff --git a/microsite/data/plugins/home.yaml b/microsite/data/plugins/home.yaml index cf67f6b43f..0ca2de56f2 100644 --- a/microsite/data/plugins/home.yaml +++ b/microsite/data/plugins/home.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/spotify category: Discovery description: This plugin provides a composable home page, and ability to create home page components documentation: https://github.com/backstage/backstage/blob/master/plugins/home/README.md -iconUrl: img/home.png +iconUrl: /img/home.png npmPackageName: '@backstage/plugin-home' addedDate: '2021-08-31' diff --git a/microsite/data/plugins/humanitec.yaml b/microsite/data/plugins/humanitec.yaml index 1b79270582..7265b9d917 100644 --- a/microsite/data/plugins/humanitec.yaml +++ b/microsite/data/plugins/humanitec.yaml @@ -4,9 +4,9 @@ author: Frontside authorUrl: 'https://frontside.com' category: Deployment # A single category e.g. CI, Machine Learning, Services, Monitoring description: | - Show workloads, environments and resources deployed by Humanitec Platform Orchestrator. + Show workloads, environments and resources deployed by Humanitec Platform Orchestrator. Plugin includes an Entity ComponentCard, Backend API route and scaffolder actions. documentation: https://github.com/thefrontside/playhouse/tree/main/plugins/humanitec -iconUrl: img/humanitec-logo.png +iconUrl: /img/humanitec-logo.png npmPackageName: '@frontside/backstage-plugin-humanitec' addedDate: '2022-06-22' diff --git a/microsite/data/plugins/octopus-deploy.yaml b/microsite/data/plugins/octopus-deploy.yaml index a6997e597d..1ed7439695 100644 --- a/microsite/data/plugins/octopus-deploy.yaml +++ b/microsite/data/plugins/octopus-deploy.yaml @@ -5,6 +5,6 @@ authorUrl: https://jmezach.github.io/ category: CI/CD description: Easily view your Octopus Deploy releases within the Software Catalog documentation: https://github.com/backstage/backstage/blob/master/plugins/octopus-deploy/README.md -iconUrl: img/octopus-deploy.svg +iconUrl: /img/octopus-deploy.svg npmPackageName: '@backstage/plugin-octopus-deploy' addedDate: '2023-02-24' diff --git a/microsite/data/plugins/playlist.yaml b/microsite/data/plugins/playlist.yaml index 7d9c13082d..4e7f442e03 100644 --- a/microsite/data/plugins/playlist.yaml +++ b/microsite/data/plugins/playlist.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/kuangp category: Discovery description: Create, share, and follow custom collections of entities available in the Backstage catalog. documentation: https://github.com/backstage/backstage/tree/master/plugins/playlist -iconUrl: img/playlist-logo.png +iconUrl: /img/playlist-logo.png npmPackageName: '@backstage/plugin-playlist' addedDate: '2022-07-02' diff --git a/microsite/data/plugins/qeta.yaml b/microsite/data/plugins/qeta.yaml index a4a3c343a4..0b9dc21cfd 100644 --- a/microsite/data/plugins/qeta.yaml +++ b/microsite/data/plugins/qeta.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/drodil category: Services description: Ask and answer questions within Backstage documentation: https://github.com/drodil/backstage-plugin-qeta -iconUrl: img/qeta-logo.png +iconUrl: /img/qeta-logo.png npmPackageName: '@drodil/backstage-plugin-qeta' addedDate: '2022-12-21' diff --git a/microsite/data/plugins/s3-viewer.yaml b/microsite/data/plugins/s3-viewer.yaml index 23637133c8..2eeeb23415 100644 --- a/microsite/data/plugins/s3-viewer.yaml +++ b/microsite/data/plugins/s3-viewer.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/spreadshirt category: AWS description: Visualization of S3 buckets and their contents in file explorer style. documentation: https://github.com/spreadshirt/backstage-plugin-s3/ -iconUrl: img/s3-bucket.svg +iconUrl: /img/s3-bucket.svg npmPackageName: '@spreadshirt/backstage-plugin-s3-viewer' tags: - s3 diff --git a/microsite/data/plugins/scaffolder-backend-dotnet.yaml b/microsite/data/plugins/scaffolder-backend-dotnet.yaml index 2b032621c9..03fee737cb 100644 --- a/microsite/data/plugins/scaffolder-backend-dotnet.yaml +++ b/microsite/data/plugins/scaffolder-backend-dotnet.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/alefcarlos category: Scaffolder description: Here you can find all .NET related actions to improve your scaffolder. documentation: https://github.com/alefcarlos/plusultra-dotnet-backstage-plugins/blob/main/plugins/scaffolder-dotnet-backend/README.md -iconUrl: img/scaffolder-backend-dotnet-icon.png +iconUrl: /img/scaffolder-backend-dotnet-icon.png npmPackageName: '@plusultra/plugin-scaffolder-dotnet-backend' addedDate: '2022-01-24' diff --git a/microsite/data/plugins/scaffolder-backend-module-rails.yaml b/microsite/data/plugins/scaffolder-backend-module-rails.yaml index 4110dcbed0..f740b95738 100644 --- a/microsite/data/plugins/scaffolder-backend-module-rails.yaml +++ b/microsite/data/plugins/scaffolder-backend-module-rails.yaml @@ -5,6 +5,6 @@ authorUrl: https://angeliski.com.br/ category: Scaffolder description: Here you can find all Rails related features to improve your scaffolder. documentation: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend-module-rails/README.md -iconUrl: img/rails-icon.png +iconUrl: /img/rails-icon.png npmPackageName: '@backstage/plugin-scaffolder-backend-module-rails' addedDate: '2021-06-24' diff --git a/microsite/data/plugins/scaffolder-backend-roadie-http-request.yaml b/microsite/data/plugins/scaffolder-backend-roadie-http-request.yaml index 254bebe8e7..e377f4d1f2 100644 --- a/microsite/data/plugins/scaffolder-backend-roadie-http-request.yaml +++ b/microsite/data/plugins/scaffolder-backend-roadie-http-request.yaml @@ -5,6 +5,6 @@ authorUrl: https://roadie.io/?utm_source=backstage.io&utm_medium=marketplace&utm category: Scaffolder description: An action to fire an arbitrary HTTP request documentation: https://github.com/RoadieHQ/roadie-backstage-plugins/blob/main/plugins/scaffolder-actions/scaffolder-backend-module-http-request/README.md -iconUrl: img/scaffolder-http-request-logo.svg +iconUrl: /img/scaffolder-http-request-logo.svg npmPackageName: '@roadiehq/scaffolder-backend-module-http-request' addedDate: '2022-03-03' diff --git a/microsite/data/plugins/scaffolder-backend-roadie-utils.yaml b/microsite/data/plugins/scaffolder-backend-roadie-utils.yaml index ef3814f8ea..dace41994f 100644 --- a/microsite/data/plugins/scaffolder-backend-roadie-utils.yaml +++ b/microsite/data/plugins/scaffolder-backend-roadie-utils.yaml @@ -5,6 +5,6 @@ authorUrl: https://roadie.io/?utm_source=backstage.io&utm_medium=marketplace&utm category: Scaffolder description: A collection of utility actions including sleep, zip and file manipulation. documentation: https://github.com/RoadieHQ/roadie-backstage-plugins/blob/main/plugins/scaffolder-actions/scaffolder-backend-module-utils/README.md -iconUrl: img/scaffolder-utils-logo.png +iconUrl: /img/scaffolder-utils-logo.png npmPackageName: '@roadiehq/scaffolder-backend-module-utils' addedDate: '2022-03-03' diff --git a/microsite/data/plugins/score-card.yaml b/microsite/data/plugins/score-card.yaml index c647f8dd15..48f619af2a 100644 --- a/microsite/data/plugins/score-card.yaml +++ b/microsite/data/plugins/score-card.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/Oriflame category: Quality description: Visualization of maturity (and improving it) of services/systems thanks to a review process. documentation: https://github.com/Oriflame/backstage-plugins/tree/main/plugins/score-card -iconUrl: img/score-card-plugin-logo.png +iconUrl: /img/score-card-plugin-logo.png npmPackageName: '@oriflame/backstage-plugin-score-card' addedDate: '2022-10-06' diff --git a/microsite/data/plugins/shortcuts.yaml b/microsite/data/plugins/shortcuts.yaml index d7c63ba666..7c3f2c6d56 100644 --- a/microsite/data/plugins/shortcuts.yaml +++ b/microsite/data/plugins/shortcuts.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/spotify category: Utility description: The shortcuts plugin allows a user to have easy access to pages within a Backstage app by storing them as "shortcuts" in the Sidebar. documentation: https://github.com/backstage/backstage/blob/master/plugins/shortcuts/README.md -iconUrl: img/shortcuts.svg +iconUrl: /img/shortcuts.svg npmPackageName: '@backstage/plugin-shortcuts' addedDate: '2021-10-06' diff --git a/microsite/data/plugins/sonarqube.yaml b/microsite/data/plugins/sonarqube.yaml index 2eab08efff..f24441c46e 100644 --- a/microsite/data/plugins/sonarqube.yaml +++ b/microsite/data/plugins/sonarqube.yaml @@ -5,6 +5,6 @@ authorUrl: https://sda.se/ category: Quality description: Components to display code quality metrics from SonarCloud and SonarQube. documentation: https://github.com/backstage/backstage/blob/master/plugins/sonarqube/README.md -iconUrl: img/sonarqube-icon.svg +iconUrl: /img/sonarqube-icon.svg npmPackageName: '@backstage/plugin-sonarqube' addedDate: '2020-11-03' diff --git a/microsite/data/plugins/splunk-on-call.yaml b/microsite/data/plugins/splunk-on-call.yaml index 7a29ab3bf9..b9021a337d 100644 --- a/microsite/data/plugins/splunk-on-call.yaml +++ b/microsite/data/plugins/splunk-on-call.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/ayshiff category: Monitoring description: Splunk On-Call offers a simple way to identify incidents and escalation policies. documentation: https://github.com/backstage/backstage/tree/master/plugins/splunk-on-call -iconUrl: img/splunk-black-white-bg.png +iconUrl: /img/splunk-black-white-bg.png npmPackageName: '@backstage/plugin-splunk-on-call' tags: - monitoring diff --git a/microsite/data/plugins/stack-overflow.yaml b/microsite/data/plugins/stack-overflow.yaml index 27a3a8cf97..a5e2122f8a 100644 --- a/microsite/data/plugins/stack-overflow.yaml +++ b/microsite/data/plugins/stack-overflow.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/spotify category: Discovery description: Provides Stack Overflow specific functionality that can be used in different ways (e.g. for homepage and search) to compose your Backstage App. documentation: https://github.com/backstage/backstage/blob/master/plugins/stack-overflow -iconUrl: img/stack-overflow-logo.svg +iconUrl: /img/stack-overflow-logo.svg npmPackageName: '@backstage/plugin-stack-overflow' addedDate: '2022-06-14' diff --git a/microsite/data/plugins/stackstorm.yaml b/microsite/data/plugins/stackstorm.yaml index 2524ca538f..ade6ca860f 100644 --- a/microsite/data/plugins/stackstorm.yaml +++ b/microsite/data/plugins/stackstorm.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/ExpediaGroup category: Automation description: Manage StackStorm workflow executions from within Backstage. documentation: https://github.com/backstage/backstage/tree/master/plugins/stackstorm -iconUrl: img/stackstorm.png +iconUrl: /img/stackstorm.png npmPackageName: '@backstage/plugin-stackstorm' tags: - stackstorm diff --git a/microsite/data/plugins/toolbox.yaml b/microsite/data/plugins/toolbox.yaml index 9d9adf0224..b488b24369 100644 --- a/microsite/data/plugins/toolbox.yaml +++ b/microsite/data/plugins/toolbox.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/drodil category: Tools description: Development related tools within Backstage documentation: https://github.com/drodil/backstage-plugin-toolbox -iconUrl: img/toolbox-logo.png +iconUrl: /img/toolbox-logo.png npmPackageName: '@drodil/backstage-plugin-toolbox' addedDate: '2023-01-09' diff --git a/microsite/data/plugins/vault.yaml b/microsite/data/plugins/vault.yaml index bb8f799ba7..29e20417d8 100644 --- a/microsite/data/plugins/vault.yaml +++ b/microsite/data/plugins/vault.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/ivangonzalezacuna category: Vault description: Visualize a list of the secrets stored in your HashiCorp Vault instance. documentation: https://github.com/backstage/backstage/tree/master/plugins/vault -iconUrl: img/vault.png +iconUrl: /img/vault.png npmPackageName: '@backstage/plugin-vault' tags: - vault diff --git a/microsite/data/plugins/xcmetrics.yaml b/microsite/data/plugins/xcmetrics.yaml index 91db85ebaa..f02b195eb5 100644 --- a/microsite/data/plugins/xcmetrics.yaml +++ b/microsite/data/plugins/xcmetrics.yaml @@ -5,6 +5,6 @@ authorUrl: https://github.com/spotify category: Monitoring description: Discover valuable insights hiding inside Xcode’s build logs. documentation: https://xcmetrics.io/ -iconUrl: img/xcmetrics-icon.png +iconUrl: /img/xcmetrics-icon.png npmPackageName: '@backstage/plugin-xcmetrics' addedDate: '2021-08-06' From 915e46622cf4bb809ad81011e05af3ba6af7f8b8 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 27 Feb 2023 11:24:32 +0100 Subject: [PATCH 098/236] split changesets Signed-off-by: Vincenzo Scamporlino --- .changeset/polite-wombats-smash.md | 3 +-- .changeset/what-is-going-on-babe.md | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/what-is-going-on-babe.md diff --git a/.changeset/polite-wombats-smash.md b/.changeset/polite-wombats-smash.md index 250ca215f5..98569e1799 100644 --- a/.changeset/polite-wombats-smash.md +++ b/.changeset/polite-wombats-smash.md @@ -1,6 +1,5 @@ --- -'@backstage/backend-app-api': patch '@backstage/errors': patch --- -Add NotImplementedError +Added `NotImplementedError`, which can be used when the server does not recognize the request method and is incapable of supporting it for any resource. diff --git a/.changeset/what-is-going-on-babe.md b/.changeset/what-is-going-on-babe.md new file mode 100644 index 0000000000..c22d69d1de --- /dev/null +++ b/.changeset/what-is-going-on-babe.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Add support for `NotImplementedError`, properly returning 501 as status code. From e837143bc9a3d9a112ab6df5ac10db8ef6d0d711 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 27 Feb 2023 11:30:05 +0100 Subject: [PATCH 099/236] permission-node: simplify api report Signed-off-by: Vincenzo Scamporlino --- plugins/permission-node/api-report.md | 9 ++---- .../createPermissionIntegrationRouter.test.ts | 32 ++++++++++++------- .../createPermissionIntegrationRouter.ts | 14 ++------ 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 9fda5cfd96..5d2966ec73 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -137,7 +137,9 @@ export type CreatePermissionIntegrationRouterResourceOptions< resourceType: TResourceType; permissions?: Array; rules: PermissionRule>[]; - getResources?: GetResourcesFn; + getResources?: ( + resourceRefs: string[], + ) => Promise>; }; // @public @@ -150,11 +152,6 @@ export const createPermissionRule: < rule: PermissionRule, ) => PermissionRule; -// @public -export type GetResourcesFn = ( - resourceRefs: string[], -) => Promise>; - // @alpha export const isAndCriteria: ( criteria: PermissionCriteria, diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index 577267b161..8b918cabb2 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -24,7 +24,7 @@ import request, { Response } from 'supertest'; import { z } from 'zod'; import { createPermissionIntegrationRouter, - GetResourcesFn, + CreatePermissionIntegrationRouterResourceOptions, } from './createPermissionIntegrationRouter'; import { createPermissionRule } from './createPermissionRule'; @@ -53,8 +53,11 @@ const testRule2 = createPermissionRule({ toQuery: () => ({}), }); -const defaultMockedGetResources: GetResourcesFn<{ id: string }> = jest.fn( - async resourceRefs => resourceRefs.map(resourceRef => ({ id: resourceRef })), +const defaultMockedGetResources: CreatePermissionIntegrationRouterResourceOptions< + string, + { id: string } +>['getResources'] = jest.fn(async resourceRefs => + resourceRefs.map(resourceRef => ({ id: resourceRef })), ); const createApp = ( @@ -415,8 +418,11 @@ describe('createPermissionIntegrationRouter', () => { }); it('returns 200/DENY when resource is not found', async () => { - const mockedGetResources: GetResourcesFn<{ id: string }> = jest.fn( - async resourceRefs => resourceRefs.map(() => undefined), + const mockedGetResources: CreatePermissionIntegrationRouterResourceOptions< + string, + { id: string } + >['getResources'] = jest.fn(async resourceRefs => + resourceRefs.map(() => undefined), ); const response = await request(createApp(mockedGetResources)) @@ -448,13 +454,15 @@ describe('createPermissionIntegrationRouter', () => { }); it('interleaves responses for present and missing resources', async () => { - const mockedGetResources: GetResourcesFn<{ id: string }> = jest.fn( - async resourceRefs => - resourceRefs.map(resourceRef => - resourceRef === 'default:test/missing-resource' - ? undefined - : { id: resourceRef }, - ), + const mockedGetResources: CreatePermissionIntegrationRouterResourceOptions< + string, + { id: string } + >['getResources'] = jest.fn(async resourceRefs => + resourceRefs.map(resourceRef => + resourceRef === 'default:test/missing-resource' + ? undefined + : { id: resourceRef }, + ), ); const response = await request(createApp(mockedGetResources)) diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index f78fea6ea6..715b1ec574 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -126,16 +126,6 @@ export type MetadataResponse = { rules: MetadataResponseSerializedRule[]; }; -/** - * Function type for returning an array of resources - * matching the given resourceRefs. - * - * @public - */ -export type GetResourcesFn = ( - resourceRefs: string[], -) => Promise>; - const applyConditions = ( criteria: PermissionCriteria>, resource: TResource | undefined, @@ -187,7 +177,9 @@ export type CreatePermissionIntegrationRouterResourceOptions< // consider any rules whose resource type does not match // to be an error. rules: PermissionRule>[]; - getResources?: GetResourcesFn; + getResources?: ( + resourceRefs: string[], + ) => Promise>; }; /** From 24916d2349473ac103f35833a80272a0fdf6971d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 27 Feb 2023 12:24:17 +0100 Subject: [PATCH 100/236] Do not close multiselect filters when checking boxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/young-schools-double.md | 5 +++++ .../EntityAutocompletePicker/EntityAutocompletePicker.tsx | 1 + .../EntityLifecyclePicker/EntityLifecyclePicker.tsx | 1 + .../src/components/EntityOwnerPicker/EntityOwnerPicker.tsx | 1 + .../EntityProcessingStatusPicker.tsx | 1 + 5 files changed, 9 insertions(+) create mode 100644 .changeset/young-schools-double.md diff --git a/.changeset/young-schools-double.md b/.changeset/young-schools-double.md new file mode 100644 index 0000000000..8cba2ec8f3 --- /dev/null +++ b/.changeset/young-schools-double.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Do not close `Autocomplete` powered multiple-selection filters when checking boxes diff --git a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx index 09d5a45246..b81d681e6d 100644 --- a/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx +++ b/plugins/catalog-react/src/components/EntityAutocompletePicker/EntityAutocompletePicker.tsx @@ -127,6 +127,7 @@ export function EntityAutocompletePicker< {label} diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx index 77925598e1..29002564ca 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx @@ -105,6 +105,7 @@ export const EntityLifecyclePicker = (props: { initialFilter?: string[] }) => { Lifecycle diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index b067bda0f5..c8a6c64dd6 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -108,6 +108,7 @@ export const EntityOwnerPicker = () => { Owner setSelectedOwners(value)} diff --git a/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx index 3fe3b60e25..db48534bc0 100644 --- a/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx +++ b/plugins/catalog-react/src/components/EntityProcessingStatusPicker/EntityProcessingStatusPicker.tsx @@ -74,6 +74,7 @@ export const EntityProcessingStatusPicker = () => { Processing Status { From d7ff48817b1d1ac3713ab8cbfae32ec7289ca00c Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 27 Feb 2023 12:45:56 +0100 Subject: [PATCH 101/236] chore: moving things around a little Signed-off-by: blam --- .../src/components/TaskSteps/TaskSteps.tsx | 79 ---------------- .../src/components/TaskSteps/index.ts | 16 ---- .../scaffolder-react/src/components/index.ts | 18 ---- .../TaskLogStream/TaskLogStream.test.tsx | 0 .../TaskLogStream/TaskLogStream.tsx | 2 +- .../components/TaskLogStream/index.ts | 0 .../components/TaskSteps/StepIcon.tsx | 0 .../components/TaskSteps/StepTime.test.tsx | 0 .../components/TaskSteps/StepTime.tsx | 0 .../components/TaskSteps}/TaskBorder.test.tsx | 0 .../components/TaskSteps}/TaskBorder.tsx | 2 - .../components/TaskSteps/TaskSteps.test.tsx | 0 .../next/components/TaskSteps/TaskSteps.tsx | 93 +++++++++++++++++++ .../components/TaskSteps}/index.ts | 2 +- .../src/next/components/index.ts | 2 + .../src/next/OngoingTask/OngoingTask.tsx | 24 +++-- 16 files changed, 108 insertions(+), 130 deletions(-) delete mode 100644 plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx delete mode 100644 plugins/scaffolder-react/src/components/TaskSteps/index.ts delete mode 100644 plugins/scaffolder-react/src/components/index.ts rename plugins/scaffolder-react/src/{ => next}/components/TaskLogStream/TaskLogStream.test.tsx (100%) rename plugins/scaffolder-react/src/{ => next}/components/TaskLogStream/TaskLogStream.tsx (99%) rename plugins/scaffolder-react/src/{ => next}/components/TaskLogStream/index.ts (100%) rename plugins/scaffolder-react/src/{ => next}/components/TaskSteps/StepIcon.tsx (100%) rename plugins/scaffolder-react/src/{ => next}/components/TaskSteps/StepTime.test.tsx (100%) rename plugins/scaffolder-react/src/{ => next}/components/TaskSteps/StepTime.tsx (100%) rename plugins/scaffolder-react/src/{components/TaskBorder => next/components/TaskSteps}/TaskBorder.test.tsx (100%) rename plugins/scaffolder-react/src/{components/TaskBorder => next/components/TaskSteps}/TaskBorder.tsx (98%) rename plugins/scaffolder-react/src/{ => next}/components/TaskSteps/TaskSteps.test.tsx (100%) create mode 100644 plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx rename plugins/scaffolder-react/src/{components/TaskBorder => next/components/TaskSteps}/index.ts (90%) diff --git a/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx b/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx deleted file mode 100644 index 8b43d5e17b..0000000000 --- a/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.tsx +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2022 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 React from 'react'; -import { - Stepper as MuiStepper, - Step as MuiStep, - StepButton as MuiStepButton, - StepLabel as MuiStepLabel, - StepIconProps, - Box, -} from '@material-ui/core'; -import { TaskStep } from '@backstage/plugin-scaffolder-common'; -import { type Step } from '@backstage/plugin-scaffolder-react'; -import { StepIcon } from './StepIcon'; -import { StepTime } from './StepTime'; - -/** - * - * @public - */ -export interface StepperProps { - steps: (TaskStep & Step)[]; - activeStep?: number; -} - -/** - * The visual stepper of the task event stream - * - * @public - */ -export const TaskSteps = (props: StepperProps) => { - return ( - - {props.steps.map((step, index) => { - const isCompleted = step.status === 'completed'; - const isFailed = step.status === 'failed'; - const isActive = step.status === 'processing'; - const isSkipped = step.status === 'skipped'; - const stepIconProps: Partial = { - completed: isCompleted, - error: isFailed, - active: isActive, - skipped: isSkipped, - }; - - return ( - - - - {step.name} - - - - - ); - })} - - ); -}; diff --git a/plugins/scaffolder-react/src/components/TaskSteps/index.ts b/plugins/scaffolder-react/src/components/TaskSteps/index.ts deleted file mode 100644 index 91f5537bd3..0000000000 --- a/plugins/scaffolder-react/src/components/TaskSteps/index.ts +++ /dev/null @@ -1,16 +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. - */ -export { TaskSteps, type StepperProps } from './TaskSteps'; diff --git a/plugins/scaffolder-react/src/components/index.ts b/plugins/scaffolder-react/src/components/index.ts deleted file mode 100644 index b724ed32b5..0000000000 --- a/plugins/scaffolder-react/src/components/index.ts +++ /dev/null @@ -1,18 +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. - */ -export { TaskSteps, type StepperProps } from './TaskSteps'; -export { TaskBorder } from './TaskBorder'; -export { TaskLogStream } from './TaskLogStream'; diff --git a/plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.test.tsx b/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.test.tsx similarity index 100% rename from plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.test.tsx rename to plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.test.tsx diff --git a/plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.tsx b/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx similarity index 99% rename from plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.tsx rename to plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx index 973627aa02..4ce1df7a29 100644 --- a/plugins/scaffolder-react/src/components/TaskLogStream/TaskLogStream.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskLogStream/TaskLogStream.tsx @@ -28,7 +28,7 @@ const useStyles = makeStyles({ /** * The text of the event stream * - * @public + * @alpha */ export const TaskLogStream = (props: { logs: { [k: string]: string[] } }) => { const styles = useStyles(); diff --git a/plugins/scaffolder-react/src/components/TaskLogStream/index.ts b/plugins/scaffolder-react/src/next/components/TaskLogStream/index.ts similarity index 100% rename from plugins/scaffolder-react/src/components/TaskLogStream/index.ts rename to plugins/scaffolder-react/src/next/components/TaskLogStream/index.ts diff --git a/plugins/scaffolder-react/src/components/TaskSteps/StepIcon.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/StepIcon.tsx similarity index 100% rename from plugins/scaffolder-react/src/components/TaskSteps/StepIcon.tsx rename to plugins/scaffolder-react/src/next/components/TaskSteps/StepIcon.tsx diff --git a/plugins/scaffolder-react/src/components/TaskSteps/StepTime.test.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/StepTime.test.tsx similarity index 100% rename from plugins/scaffolder-react/src/components/TaskSteps/StepTime.test.tsx rename to plugins/scaffolder-react/src/next/components/TaskSteps/StepTime.test.tsx diff --git a/plugins/scaffolder-react/src/components/TaskSteps/StepTime.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/StepTime.tsx similarity index 100% rename from plugins/scaffolder-react/src/components/TaskSteps/StepTime.tsx rename to plugins/scaffolder-react/src/next/components/TaskSteps/StepTime.tsx diff --git a/plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.test.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.test.tsx similarity index 100% rename from plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.test.tsx rename to plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.test.tsx diff --git a/plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx similarity index 98% rename from plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.tsx rename to plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx index 17eea3997a..2d8e99bfe7 100644 --- a/plugins/scaffolder-react/src/components/TaskBorder/TaskBorder.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx @@ -28,8 +28,6 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ /** * The visual progress of the task event stream - * - * @public */ export const TaskBorder = (props: { isComplete: boolean; diff --git a/plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.test.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.test.tsx similarity index 100% rename from plugins/scaffolder-react/src/components/TaskSteps/TaskSteps.test.tsx rename to plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.test.tsx diff --git a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx new file mode 100644 index 0000000000..cecf3dc76c --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx @@ -0,0 +1,93 @@ +/* + * Copyright 2022 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 React from 'react'; +import { + Stepper as MuiStepper, + Step as MuiStep, + StepButton as MuiStepButton, + StepLabel as MuiStepLabel, + StepIconProps, + Box, + Paper, +} from '@material-ui/core'; +import { TaskStep } from '@backstage/plugin-scaffolder-common'; +import { type Step } from '@backstage/plugin-scaffolder-react'; +import { StepIcon } from './StepIcon'; +import { StepTime } from './StepTime'; +import { TaskBorder } from './TaskBorder'; + +/** + * Props for the TaskSteps component + * + * @alpha + */ +export interface TaskStepsProps { + steps: (TaskStep & Step)[]; + activeStep?: number; + isComplete?: boolean; + isError?: boolean; +} + +/** + * The visual stepper of the task event stream + * + * @alpha + */ +export const TaskSteps = (props: TaskStepsProps) => { + return ( + + + + + {props.steps.map((step, index) => { + const isCompleted = step.status === 'completed'; + const isFailed = step.status === 'failed'; + const isActive = step.status === 'processing'; + const isSkipped = step.status === 'skipped'; + const stepIconProps: Partial = + { + completed: isCompleted, + error: isFailed, + active: isActive, + skipped: isSkipped, + }; + + return ( + + + + {step.name} + + + + + ); + })} + + + + ); +}; diff --git a/plugins/scaffolder-react/src/components/TaskBorder/index.ts b/plugins/scaffolder-react/src/next/components/TaskSteps/index.ts similarity index 90% rename from plugins/scaffolder-react/src/components/TaskBorder/index.ts rename to plugins/scaffolder-react/src/next/components/TaskSteps/index.ts index e1559957d1..45ff9ccf7c 100644 --- a/plugins/scaffolder-react/src/components/TaskBorder/index.ts +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/index.ts @@ -13,4 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { TaskBorder } from './TaskBorder'; +export { TaskSteps, type TaskStepsProps } from './TaskSteps'; diff --git a/plugins/scaffolder-react/src/next/components/index.ts b/plugins/scaffolder-react/src/next/components/index.ts index 60a8fd9a51..083d88927e 100644 --- a/plugins/scaffolder-react/src/next/components/index.ts +++ b/plugins/scaffolder-react/src/next/components/index.ts @@ -20,3 +20,5 @@ export * from './TemplateGroup'; export * from './Workflow'; export * from './TemplateOutputs'; export * from './Form'; +export * from './TaskSteps'; +export * from './TaskLogStream'; diff --git a/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx index 84730429dd..fc1c1a7413 100644 --- a/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/next/OngoingTask/OngoingTask.tsx @@ -18,9 +18,6 @@ import { Page, Header, Content, ErrorPanel } from '@backstage/core-components'; import { useNavigate, useParams } from 'react-router-dom'; import { Box, makeStyles, Paper } from '@material-ui/core'; import { - TaskSteps, - TaskBorder, - TaskLogStream, ScaffolderTaskOutput, useTaskEventStream, } from '@backstage/plugin-scaffolder-react'; @@ -28,7 +25,11 @@ import { nextSelectedTemplateRouteRef } from '../routes'; import { useRouteRef } from '@backstage/core-plugin-api'; import qs from 'qs'; import { ContextMenu } from './ContextMenu'; -import { DefaultTemplateOutputs } from '@backstage/plugin-scaffolder-react/alpha'; +import { + DefaultTemplateOutputs, + TaskLogStream, + TaskSteps, +} from '@backstage/plugin-scaffolder-react/alpha'; const useStyles = makeStyles({ contentWrapper: { @@ -132,15 +133,12 @@ export const OngoingTask = (props: { ) : null} - - - - - - + From 1ec47ea20f1d598fd60357d3d757e97925193bc0 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 27 Feb 2023 13:08:23 +0100 Subject: [PATCH 102/236] chore: fix api reports Signed-off-by: blam --- plugins/scaffolder-react/alpha-api-report.md | 24 +++++++++ plugins/scaffolder-react/api-report.md | 50 ++++--------------- plugins/scaffolder-react/src/hooks/index.ts | 2 +- .../src/hooks/useEventStream.ts | 23 +++++---- plugins/scaffolder-react/src/index.ts | 1 - .../src/next/components/Stepper/Stepper.tsx | 6 ++- .../components/TaskSteps/TaskSteps.test.tsx | 2 +- .../next/components/TaskSteps/TaskSteps.tsx | 4 +- .../src/next/extensions/index.tsx | 3 +- .../src/next/hooks/useTemplateSchema.test.tsx | 2 +- .../next/hooks/useTransformSchemaToProps.ts | 2 +- 11 files changed, 58 insertions(+), 61 deletions(-) diff --git a/plugins/scaffolder-react/alpha-api-report.md b/plugins/scaffolder-react/alpha-api-report.md index ad44cd3707..eca06b9af6 100644 --- a/plugins/scaffolder-react/alpha-api-report.md +++ b/plugins/scaffolder-react/alpha-api-report.md @@ -22,8 +22,10 @@ import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RJSFSchema } from '@rjsf/utils'; +import { ScaffolderStep } from '@backstage/plugin-scaffolder-react'; import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; import { SetStateAction } from 'react'; +import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; import { UIOptionsType } from '@rjsf/utils'; @@ -140,6 +142,28 @@ export type StepperProps = { layouts?: LayoutOptions[]; }; +// @alpha +export const TaskLogStream: (props: { + logs: { + [k: string]: string[]; + }; +}) => JSX.Element; + +// @alpha +export const TaskSteps: (props: TaskStepsProps) => JSX.Element; + +// @alpha +export interface TaskStepsProps { + // (undocumented) + activeStep?: number; + // (undocumented) + isComplete?: boolean; + // (undocumented) + isError?: boolean; + // (undocumented) + steps: (TaskStep & ScaffolderStep)[]; +} + // @alpha export const TemplateCard: (props: TemplateCardProps) => JSX.Element; diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index 384c4b80f2..ab11d73c46 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -17,10 +17,6 @@ import { JsonValue } from '@backstage/types'; import { Observable } from '@backstage/types'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; -import { ScaffolderTask as ScaffolderTask_2 } from '@backstage/plugin-scaffolder-react'; -import { ScaffolderTaskOutput as ScaffolderTaskOutput_2 } from '@backstage/plugin-scaffolder-react'; -import { ScaffolderTaskStatus as ScaffolderTaskStatus_2 } from '@backstage/plugin-scaffolder-react'; -import { Step as Step_2 } from '@backstage/plugin-scaffolder-react'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; @@ -236,6 +232,14 @@ export interface ScaffolderScaffoldResponse { taskId: string; } +// @public +export type ScaffolderStep = { + id: string; + status: ScaffolderTaskStatus; + endedAt?: string; + startedAt?: string; +}; + // @public export interface ScaffolderStreamLogsOptions { // (undocumented) @@ -281,38 +285,6 @@ export const SecretsContextProvider: ({ children, }: PropsWithChildren<{}>) => JSX.Element; -// @public -export type Step = { - id: string; - status: ScaffolderTaskStatus_2; - endedAt?: string; - startedAt?: string; -}; - -// @public (undocumented) -export interface StepperProps { - // (undocumented) - activeStep?: number; - // (undocumented) - steps: (TaskStep & Step_2)[]; -} - -// @public -export const TaskBorder: (props: { - isComplete: boolean; - isError: boolean; -}) => JSX.Element; - -// @public -export const TaskLogStream: (props: { - logs: { - [k: string]: string[]; - }; -}) => JSX.Element; - -// @public -export const TaskSteps: (props: StepperProps) => JSX.Element; - // @public export type TaskStream = { loading: boolean; @@ -321,11 +293,11 @@ export type TaskStream = { [stepId in string]: string[]; }; completed: boolean; - task?: ScaffolderTask_2; + task?: ScaffolderTask; steps: { - [stepId in string]: Step; + [stepId in string]: ScaffolderStep; }; - output?: ScaffolderTaskOutput_2; + output?: ScaffolderTaskOutput; }; // @public diff --git a/plugins/scaffolder-react/src/hooks/index.ts b/plugins/scaffolder-react/src/hooks/index.ts index 124950e83d..1016a0af09 100644 --- a/plugins/scaffolder-react/src/hooks/index.ts +++ b/plugins/scaffolder-react/src/hooks/index.ts @@ -19,5 +19,5 @@ export { useCustomLayouts } from './useCustomLayouts'; export { useTaskEventStream, type TaskStream, - type Step, + type ScaffolderStep, } from './useEventStream'; diff --git a/plugins/scaffolder-react/src/hooks/useEventStream.ts b/plugins/scaffolder-react/src/hooks/useEventStream.ts index 101063d258..d8c9cf4c1f 100644 --- a/plugins/scaffolder-react/src/hooks/useEventStream.ts +++ b/plugins/scaffolder-react/src/hooks/useEventStream.ts @@ -15,22 +15,23 @@ */ import { useImmerReducer } from 'use-immer'; import { useEffect } from 'react'; -import { - ScaffolderTask, - ScaffolderTaskStatus, - ScaffolderTaskOutput, - LogEvent, - scaffolderApiRef, -} from '@backstage/plugin-scaffolder-react'; + import { useApi } from '@backstage/core-plugin-api'; import { Subscription } from '@backstage/types'; +import { + LogEvent, + scaffolderApiRef, + ScaffolderTask, + ScaffolderTaskOutput, + ScaffolderTaskStatus, +} from '../api'; /** * The status of the step being processed * * @public */ -export type Step = { +export type ScaffolderStep = { id: string; status: ScaffolderTaskStatus; endedAt?: string; @@ -48,7 +49,7 @@ export type TaskStream = { stepLogs: { [stepId in string]: string[] }; completed: boolean; task?: ScaffolderTask; - steps: { [stepId in string]: Step }; + steps: { [stepId in string]: ScaffolderStep }; output?: ScaffolderTaskOutput; }; @@ -75,7 +76,7 @@ function reducer(draft: TaskStream, action: ReducerAction) { draft.steps = action.data.spec.steps.reduce((current, next) => { current[next.id] = { status: 'open', id: next.id }; return current; - }, {} as { [stepId in string]: Step }); + }, {} as { [stepId in string]: ScaffolderStep }); draft.stepLogs = action.data.spec.steps.reduce((current, next) => { current[next.id] = []; return current; @@ -153,7 +154,7 @@ export const useTaskEventStream = (taskId: string): TaskStream => { loading: true, completed: false, stepLogs: {} as { [stepId in string]: string[] }, - steps: {} as { [stepId in string]: Step }, + steps: {} as { [stepId in string]: ScaffolderStep }, }); useEffect(() => { diff --git a/plugins/scaffolder-react/src/index.ts b/plugins/scaffolder-react/src/index.ts index 38584ab25b..1b20c19414 100644 --- a/plugins/scaffolder-react/src/index.ts +++ b/plugins/scaffolder-react/src/index.ts @@ -20,4 +20,3 @@ export * from './secrets'; export * from './api'; export * from './hooks'; export * from './layouts'; -export * from './components'; diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 444e463064..51a00dfa64 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -26,7 +26,6 @@ import { type IChangeEvent } from '@rjsf/core-v5'; import { ErrorSchema } from '@rjsf/utils'; import React, { useCallback, useMemo, useState, type ReactNode } from 'react'; import { NextFieldExtensionOptions } from '../../extensions'; -import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; import { createAsyncValidators, type FormValidation, @@ -36,11 +35,14 @@ import { useTemplateSchema } from '../../hooks/useTemplateSchema'; import validator from '@rjsf/validator-ajv8'; import { useFormDataFromQuery } from '../../hooks'; import { FormProps } from '../../types'; -import { LayoutOptions } from '@backstage/plugin-scaffolder-react'; import { useTransformSchemaToProps } from '../../hooks/useTransformSchemaToProps'; import { hasErrors } from './utils'; import * as FieldOverrides from './FieldOverrides'; import { Form } from '../Form'; +import { + TemplateParameterSchema, + LayoutOptions, +} from '@backstage/plugin-scaffolder-react'; const useStyles = makeStyles(theme => ({ backButton: { diff --git a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.test.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.test.tsx index afd1f58190..0945843961 100644 --- a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.test.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.test.tsx @@ -15,8 +15,8 @@ */ import React from 'react'; import { TaskSteps } from './TaskSteps'; -import { ScaffolderTaskStatus } from '@backstage/plugin-scaffolder-react'; import { renderInTestApp } from '@backstage/test-utils'; +import { ScaffolderTaskStatus } from '../../../api'; describe('TaskSteps', () => { it('should render each of the steps', async () => { diff --git a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx index cecf3dc76c..632500f07e 100644 --- a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskSteps.tsx @@ -24,10 +24,10 @@ import { Paper, } from '@material-ui/core'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; -import { type Step } from '@backstage/plugin-scaffolder-react'; import { StepIcon } from './StepIcon'; import { StepTime } from './StepTime'; import { TaskBorder } from './TaskBorder'; +import { ScaffolderStep } from '@backstage/plugin-scaffolder-react'; /** * Props for the TaskSteps component @@ -35,7 +35,7 @@ import { TaskBorder } from './TaskBorder'; * @alpha */ export interface TaskStepsProps { - steps: (TaskStep & Step)[]; + steps: (TaskStep & ScaffolderStep)[]; activeStep?: number; isComplete?: boolean; isError?: boolean; diff --git a/plugins/scaffolder-react/src/next/extensions/index.tsx b/plugins/scaffolder-react/src/next/extensions/index.tsx index b2d402c93f..a856addb29 100644 --- a/plugins/scaffolder-react/src/next/extensions/index.tsx +++ b/plugins/scaffolder-react/src/next/extensions/index.tsx @@ -21,9 +21,8 @@ import { } from './types'; import { Extension, attachComponentData } from '@backstage/core-plugin-api'; import { UIOptionsType } from '@rjsf/utils'; -// eslint-disable-next-line import/no-extraneous-dependencies -import { FieldExtensionComponent } from '@backstage/plugin-scaffolder-react'; import { FIELD_EXTENSION_KEY } from '../../extensions/keys'; +import { FieldExtensionComponent } from '@backstage/plugin-scaffolder-react'; /** * Method for creating field extensions that can be used in the scaffolder diff --git a/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx b/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx index ab33652338..153c48468d 100644 --- a/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx +++ b/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx @@ -18,7 +18,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { TestApiProvider } from '@backstage/test-utils'; import React from 'react'; import { featureFlagsApiRef } from '@backstage/core-plugin-api'; -import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; +import { TemplateParameterSchema } from '../../types'; describe('useTemplateSchema', () => { it('should generate the correct schema', () => { diff --git a/plugins/scaffolder-react/src/next/hooks/useTransformSchemaToProps.ts b/plugins/scaffolder-react/src/next/hooks/useTransformSchemaToProps.ts index 612ad5427b..1e714eb874 100644 --- a/plugins/scaffolder-react/src/next/hooks/useTransformSchemaToProps.ts +++ b/plugins/scaffolder-react/src/next/hooks/useTransformSchemaToProps.ts @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { LayoutOptions } from '../../layouts'; import { type ParsedTemplateSchema } from './useTemplateSchema'; -import { type LayoutOptions } from '@backstage/plugin-scaffolder-react'; interface Options { layouts?: LayoutOptions[]; From 1545e3293ed7a8f76470d9ffc971052d75035c6d Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 27 Feb 2023 13:41:58 +0100 Subject: [PATCH 103/236] remove redundant link Signed-off-by: Johan Haals --- docs/plugins/structure-of-a-plugin.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/plugins/structure-of-a-plugin.md b/docs/plugins/structure-of-a-plugin.md index c71b27b448..8bf21bf589 100644 --- a/docs/plugins/structure-of-a-plugin.md +++ b/docs/plugins/structure-of-a-plugin.md @@ -117,5 +117,3 @@ backend-side authorization. To smooth this process out you can use proxy - either the one you already have (like Nginx, HAProxy, etc.) or the proxy-backend plugin that we provide for the Backstage backend. [Read more](https://github.com/backstage/backstage/blob/master/plugins/proxy-backend/README.md) - -[Back to Getting Started](../README.md) From 54a1e133b56ab96ae1384ced8c09fe7be3866c8f Mon Sep 17 00:00:00 2001 From: Morgan Bentell Date: Mon, 27 Feb 2023 13:53:20 +0100 Subject: [PATCH 104/236] enable pointer events for element that has new classname in newer versions of mkdocs-material Signed-off-by: Morgan Bentell --- .changeset/great-trains-jam.md | 5 +++++ .../techdocs/src/reader/transformers/styles/rules/layout.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/great-trains-jam.md diff --git a/.changeset/great-trains-jam.md b/.changeset/great-trains-jam.md new file mode 100644 index 0000000000..91133bfe32 --- /dev/null +++ b/.changeset/great-trains-jam.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fix bug that caused next and previous links not to work with certain versions of mkdocs-material diff --git a/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts index e6bad41045..979a616f01 100644 --- a/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts +++ b/plugins/techdocs/src/reader/transformers/styles/rules/layout.ts @@ -108,7 +108,7 @@ export default ({ theme, sidebar }: RuleOptions) => ` pointer-events: none; } -.md-footer-nav__link { +.md-footer-nav__link, .md-footer__link { pointer-events: all; } From ab229c55f21ede3a8820ebd3db871b9ddb863658 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 27 Feb 2023 13:55:50 +0100 Subject: [PATCH 105/236] add patch changeset for cli Signed-off-by: Johan Haals --- .changeset/twelve-cars-push.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/twelve-cars-push.md b/.changeset/twelve-cars-push.md index 632688c933..599d456d2b 100644 --- a/.changeset/twelve-cars-push.md +++ b/.changeset/twelve-cars-push.md @@ -14,6 +14,7 @@ '@backstage/codemods': patch '@backstage/config': patch '@backstage/theme': patch +'@backstage/cli': patch --- Updated link to docs. From 4c0ba1cfc77df577e4fff9fc0de4773f7462a27f Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Mon, 27 Feb 2023 15:20:08 +0100 Subject: [PATCH 106/236] permission-node: improve createPermissionIntegrationRouter docs Signed-off-by: Vincenzo Scamporlino --- plugins/permission-node/api-report.md | 20 +++---- .../createPermissionIntegrationRouter.test.ts | 18 +++--- .../createPermissionIntegrationRouter.ts | 60 +++++++++++++------ 3 files changed, 58 insertions(+), 40 deletions(-) diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 5d2966ec73..52e666996a 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -112,22 +112,20 @@ export const createConditionTransformer: < ) => ConditionTransformer; // @public -export const createPermissionIntegrationRouter: < +export function createPermissionIntegrationRouter< TResourceType extends string, TResource, >( - options: CreatePermissionIntegrationRouterOptions, -) => express.Router; + options: CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource + >, +): express.Router; // @public -export type CreatePermissionIntegrationRouterOptions< - TResourceType extends string, - TResource, -> = - | { - permissions: Array; - } - | CreatePermissionIntegrationRouterResourceOptions; +export function createPermissionIntegrationRouter(options: { + permissions: Array; +}): express.Router; // @public export type CreatePermissionIntegrationRouterResourceOptions< diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts index 8b918cabb2..1a7462665e 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.test.ts @@ -65,16 +65,14 @@ const createApp = ( | typeof defaultMockedGetResources | null = defaultMockedGetResources, ) => { - const router = createPermissionIntegrationRouter( - mockedGetResources - ? { - resourceType: 'test-resource', - permissions: [testPermission], - getResources: mockedGetResources, - rules: [testRule1, testRule2], - } - : { permissions: [testPermission] }, - ); + const router = mockedGetResources + ? createPermissionIntegrationRouter({ + resourceType: 'test-resource', + permissions: [testPermission], + getResources: mockedGetResources, + rules: [testRule1, testRule2], + }) + : createPermissionIntegrationRouter({ permissions: [testPermission] }); return express().use(router); }; diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index 715b1ec574..087de23198 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -182,20 +182,6 @@ export type CreatePermissionIntegrationRouterResourceOptions< ) => Promise>; }; -/** - * Options for creating a permission integration router. - * - * @public - */ -export type CreatePermissionIntegrationRouterOptions< - TResourceType extends string, - TResource, -> = - | { - permissions: Array; - } - | CreatePermissionIntegrationRouterResourceOptions; - /** * Create an express Router which provides an authorization route to allow * integration between the permission backend and other Backstage backend @@ -203,6 +189,9 @@ export type CreatePermissionIntegrationRouterOptions< * their resources should add the router created by this function to their * express app inside their `createRouter` implementation. * + * In case the `permissions` option is provided, the router also + * provides a route that exposes permissions and routes of a plugin. + * * @remarks * * To make this concrete, we can use the Backstage software catalog as an @@ -231,12 +220,40 @@ export type CreatePermissionIntegrationRouterOptions< * * @public */ -export const createPermissionIntegrationRouter = < +export function createPermissionIntegrationRouter< TResourceType extends string, TResource, >( - options: CreatePermissionIntegrationRouterOptions, -): express.Router => { + options: CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource + >, +): express.Router; + +/** + * + * Create an express Router which provides a route that exposes + * permissions and routes of a plugin. + * @public + */ +export function createPermissionIntegrationRouter(options: { + permissions: Array; +}): express.Router; + +/** + * @public + */ +export function createPermissionIntegrationRouter< + TResourceType extends string, + TResource, +>( + options: + | { permissions: Array } + | CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource + >, +): express.Router { const router = Router(); router.use(express.json()); @@ -326,13 +343,18 @@ export const createPermissionIntegrationRouter = < router.use(errorHandler()); return router; -}; +} function isCreatePermissionIntegrationRouterResourceOptions< TResourceType extends string, TResource, >( - options: CreatePermissionIntegrationRouterOptions, + options: + | { permissions: Array } + | CreatePermissionIntegrationRouterResourceOptions< + TResourceType, + TResource + >, ): options is CreatePermissionIntegrationRouterResourceOptions< TResourceType, TResource From 65454876fb21ec1a8cda1dc556aa1f2af3d874fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Mon, 27 Feb 2023 15:31:15 +0100 Subject: [PATCH 107/236] unpack props inside component bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/clever-dogs-cheat.md | 21 +++++++++ plugins/auth-node/api-report.md | 8 ++-- .../auth-node/src/DefaultIdentityClient.ts | 9 ++-- .../api-report.md | 2 +- .../src/analyzers/GithubLocationAnalyzer.ts | 3 +- plugins/catalog-react/api-report.md | 27 +++++------- plugins/catalog-react/src/hooks/useEntity.tsx | 9 +--- .../src/hooks/useEntityListProvider.tsx | 8 ++-- .../catalog-react/src/testUtils/providers.tsx | 13 +++--- plugins/explore/api-report.md | 6 +-- plugins/explore/src/api/ExploreClient.ts | 18 ++++---- plugins/scaffolder-backend/api-report.md | 16 +++---- .../builtin/publish/githubPullRequest.ts | 14 +++--- .../src/scaffolder/tasks/DatabaseTaskStore.ts | 3 +- .../src/scaffolder/tasks/types.ts | 11 +++-- plugins/scaffolder-react/api-report.md | 6 +-- .../src/secrets/SecretsContext.tsx | 4 +- plugins/scaffolder/api-report.md | 2 +- .../src/components/TaskPage/TaskPage.tsx | 4 +- .../api-report.md | 9 ++-- .../src/engines/ElasticSearchSearchEngine.ts | 27 +++++++----- plugins/search-react/api-report.md | 21 +++------ .../HighlightedSearchResultText.tsx | 11 ++--- .../SearchAutocompleteDefaultOption.tsx | 44 +++++++++++-------- .../components/SearchFilter/SearchFilter.tsx | 8 ++-- plugins/search/api-report.md | 20 +++------ .../HomePageComponent/HomePageSearchBar.tsx | 2 +- .../components/SearchModal/SearchModal.tsx | 9 ++-- .../components/SearchModal/useSearchModal.tsx | 9 ++-- plugins/sonarqube/api-report.md | 5 +-- plugins/sonarqube/src/api/SonarQubeClient.ts | 9 ++-- plugins/splunk-on-call/api-report.md | 11 +---- plugins/splunk-on-call/src/api/client.ts | 22 +++++----- .../src/components/SplunkOnCallPage.tsx | 7 +-- .../api-report.md | 15 +------ .../src/service/JsonRulesEngineFactChecker.ts | 25 ++++------- plugins/tech-insights/api-report.md | 6 +-- .../components/BooleanCheck/BooleanCheck.tsx | 4 +- plugins/techdocs-node/api-report.md | 11 ++--- .../techdocs-node/src/stages/prepare/dir.ts | 4 +- .../src/stages/prepare/preparers.ts | 11 +++-- .../techdocs-node/src/stages/prepare/url.ts | 4 +- .../src/stages/publish/publish.ts | 4 +- plugins/techdocs-react/api-report.md | 8 +--- plugins/techdocs-react/src/component.tsx | 8 ++-- plugins/techdocs-react/src/context.tsx | 4 +- plugins/techdocs/api-report.md | 23 +++++----- .../components/Grids/EntityListDocsGrid.tsx | 10 ++--- .../src/home/components/Tables/actions.tsx | 3 +- .../TechDocsReaderPage/TechDocsReaderPage.tsx | 6 +-- .../components/TechDocsReaderProvider.tsx | 6 +-- 51 files changed, 253 insertions(+), 297 deletions(-) create mode 100644 .changeset/clever-dogs-cheat.md diff --git a/.changeset/clever-dogs-cheat.md b/.changeset/clever-dogs-cheat.md new file mode 100644 index 0000000000..56fcf3d30c --- /dev/null +++ b/.changeset/clever-dogs-cheat.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-tech-insights-backend-module-jsonfc': patch +'@backstage/plugin-catalog-backend-module-github': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-splunk-on-call': patch +'@backstage/plugin-techdocs-react': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-tech-insights': patch +'@backstage/plugin-techdocs-node': patch +'@backstage/plugin-search-react': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-auth-node': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-search': patch +--- + +Minor API report tweaks diff --git a/plugins/auth-node/api-report.md b/plugins/auth-node/api-report.md index c6d4b4e3f8..bc9e392929 100644 --- a/plugins/auth-node/api-report.md +++ b/plugins/auth-node/api-report.md @@ -29,11 +29,9 @@ export class DefaultIdentityClient implements IdentityApi { authenticate(token: string | undefined): Promise; static create(options: IdentityClientOptions): DefaultIdentityClient; // (undocumented) - getIdentity({ - request, - }: IdentityApiGetIdentityRequest): Promise< - BackstageIdentityResponse | undefined - >; + getIdentity( + options: IdentityApiGetIdentityRequest, + ): Promise; } // @public diff --git a/plugins/auth-node/src/DefaultIdentityClient.ts b/plugins/auth-node/src/DefaultIdentityClient.ts index b8f6b73865..8af8bf09e1 100644 --- a/plugins/auth-node/src/DefaultIdentityClient.ts +++ b/plugins/auth-node/src/DefaultIdentityClient.ts @@ -78,13 +78,16 @@ export class DefaultIdentityClient implements IdentityApi { : ['ES256']; } - async getIdentity({ request }: IdentityApiGetIdentityRequest) { - if (!request.headers.authorization) { + async getIdentity(options: IdentityApiGetIdentityRequest) { + const { + request: { headers }, + } = options; + if (!headers.authorization) { return undefined; } try { return await this.authenticate( - getBearerTokenFromAuthorizationHeader(request.headers.authorization), + getBearerTokenFromAuthorizationHeader(headers.authorization), ); } catch (e) { throw new AuthenticationError(e.message); diff --git a/plugins/catalog-backend-module-github/api-report.md b/plugins/catalog-backend-module-github/api-report.md index e2b73d267b..c732740997 100644 --- a/plugins/catalog-backend-module-github/api-report.md +++ b/plugins/catalog-backend-module-github/api-report.md @@ -103,7 +103,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { export class GithubLocationAnalyzer implements ScmLocationAnalyzer { constructor(options: GithubLocationAnalyzerOptions); // (undocumented) - analyze({ url, catalogFilename }: AnalyzeOptions): Promise<{ + analyze(options: AnalyzeOptions): Promise<{ existing: { location: { type: string; diff --git a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts index 139a92ee83..9341743413 100644 --- a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts +++ b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts @@ -63,7 +63,8 @@ export class GithubLocationAnalyzer implements ScmLocationAnalyzer { return integration?.type === 'github'; } - async analyze({ url, catalogFilename }: AnalyzeOptions) { + async analyze(options: AnalyzeOptions) { + const { url, catalogFilename } = options; const { owner, name: repo } = parseGitUrl(url); const catalogFile = catalogFilename || 'catalog-info.yaml'; diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 55c72122f5..77f439018f 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -27,13 +27,9 @@ import { SystemEntity } from '@backstage/catalog-model'; import { TableColumn } from '@backstage/core-components'; // @public -export const AsyncEntityProvider: ({ - children, - entity, - loading, - error, - refresh, -}: AsyncEntityProviderProps) => JSX.Element; +export const AsyncEntityProvider: ( + props: AsyncEntityProviderProps, +) => JSX.Element; // @public export interface AsyncEntityProviderProps { @@ -220,9 +216,9 @@ export type EntityListContextProps< }; // @public -export const EntityListProvider: ({ - children, -}: PropsWithChildren<{}>) => JSX.Element; +export const EntityListProvider: ( + props: PropsWithChildren<{}>, +) => JSX.Element; // @public (undocumented) export type EntityLoadingStatus = { @@ -476,12 +472,11 @@ export function InspectEntityDialog(props: { // @public (undocumented) export function MockEntityListContextProvider< T extends DefaultEntityFilters = DefaultEntityFilters, ->({ - children, - value, -}: PropsWithChildren<{ - value?: Partial>; -}>): JSX.Element; +>( + props: PropsWithChildren<{ + value?: Partial>; + }>, +): JSX.Element; // @public export class MockStarredEntitiesApi implements StarredEntitiesApi { diff --git a/plugins/catalog-react/src/hooks/useEntity.tsx b/plugins/catalog-react/src/hooks/useEntity.tsx index 84b7883c8a..63e7c7b6d8 100644 --- a/plugins/catalog-react/src/hooks/useEntity.tsx +++ b/plugins/catalog-react/src/hooks/useEntity.tsx @@ -55,13 +55,8 @@ export interface AsyncEntityProviderProps { * * @public */ -export const AsyncEntityProvider = ({ - children, - entity, - loading, - error, - refresh, -}: AsyncEntityProviderProps) => { +export const AsyncEntityProvider = (props: AsyncEntityProviderProps) => { + const { children, entity, loading, error, refresh } = props; const value = { entity, loading, error, refresh }; // We provide both the old and the new context, since // consumers might be doing things like `useContext(EntityContext)` diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index b5dfb4d8f7..a2b680c02e 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -115,9 +115,9 @@ type OutputState = { * Provides entities and filters for a catalog listing. * @public */ -export const EntityListProvider = ({ - children, -}: PropsWithChildren<{}>) => { +export const EntityListProvider = ( + props: PropsWithChildren<{}>, +) => { const isMounted = useMountedState(); const catalogApi = useApi(catalogApiRef); const [requestedFilters, setRequestedFilters] = useState( @@ -248,7 +248,7 @@ export const EntityListProvider = ({ return ( - {children} + {props.children} ); }; diff --git a/plugins/catalog-react/src/testUtils/providers.tsx b/plugins/catalog-react/src/testUtils/providers.tsx index 9bf5b21f94..8ab6d118a9 100644 --- a/plugins/catalog-react/src/testUtils/providers.tsx +++ b/plugins/catalog-react/src/testUtils/providers.tsx @@ -29,12 +29,13 @@ import { /** @public */ export function MockEntityListContextProvider< T extends DefaultEntityFilters = DefaultEntityFilters, ->({ - children, - value, -}: PropsWithChildren<{ - value?: Partial>; -}>) { +>( + props: PropsWithChildren<{ + value?: Partial>; + }>, +) { + const { children, value } = props; + // Provides a default implementation that stores filter state, for testing components that // reflect filter state. const [filters, setFilters] = useState(value?.filters ?? ({} as T)); diff --git a/plugins/explore/api-report.md b/plugins/explore/api-report.md index 8820411392..db89db6503 100644 --- a/plugins/explore/api-report.md +++ b/plugins/explore/api-report.md @@ -50,11 +50,7 @@ export const exploreApiRef: ApiRef; // @public export class ExploreClient implements ExploreApi { - constructor({ - discoveryApi, - fetchApi, - exploreToolsConfig, - }: { + constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi; exploreToolsConfig?: ExploreToolsConfig; diff --git a/plugins/explore/src/api/ExploreClient.ts b/plugins/explore/src/api/ExploreClient.ts index e913aeb041..9dca2755fe 100644 --- a/plugins/explore/src/api/ExploreClient.ts +++ b/plugins/explore/src/api/ExploreClient.ts @@ -24,7 +24,7 @@ import { ExploreToolsConfig } from '@backstage/plugin-explore-react'; import { ExploreApi } from './ExploreApi'; /** - * Default implementation of the ExploreApi. + * Default implementation of the {@link ExploreApi}. * * @public */ @@ -35,21 +35,19 @@ export class ExploreClient implements ExploreApi { private readonly exploreToolsConfig: ExploreToolsConfig | undefined; /** - * @remarks The exploreToolsConfig is for backwards compatibility with the exporeToolsConfigRef + * @remarks + * + * The `exploreToolsConfig` is for backwards compatibility with the `exploreToolsConfigRef`• * and will be removed in the future. */ - constructor({ - discoveryApi, - fetchApi, - exploreToolsConfig = undefined, - }: { + constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi; exploreToolsConfig?: ExploreToolsConfig; }) { - this.discoveryApi = discoveryApi; - this.fetchApi = fetchApi; - this.exploreToolsConfig = exploreToolsConfig; + this.discoveryApi = options.discoveryApi; + this.fetchApi = options.fetchApi; + this.exploreToolsConfig = options.exploreToolsConfig; } async getTools( diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index 293ff7ed29..2c11e361e7 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -459,11 +459,9 @@ export function createPublishGithubAction(options: { }>; // @public -export const createPublishGithubPullRequestAction: ({ - integrations, - githubCredentialsProvider, - clientFactory, -}: CreateGithubPullRequestActionOptions) => TemplateAction_2<{ +export const createPublishGithubPullRequestAction: ( + options: CreateGithubPullRequestActionOptions, +) => TemplateAction_2<{ title: string; branchName: string; description: string; @@ -581,7 +579,7 @@ export class DatabaseTaskStore implements TaskStore { }[]; }>; // (undocumented) - shutdownTask({ taskId }: TaskStoreShutDownTaskOptions): Promise; + shutdownTask(options: TaskStoreShutDownTaskOptions): Promise; } // @public @@ -792,7 +790,7 @@ export interface TaskStore { options: TaskStoreCreateTaskOptions, ): Promise; // (undocumented) - emitLogEvent({ taskId, body }: TaskStoreEmitOptions): Promise; + emitLogEvent(options: TaskStoreEmitOptions): Promise; // (undocumented) getTask(taskId: string): Promise; // (undocumented) @@ -802,7 +800,7 @@ export interface TaskStore { tasks: SerializedTask[]; }>; // (undocumented) - listEvents({ taskId, after }: TaskStoreListEventsOptions): Promise<{ + listEvents(options: TaskStoreListEventsOptions): Promise<{ events: SerializedTaskEvent[]; }>; // (undocumented) @@ -812,7 +810,7 @@ export interface TaskStore { }[]; }>; // (undocumented) - shutdownTask?({ taskId }: TaskStoreShutDownTaskOptions): Promise; + shutdownTask?(options: TaskStoreShutDownTaskOptions): Promise; } // @public diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index 764354e8ec..b8c0068d70 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -116,11 +116,15 @@ type GithubPullRequest = { * Creates a Github Pull Request action. * @public */ -export const createPublishGithubPullRequestAction = ({ - integrations, - githubCredentialsProvider, - clientFactory = defaultClientFactory, -}: CreateGithubPullRequestActionOptions) => { +export const createPublishGithubPullRequestAction = ( + options: CreateGithubPullRequestActionOptions, +) => { + const { + integrations, + githubCredentialsProvider, + clientFactory = defaultClientFactory, + } = options; + return createTemplateAction<{ title: string; branchName: string; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index b61c3d2676..879b457544 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -381,7 +381,8 @@ export class DatabaseTaskStore implements TaskStore { return { events }; } - async shutdownTask({ taskId }: TaskStoreShutDownTaskOptions): Promise { + async shutdownTask(options: TaskStoreShutDownTaskOptions): Promise { + const { taskId } = options; const message = `This task was marked as stale as it exceeded its timeout`; const statusStepEvents = (await this.listEvents({ taskId })).events.filter( diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 89f6c3e213..5a91802f3e 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -197,12 +197,11 @@ export interface TaskStore { }>; list?(options: { createdBy?: string }): Promise<{ tasks: SerializedTask[] }>; - emitLogEvent({ taskId, body }: TaskStoreEmitOptions): Promise; - listEvents({ - taskId, - after, - }: TaskStoreListEventsOptions): Promise<{ events: SerializedTaskEvent[] }>; - shutdownTask?({ taskId }: TaskStoreShutDownTaskOptions): Promise; + emitLogEvent(options: TaskStoreEmitOptions): Promise; + listEvents( + options: TaskStoreListEventsOptions, + ): Promise<{ events: SerializedTaskEvent[] }>; + shutdownTask?(options: TaskStoreShutDownTaskOptions): Promise; } export type WorkflowResponse = { output: { [key: string]: JsonValue } }; diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index 384c4b80f2..6f7f7d845b 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -277,9 +277,9 @@ export interface ScaffolderUseTemplateSecrets { } // @public -export const SecretsContextProvider: ({ - children, -}: PropsWithChildren<{}>) => JSX.Element; +export const SecretsContextProvider: ( + props: PropsWithChildren<{}>, +) => JSX.Element; // @public export type Step = { diff --git a/plugins/scaffolder-react/src/secrets/SecretsContext.tsx b/plugins/scaffolder-react/src/secrets/SecretsContext.tsx index af5c2393cd..3cbd23597a 100644 --- a/plugins/scaffolder-react/src/secrets/SecretsContext.tsx +++ b/plugins/scaffolder-react/src/secrets/SecretsContext.tsx @@ -43,14 +43,14 @@ const SecretsContext = createVersionedContext<{ * The Context Provider that holds the state for the secrets. * @public */ -export const SecretsContextProvider = ({ children }: PropsWithChildren<{}>) => { +export const SecretsContextProvider = (props: PropsWithChildren<{}>) => { const [secrets, setSecrets] = useState>({}); return ( - {children} + {props.children} ); }; diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 43cc657a03..6b8d6d345f 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -460,7 +460,7 @@ export type ScaffolderTaskStatus = ScaffolderTaskStatus_2; export type ScaffolderUseTemplateSecrets = ScaffolderUseTemplateSecrets_2; // @public -export const TaskPage: ({ loadingText }: TaskPageProps) => JSX.Element; +export const TaskPage: (props: TaskPageProps) => JSX.Element; // @public export type TaskPageProps = { diff --git a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx index a45f0cf413..f78d48e686 100644 --- a/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx +++ b/plugins/scaffolder/src/components/TaskPage/TaskPage.tsx @@ -243,7 +243,9 @@ export type TaskPageProps = { * * @public */ -export const TaskPage = ({ loadingText }: TaskPageProps) => { +export const TaskPage = (props: TaskPageProps) => { + const { loadingText } = props; + const classes = useStyles(); const navigate = useNavigate(); const rootPath = useRouteRef(rootRouteRef); diff --git a/plugins/search-backend-module-elasticsearch/api-report.md b/plugins/search-backend-module-elasticsearch/api-report.md index 8545e3fd8f..c7add1e8cb 100644 --- a/plugins/search-backend-module-elasticsearch/api-report.md +++ b/plugins/search-backend-module-elasticsearch/api-report.md @@ -327,12 +327,9 @@ export class ElasticSearchSearchEngine implements SearchEngine { highlightOptions?: ElasticSearchHighlightOptions, ); // (undocumented) - static fromConfig({ - logger, - config, - aliasPostfix, - indexPrefix, - }: ElasticSearchOptions): Promise; + static fromConfig( + options: ElasticSearchOptions, + ): Promise; // (undocumented) getIndexer(type: string): Promise; newClient(create: (options: ElasticSearchClientOptions) => T): T; diff --git a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts index c2f4224db3..25df3101de 100644 --- a/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts +++ b/plugins/search-backend-module-elasticsearch/src/engines/ElasticSearchSearchEngine.ts @@ -138,27 +138,29 @@ export class ElasticSearchSearchEngine implements SearchEngine { }; } - static async fromConfig({ - logger, - config, - aliasPostfix = `search`, - indexPrefix = ``, - }: ElasticSearchOptions) { - const options = await createElasticSearchClientOptions( + static async fromConfig(options: ElasticSearchOptions) { + const { + logger, + config, + aliasPostfix = `search`, + indexPrefix = ``, + } = options; + + const clientOptions = await createElasticSearchClientOptions( config.getConfig('search.elasticsearch'), ); - if (options.provider === 'elastic') { + if (clientOptions.provider === 'elastic') { logger.info('Initializing Elastic.co ElasticSearch search engine.'); - } else if (options.provider === 'aws') { + } else if (clientOptions.provider === 'aws') { logger.info('Initializing AWS OpenSearch search engine.'); - } else if (options.provider === 'opensearch') { + } else if (clientOptions.provider === 'opensearch') { logger.info('Initializing OpenSearch search engine.'); } else { logger.info('Initializing ElasticSearch search engine.'); } return new ElasticSearchSearchEngine( - options, + clientOptions, aliasPostfix, indexPrefix, logger, @@ -175,12 +177,13 @@ export class ElasticSearchSearchEngine implements SearchEngine { * This need not be the same client that the engine uses internally. * * @example Instantiate an instance of an Elasticsearch client. + * * ```ts * import { isOpenSearchCompatible } from '@backstage/plugin-search-backend-module-elasticsearch'; * import { Client } from '@elastic/elasticsearch'; * * const client = searchEngine.newClient(options => { - * // This typeguard ensures options are compatible with either OpenSearch + * // This type guard ensures options are compatible with either OpenSearch * // or Elasticsearch client constructors. * if (!isOpenSearchCompatible(options)) { * return new Client(options); diff --git a/plugins/search-react/api-report.md b/plugins/search-react/api-report.md index 375af20d5c..bd8d1e0794 100644 --- a/plugins/search-react/api-report.md +++ b/plugins/search-react/api-report.md @@ -60,11 +60,9 @@ export type DefaultResultListItemProps = { }; // @public (undocumented) -export const HighlightedSearchResultText: ({ - text, - preTag, - postTag, -}: HighlightedSearchResultTextProps) => JSX.Element; +export const HighlightedSearchResultText: ( + props: HighlightedSearchResultTextProps, +) => JSX.Element; // @public export type HighlightedSearchResultTextProps = { @@ -100,14 +98,9 @@ export type SearchAutocompleteComponent =