From 5af3be07bb17cefaf72f8a3bfb49f3fe2841b901 Mon Sep 17 00:00:00 2001 From: Blake Stoddard Date: Mon, 28 Nov 2022 09:05:36 -0500 Subject: [PATCH 001/205] 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/205] 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/205] 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/205] 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/205] 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/205] 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 5cee4f71caba40e2758f1f9a458178d5ec274ebb Mon Sep 17 00:00:00 2001 From: Claire Casey Date: Tue, 14 Feb 2023 14:45:56 -0500 Subject: [PATCH 007/205] 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 008/205] 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 009/205] 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 010/205] 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 011/205] 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 012/205] 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 013/205] 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 ad0f2169d697d8dd5ff0fb3a80bf7b3151e1dd24 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Fri, 17 Feb 2023 10:10:21 +0100 Subject: [PATCH 014/205] 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 015/205] 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 016/205] 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 017/205] 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 018/205] 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 019/205] 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 020/205] 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 021/205] 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 919976d33c962d0db730a1d36cc4534e12ebad40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 05:37:46 +0000 Subject: [PATCH 022/205] fix(deps): update jest monorepo Signed-off-by: Renovate Bot --- yarn.lock | 756 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 386 insertions(+), 370 deletions(-) diff --git a/yarn.lock b/yarn.lock index ee6415ebba..084164a6d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10800,50 +10800,50 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/console@npm:29.3.1" +"@jest/console@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/console@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 "@types/node": "*" chalk: ^4.0.0 - jest-message-util: ^29.3.1 - jest-util: ^29.3.1 + jest-message-util: ^29.4.3 + jest-util: ^29.4.3 slash: ^3.0.0 - checksum: 9eecbfb6df4f5b810374849b7566d321255e6fd6e804546236650384966be532ff75a3e445a3277eadefe67ddf4dc56cd38332abd72d6a450f1bea9866efc6d7 + checksum: 8d9b163febe735153b523db527742309f4d598eda22f17f04e030060329bd3da4de7420fc1f7812f7a16f08273654a7de094c4b4e8b81a99dbfc17cfb1629008 languageName: node linkType: hard -"@jest/core@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/core@npm:29.3.1" +"@jest/core@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/core@npm:29.4.3" dependencies: - "@jest/console": ^29.3.1 - "@jest/reporters": ^29.3.1 - "@jest/test-result": ^29.3.1 - "@jest/transform": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/console": ^29.4.3 + "@jest/reporters": ^29.4.3 + "@jest/test-result": ^29.4.3 + "@jest/transform": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 ci-info: ^3.2.0 exit: ^0.1.2 graceful-fs: ^4.2.9 - jest-changed-files: ^29.2.0 - jest-config: ^29.3.1 - jest-haste-map: ^29.3.1 - jest-message-util: ^29.3.1 - jest-regex-util: ^29.2.0 - jest-resolve: ^29.3.1 - jest-resolve-dependencies: ^29.3.1 - jest-runner: ^29.3.1 - jest-runtime: ^29.3.1 - jest-snapshot: ^29.3.1 - jest-util: ^29.3.1 - jest-validate: ^29.3.1 - jest-watcher: ^29.3.1 + jest-changed-files: ^29.4.3 + jest-config: ^29.4.3 + jest-haste-map: ^29.4.3 + jest-message-util: ^29.4.3 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.4.3 + jest-resolve-dependencies: ^29.4.3 + jest-runner: ^29.4.3 + jest-runtime: ^29.4.3 + jest-snapshot: ^29.4.3 + jest-util: ^29.4.3 + jest-validate: ^29.4.3 + jest-watcher: ^29.4.3 micromatch: ^4.0.4 - pretty-format: ^29.3.1 + pretty-format: ^29.4.3 slash: ^3.0.0 strip-ansi: ^6.0.0 peerDependencies: @@ -10851,7 +10851,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: e3ac9201e8a084ccd832b17877b56490402b919f227622bb24f9372931e77b869e60959d34144222ce20fb619d0a6a6be20b257adb077a6b0f430a4584a45b0f + checksum: 4aa10644d66f44f051d5dd9cdcedce27acc71216dbcc5e7adebdea458e27aefe27c78f457d7efd49f58b968c35f42de5a521590876e2013593e675120b9e6ab1 languageName: node linkType: hard @@ -10864,15 +10864,15 @@ __metadata: languageName: node linkType: hard -"@jest/environment@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/environment@npm:29.3.1" +"@jest/environment@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/environment@npm:29.4.3" dependencies: - "@jest/fake-timers": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/fake-timers": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" - jest-mock: ^29.3.1 - checksum: 974102aba7cc80508f787bb5504dcc96e5392e0a7776a63dffbf54ddc2c77d52ef4a3c08ed2eedec91965befff873f70cd7c9ed56f62bb132dcdb821730e6076 + jest-mock: ^29.4.3 + checksum: 7c1b0cc4e84b90f8a3bbeca9bbf088882c88aee70a81b3b8e24265dcb1cbc302cd1eee3319089cf65bfd39adbaea344903c712afea106cb8da6c86088d99c5fb languageName: node linkType: hard @@ -10885,60 +10885,60 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/expect-utils@npm:29.3.1" +"@jest/expect-utils@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/expect-utils@npm:29.4.3" dependencies: - jest-get-type: ^29.2.0 - checksum: 7f3b853eb1e4299988f66b9aa49c1aacb7b8da1cf5518dca4ccd966e865947eed8f1bde6c8f5207d8400e9af870112a44b57aa83515ad6ea5e4a04a971863adb + jest-get-type: ^29.4.3 + checksum: 2bbed39ff2fb59f5acac465a1ce7303e3b4b62b479e4f386261986c9827f7f799ea912761e22629c5daf10addf8513f16733c14a29c2647bb66d4ee625e9ff92 languageName: node linkType: hard -"@jest/expect@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/expect@npm:29.3.1" +"@jest/expect@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/expect@npm:29.4.3" dependencies: - expect: ^29.3.1 - jest-snapshot: ^29.3.1 - checksum: 1d7b5cc735c8a99bfbed884d80fdb43b23b3456f4ec88c50fd86404b097bb77fba84f44e707fc9b49f106ca1154ae03f7c54dc34754b03f8a54eeb420196e5bf + expect: ^29.4.3 + jest-snapshot: ^29.4.3 + checksum: 08d0d40077ec99a7491fe59d05821dbd31126cfba70875855d8a063698b7126b5f6c309c50811caacc6ae2f727c6e44f51bdcf1d6c1ea832b4f020045ef22d45 languageName: node linkType: hard -"@jest/fake-timers@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/fake-timers@npm:29.3.1" +"@jest/fake-timers@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/fake-timers@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 - "@sinonjs/fake-timers": ^9.1.2 + "@jest/types": ^29.4.3 + "@sinonjs/fake-timers": ^10.0.2 "@types/node": "*" - jest-message-util: ^29.3.1 - jest-mock: ^29.3.1 - jest-util: ^29.3.1 - checksum: b1dafa8cdc439ef428cd772c775f0b22703677f52615513eda11a104bbfc352d7ec69b1225db95d4ef2e1b4ef0f23e1a7d96de5313aeb0950f672e6548ae069d + jest-message-util: ^29.4.3 + jest-mock: ^29.4.3 + jest-util: ^29.4.3 + checksum: adaceb9143c395cccf3d7baa0e49b7042c3092a554e8283146df19926247e34c21b5bde5688bb90e9e87b4a02e4587926c5d858ee0a38d397a63175d0a127874 languageName: node linkType: hard -"@jest/globals@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/globals@npm:29.3.1" +"@jest/globals@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/globals@npm:29.4.3" dependencies: - "@jest/environment": ^29.3.1 - "@jest/expect": ^29.3.1 - "@jest/types": ^29.3.1 - jest-mock: ^29.3.1 - checksum: 4d2b9458aabf7c28fd167e53984477498c897b64eec67a7f84b8fff465235cae1456ee0721cb0e7943f0cda443c7656adb9801f9f34e27495b8ebbd9f3033100 + "@jest/environment": ^29.4.3 + "@jest/expect": ^29.4.3 + "@jest/types": ^29.4.3 + jest-mock: ^29.4.3 + checksum: ea76b546ceb4aa5ce2bb3726df12f989b23150b51c9f7664790caa81b943012a657cf3a8525498af1c3518cdb387f54b816cfba1b0ddd22c7b20f03b1d7290b4 languageName: node linkType: hard -"@jest/reporters@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/reporters@npm:29.3.1" +"@jest/reporters@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/reporters@npm:29.4.3" dependencies: "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.3.1 - "@jest/test-result": ^29.3.1 - "@jest/transform": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/console": ^29.4.3 + "@jest/test-result": ^29.4.3 + "@jest/transform": ^29.4.3 + "@jest/types": ^29.4.3 "@jridgewell/trace-mapping": ^0.3.15 "@types/node": "*" chalk: ^4.0.0 @@ -10951,9 +10951,9 @@ __metadata: istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.0 istanbul-reports: ^3.1.3 - jest-message-util: ^29.3.1 - jest-util: ^29.3.1 - jest-worker: ^29.3.1 + jest-message-util: ^29.4.3 + jest-util: ^29.4.3 + jest-worker: ^29.4.3 slash: ^3.0.0 string-length: ^4.0.1 strip-ansi: ^6.0.0 @@ -10963,7 +10963,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 273e0c6953285f01151e9d84ac1e55744802a1ec79fb62dafeea16a49adfe7b24e7f35bef47a0214e5e057272dbfdacf594208286b7766046fd0f3cfa2043840 + checksum: 7aa2e429c915bd96c3334962addd69d2bbf52065725757ddde26b293f8c4420a1e8c65363cc3e1e5ec89100a5273ccd3771bec58325a2cc0d97afdc81995073a languageName: node linkType: hard @@ -10976,70 +10976,70 @@ __metadata: languageName: node linkType: hard -"@jest/schemas@npm:^29.0.0": - version: 29.0.0 - resolution: "@jest/schemas@npm:29.0.0" +"@jest/schemas@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/schemas@npm:29.4.3" dependencies: - "@sinclair/typebox": ^0.24.1 - checksum: 41355c78f09eb1097e57a3c5d0ca11c9099e235e01ea5fa4e3953562a79a6a9296c1d300f1ba50ca75236048829e056b00685cd2f1ff8285e56fd2ce01249acb + "@sinclair/typebox": ^0.25.16 + checksum: ac754e245c19dc39e10ebd41dce09040214c96a4cd8efa143b82148e383e45128f24599195ab4f01433adae4ccfbe2db6574c90db2862ccd8551a86704b5bebd languageName: node linkType: hard -"@jest/source-map@npm:^29.2.0": - version: 29.2.0 - resolution: "@jest/source-map@npm:29.2.0" +"@jest/source-map@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/source-map@npm:29.4.3" dependencies: "@jridgewell/trace-mapping": ^0.3.15 callsites: ^3.0.0 graceful-fs: ^4.2.9 - checksum: 09f76ab63d15dcf44b3035a79412164f43be34ec189575930f1a00c87e36ea0211ebd6a4fbe2253c2516e19b49b131f348ddbb86223ca7b6bbac9a6bc76ec96e + checksum: 2301d225145f8123540c0be073f35a80fd26a2f5e59550fd68525d8cea580fb896d12bf65106591ffb7366a8a19790076dbebc70e0f5e6ceb51f81827ed1f89c languageName: node linkType: hard -"@jest/test-result@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/test-result@npm:29.3.1" +"@jest/test-result@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/test-result@npm:29.4.3" dependencies: - "@jest/console": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/console": ^29.4.3 + "@jest/types": ^29.4.3 "@types/istanbul-lib-coverage": ^2.0.0 collect-v8-coverage: ^1.0.0 - checksum: b24ac283321189b624c372a6369c0674b0ee6d9e3902c213452c6334d037113718156b315364bee8cee0f03419c2bdff5e2c63967193fb422830e79cbb26866a + checksum: 164f102b96619ec283c2c39e208b8048e4674f75bf3c3a4f2e95048ae0f9226105add684b25f10d286d91c221625f877e2c1cfc3da46c42d7e1804da239318cb languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/test-sequencer@npm:29.3.1" +"@jest/test-sequencer@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/test-sequencer@npm:29.4.3" dependencies: - "@jest/test-result": ^29.3.1 + "@jest/test-result": ^29.4.3 graceful-fs: ^4.2.9 - jest-haste-map: ^29.3.1 + jest-haste-map: ^29.4.3 slash: ^3.0.0 - checksum: a8325b1ea0ce644486fb63bb67cedd3524d04e3d7b1e6c1e3562bf12ef477ecd0cf34044391b2a07d925e1c0c8b4e0f3285035ceca3a474a2c55980f1708caf3 + checksum: 145e1fa9379e5be3587bde6d585b8aee5cf4442b06926928a87e9aec7de5be91b581711d627c6ca13144d244fe05e5d248c13b366b51bedc404f9dcfbfd79e9e languageName: node linkType: hard -"@jest/transform@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/transform@npm:29.3.1" +"@jest/transform@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/transform@npm:29.4.3" dependencies: "@babel/core": ^7.11.6 - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 "@jridgewell/trace-mapping": ^0.3.15 babel-plugin-istanbul: ^6.1.1 chalk: ^4.0.0 convert-source-map: ^2.0.0 fast-json-stable-stringify: ^2.1.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.3.1 - jest-regex-util: ^29.2.0 - jest-util: ^29.3.1 + jest-haste-map: ^29.4.3 + jest-regex-util: ^29.4.3 + jest-util: ^29.4.3 micromatch: ^4.0.4 pirates: ^4.0.4 slash: ^3.0.0 - write-file-atomic: ^4.0.1 - checksum: 673df5900ffc95bc811084e09d6e47948034dea6ab6cc4f81f80977e3a52468a6c2284d0ba9796daf25a62ae50d12f7e97fc9a3a0c587f11f2a479ff5493ca53 + write-file-atomic: ^4.0.2 + checksum: 082d74e04044213aa7baa8de29f8383e5010034f867969c8602a2447a4ef2f484cfaf2491eba3179ce42f369f7a0af419cbd087910f7e5caf7aa5d1fe03f2ff9 languageName: node linkType: hard @@ -11070,17 +11070,17 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^29.3.1": - version: 29.3.1 - resolution: "@jest/types@npm:29.3.1" +"@jest/types@npm:^29.4.3": + version: 29.4.3 + resolution: "@jest/types@npm:29.4.3" dependencies: - "@jest/schemas": ^29.0.0 + "@jest/schemas": ^29.4.3 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: 6f9faf27507b845ff3839c1adc6dbd038d7046d03d37e84c9fc956f60718711a801a5094c7eeee6b39ccf42c0ab61347fdc0fa49ab493ae5a8efd2fd41228ee8 + checksum: 1756f4149d360f98567f56f434144f7af23ed49a2c42889261a314df6b6654c2de70af618fb2ee0ee39cadaf10835b885845557184509503646c9cb9dcc02bac languageName: node linkType: hard @@ -13191,6 +13191,13 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.25.16": + version: 0.25.23 + resolution: "@sinclair/typebox@npm:0.25.23" + checksum: 5720daec6e604be9ac849e6361cfa30d19f4d01934c9b79a3a5f5290dfcefaa300192ea0d384bb5dd0104432d88447bbad27adfacdf0b0f042b510bf15fbd5db + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.0.0": version: 4.0.0 resolution: "@sindresorhus/is@npm:4.0.0" @@ -13216,6 +13223,15 @@ __metadata: languageName: node linkType: hard +"@sinonjs/fake-timers@npm:^10.0.2": + version: 10.0.2 + resolution: "@sinonjs/fake-timers@npm:10.0.2" + dependencies: + "@sinonjs/commons": ^2.0.0 + checksum: c62aa98e7cefda8dedc101ce227abc888dc46b8ff9706c5f0a8dfd9c3ada97d0a5611384738d9ba0b26b59f99c2ba24efece8e779bb08329e9e87358fa309824 + languageName: node + linkType: hard + "@sinonjs/fake-timers@npm:^7.0.4": version: 7.1.2 resolution: "@sinonjs/fake-timers@npm:7.1.2" @@ -14657,12 +14673,12 @@ __metadata: linkType: hard "@types/jest@npm:*, @types/jest@npm:^29.0.0": - version: 29.2.6 - resolution: "@types/jest@npm:29.2.6" + version: 29.4.0 + resolution: "@types/jest@npm:29.4.0" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 90190ac830334af1470d255853f9621fe657e5030b4d96773fc1f884833cd303c76580b00c1b86dc38a8db94f1c7141d462190437a10af31852b8845a57c48ba + checksum: 23760282362a252e6690314584d83a47512d4cd61663e957ed3398ecf98195fe931c45606ee2f9def12f8ed7d8aa102d492ec42d26facdaf8b78094a31e6568e languageName: node linkType: hard @@ -17332,20 +17348,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.3.1": - version: 29.3.1 - resolution: "babel-jest@npm:29.3.1" +"babel-jest@npm:^29.4.3": + version: 29.4.3 + resolution: "babel-jest@npm:29.4.3" dependencies: - "@jest/transform": ^29.3.1 + "@jest/transform": ^29.4.3 "@types/babel__core": ^7.1.14 babel-plugin-istanbul: ^6.1.1 - babel-preset-jest: ^29.2.0 + babel-preset-jest: ^29.4.3 chalk: ^4.0.0 graceful-fs: ^4.2.9 slash: ^3.0.0 peerDependencies: "@babel/core": ^7.8.0 - checksum: 793848238a771a931ddeb5930b9ec8ab800522ac8d64933665698f4a39603d157e572e20b57d79610277e1df88d3ee82b180d59a21f3570388f602beeb38a595 + checksum: a1a95937adb5e717dbffc2eb9e583fa6d26c7e5d5b07bb492a2d7f68631510a363e9ff097eafb642ad642dfac9dc2b13872b584f680e166a4f0922c98ea95853 languageName: node linkType: hard @@ -17371,15 +17387,15 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:^29.2.0": - version: 29.2.0 - resolution: "babel-plugin-jest-hoist@npm:29.2.0" +"babel-plugin-jest-hoist@npm:^29.4.3": + version: 29.4.3 + resolution: "babel-plugin-jest-hoist@npm:29.4.3" dependencies: "@babel/template": ^7.3.3 "@babel/types": ^7.3.3 "@types/babel__core": ^7.1.14 "@types/babel__traverse": ^7.0.6 - checksum: 368d271ceae491ae6b96cd691434859ea589fbe5fd5aead7660df75d02394077273c6442f61f390e9347adffab57a32b564d0fabcf1c53c4b83cd426cb644072 + checksum: c8702a6db6b30ec39dfb9f8e72b501c13895231ed80b15ed2648448f9f0c7b7cc4b1529beac31802ae655f63479a05110ca612815aa25fb1b0e6c874e1589137 languageName: node linkType: hard @@ -17518,15 +17534,15 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.2.0": - version: 29.2.0 - resolution: "babel-preset-jest@npm:29.2.0" +"babel-preset-jest@npm:^29.4.3": + version: 29.4.3 + resolution: "babel-preset-jest@npm:29.4.3" dependencies: - babel-plugin-jest-hoist: ^29.2.0 + babel-plugin-jest-hoist: ^29.4.3 babel-preset-current-node-syntax: ^1.0.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: 1b09a2db968c36e064daf98082cfffa39c849b63055112ddc56fc2551fd0d4783897265775b1d2f8a257960a3339745de92e74feb01bad86d41c4cecbfa854fc + checksum: a091721861ea2f8d969ace8fe06570cff8f2e847dbc6e4800abacbe63f72131abde615ce0a3b6648472c97e55a5be7f8bf7ae381e2b194ad2fa1737096febcf5 languageName: node linkType: hard @@ -20760,10 +20776,10 @@ __metadata: languageName: node linkType: hard -"diff-sequences@npm:^29.3.1": - version: 29.3.1 - resolution: "diff-sequences@npm:29.3.1" - checksum: 8edab8c383355022e470779a099852d595dd856f9f5bd7af24f177e74138a668932268b4c4fd54096eed643861575c3652d4ecbbb1a9d710488286aed3ffa443 +"diff-sequences@npm:^29.4.3": + version: 29.4.3 + resolution: "diff-sequences@npm:29.4.3" + checksum: 28b265e04fdddcf7f9f814effe102cc95a9dec0564a579b5aed140edb24fc345c611ca52d76d725a3cab55d3888b915b5e8a4702e0f6058968a90fa5f41fcde7 languageName: node linkType: hard @@ -22672,16 +22688,16 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.3.1": - version: 29.3.1 - resolution: "expect@npm:29.3.1" +"expect@npm:^29.0.0, expect@npm:^29.4.3": + version: 29.4.3 + resolution: "expect@npm:29.4.3" dependencies: - "@jest/expect-utils": ^29.3.1 - jest-get-type: ^29.2.0 - jest-matcher-utils: ^29.3.1 - jest-message-util: ^29.3.1 - jest-util: ^29.3.1 - checksum: e9588c2a430b558b9a3dc72d4ad05f36b047cb477bc6a7bb9cfeef7614fe7e5edbab424c2c0ce82739ee21ecbbbd24596259528209f84cd72500cc612d910d30 + "@jest/expect-utils": ^29.4.3 + jest-get-type: ^29.4.3 + jest-matcher-utils: ^29.4.3 + jest-message-util: ^29.4.3 + jest-util: ^29.4.3 + checksum: ff9dd8c50c0c6fd4b2b00f6dbd7ab0e2063fe1953be81a8c10ae1c005c7f5667ba452918e2efb055504b72b701a4f82575a081a0a7158efb16d87991b0366feb languageName: node linkType: hard @@ -26234,57 +26250,57 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:^29.2.0": - version: 29.2.0 - resolution: "jest-changed-files@npm:29.2.0" +"jest-changed-files@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-changed-files@npm:29.4.3" dependencies: execa: ^5.0.0 p-limit: ^3.1.0 - checksum: 8ad8290324db1de2ee3c9443d3e3fbfdcb6d72ec7054c5796be2854b2bc239dea38a7c797c8c9c2bd959f539d44305790f2f75b18f3046b04317ed77c7480cb1 + checksum: 9a70bd8e92b37e18ad26d8bea97c516f41119fb7046b4255a13c76d557b0e54fa0629726de5a093fadfd6a0a08ce45da65a57086664d505b8db4b3133133e141 languageName: node linkType: hard -"jest-circus@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-circus@npm:29.3.1" +"jest-circus@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-circus@npm:29.4.3" dependencies: - "@jest/environment": ^29.3.1 - "@jest/expect": ^29.3.1 - "@jest/test-result": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/environment": ^29.4.3 + "@jest/expect": ^29.4.3 + "@jest/test-result": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" chalk: ^4.0.0 co: ^4.6.0 dedent: ^0.7.0 is-generator-fn: ^2.0.0 - jest-each: ^29.3.1 - jest-matcher-utils: ^29.3.1 - jest-message-util: ^29.3.1 - jest-runtime: ^29.3.1 - jest-snapshot: ^29.3.1 - jest-util: ^29.3.1 + jest-each: ^29.4.3 + jest-matcher-utils: ^29.4.3 + jest-message-util: ^29.4.3 + jest-runtime: ^29.4.3 + jest-snapshot: ^29.4.3 + jest-util: ^29.4.3 p-limit: ^3.1.0 - pretty-format: ^29.3.1 + pretty-format: ^29.4.3 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 125710debd998ad9693893e7c1235e271b79f104033b8169d82afe0bc0d883f8f5245feef87adcbb22ad27ff749fd001aa998d11a132774b03b4e2b8af77d5d8 + checksum: 2739bef9c888743b49ff3fe303131381618e5d2f250f613a91240d9c86e19e6874fc904cbd8bcb02ec9ec59a84e5dae4ffec929f0c6171e87ddbc05508a137f4 languageName: node linkType: hard -"jest-cli@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-cli@npm:29.3.1" +"jest-cli@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-cli@npm:29.4.3" dependencies: - "@jest/core": ^29.3.1 - "@jest/test-result": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/core": ^29.4.3 + "@jest/test-result": ^29.4.3 + "@jest/types": ^29.4.3 chalk: ^4.0.0 exit: ^0.1.2 graceful-fs: ^4.2.9 import-local: ^3.0.2 - jest-config: ^29.3.1 - jest-util: ^29.3.1 - jest-validate: ^29.3.1 + jest-config: ^29.4.3 + jest-util: ^29.4.3 + jest-validate: ^29.4.3 prompts: ^2.0.1 yargs: ^17.3.1 peerDependencies: @@ -26294,34 +26310,34 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 829895d33060042443bd1e9e87eb68993773d74f2c8a9b863acf53cece39d227ae0e7d76df2e9c5934c414bdf70ce398a34b3122cfe22164acb2499a74d7288d + checksum: f4c9f6d76cde2c60a4169acbebb3f862728be03bcf3fe0077d2e55da7f9f3c3e9483cfa6e936832d35eabf96ee5ebf0300c4b0bd43cffff099801793466bfdd8 languageName: node linkType: hard -"jest-config@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-config@npm:29.3.1" +"jest-config@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-config@npm:29.4.3" dependencies: "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.3.1 - "@jest/types": ^29.3.1 - babel-jest: ^29.3.1 + "@jest/test-sequencer": ^29.4.3 + "@jest/types": ^29.4.3 + babel-jest: ^29.4.3 chalk: ^4.0.0 ci-info: ^3.2.0 deepmerge: ^4.2.2 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-circus: ^29.3.1 - jest-environment-node: ^29.3.1 - jest-get-type: ^29.2.0 - jest-regex-util: ^29.2.0 - jest-resolve: ^29.3.1 - jest-runner: ^29.3.1 - jest-util: ^29.3.1 - jest-validate: ^29.3.1 + jest-circus: ^29.4.3 + jest-environment-node: ^29.4.3 + jest-get-type: ^29.4.3 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.4.3 + jest-runner: ^29.4.3 + jest-util: ^29.4.3 + jest-validate: ^29.4.3 micromatch: ^4.0.4 parse-json: ^5.2.0 - pretty-format: ^29.3.1 + pretty-format: ^29.4.3 slash: ^3.0.0 strip-json-comments: ^3.1.1 peerDependencies: @@ -26332,7 +26348,7 @@ __metadata: optional: true ts-node: optional: true - checksum: 6e663f04ae1024a53a4c2c744499b4408ca9a8b74381dd5e31b11bb3c7393311ecff0fb61b06287768709eb2c9e5a2fd166d258f5a9123abbb4c5812f99c12fe + checksum: 92f9a9c6850b18682cb01892774a33967472af23a5844438d8c68077d5f2a29b15b665e4e4db7de3d74002a6dca158cd5b2cb9f5debfd2cce5e1aee6c74e3873 languageName: node linkType: hard @@ -26357,72 +26373,72 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-diff@npm:29.3.1" +"jest-diff@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-diff@npm:29.4.3" dependencies: chalk: ^4.0.0 - diff-sequences: ^29.3.1 - jest-get-type: ^29.2.0 - pretty-format: ^29.3.1 - checksum: ac5c09745f2b1897e6f53216acaf6ed44fc4faed8e8df053ff4ac3db5d2a1d06a17b876e49faaa15c8a7a26f5671bcbed0a93781dcc2835f781c79a716a591a9 + diff-sequences: ^29.4.3 + jest-get-type: ^29.4.3 + pretty-format: ^29.4.3 + checksum: 877fd1edffef6b319688c27b152e5b28e2bc4bcda5ce0ca90d7e137f9fafda4280bae25403d4c0bfd9806c2c0b15d966aa2dfaf5f9928ec8f1ccea7fa1d08ed6 languageName: node linkType: hard -"jest-docblock@npm:^29.2.0": - version: 29.2.0 - resolution: "jest-docblock@npm:29.2.0" +"jest-docblock@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-docblock@npm:29.4.3" dependencies: detect-newline: ^3.0.0 - checksum: b3f1227b7d73fc9e4952180303475cf337b36fa65c7f730ac92f0580f1c08439983262fee21cf3dba11429aa251b4eee1e3bc74796c5777116b400d78f9d2bbe + checksum: e0e9df1485bb8926e5b33478cdf84b3387d9caf3658e7dc1eaa6dc34cb93dea0d2d74797f6e940f0233a88f3dadd60957f2288eb8f95506361f85b84bf8661df languageName: node linkType: hard -"jest-each@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-each@npm:29.3.1" +"jest-each@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-each@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 chalk: ^4.0.0 - jest-get-type: ^29.2.0 - jest-util: ^29.3.1 - pretty-format: ^29.3.1 - checksum: 16d51ef8f96fba44a3479f1c6f7672027e3b39236dc4e41217c38fe60a3b66b022ffcee72f8835a442f7a8a0a65980a93fb8e73a9782d192452526e442ad049a + jest-get-type: ^29.4.3 + jest-util: ^29.4.3 + pretty-format: ^29.4.3 + checksum: 1f72738338399efab0139eaea18bc198be0c6ed889770c8cbfa70bf9c724e8171fe1d3a29a94f9f39b8493ee6b2529bb350fb7c7c75e0d7eddfd28c253c79f9d languageName: node linkType: hard "jest-environment-jsdom@npm:^29.0.2": - version: 29.3.1 - resolution: "jest-environment-jsdom@npm:29.3.1" + version: 29.4.3 + resolution: "jest-environment-jsdom@npm:29.4.3" dependencies: - "@jest/environment": ^29.3.1 - "@jest/fake-timers": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/environment": ^29.4.3 + "@jest/fake-timers": ^29.4.3 + "@jest/types": ^29.4.3 "@types/jsdom": ^20.0.0 "@types/node": "*" - jest-mock: ^29.3.1 - jest-util: ^29.3.1 + jest-mock: ^29.4.3 + jest-util: ^29.4.3 jsdom: ^20.0.0 peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 91b04ed02b2275c3a47740e20c2691f67c4295e17174c8ccd3a71fe77707239e487506bd157279b4257ce1be0a8c2be377817ee85689966a9e604bb6ef1199f0 + checksum: 3fb29bb4b472e05a38fdb235aa936ad469dfa2f6c1cab97fe3d1a7c585351976d05c7bbbd715b9747f070a225dcf10a9166df1461e0fb838ea7a377a8e64bed4 languageName: node linkType: hard -"jest-environment-node@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-environment-node@npm:29.3.1" +"jest-environment-node@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-environment-node@npm:29.4.3" dependencies: - "@jest/environment": ^29.3.1 - "@jest/fake-timers": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/environment": ^29.4.3 + "@jest/fake-timers": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" - jest-mock: ^29.3.1 - jest-util: ^29.3.1 - checksum: 16d4854bd2d35501bd4862ca069baf27ce9f5fd7642fdcab9d2dab49acd28c082d0c8882bf2bb28ed7bbaada486da577c814c9688ddc62d1d9f74a954fde996a + jest-mock: ^29.4.3 + jest-util: ^29.4.3 + checksum: 3c7362edfdbd516e83af7367c95dde35761a482b174de9735c07633405486ec73e19624e9bea4333fca33c24e8d65eaa1aa6594e0cb6bfeeeb564ccc431ee61d languageName: node linkType: hard @@ -26433,43 +26449,43 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^29.2.0": - version: 29.2.0 - resolution: "jest-get-type@npm:29.2.0" - checksum: e396fd880a30d08940ed8a8e43cd4595db1b8ff09649018eb358ca701811137556bae82626af73459e3c0f8c5e972ed1e57fd3b1537b13a260893dac60a90942 +"jest-get-type@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-get-type@npm:29.4.3" + checksum: 6ac7f2dde1c65e292e4355b6c63b3a4897d7e92cb4c8afcf6d397f2682f8080e094c8b0b68205a74d269882ec06bf696a9de6cd3e1b7333531e5ed7b112605ce languageName: node linkType: hard -"jest-haste-map@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-haste-map@npm:29.3.1" +"jest-haste-map@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-haste-map@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 "@types/graceful-fs": ^4.1.3 "@types/node": "*" anymatch: ^3.0.3 fb-watchman: ^2.0.0 fsevents: ^2.3.2 graceful-fs: ^4.2.9 - jest-regex-util: ^29.2.0 - jest-util: ^29.3.1 - jest-worker: ^29.3.1 + jest-regex-util: ^29.4.3 + jest-util: ^29.4.3 + jest-worker: ^29.4.3 micromatch: ^4.0.4 walker: ^1.0.8 dependenciesMeta: fsevents: optional: true - checksum: 97ea26af0c28a2ba568c9c65d06211487bbcd501cb4944f9d55e07fd2b00ad96653ea2cc9033f3d5b7dc1feda33e47ae9cc56b400191ea4533be213c9f82e67c + checksum: c7a83ebe6008b3fe96a96235e8153092e54b14df68e0f4205faedec57450df26b658578495a71c6d82494c01fbb44bca98c1506a6b2b9c920696dcc5d2e2bc59 languageName: node linkType: hard -"jest-leak-detector@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-leak-detector@npm:29.3.1" +"jest-leak-detector@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-leak-detector@npm:29.4.3" dependencies: - jest-get-type: ^29.2.0 - pretty-format: ^29.3.1 - checksum: 0dd8ed31ae0b5a3d14f13f567ca8567f2663dd2d540d1e55511d3b3fd7f80a1d075392179674ebe9fab9be0b73678bf4d2f8bbbc0f4bdd52b9815259194da559 + jest-get-type: ^29.4.3 + pretty-format: ^29.4.3 + checksum: ec2b45e6f0abce81bd0dd0f6fd06b433c24d1ec865267af7640fae540ec868b93752598e407a9184d9c7419cbf32e8789007cc8c1be1a84f8f7321a0f8ad01f1 languageName: node linkType: hard @@ -26485,15 +26501,15 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-matcher-utils@npm:29.3.1" +"jest-matcher-utils@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-matcher-utils@npm:29.4.3" dependencies: chalk: ^4.0.0 - jest-diff: ^29.3.1 - jest-get-type: ^29.2.0 - pretty-format: ^29.3.1 - checksum: 311e8d9f1e935216afc7dd8c6acf1fbda67a7415e1afb1bf72757213dfb025c1f2dc5e2c185c08064a35cdc1f2d8e40c57616666774ed1b03e57eb311c20ec77 + jest-diff: ^29.4.3 + jest-get-type: ^29.4.3 + pretty-format: ^29.4.3 + checksum: 9e13cbe42d2113bab2691110c7c3ba5cec3b94abad2727e1de90929d0f67da444e9b2066da3b476b5bf788df53a8ede0e0a950cfb06a04e4d6d566d115ee4f1d languageName: node linkType: hard @@ -26514,31 +26530,31 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-message-util@npm:29.3.1" +"jest-message-util@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-message-util@npm:29.4.3" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.3.1 + pretty-format: ^29.4.3 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 15d0a2fca3919eb4570bbf575734780c4b9e22de6aae903c4531b346699f7deba834c6c86fe6e9a83ad17fac0f7935511cf16dce4d71a93a71ebb25f18a6e07b + checksum: 64f06b9550021e68da0059020bea8691283cf818918810bb67192d7b7fb9b691c7eadf55c2ca3cd04df5394918f2327245077095cdc0d6b04be3532d2c7d0ced languageName: node linkType: hard -"jest-mock@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-mock@npm:29.3.1" +"jest-mock@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-mock@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 "@types/node": "*" - jest-util: ^29.3.1 - checksum: 9098852cb2866db4a1a59f9f7581741dfc572f648e9e574a1b187fd69f5f2f6190ad387ede21e139a8b80a6a1343ecc3d6751cd2ae1ae11d7ea9fa1950390fb2 + jest-util: ^29.4.3 + checksum: 8eb4a29b02d2cd03faac0290b6df6d23b4ffa43f72b21c7fff3c7dd04a2797355b1e85862b70b15341dd33ee3a693b17db5520a6f6e6b81ee75601987de6a1a2 languageName: node linkType: hard @@ -26554,102 +26570,102 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:^29.2.0": - version: 29.2.0 - resolution: "jest-regex-util@npm:29.2.0" - checksum: 7c533e51c51230dac20c0d7395b19b8366cb022f7c6e08e6bcf2921626840ff90424af4c9b4689f02f0addfc9b071c4cd5f8f7a989298a4c8e0f9c94418ca1c3 +"jest-regex-util@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-regex-util@npm:29.4.3" + checksum: 96fc7fc28cd4dd73a63c13a526202c4bd8b351d4e5b68b1a2a2c88da3308c2a16e26feaa593083eb0bac38cca1aa9dd05025412e7de013ba963fb8e66af22b8a languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-resolve-dependencies@npm:29.3.1" +"jest-resolve-dependencies@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-resolve-dependencies@npm:29.4.3" dependencies: - jest-regex-util: ^29.2.0 - jest-snapshot: ^29.3.1 - checksum: 6ec4727a87c6e7954e93de9949ab9967b340ee2f07626144c273355f05a2b65fa47eb8dece2d6e5f4fd99cdb893510a3540aa5e14ba443f70b3feb63f6f98982 + jest-regex-util: ^29.4.3 + jest-snapshot: ^29.4.3 + checksum: 3ad934cd2170c9658d8800f84a975dafc866ec85b7ce391c640c09c3744ced337787620d8667dc8d1fa5e0b1493f973caa1a1bb980e4e6a50b46a1720baf0bd1 languageName: node linkType: hard -"jest-resolve@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-resolve@npm:29.3.1" +"jest-resolve@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-resolve@npm:29.4.3" dependencies: chalk: ^4.0.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.3.1 + jest-haste-map: ^29.4.3 jest-pnp-resolver: ^1.2.2 - jest-util: ^29.3.1 - jest-validate: ^29.3.1 + jest-util: ^29.4.3 + jest-validate: ^29.4.3 resolve: ^1.20.0 - resolve.exports: ^1.1.0 + resolve.exports: ^2.0.0 slash: ^3.0.0 - checksum: 0dea22ed625e07b8bfee52dea1391d3a4b453c1a0c627a0fa7c22e44bb48e1c289afe6f3c316def70753773f099c4e8f436c7a2cc12fcc6c7dd6da38cba2cd5f + checksum: 056a66beccf833f3c7e5a8fc9bfec218886e87b0b103decdbdf11893669539df489d1490cd6d5f0eea35731e8be0d2e955a6710498f970d2eae734da4df029dc languageName: node linkType: hard -"jest-runner@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-runner@npm:29.3.1" +"jest-runner@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-runner@npm:29.4.3" dependencies: - "@jest/console": ^29.3.1 - "@jest/environment": ^29.3.1 - "@jest/test-result": ^29.3.1 - "@jest/transform": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/console": ^29.4.3 + "@jest/environment": ^29.4.3 + "@jest/test-result": ^29.4.3 + "@jest/transform": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" chalk: ^4.0.0 emittery: ^0.13.1 graceful-fs: ^4.2.9 - jest-docblock: ^29.2.0 - jest-environment-node: ^29.3.1 - jest-haste-map: ^29.3.1 - jest-leak-detector: ^29.3.1 - jest-message-util: ^29.3.1 - jest-resolve: ^29.3.1 - jest-runtime: ^29.3.1 - jest-util: ^29.3.1 - jest-watcher: ^29.3.1 - jest-worker: ^29.3.1 + jest-docblock: ^29.4.3 + jest-environment-node: ^29.4.3 + jest-haste-map: ^29.4.3 + jest-leak-detector: ^29.4.3 + jest-message-util: ^29.4.3 + jest-resolve: ^29.4.3 + jest-runtime: ^29.4.3 + jest-util: ^29.4.3 + jest-watcher: ^29.4.3 + jest-worker: ^29.4.3 p-limit: ^3.1.0 source-map-support: 0.5.13 - checksum: 61ad445d8a5f29573332f27a21fc942fb0d2a82bf901a0ea1035bf3bd7f349d1e425f71f54c3a3f89b292a54872c3248d395a2829d987f26b6025b15530ea5d2 + checksum: c41108e5da01e0b8fdc2a06c5042eb49bb1d8db0e0d4651769fd1b9fe84ab45188617c11a3a8e1c83748b29bfe57dd77001ec57e86e3e3c30f3534e0314f8882 languageName: node linkType: hard -"jest-runtime@npm:^29.0.2, jest-runtime@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-runtime@npm:29.3.1" +"jest-runtime@npm:^29.0.2, jest-runtime@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-runtime@npm:29.4.3" dependencies: - "@jest/environment": ^29.3.1 - "@jest/fake-timers": ^29.3.1 - "@jest/globals": ^29.3.1 - "@jest/source-map": ^29.2.0 - "@jest/test-result": ^29.3.1 - "@jest/transform": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/environment": ^29.4.3 + "@jest/fake-timers": ^29.4.3 + "@jest/globals": ^29.4.3 + "@jest/source-map": ^29.4.3 + "@jest/test-result": ^29.4.3 + "@jest/transform": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" chalk: ^4.0.0 cjs-module-lexer: ^1.0.0 collect-v8-coverage: ^1.0.0 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-haste-map: ^29.3.1 - jest-message-util: ^29.3.1 - jest-mock: ^29.3.1 - jest-regex-util: ^29.2.0 - jest-resolve: ^29.3.1 - jest-snapshot: ^29.3.1 - jest-util: ^29.3.1 + jest-haste-map: ^29.4.3 + jest-message-util: ^29.4.3 + jest-mock: ^29.4.3 + jest-regex-util: ^29.4.3 + jest-resolve: ^29.4.3 + jest-snapshot: ^29.4.3 + jest-util: ^29.4.3 slash: ^3.0.0 strip-bom: ^4.0.0 - checksum: 82f27b48f000be074064a854e16e768f9453e9b791d8c5f9316606c37f871b5b10f70544c1b218ab9784f00bd972bb77f868c5ab6752c275be2cd219c351f5a7 + checksum: b99f8a910d1a38e7476058ba04ad44dfd3d93e837bb7c301d691e646a1085412fde87f06fbe271c9145f0e72d89400bfa7f6994bc30d456c7742269f37d0f570 languageName: node linkType: hard -"jest-snapshot@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-snapshot@npm:29.3.1" +"jest-snapshot@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-snapshot@npm:29.4.3" dependencies: "@babel/core": ^7.11.6 "@babel/generator": ^7.7.2 @@ -26657,25 +26673,25 @@ __metadata: "@babel/plugin-syntax-typescript": ^7.7.2 "@babel/traverse": ^7.7.2 "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.3.1 - "@jest/transform": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/expect-utils": ^29.4.3 + "@jest/transform": ^29.4.3 + "@jest/types": ^29.4.3 "@types/babel__traverse": ^7.0.6 "@types/prettier": ^2.1.5 babel-preset-current-node-syntax: ^1.0.0 chalk: ^4.0.0 - expect: ^29.3.1 + expect: ^29.4.3 graceful-fs: ^4.2.9 - jest-diff: ^29.3.1 - jest-get-type: ^29.2.0 - jest-haste-map: ^29.3.1 - jest-matcher-utils: ^29.3.1 - jest-message-util: ^29.3.1 - jest-util: ^29.3.1 + jest-diff: ^29.4.3 + jest-get-type: ^29.4.3 + jest-haste-map: ^29.4.3 + jest-matcher-utils: ^29.4.3 + jest-message-util: ^29.4.3 + jest-util: ^29.4.3 natural-compare: ^1.4.0 - pretty-format: ^29.3.1 + pretty-format: ^29.4.3 semver: ^7.3.5 - checksum: d7d0077935e78c353c828be78ccb092e12ba7622cb0577f21641fadd728ae63a7c1f4a0d8113bfb38db3453a64bfa232fb1cdeefe0e2b48c52ef4065b0ab75ae + checksum: 79ba52f2435e23ce72b1309be4b17fdbcb299d1c2ce97ebb61df9a62711e9463035f63b4c849181b2fe5aa17b3e09d30ee4668cc25fb3c6f59511c010b4d9494 languageName: node linkType: hard @@ -26693,47 +26709,47 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-util@npm:29.3.1" +"jest-util@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-util@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: f67c60f062b94d21cb60e84b3b812d64b7bfa81fe980151de5c17a74eb666042d0134e2e756d099b7606a1fcf1d633824d2e58197d01d76dde1e2dc00dfcd413 + checksum: 606b3e6077895baf8fb4ad4d08c134f37a6b81d5ba77ae654c942b1ae4b7294ab3b5a0eb93db34f129407b367970cf3b76bf5c80897b30f215f2bc8bf20a5f3f languageName: node linkType: hard -"jest-validate@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-validate@npm:29.3.1" +"jest-validate@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-validate@npm:29.4.3" dependencies: - "@jest/types": ^29.3.1 + "@jest/types": ^29.4.3 camelcase: ^6.2.0 chalk: ^4.0.0 - jest-get-type: ^29.2.0 + jest-get-type: ^29.4.3 leven: ^3.1.0 - pretty-format: ^29.3.1 - checksum: 92584f0b8ac284235f12b3b812ccbc43ef6dea080a3b98b1aa81adbe009e962d0aa6131f21c8157b30ac3d58f335961694238a93d553d1d1e02ab264c923778c + pretty-format: ^29.4.3 + checksum: 983e56430d86bed238448cae031535c1d908f760aa312cd4a4ec0e92f3bc1b6675415ddf57cdeceedb8ad9c698e5bcd10f0a856dfc93a8923bdecc7733f4ba80 languageName: node linkType: hard -"jest-watcher@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-watcher@npm:29.3.1" +"jest-watcher@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-watcher@npm:29.4.3" dependencies: - "@jest/test-result": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/test-result": ^29.4.3 + "@jest/types": ^29.4.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 emittery: ^0.13.1 - jest-util: ^29.3.1 + jest-util: ^29.4.3 string-length: ^4.0.1 - checksum: 60d189473486c73e9d540406a30189da5a3c67bfb0fb4ad4a83991c189135ef76d929ec99284ca5a505fe4ee9349ae3c99b54d2e00363e72837b46e77dec9642 + checksum: 44b64991b3414db853c3756f14690028f4edef7aebfb204a4291cc1901c2239fa27a8687c5c5abbecc74bf613e0bb9b1378bf766430c9febcc71e9c0cb5ad8fc languageName: node linkType: hard @@ -26768,26 +26784,26 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.3.1": - version: 29.3.1 - resolution: "jest-worker@npm:29.3.1" +"jest-worker@npm:^29.4.3": + version: 29.4.3 + resolution: "jest-worker@npm:29.4.3" dependencies: "@types/node": "*" - jest-util: ^29.3.1 + jest-util: ^29.4.3 merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: 38687fcbdc2b7ddc70bbb5dfc703ae095b46b3c7f206d62ecdf5f4d16e336178e217302138f3b906125576bb1cfe4cfe8d43681276fa5899d138ed9422099fb3 + checksum: c99ae66f257564613e72c5797c3a68f21a22e1c1fb5f30d14695ff5b508a0d2405f22748f13a3df8d1015b5e16abb130170f81f047ff68f58b6b1d2ff6ebc51b languageName: node linkType: hard "jest@npm:^29.0.2": - version: 29.3.1 - resolution: "jest@npm:29.3.1" + version: 29.4.3 + resolution: "jest@npm:29.4.3" dependencies: - "@jest/core": ^29.3.1 - "@jest/types": ^29.3.1 + "@jest/core": ^29.4.3 + "@jest/types": ^29.4.3 import-local: ^3.0.2 - jest-cli: ^29.3.1 + jest-cli: ^29.4.3 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -26795,7 +26811,7 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 613f4ec657b14dd84c0056b2fef1468502927fd551bef0b19d4a91576a609678fb316c6a5b5fc6120dd30dd4ff4569070ffef3cb507db9bb0260b28ddaa18d7a + checksum: 084d10d1ceaade3c40e6d3bbd71b9b71b8919ba6fbd6f1f6699bdc259a6ba2f7350c7ccbfa10c11f7e3e01662853650a6244210179542fe4ba87e77dc3f3109f languageName: node linkType: hard @@ -32441,14 +32457,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.3.1": - version: 29.3.1 - resolution: "pretty-format@npm:29.3.1" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.4.3": + version: 29.4.3 + resolution: "pretty-format@npm:29.4.3" dependencies: - "@jest/schemas": ^29.0.0 + "@jest/schemas": ^29.4.3 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: 9917a0bb859cd7a24a343363f70d5222402c86d10eb45bcc2f77b23a4e67586257390e959061aec22762a782fe6bafb59bf34eb94527bc2e5d211afdb287eb4e + checksum: 3258b9a010bd79b3cf73783ad1e4592b6326fc981b6e31b742f316f14e7fbac09b48a9dbf274d092d9bde404db9fe16f518370e121837dc078a597392e6e5cc5 languageName: node linkType: hard @@ -34209,10 +34225,10 @@ __metadata: languageName: node linkType: hard -"resolve.exports@npm:^1.1.0": - version: 1.1.0 - resolution: "resolve.exports@npm:1.1.0" - checksum: 52865af8edb088f6c7759a328584a5de6b226754f004b742523adcfe398cfbc4559515104bc2ae87b8e78b1e4de46c9baec400b3fb1f7d517b86d2d48a098a2d +"resolve.exports@npm:^2.0.0": + version: 2.0.0 + resolution: "resolve.exports@npm:2.0.0" + checksum: d8bee3b0cc0a0ae6c8323710983505bc6a3a2574f718e96f01e048a0f0af035941434b386cc9efc7eededc5e1199726185c306ec6f6a1aa55d5fbad926fd0634 languageName: node linkType: hard @@ -38738,7 +38754,7 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^4.0.0, write-file-atomic@npm:^4.0.1": +"write-file-atomic@npm:^4.0.0, write-file-atomic@npm:^4.0.2": version: 4.0.2 resolution: "write-file-atomic@npm:4.0.2" dependencies: From e1dafc78dfc236a0fbe602a05ce521608974c692 Mon Sep 17 00:00:00 2001 From: Joep Peeters Date: Tue, 21 Feb 2023 16:50:39 +0100 Subject: [PATCH 023/205] 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 024/205] 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 025/205] 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 cee0cd96cced77db8ff6a302a6726f7e48188f84 Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Sun, 19 Feb 2023 13:27:08 -0500 Subject: [PATCH 026/205] 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 027/205] 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 028/205] 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 029/205] 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 030/205] 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 031/205] 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 032/205] 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 033/205] 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 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 034/205] 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 035/205] 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 036/205] 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 037/205] 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 553f3c950113e1e975f2293e1e80d52a4633cec1 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Thu, 23 Feb 2023 15:53:30 -0500 Subject: [PATCH 038/205] 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 039/205] 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 e0ee35568b565c0a1e05723062435d48f88a056b Mon Sep 17 00:00:00 2001 From: Carlos Esteban Lopez Date: Thu, 23 Feb 2023 22:30:43 -0500 Subject: [PATCH 040/205] 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 041/205] 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 1a2fa2f4c7d16ee087922096fe3dda44d2240c11 Mon Sep 17 00:00:00 2001 From: Aramis Sennyey Date: Fri, 24 Feb 2023 12:56:30 -0500 Subject: [PATCH 042/205] 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 043/205] 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 044/205] 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 045/205] 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 2a73ded3861feac95c4e7cd5928fccc82de5d6be Mon Sep 17 00:00:00 2001 From: Kumar Date: Sun, 19 Feb 2023 00:04:41 +1100 Subject: [PATCH 046/205] 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 e2e7cb207cfe07cd74642c4e4eed184a7f96d952 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Sun, 5 Feb 2023 14:41:17 +0200 Subject: [PATCH 047/205] 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 048/205] 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 049/205] 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 050/205] 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 051/205] 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 052/205] 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 053/205] 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 054/205] 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 055/205] 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 056/205] 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 057/205] 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 058/205] 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 059/205] 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 060/205] 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 061/205] 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 062/205] 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 063/205] 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 2128963db1dd4986c87902ca6fbc38dbcf4510d1 Mon Sep 17 00:00:00 2001 From: Tomas Dabasinskas Date: Mon, 27 Feb 2023 09:48:06 +0200 Subject: [PATCH 064/205] 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 c47a5ede5ca3e9c92376576c6f8e9c96aa9f1a3d Mon Sep 17 00:00:00 2001 From: Matteo Silvestri Date: Mon, 27 Feb 2023 11:04:20 +0100 Subject: [PATCH 065/205] 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 d7ff48817b1d1ac3713ab8cbfae32ec7289ca00c Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 27 Feb 2023 12:45:56 +0100 Subject: [PATCH 066/205] 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 067/205] 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 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 068/205] 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 =