From 53470ada548eea440975a03075f3e1110d390b49 Mon Sep 17 00:00:00 2001 From: Johan Hammar Date: Fri, 1 Oct 2021 17:20:09 +0200 Subject: [PATCH 01/54] Fix handling of search term when performing searches from the sidebar Signed-off-by: Johan Hammar --- .changeset/neat-cooks-sell.md | 5 +++++ packages/core-components/src/layout/Sidebar/Items.tsx | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/neat-cooks-sell.md diff --git a/.changeset/neat-cooks-sell.md b/.changeset/neat-cooks-sell.md new file mode 100644 index 0000000000..11859c3589 --- /dev/null +++ b/.changeset/neat-cooks-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Fix search in Firefox. When the search was performed by pressing enter, the query parameter was first set but then reverted back. diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index fbe1ffbb82..1246fda82e 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -311,6 +311,7 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { const handleEnter: KeyboardEventHandler = ev => { if (ev.key === 'Enter') { search(); + ev.preventDefault(); } }; From 5c4236057793bb32990e6c4a8628e4c3e3fb76a6 Mon Sep 17 00:00:00 2001 From: Tomasz Szuba Date: Mon, 4 Oct 2021 16:23:13 +0200 Subject: [PATCH 02/54] Documentation and better type safety of DependencyGraph Signed-off-by: Tomasz Szuba --- .changeset/mean-eggs-knock.md | 6 + packages/core-components/api-report.md | 133 +++++++---------- .../DependencyGraph/DependencyGraph.tsx | 139 ++++++++++++++++-- .../components/DependencyGraph/Edge.test.tsx | 8 +- .../src/components/DependencyGraph/Edge.tsx | 35 ++++- .../components/DependencyGraph/Node.test.tsx | 13 +- .../src/components/DependencyGraph/Node.tsx | 16 +- .../src/components/DependencyGraph/types.ts | 121 +++++++++++---- plugins/catalog-graph/api-report.md | 18 ++- .../EntityRelationsGraph/CustomLabel.test.tsx | 12 -- .../EntityRelationsGraph/CustomLabel.tsx | 4 +- .../EntityRelationsGraph/CustomNode.test.tsx | 16 -- .../EntityRelationsGraph/CustomNode.tsx | 4 +- .../components/EntityRelationsGraph/index.ts | 7 +- .../components/EntityRelationsGraph/types.ts | 28 ++-- 15 files changed, 363 insertions(+), 197 deletions(-) create mode 100644 .changeset/mean-eggs-knock.md diff --git a/.changeset/mean-eggs-knock.md b/.changeset/mean-eggs-knock.md new file mode 100644 index 0000000000..dd9336a0e7 --- /dev/null +++ b/.changeset/mean-eggs-knock.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': minor +'@backstage/plugin-catalog-graph': minor +--- + +Add documentation and more type safety around DependencyGraph diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 4a94135b4a..1067cd3d02 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -17,7 +17,6 @@ import { ComponentProps } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; -import { default as dagre_2 } from 'dagre'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; @@ -53,15 +52,11 @@ export function AlertDisplay(_props: {}): JSX.Element | null; // Warning: (ae-missing-release-tag) "Alignment" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum Alignment { - // (undocumented) DOWN_LEFT = 'DL', - // (undocumented) DOWN_RIGHT = 'DR', - // (undocumented) UP_LEFT = 'UL', - // (undocumented) UP_RIGHT = 'UR', } @@ -264,11 +259,10 @@ export type CustomProviderClassKey = 'form' | 'button'; // @public (undocumented) export function DashboardIcon(props: IconComponentProps): JSX.Element; -// Warning: (ae-forgotten-export) The symbol "CustomType" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "DependencyEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -type DependencyEdge = T & { +// @public +type DependencyEdge = T & { from: string; to: string; label?: string; @@ -276,8 +270,10 @@ type DependencyEdge = T & { // Warning: (ae-missing-release-tag) "DependencyGraph" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export function DependencyGraph(props: DependencyGraphProps): JSX.Element; +// @public +export function DependencyGraph( + props: DependencyGraphProps, +): JSX.Element; // Warning: (ae-missing-release-tag) "DependencyGraphDefaultLabelClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -301,40 +297,47 @@ export type DependencyGraphNodeClassKey = 'node'; // Warning: (ae-missing-release-tag) "DependencyGraphProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -export type DependencyGraphProps = React_2.SVGProps & { - edges: DependencyEdge[]; - nodes: DependencyNode[]; - direction?: Direction; +// @public +export interface DependencyGraphProps + extends React_2.SVGProps { + acyclicer?: 'greedy'; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver align?: Alignment; - nodeMargin?: number; + defs?: SVGDefsElement | SVGDefsElement[]; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + direction?: Direction; edgeMargin?: number; - rankMargin?: number; + edgeRanks?: number; + edges: DependencyEdge[]; + edgeWeight?: number; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + labelOffset?: number; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + labelPosition?: LabelPosition; + nodeMargin?: number; + nodes: DependencyNode[]; paddingX?: number; paddingY?: number; - acyclicer?: 'greedy'; + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver + // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver ranker?: Ranker; - labelPosition?: LabelPosition; - labelOffset?: number; - edgeRanks?: number; - edgeWeight?: number; - renderNode?: RenderNodeFunction; - renderLabel?: RenderLabelFunction; - defs?: SVGDefsElement | SVGDefsElement[]; + rankMargin?: number; + renderLabel?: RenderLabelFunction; + renderNode?: RenderNodeFunction; zoom?: 'enabled' | 'disabled' | 'enable-on-click'; -}; +} declare namespace DependencyGraphTypes { export { DependencyEdge, - GraphEdge, RenderLabelProps, RenderLabelFunction, DependencyNode, - GraphNode, RenderNodeProps, RenderNodeFunction, - EdgeProperties, Direction, Alignment, Ranker, @@ -345,22 +348,18 @@ export { DependencyGraphTypes }; // Warning: (ae-missing-release-tag) "DependencyNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -type DependencyNode = T & { +// @public +type DependencyNode = T & { id: string; }; // Warning: (ae-missing-release-tag) "Direction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum Direction { - // (undocumented) BOTTOM_TOP = 'BT', - // (undocumented) LEFT_RIGHT = 'LR', - // (undocumented) RIGHT_LEFT = 'RL', - // (undocumented) TOP_BOTTOM = 'TB', } @@ -387,20 +386,6 @@ export type DismissbleBannerClassKey = // @public (undocumented) export function DocsIcon(props: IconComponentProps): JSX.Element; -// Warning: (ae-missing-release-tag) "EdgeProperties" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -type EdgeProperties = { - label?: string; - width?: number; - height?: number; - labeloffset?: number; - labelpos?: LabelPosition; - minlen?: number; - weight?: number; - [customKey: string]: any; -}; - // Warning: (ae-missing-release-tag) "EmailIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -520,18 +505,6 @@ export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown'; // @public (undocumented) export function GitHubIcon(props: IconComponentProps): JSX.Element; -// Warning: (ae-missing-release-tag) "GraphEdge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -type GraphEdge = DependencyEdge & - dagre_2.GraphEdge & - EdgeProperties; - -// Warning: (ae-missing-release-tag) "GraphNode" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -type GraphNode = dagre_2.Node>; - // Warning: (ae-missing-release-tag) "GroupIcon" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -738,7 +711,7 @@ export type ItemCardHeaderProps = Partial> & { // Warning: (ae-missing-release-tag) "LabelPosition" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum LabelPosition { // (undocumented) CENTER = 'c', @@ -888,37 +861,43 @@ export function Progress( // Warning: (ae-missing-release-tag) "Ranker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public enum Ranker { - // (undocumented) LONGEST_PATH = 'longest-path', - // (undocumented) NETWORK_SIMPLEX = 'network-simplex', - // (undocumented) TIGHT_TREE = 'tight-tree', } // Warning: (ae-missing-release-tag) "RenderLabelFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) -type RenderLabelFunction = (props: RenderLabelProps) => React.ReactNode; +// @public +type RenderLabelFunction = ( + props: RenderLabelProps, +) => React_2.ReactNode; // Warning: (ae-missing-release-tag) "RenderLabelProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // -// @public (undocumented) -type RenderLabelProps = { +// @public +type RenderLabelProps = { edge: DependencyEdge; }; // Warning: (ae-missing-release-tag) "RenderNodeFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // -// @public (undocumented) -type RenderNodeFunction = (props: RenderNodeProps) => React.ReactNode; +// @public +type RenderNodeFunction = ( + props: RenderNodeProps, +) => React_2.ReactNode; // Warning: (ae-missing-release-tag) "RenderNodeProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver +// Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver // -// @public (undocumented) -type RenderNodeProps = { +// @public +type RenderNodeProps = { node: DependencyNode; }; @@ -2498,6 +2477,8 @@ export type WarningPanelClassKey = // Warnings were encountered during analysis: // +// src/components/DependencyGraph/types.d.ts:14:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" +// src/components/DependencyGraph/types.d.ts:18:5 - (ae-unresolved-link) The @link reference could not be resolved: The package "@backstage/core-components" does not have an export "DependencyNode" // src/components/TabbedLayout/RoutedTabs.d.ts:9:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts // src/components/Table/Table.d.ts:19:5 - (ae-forgotten-export) The symbol "SelectedFilters" needs to be exported by the entry point index.d.ts // src/layout/ErrorBoundary/ErrorBoundary.d.ts:7:5 - (ae-forgotten-export) The symbol "SlackChannel" needs to be exported by the entry point index.d.ts diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index abc98dd60a..7a9ef9a9c7 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -29,39 +29,148 @@ import { Ranker, RenderNodeFunction, RenderLabelFunction, - GraphEdge, - GraphNode, LabelPosition, } from './types'; import { Node } from './Node'; -import { Edge } from './Edge'; +import { Edge, GraphEdge } from './Edge'; import { ARROW_MARKER_ID } from './constants'; -export type DependencyGraphProps = React.SVGProps & { - edges: DependencyEdge[]; - nodes: DependencyNode[]; +/** + * Properties of {@link DependencyGraph} + * + * @remarks + * and are useful when rendering custom or edge labels + */ +export interface DependencyGraphProps + extends React.SVGProps { + /** + * Edges of graph + */ + edges: DependencyEdge[]; + /** + * Nodes of Graph + */ + nodes: DependencyNode[]; + /** + * Graph {@link DependencyGraphTypes.Direction | direction} + * + * @remarks + * + * Default: {@link DependencyGraphTypes.Direction.TOP_BOTTOM} + */ direction?: Direction; + /** + * Node {@link DependencyGraphTypes.Alignment | alignment} + */ align?: Alignment; + /** + * Margin between nodes on each rank + * + * @remarks + * + * Default: 50 + */ nodeMargin?: number; + /** + * Margin between edges + * + * @remarks + * + * Default: 10 + */ edgeMargin?: number; + /** + * Margin between each rank + * + * @remarks + * + * Default: 50 + */ rankMargin?: number; + /** + * Margin on left and right of whole graph + * + * @remarks + * + * Default: 0 + */ paddingX?: number; + /** + * Margin on top and bottom of whole graph + * + * @remarks + * + * Default: 0 + */ paddingY?: number; + /** + * Heuristic used to find set of edges that will make graph acyclic + */ acyclicer?: 'greedy'; + /** + * {@link DependencyGraphTypes.Ranker | Algorithm} used to rank nodes + * + * @remarks + * + * Default: {@link DependencyGraphTypes.Ranker.NETWORK_SIMPLEX} + */ ranker?: Ranker; + /** + * {@link DependencyGraphTypes.LabelPosition | Position} of label in relation to edge + * + * @remarks + * + * Default: {@link DependencyGraphTypes.LabelPosition.RIGHT} + */ labelPosition?: LabelPosition; + /** + * How much to move label away from edge + * + * @remarks + * + * Applies only when {@link DependencyGraphProps.labelPosition} is {@link DependencyGraphTypes.LabelPosition.LEFT} or + * {@link DependencyGraphTypes.LabelPosition.RIGHT} + */ labelOffset?: number; + /** + * Minimum number of ranks to keep between connected nodes + */ edgeRanks?: number; + /** + * Weight applied to edges in graph + */ edgeWeight?: number; - renderNode?: RenderNodeFunction; - renderLabel?: RenderLabelFunction; + /** + * Custom node rendering component + */ + renderNode?: RenderNodeFunction; + /** + * Custom label rendering component + */ + renderLabel?: RenderLabelFunction; + /** + * {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs | Defs} shared by rendered SVG to be used by + * {@link DependencyGraphProps.renderNode} and/or {@link DependencyGraphProps.renderLabel} + */ defs?: SVGDefsElement | SVGDefsElement[]; + /** + * Controls zoom behavior of graph + * + * @remarks + * + * Default: `enabled` + */ zoom?: 'enabled' | 'disabled' | 'enable-on-click'; -}; +} const WORKSPACE_ID = 'workspace'; -export function DependencyGraph(props: DependencyGraphProps) { +/** + * Graph component used to visualize relations between entities + */ +export function DependencyGraph( + props: DependencyGraphProps, +) { const { edges, nodes, @@ -88,7 +197,7 @@ export function DependencyGraph(props: DependencyGraphProps) { const [containerWidth, setContainerWidth] = React.useState(100); const [containerHeight, setContainerHeight] = React.useState(100); - const graph = React.useRef>( + const graph = React.useRef>>( new dagre.graphlib.Graph(), ); const [graphWidth, setGraphWidth] = React.useState( @@ -256,13 +365,13 @@ export function DependencyGraph(props: DependencyGraphProps) { updateGraph, ]); - function setNode(id: string, node: DependencyNode) { + function setNode(id: string, node: DependencyNode) { graph.current.setNode(id, node); updateGraph(); return graph.current; } - function setEdge(id: dagre.Edge, edge: DependencyEdge) { + function setEdge(id: dagre.Edge, edge: DependencyEdge) { graph.current.setEdge(id, edge); updateGraph(); return graph.current; @@ -303,7 +412,7 @@ export function DependencyGraph(props: DependencyGraphProps) { viewBox={`0 0 ${graphWidth} ${graphHeight}`} > {graphEdges.map(e => { - const edge = graph.current.edge(e) as GraphEdge; + const edge = graph.current.edge(e) as GraphEdge; if (!edge) return null; return ( { - const node = graph.current.node(id) as GraphNode; + const node = graph.current.node(id); if (!node) return null; return ( ( )); const minProps = { - points: [ - { x: 10, y: 20 }, - { x: 20, y: 20 }, - ], id, setEdge, renderElement, diff --git a/packages/core-components/src/components/DependencyGraph/Edge.tsx b/packages/core-components/src/components/DependencyGraph/Edge.tsx index a82c5d4813..fd4d92b834 100644 --- a/packages/core-components/src/components/DependencyGraph/Edge.tsx +++ b/packages/core-components/src/components/DependencyGraph/Edge.tsx @@ -20,13 +20,26 @@ import isFinite from 'lodash/isFinite'; import makeStyles from '@material-ui/core/styles/makeStyles'; import { BackstageTheme } from '@backstage/theme'; import { - GraphEdge, RenderLabelProps, RenderLabelFunction, DependencyEdge, + LabelPosition, } from './types'; import { ARROW_MARKER_ID, EDGE_TEST_ID, LABEL_TEST_ID } from './constants'; import { DefaultLabel } from './DefaultLabel'; +import dagre from 'dagre'; + +/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */ +export type EdgeProperties = { + label?: string; + width?: number; + height?: number; + labeloffset?: number; + labelpos?: LabelPosition; + minlen?: number; + weight?: number; +}; +export type GraphEdge = DependencyEdge & dagre.GraphEdge & EdgeProperties; export type DependencyGraphEdgeClassKey = 'path' | 'label'; @@ -47,14 +60,19 @@ const useStyles = makeStyles( type EdgePoint = dagre.GraphEdge['points'][0]; -export type EdgeComponentProps = { +export type EdgeComponentProps = { id: dagre.Edge; edge: GraphEdge; - render?: RenderLabelFunction; - setEdge: (id: dagre.Edge, edge: DependencyEdge) => dagre.graphlib.Graph<{}>; + render?: RenderLabelFunction; + setEdge: ( + id: dagre.Edge, + edge: DependencyEdge, + ) => dagre.graphlib.Graph<{}>; }; -const renderDefault = (props: RenderLabelProps) => ; +const renderDefault = (props: RenderLabelProps) => ( + +); const createPath = d3Shape .line() @@ -62,13 +80,14 @@ const createPath = d3Shape .y(d => d.y) .curve(d3Shape.curveMonotoneX); -export function Edge({ +export function Edge({ render = renderDefault, setEdge, id, edge, -}: EdgeComponentProps) { - const { x = 0, y = 0, width, height, points, ...labelProps } = edge; +}: EdgeComponentProps) { + const { x = 0, y = 0, width, height, points } = edge; + const labelProps: DependencyEdge = edge; const classes = useStyles(); const labelRef = React.useRef(null); diff --git a/packages/core-components/src/components/DependencyGraph/Node.test.tsx b/packages/core-components/src/components/DependencyGraph/Node.test.tsx index ebd6478db9..aaba09c004 100644 --- a/packages/core-components/src/components/DependencyGraph/Node.test.tsx +++ b/packages/core-components/src/components/DependencyGraph/Node.test.tsx @@ -20,21 +20,16 @@ import { render } from '@testing-library/react'; import { Node } from './Node'; import { RenderNodeProps } from './types'; -const node = { id: 'abc' }; +const node = { id: 'abc', x: 0, y: 0, width: 0, height: 0 }; const setNode = jest.fn(() => new dagre.graphlib.Graph()); const renderElement = jest.fn((props: RenderNodeProps) => ( {props.node.id} )); const minProps = { - id: node.id, node, setNode, render: renderElement, - x: 0, - y: 0, - width: 0, - height: 0, }; describe('', () => { @@ -50,7 +45,7 @@ describe('', () => { it('renders the supplied element', () => { const { getByText } = render(); - expect(getByText(minProps.id)).toBeInTheDocument(); + expect(getByText(minProps.node.id)).toBeInTheDocument(); }); it('passes down node properties to the render method', () => { @@ -62,13 +57,13 @@ describe('', () => { it('calls setNode with node ID and actual size after rendering', () => { const { getByText } = render(); - expect(getByText(minProps.id)).toBeInTheDocument(); + expect(getByText(minProps.node.id)).toBeInTheDocument(); // Updates the node in the graph expect(setNode).toHaveBeenCalledWith(node.id, { + ...node, height: 100, width: 100, - ...node, }); // Does not pass down width/height to node diff --git a/packages/core-components/src/components/DependencyGraph/Node.tsx b/packages/core-components/src/components/DependencyGraph/Node.tsx index e37f7a6e37..01d518b554 100644 --- a/packages/core-components/src/components/DependencyGraph/Node.tsx +++ b/packages/core-components/src/components/DependencyGraph/Node.tsx @@ -17,8 +17,9 @@ import React from 'react'; import makeStyles from '@material-ui/core/styles/makeStyles'; import { DefaultNode } from './DefaultNode'; -import { RenderNodeFunction, RenderNodeProps, GraphNode } from './types'; +import { RenderNodeFunction, RenderNodeProps, DependencyNode } from './types'; import { NODE_TEST_ID } from './constants'; +import dagre from 'dagre'; export type DependencyGraphNodeClassKey = 'node'; @@ -31,20 +32,23 @@ const useStyles = makeStyles( { name: 'BackstageDependencyGraphNode' }, ); -export type NodeComponentProps = { +export type GraphNode = dagre.Node>; + +export type NodeComponentProps = { node: GraphNode; - render?: RenderNodeFunction; + render?: RenderNodeFunction; setNode: dagre.graphlib.Graph['setNode']; }; const renderDefault = (props: RenderNodeProps) => ; -export function Node({ +export function Node({ render = renderDefault, setNode, node, -}: NodeComponentProps) { - const { width, height, x = 0, y = 0, ...nodeProps } = node; +}: NodeComponentProps) { + const { width, height, x = 0, y = 0 } = node; + const nodeProps: DependencyNode = node; const classes = useStyles(); const nodeRef = React.useRef(null); diff --git a/packages/core-components/src/components/DependencyGraph/types.ts b/packages/core-components/src/components/DependencyGraph/types.ts index ed22007f9a..eab793685b 100644 --- a/packages/core-components/src/components/DependencyGraph/types.ts +++ b/packages/core-components/src/components/DependencyGraph/types.ts @@ -14,73 +14,132 @@ * limitations under the License. */ -import dagre from 'dagre'; +/** + * Types used to customize and provide data to {@link DependencyGraph} + * + * @packageDocumentation + */ -type CustomType = { [customKey: string]: any }; +import React from 'react'; -/* Edges */ -export type DependencyEdge = T & { +/** + * Edge of {@link DependencyGraph} + */ +export type DependencyEdge = T & { + /** + * ID of {@link DependencyNode} from where the Edge start + */ from: string; + /** + * ID of {@link DependencyNode} to where the Edge goes to + */ to: string; + /** + * Label assigned and rendered with the Edge + */ label?: string; }; -export type GraphEdge = DependencyEdge & - dagre.GraphEdge & - EdgeProperties; +/** + * Properties of {@link DependencyGraphTypes.RenderLabelFunction} for {@link DependencyGraphTypes.DependencyEdge} + */ +export type RenderLabelProps = { edge: DependencyEdge }; -export type RenderLabelProps = { edge: DependencyEdge }; - -export type RenderLabelFunction = ( - props: RenderLabelProps, +/** + * Custom React component for edge labels + */ +export type RenderLabelFunction = ( + props: RenderLabelProps, ) => React.ReactNode; -/* Nodes */ -export type DependencyNode = T & { +/** + * Node of {@link DependencyGraph} + */ +export type DependencyNode = T & { id: string; }; -export type GraphNode = dagre.Node>; +/** + * Properties of {@link DependencyGraphTypes.RenderNodeFunction} for {@link DependencyGraphTypes.DependencyNode} + */ +export type RenderNodeProps = { node: DependencyNode }; -export type RenderNodeProps = { node: DependencyNode }; - -export type RenderNodeFunction = ( - props: RenderNodeProps, +/** + * Custom React component for graph {@link DependencyGraphTypes.DependencyNode} + */ +export type RenderNodeFunction = ( + props: RenderNodeProps, ) => React.ReactNode; -/* Based on: https://github.com/dagrejs/dagre/wiki#configuring-the-layout */ - -export type EdgeProperties = { - label?: string; - width?: number; - height?: number; - labeloffset?: number; - labelpos?: LabelPosition; - minlen?: number; - weight?: number; - [customKey: string]: any; -}; - +/** + * Graph direction + */ export enum Direction { + /** + * Top to Bottom + */ TOP_BOTTOM = 'TB', + /** + * Bottom to Top + */ BOTTOM_TOP = 'BT', + /** + * Left to Right + */ LEFT_RIGHT = 'LR', + /** + * Right to Left + */ RIGHT_LEFT = 'RL', } +/** + * Node alignment + */ export enum Alignment { + /** + * Up Left + */ UP_LEFT = 'UL', + /** + * Up Right + */ UP_RIGHT = 'UR', + /** + * Down Left + */ DOWN_LEFT = 'DL', + /** + * Down Right + */ DOWN_RIGHT = 'DR', } +/** + * Algorithm used to rand nodes in graph + */ export enum Ranker { + /** + * {@link https://en.wikipedia.org/wiki/Network_simplex_algorithm | Network Simplex} algorithm + */ NETWORK_SIMPLEX = 'network-simplex', + /** + * Tight Tree algorithm + */ TIGHT_TREE = 'tight-tree', + /** + * Longest path algorithm + * + * @remarks + * + * Simplest and fastest + */ LONGEST_PATH = 'longest-path', } +/** + * Position of label in relation to the edge + */ export enum LabelPosition { LEFT = 'l', RIGHT = 'r', diff --git a/plugins/catalog-graph/api-report.md b/plugins/catalog-graph/api-report.md index b3694b9fe8..815f3427f2 100644 --- a/plugins/catalog-graph/api-report.md +++ b/plugins/catalog-graph/api-report.md @@ -93,13 +93,23 @@ export const EntityCatalogGraphCard: ({ }) => JSX.Element; // @public -export type EntityEdge = DependencyGraphTypes.DependencyEdge<{ +export type EntityEdge = DependencyGraphTypes.DependencyEdge; + +// Warning: (ae-missing-release-tag) "EntityEdgeData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type EntityEdgeData = { relations: string[]; label: 'visible'; -}>; +}; // @public -export type EntityNode = DependencyGraphTypes.DependencyNode<{ +export type EntityNode = DependencyGraphTypes.DependencyNode; + +// Warning: (ae-missing-release-tag) "EntityNodeData" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export type EntityNodeData = { name: string; kind?: string; title?: string; @@ -107,7 +117,7 @@ export type EntityNode = DependencyGraphTypes.DependencyNode<{ focused?: boolean; color?: 'primary' | 'secondary' | 'default'; onClick?: MouseEventHandler; -}>; +}; // @public export const EntityRelationsGraph: ({ diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx index 2a48600941..5b8f50b983 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.test.tsx @@ -31,12 +31,6 @@ describe('', () => { relations: [RELATION_PARENT_OF], from: 'from-id', to: 'to-id', - id: 'id', - x: 111, - y: 222, - width: 100, - height: 25, - points: [], }} /> , @@ -54,12 +48,6 @@ describe('', () => { relations: [RELATION_PARENT_OF, RELATION_CHILD_OF], from: 'from-id', to: 'to-id', - id: 'id', - x: 111, - y: 222, - width: 100, - height: 25, - points: [], }} /> , diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx index 9016891efa..60a858eee6 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx @@ -17,7 +17,7 @@ import { DependencyGraphTypes } from '@backstage/core-components'; import { BackstageTheme } from '@backstage/theme'; import makeStyles from '@material-ui/core/styles/makeStyles'; import React from 'react'; -import { GraphEdge } from './types'; +import { EntityEdgeData } from './types'; import classNames from 'classnames'; const useStyles = makeStyles((theme: BackstageTheme) => ({ @@ -31,7 +31,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ export function CustomLabel({ edge: { relations }, -}: DependencyGraphTypes.RenderLabelProps) { +}: DependencyGraphTypes.RenderLabelProps) { const classes = useStyles(); return ( diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx index f411f8255a..48272b8d1e 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.test.tsx @@ -36,10 +36,6 @@ describe('', () => { name: 'name', namespace: 'namespace', id: 'kind:namespace/name', - x: 111, - y: 222, - width: 100, - height: 25, color: 'primary', }} /> @@ -59,10 +55,6 @@ describe('', () => { name: 'name', namespace: 'default', id: 'kind:default/name', - x: 111, - y: 222, - width: 100, - height: 25, }} /> , @@ -83,10 +75,6 @@ describe('', () => { namespace: 'namespace', onClick, id: 'kind:namespace/name', - x: 111, - y: 222, - width: 100, - height: 25, }} /> , @@ -108,10 +96,6 @@ describe('', () => { namespace: 'namespace', title: 'Custom Title', id: 'kind:namespace/name', - x: 111, - y: 222, - width: 100, - height: 25, }} /> , diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx index 53542cfbd6..9cd752a5d7 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx @@ -20,7 +20,7 @@ import { makeStyles } from '@material-ui/core/styles'; import classNames from 'classnames'; import React, { useLayoutEffect, useRef, useState } from 'react'; import { EntityKindIcon } from './EntityKindIcon'; -import { GraphNode } from './types'; +import { EntityNodeData } from './types'; const useStyles = makeStyles((theme: BackstageTheme) => ({ node: { @@ -65,7 +65,7 @@ export function CustomNode({ title, onClick, }, -}: DependencyGraphTypes.RenderNodeProps) { +}: DependencyGraphTypes.RenderNodeProps) { const classes = useStyles(); const [width, setWidth] = useState(0); const [height, setHeight] = useState(0); diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts index 63ac9c2eac..b86c8f05ae 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/index.ts @@ -17,4 +17,9 @@ export { EntityRelationsGraph } from './EntityRelationsGraph'; export { ALL_RELATION_PAIRS } from './relations'; export type { RelationPairs } from './relations'; export { Direction } from './types'; -export type { EntityEdge, EntityNode } from './types'; +export type { + EntityEdgeData, + EntityEdge, + EntityNodeData, + EntityNode, +} from './types'; diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts b/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts index d6bda71a33..830032d527 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/types.ts @@ -17,11 +17,9 @@ import { DependencyGraphTypes } from '@backstage/core-components'; import { MouseEventHandler } from 'react'; /** - * Edge between two entities. - * - * @public + * Additional Data for entities */ -export type EntityEdge = DependencyGraphTypes.DependencyEdge<{ +export type EntityEdgeData = { /** * Up to two relations that are connecting an entity. */ @@ -31,14 +29,19 @@ export type EntityEdge = DependencyGraphTypes.DependencyEdge<{ */ // Not used, but has to be non empty to draw a label at all! label: 'visible'; -}>; +}; /** - * Node representing an entity. + * Edge between two entities. * * @public */ -export type EntityNode = DependencyGraphTypes.DependencyNode<{ +export type EntityEdge = DependencyGraphTypes.DependencyEdge; + +/** + * Additional data for Entity Node + */ +export type EntityNodeData = { /** * Name of the entity. */ @@ -68,11 +71,14 @@ export type EntityNode = DependencyGraphTypes.DependencyNode<{ * Optional click handler. */ onClick?: MouseEventHandler; -}>; +}; -export type GraphEdge = DependencyGraphTypes.GraphEdge; - -export type GraphNode = DependencyGraphTypes.GraphNode; +/** + * Node representing an entity. + * + * @public + */ +export type EntityNode = DependencyGraphTypes.DependencyNode; /** * Render direction of the graph. From d748dea84ace1e28847cd4a40174b777e03dacdc Mon Sep 17 00:00:00 2001 From: Tomasz Szuba Date: Mon, 4 Oct 2021 16:38:57 +0200 Subject: [PATCH 03/54] Something along the way does not support linking to enum values. Fix it. Signed-off-by: Tomasz Szuba --- packages/core-components/api-report.md | 5 ----- .../src/components/DependencyGraph/DependencyGraph.tsx | 10 +++++----- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 1067cd3d02..235e11607f 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -305,24 +305,19 @@ export interface DependencyGraphProps align?: Alignment; defs?: SVGDefsElement | SVGDefsElement[]; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver - // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver direction?: Direction; edgeMargin?: number; edgeRanks?: number; edges: DependencyEdge[]; edgeWeight?: number; - // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver - // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver labelOffset?: number; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver - // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver labelPosition?: LabelPosition; nodeMargin?: number; nodes: DependencyNode[]; paddingX?: number; paddingY?: number; // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver - // Warning: (ae-unresolved-link) The @link reference could not be resolved: This type of declaration is not supported yet by the resolver ranker?: Ranker; rankMargin?: number; renderLabel?: RenderLabelFunction; diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index 7a9ef9a9c7..1c3a4fce96 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -56,7 +56,7 @@ export interface DependencyGraphProps * * @remarks * - * Default: {@link DependencyGraphTypes.Direction.TOP_BOTTOM} + * Default: `DependencyGraphTypes.Direction.TOP_BOTTOM` */ direction?: Direction; /** @@ -112,7 +112,7 @@ export interface DependencyGraphProps * * @remarks * - * Default: {@link DependencyGraphTypes.Ranker.NETWORK_SIMPLEX} + * Default: `DependencyGraphTypes.Ranker.NETWORK_SIMPLEX` */ ranker?: Ranker; /** @@ -120,7 +120,7 @@ export interface DependencyGraphProps * * @remarks * - * Default: {@link DependencyGraphTypes.LabelPosition.RIGHT} + * Default: `DependencyGraphTypes.LabelPosition.RIGHT` */ labelPosition?: LabelPosition; /** @@ -128,8 +128,8 @@ export interface DependencyGraphProps * * @remarks * - * Applies only when {@link DependencyGraphProps.labelPosition} is {@link DependencyGraphTypes.LabelPosition.LEFT} or - * {@link DependencyGraphTypes.LabelPosition.RIGHT} + * Applies only when {@link DependencyGraphProps.labelPosition} is `DependencyGraphTypes.LabelPosition.LEFT` or + * `DependencyGraphTypes.LabelPosition.RIGHT` */ labelOffset?: number; /** From 19681e1c99ee38f27aeb632c200c95e5770de935 Mon Sep 17 00:00:00 2001 From: Tomasz Szuba Date: Mon, 4 Oct 2021 22:41:42 +0200 Subject: [PATCH 04/54] Move @types/dagre to devDependencies in core-components Signed-off-by: Tomasz Szuba --- packages/core-components/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 67e359da78..8053d4fe3c 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -37,7 +37,6 @@ "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.57", - "@types/dagre": "^0.7.44", "@types/react": "*", "@types/react-sparklines": "^1.7.0", "@types/react-text-truncate": "^0.14.0", @@ -79,6 +78,7 @@ "@types/d3-selection": "^2.0.0", "@types/d3-shape": "^3.0.1", "@types/d3-zoom": "^2.0.0", + "@types/dagre": "^0.7.44", "@types/google-protobuf": "^3.7.2", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", From ebb2886122f57bfe438bc7b60c1f8691fb3ae6ae Mon Sep 17 00:00:00 2001 From: Johan Hammar Date: Tue, 5 Oct 2021 20:07:03 +0200 Subject: [PATCH 05/54] Call ev.preventDefault() before calling search() Signed-off-by: Johan Hammar --- packages/core-components/src/layout/Sidebar/Items.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 1246fda82e..91af93439c 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -310,8 +310,8 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { const handleEnter: KeyboardEventHandler = ev => { if (ev.key === 'Enter') { - search(); ev.preventDefault(); + search(); } }; From 3539f88c6d731ca2ce4f9584c48b189659e19a7d Mon Sep 17 00:00:00 2001 From: Johan Hammar Date: Wed, 6 Oct 2021 20:14:12 +0200 Subject: [PATCH 06/54] Add test for defaultPrevented Signed-off-by: Johan Hammar --- .../src/layout/Sidebar/Items.test.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.test.tsx b/packages/core-components/src/layout/Sidebar/Items.test.tsx index 89d2a2c294..a3d3845028 100644 --- a/packages/core-components/src/layout/Sidebar/Items.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.test.tsx @@ -16,12 +16,12 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; -import { screen } from '@testing-library/react'; +import { createEvent, fireEvent, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import HomeIcon from '@material-ui/icons/Home'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import { Sidebar } from './Bar'; -import { SidebarItem } from './Items'; +import { SidebarItem, SidebarSearchField } from './Items'; import { renderHook } from '@testing-library/react-hooks'; import { hexToRgb, makeStyles } from '@material-ui/core'; @@ -36,6 +36,7 @@ async function renderSidebar() { await renderInTestApp( + {}} to="/search" /> { ).toHaveStyle(`background-color: ${hexToRgb('2b2a2a')}`); }); }); + describe('SidebarSearchField', () => { + it('should be defaultPrevented when enter is pressed', async () => { + const searchEvent = createEvent.keyDown( + await screen.findByPlaceholderText('Search'), + { key: 'Enter', code: 'Enter', charCode: 13 }, + ); + fireEvent(await screen.findByPlaceholderText('Search'), searchEvent); + expect(searchEvent.defaultPrevented).toBeTruthy(); + }); + }); }); From 4aca84f86b0ba13589d911c2080740105aba3f35 Mon Sep 17 00:00:00 2001 From: Alex Rybchenko Date: Thu, 7 Oct 2021 12:11:26 +0200 Subject: [PATCH 07/54] added support for material-ui overrides in react-catalog Signed-off-by: Alex Rybchenko --- .changeset/wise-clocks-accept.md | 5 ++ .../EntityLifecyclePicker.tsx | 15 +++++- .../EntityOwnerPicker/EntityOwnerPicker.tsx | 15 +++++- .../EntitySearchBar/EntitySearchBar.tsx | 21 +++++--- .../EntityTagPicker/EntityTagPicker.tsx | 15 +++++- .../UserListPicker/UserListPicker.tsx | 49 ++++++++++--------- 6 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 .changeset/wise-clocks-accept.md diff --git a/.changeset/wise-clocks-accept.md b/.changeset/wise-clocks-accept.md new file mode 100644 index 0000000000..7b9090937b --- /dev/null +++ b/.changeset/wise-clocks-accept.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Support `material-ui` overrides in plugin-catalog-react components diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx index 628526e9ab..750692ed2e 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx @@ -19,6 +19,7 @@ import { Box, Checkbox, FormControlLabel, + makeStyles, TextField, Typography, } from '@material-ui/core'; @@ -30,10 +31,20 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; import { EntityLifecycleFilter } from '../../filters'; +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityLifecyclePicker', + }, +); + const icon = ; const checkedIcon = ; export const EntityLifecyclePicker = () => { + const classes = useStyles(); const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider(); @@ -91,7 +102,9 @@ export const EntityLifecyclePicker = () => { )} size="small" popupIcon={} - renderInput={params => } + renderInput={params => ( + + )} /> ); diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 49c950f57e..dbf6a33164 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -19,6 +19,7 @@ import { Box, Checkbox, FormControlLabel, + makeStyles, TextField, Typography, } from '@material-ui/core'; @@ -32,10 +33,20 @@ import { EntityOwnerFilter } from '../../filters'; import { getEntityRelations } from '../../utils'; import { formatEntityRefTitle } from '../EntityRefLink'; +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityOwnerPicker', + }, +); + const icon = ; const checkedIcon = ; export const EntityOwnerPicker = () => { + const classes = useStyles(); const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider(); @@ -95,7 +106,9 @@ export const EntityOwnerPicker = () => { )} size="small" popupIcon={} - renderInput={params => } + renderInput={params => ( + + )} /> ); diff --git a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx index 9fd30b5182..3a848591a3 100644 --- a/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx +++ b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx @@ -29,15 +29,21 @@ import { useDebounce } from 'react-use'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; import { EntityTextFilter } from '../../filters'; -const useStyles = makeStyles(_theme => ({ - searchToolbar: { - paddingLeft: 0, - paddingRight: 0, +const useStyles = makeStyles( + _theme => ({ + searchToolbar: { + paddingLeft: 0, + paddingRight: 0, + }, + input: {}, + }), + { + name: 'CatalogReactEntitySearchBar', }, -})); +); export const EntitySearchBar = () => { - const styles = useStyles(); + const classes = useStyles(); const { filters, updateFilters } = useEntityListProvider(); const [search, setSearch] = useState(filters.text?.value ?? ''); @@ -53,10 +59,11 @@ export const EntitySearchBar = () => { ); return ( - + setSearch(event.target.value)} diff --git a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx index 64c80d1cde..9e00f7e7b0 100644 --- a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx +++ b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx @@ -19,6 +19,7 @@ import { Box, Checkbox, FormControlLabel, + makeStyles, TextField, Typography, } from '@material-ui/core'; @@ -30,10 +31,20 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; import { EntityTagFilter } from '../../filters'; +const useStyles = makeStyles( + { + input: {}, + }, + { + name: 'CatalogReactEntityTagPicker', + }, +); + const icon = ; const checkedIcon = ; export const EntityTagPicker = () => { + const classes = useStyles(); const { updateFilters, backendEntities, filters, queryParameters } = useEntityListProvider(); @@ -87,7 +98,9 @@ export const EntityTagPicker = () => { )} size="small" popupIcon={} - renderInput={params => } + renderInput={params => ( + + )} /> ); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx index bfc55142a6..f861355762 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx @@ -43,29 +43,34 @@ import { import { UserListFilterKind } from '../../types'; import { reduceEntityFilters } from '../../utils'; -const useStyles = makeStyles(theme => ({ - root: { - backgroundColor: 'rgba(0, 0, 0, .11)', - boxShadow: 'none', - margin: theme.spacing(1, 0, 1, 0), +const useStyles = makeStyles( + theme => ({ + root: { + backgroundColor: 'rgba(0, 0, 0, .11)', + boxShadow: 'none', + margin: theme.spacing(1, 0, 1, 0), + }, + title: { + margin: theme.spacing(1, 0, 0, 1), + textTransform: 'uppercase', + fontSize: 12, + fontWeight: 'bold', + }, + listIcon: { + minWidth: 30, + color: theme.palette.text.primary, + }, + menuItem: { + minHeight: theme.spacing(6), + }, + groupWrapper: { + margin: theme.spacing(1, 1, 2, 1), + }, + }), + { + name: 'CatalogReactUserListPicker', }, - title: { - margin: theme.spacing(1, 0, 0, 1), - textTransform: 'uppercase', - fontSize: 12, - fontWeight: 'bold', - }, - listIcon: { - minWidth: 30, - color: theme.palette.text.primary, - }, - menuItem: { - minHeight: theme.spacing(6), - }, - groupWrapper: { - margin: theme.spacing(1, 1, 2, 1), - }, -})); +); export type ButtonGroup = { name: string; From 888b9704988a687b3831c9aff17016adc3a49ef7 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 7 Oct 2021 14:48:25 +0200 Subject: [PATCH 08/54] Add docs for writing changesets Signed-off-by: Johan Haals --- CONTRIBUTING.md | 7 ++- docs/getting-started/contributors.md | 85 ++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5242309eee..ad17ff17f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -129,9 +129,10 @@ In general, changesets are not needed for the documentation, build utilities, co 1. Run `yarn changeset` 2. Select which packages you want to include a changeset for 3. Select impact of change that you're introducing, using `minor` for breaking changes and `patch` otherwise. We do not use `major` changes while packages are at version `0.x`. -4. Add generated changeset to Git -5. Push the commit with your changeset to the branch associated with your PR -6. Accept our gratitude for making the release process easier on the maintainers +4. Explain your changes in the generated changeset. Examples of a well written changeset can be found [here](https://backstage.io/docs/getting-started/contributors#writing-changesets). +5. Add generated changeset to Git +6. Push the commit with your changeset to the branch associated with your PR +7. Accept our gratitude for making the release process easier on the maintainers For more information, checkout [adding a changeset](https://github.com/atlassian/changesets/blob/master/docs/adding-a-changeset.md) documentation in the changesets repository. diff --git a/docs/getting-started/contributors.md b/docs/getting-started/contributors.md index e12c0c5f17..9af57277d9 100644 --- a/docs/getting-started/contributors.md +++ b/docs/getting-started/contributors.md @@ -139,3 +139,88 @@ default app configs. You can learn more about the local configuration in [Static Configuration in Backstage](../conf/) section. + +## Writing changesets + +Changesets are an important part of the development process. They are used to +generate Changelog entries for all changes to the project. Ultimately they are +read by the end users to learn about important changes and fixes to the project. +Some of these fixes might require manual intervention from users so it's +important to write changesets that users understand and can take action on. + +Here are some important do's and don'ts when writing changesets: + +### Changeset should give a clear description to what has changed + +#### Bad + +``` +--- +'@backstage/catalog': patch +--- +Fixed table layout +``` + +#### Good + +``` +--- +'@backstage/catalog': patch +--- + +Fixed bug in EntityTable component where table layout did not readjust properly below 1080x768 pixels. +``` + +### Breaking changes not caught by the type checker should be clearly marked with bold **BREAKING** text + +#### Bad + +``` +--- +'@backstage/catalog': minor +--- + +getEntity is now a function that returns a Promise. +``` + +#### Good + +``` +--- +'@backstage/catalog': minor +--- + +**BREAKING** The getEntity function now returns a Promise and **must** be awaited from now on. +``` + +### Changes to code should include a diff of the files that needs updating + +#### Bad + +``` +--- +'@backstage/catalog': patch +--- + +**BREAKING** The catalogEngine now requires a flux capacitor to be passed. +``` + +#### Good + +**BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is +passed to the router. + +These changes are **required** to `packages/backend/src/plugins/catalog.ts` + +```diff ++ import { FluxCapacitor } from '@backstage/time'; ++ const fluxCapacitor = new FluxCapacitor(); + return await createRouter({ + entitiesCatalog, + locationAnalyzer, + locationService, ++ fluxCapacitor, + logger: env.logger, + config: env.config, + }); +``` From c3b29843b2b5452ea9642d638bbaf3490637a3cb Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Thu, 7 Oct 2021 16:23:06 +0200 Subject: [PATCH 09/54] Update docs/getting-started/contributors.md Signed-off-by: Johan Haals Co-authored-by: Adam Harvey --- docs/getting-started/contributors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/contributors.md b/docs/getting-started/contributors.md index 9af57277d9..7b612ccd0d 100644 --- a/docs/getting-started/contributors.md +++ b/docs/getting-started/contributors.md @@ -193,7 +193,7 @@ getEntity is now a function that returns a Promise. **BREAKING** The getEntity function now returns a Promise and **must** be awaited from now on. ``` -### Changes to code should include a diff of the files that needs updating +### Changes to code should include a diff of the files that need updating #### Bad From 0de80576e3d11725216d04a21c65ab44758250ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Frinnstr=C3=B6m?= Date: Tue, 5 Oct 2021 09:20:07 +0200 Subject: [PATCH 10/54] auth-backend: update aws-alb provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Frinnström --- .../src/providers/aws-alb/provider.test.ts | 265 +++++++++++++----- .../src/providers/aws-alb/provider.ts | 264 +++++++++++++---- plugins/auth-backend/src/providers/index.ts | 1 + plugins/auth-backend/src/providers/types.ts | 14 - 4 files changed, 399 insertions(+), 145 deletions(-) diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.test.ts b/plugins/auth-backend/src/providers/aws-alb/provider.test.ts index ca4e521316..2f5ffa1833 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.test.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.test.ts @@ -17,8 +17,14 @@ import { getVoidLogger } from '@backstage/backend-common'; import express from 'express'; import { JWT } from 'jose'; -import { AwsAlbAuthProvider } from './provider'; -import { AuthResponse } from '../types'; +import { + ALB_ACCESSTOKEN_HEADER, + ALB_JWT_HEADER, + AwsAlbAuthProvider, +} from './provider'; +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient } from '../../lib/catalog'; +import { makeProfileInfo } from '../../lib/passport'; const jwtMock = JWT as jest.Mocked; @@ -29,9 +35,21 @@ yOlxJ2VW88mLAQGJ7HPAvOdylxZsItMnzCuqNzZvie8m/NJsOjhDncVkrw== -----END PUBLIC KEY----- `; }; +const mockJwt = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IktFWV9JRCIsImlzcyI6IklTU1VFUl9VUkwifQ.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlVzZXIgTmFtZSIsImlhdCI6MTUxNjIzOTAyMn0.uMCSBGhij1xn5pnot8XgD-huQuTIBOFGs6kkW_p_X94'; +const mockAccessToken = 'ACCESS_TOKEN'; +const mockClaims = { + sub: '1234567890', + name: 'User Name', + family_name: 'Name', + given_name: 'User', + picture: 'PICTURE_URL', + email: 'user.name@email.test', + exp: 1632833763, + iss: 'ISSUER_URL', +}; jest.mock('jose'); - jest.mock('cross-fetch', () => ({ __esModule: true, default: async () => { @@ -43,53 +61,48 @@ jest.mock('cross-fetch', () => ({ }, })); -const identityResolutionCallbackMock = async (): Promise> => { - return { - backstageIdentity: { - id: 'foo', - idToken: '', - }, - profile: { - displayName: 'Foo Bar', - }, - providerInfo: {}, - }; -}; - -const identityResolutionCallbackRejectedMock = async (): Promise< - AuthResponse -> => { - throw new Error('failed'); -}; - beforeEach(() => { jest.clearAllMocks(); }); -describe('AwsALBAuthProvider', () => { - const catalogApi = { - addLocation: jest.fn(), - removeLocationById: jest.fn(), - getEntities: jest.fn(), - getOriginLocationByEntity: jest.fn(), - getLocationByEntity: jest.fn(), - getLocationById: jest.fn(), - removeEntityByUid: jest.fn(), - getEntityByName: jest.fn(), - refreshEntity: jest.fn(), - getEntityAncestors: jest.fn(), +describe('AwsAlbAuthProvider', () => { + const tokenIssuer: TokenIssuer = { + listPublicKeys: jest.fn(), + async issueToken(params) { + return `token-for-${params.claims.sub}`; + }, }; + const catalogIdentityClient: CatalogIdentityClient = { + findUser: jest.fn(), + } as unknown as CatalogIdentityClient; const mockRequest = { - header: jest.fn(() => { - return 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyIsImlzcyI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.T2BNS4G-6RoiFnXc8Q8TiwdWzTpNitY8jcsGM3N3-Yo'; - }), - } as unknown as express.Request; - const mockRequestWithoutJwt = { - header: jest.fn(() => { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return mockJwt; + } else if (name === ALB_ACCESSTOKEN_HEADER) { + return mockAccessToken; + } return undefined; }), } as unknown as express.Request; + const mockRequestWithoutJwt = { + header: jest.fn(name => { + if (name === ALB_ACCESSTOKEN_HEADER) { + return mockAccessToken; + } + return undefined; + }), + } as unknown as express.Request; + const mockRequestWithoutAccessToken = { + header: jest.fn(name => { + if (name === ALB_JWT_HEADER) { + return mockJwt; + } + return undefined; + }), + } as unknown as express.Request; + const mockResponse = { end: jest.fn(), header: () => jest.fn(), @@ -97,38 +110,78 @@ describe('AwsALBAuthProvider', () => { status: jest.fn(), } as unknown as express.Response; - describe('should transform to type OAuthResponse', () => { + describe('should transform to type AwsAlbResponse', () => { it('when JWT is valid and identity is resolved successfully', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foo', + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); - jwtMock.verify.mockImplementationOnce(() => ({ - sub: 'foo', - })); + jwtMock.verify.mockReturnValueOnce(mockClaims); await provider.refresh(mockRequest, mockResponse); expect(mockResponse.json).toHaveBeenCalledWith({ backstageIdentity: { - id: 'foo', - idToken: '', + id: 'user.name', + token: 'TOKEN', }, profile: { - displayName: 'Foo Bar', + displayName: 'User Name', + email: 'user.name@email.test', + picture: 'PICTURE_URL', + }, + providerInfo: { + accessToken: mockAccessToken, + expiresInSeconds: mockClaims.exp, }, - providerInfo: {}, }); }); }); + describe('should fail when', () => { + it('Access token is missing', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, + }); + + await provider.refresh(mockRequestWithoutAccessToken, mockResponse); + + expect(mockResponse.status).toHaveBeenCalledWith(401); + }); + it('JWT is missing', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foo', + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); await provider.refresh(mockRequestWithoutJwt, mockResponse); @@ -137,10 +190,18 @@ describe('AwsALBAuthProvider', () => { }); it('JWT is invalid', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foo', + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); jwtMock.verify.mockImplementationOnce(() => { @@ -152,11 +213,19 @@ describe('AwsALBAuthProvider', () => { expect(mockResponse.status).toHaveBeenCalledWith(401); }); - it('issuer is invalid', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackMock, - issuer: 'foobar', + it('issuer is missing', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); jwtMock.verify.mockReturnValueOnce({}); @@ -165,14 +234,68 @@ describe('AwsALBAuthProvider', () => { expect(mockResponse.status).toHaveBeenCalledWith(401); }); - it('identity resolution callback rejects', async () => { - const provider = new AwsAlbAuthProvider(getVoidLogger(), catalogApi, { - region: 'us-west-2', - identityResolutionCallback: identityResolutionCallbackRejectedMock, - issuer: 'foo', + it('issuer is invalid', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, }); - jwtMock.verify.mockReturnValueOnce({}); + jwtMock.verify.mockReturnValueOnce({ + iss: 'INVALID_ISSUE_URL', + }); + + await provider.refresh(mockRequest, mockResponse); + expect(mockResponse.status).toHaveBeenCalledWith(401); + }); + + it('SignInResolver rejects', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }), + signInResolver: async () => { + throw new Error(); + }, + }); + + jwtMock.verify.mockReturnValueOnce(mockClaims); + + await provider.refresh(mockRequest, mockResponse); + + expect(mockResponse.status).toHaveBeenCalledWith(401); + expect(mockResponse.end).toHaveBeenCalledTimes(1); + }); + + it('AuthHandler rejects', async () => { + const provider = new AwsAlbAuthProvider({ + region: 'eu-west-1', + issuer: 'ISSUER_URL', + logger: getVoidLogger(), + catalogIdentityClient, + tokenIssuer, + authHandler: async () => { + throw new Error(); + }, + signInResolver: async () => { + return { id: 'user.name', token: 'TOKEN' }; + }, + }); + + jwtMock.verify.mockReturnValueOnce(mockClaims); await provider.refresh(mockRequest, mockResponse); diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index bd16ae1960..04b192d562 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -14,9 +14,11 @@ * limitations under the License. */ import { + AuthHandler, AuthProviderFactoryOptions, AuthProviderRouteHandlers, - ExperimentalIdentityResolver, + AuthResponse, + SignInResolver, } from '../types'; import express from 'express'; import fetch from 'cross-fetch'; @@ -25,66 +27,101 @@ import { KeyObject } from 'crypto'; import { Logger } from 'winston'; import NodeCache from 'node-cache'; import { JWT } from 'jose'; -import { CatalogApi } from '@backstage/catalog-client'; +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient } from '../../lib/catalog'; +import { Profile as PassportProfile } from 'passport'; +import { makeProfileInfo } from '../../lib/passport'; +import { AuthenticationError } from '@backstage/errors'; -const ALB_JWT_HEADER = 'x-amzn-oidc-data'; -/** - * A callback function that receives a verified JWT and returns a UserEntity - * @param {payload} The verified JWT payload - */ -type AwsAlbAuthProviderOptions = { +export const ALB_JWT_HEADER = 'x-amzn-oidc-data'; +export const ALB_ACCESSTOKEN_HEADER = 'x-amzn-oidc-accesstoken'; + +type Options = { region: string; issuer?: string; - identityResolutionCallback: ExperimentalIdentityResolver; + logger: Logger; + authHandler: AuthHandler; + signInResolver: SignInResolver; + tokenIssuer: TokenIssuer; + catalogIdentityClient: CatalogIdentityClient; }; -export const getJWTHeaders = (input: string) => { + +export const getJWTHeaders = (input: string): AwsAlbHeaders => { const encoded = input.split('.')[0]; return JSON.parse(Buffer.from(encoded, 'base64').toString('utf8')); }; -export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { - private logger: Logger; - private readonly catalogClient: CatalogApi; - private options: AwsAlbAuthProviderOptions; - private readonly keyCache: NodeCache; +export type AwsAlbHeaders = { + alg: string; + kid: string; + signer: string; + iss: string; + client: string; + exp: number; +}; - constructor( - logger: Logger, - catalogClient: CatalogApi, - options: AwsAlbAuthProviderOptions, - ) { - this.logger = logger; - this.catalogClient = catalogClient; - this.options = options; +export type AwsAlbClaims = { + sub: string; + name: string; + family_name: string; + given_name: string; + picture: string; + email: string; + exp: number; + iss: string; +}; + +export type AwsAlbResult = { + fullProfile: PassportProfile; + expiresInSeconds?: number; + accessToken: string; +}; + +export type AwsAlbProviderInfo = { + /** + * An access token issued for the signed in user. + */ + accessToken: string; + /** + * Expiry of the access token in seconds. + */ + expiresInSeconds?: number; +}; + +export type AwsAlbResponse = AuthResponse; + +export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { + private readonly region: string; + private readonly issuer?: string; + private readonly tokenIssuer: TokenIssuer; + private readonly catalogIdentityClient: CatalogIdentityClient; + private readonly logger: Logger; + private readonly keyCache: NodeCache; + private readonly authHandler: AuthHandler; + private readonly signInResolver: SignInResolver; + + constructor(options: Options) { + this.region = options.region; + this.issuer = options.issuer; + this.authHandler = options.authHandler; + this.signInResolver = options.signInResolver; + this.tokenIssuer = options.tokenIssuer; + this.catalogIdentityClient = options.catalogIdentityClient; + this.logger = options.logger; this.keyCache = new NodeCache({ stdTTL: 3600 }); } + frameHandler(): Promise { return Promise.resolve(undefined); } async refresh(req: express.Request, res: express.Response): Promise { - const jwt = req.header(ALB_JWT_HEADER); - if (jwt !== undefined) { - try { - const headers = getJWTHeaders(jwt); - const key = await this.getKey(headers.kid); - const payload = JWT.verify(jwt, key); - - if (this.options.issuer && headers.iss !== this.options.issuer) { - throw new Error('issuer mismatch on JWT'); - } - - const resolvedEntity = await this.options.identityResolutionCallback( - payload, - this.catalogClient, - ); - res.json(resolvedEntity); - } catch (e) { - this.logger.error('exception occurred during JWT processing', e); - res.status(401); - res.end(); - } - } else { + try { + const result = await this.getResult(req); + const response = await this.handleResult(result); + res.json(response); + } catch (e) { + this.logger.error('Exception occurred during AWS ALB token refresh', e); res.status(401); res.end(); } @@ -94,13 +131,85 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { return Promise.resolve(undefined); } + private async getResult(req: express.Request): Promise { + const jwt = req.header(ALB_JWT_HEADER); + const accessToken = req.header(ALB_ACCESSTOKEN_HEADER); + + if (jwt === undefined) { + throw new AuthenticationError( + `Missing ALB OIDC header: ${ALB_JWT_HEADER}`, + ); + } + + if (accessToken === undefined) { + throw new AuthenticationError( + `Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`, + ); + } + + try { + const headers = getJWTHeaders(jwt); + const key = await this.getKey(headers.kid); + const claims = JWT.verify(jwt, key) as AwsAlbClaims; + + if (this.issuer && claims.iss !== this.issuer) { + throw new AuthenticationError('Issuer mismatch on JWT token'); + } + + const fullProfile: PassportProfile = { + provider: 'unknown', + id: claims.sub, + displayName: claims.name, + username: claims.email.split('@')[0].toLowerCase(), + name: { + familyName: claims.family_name, + givenName: claims.given_name, + }, + emails: [{ value: claims.email.toLowerCase() }], + photos: [{ value: claims.picture }], + }; + + return { + fullProfile, + expiresInSeconds: claims.exp, + accessToken, + }; + } catch (e) { + throw new Error(`Exception occurred during JWT processing: ${e}`); + } + } + + private async handleResult(result: AwsAlbResult): Promise { + const { profile } = await this.authHandler(result); + const backstageIdentity = await this.signInResolver( + { + result, + profile, + }, + { + tokenIssuer: this.tokenIssuer, + catalogIdentityClient: this.catalogIdentityClient, + logger: this.logger, + }, + ); + + return { + providerInfo: { + accessToken: result.accessToken, + expiresInSeconds: result.expiresInSeconds, + }, + backstageIdentity, + profile, + }; + } + async getKey(keyId: string): Promise { const optionalCacheKey = this.keyCache.get(keyId); if (optionalCacheKey) { return crypto.createPublicKey(optionalCacheKey); } const keyText: string = await fetch( - `https://public-keys.auth.elb.${this.options.region}.amazonaws.com/${keyId}`, + `https://public-keys.auth.elb.${this.region}.amazonaws.com/${keyId}`, ).then(response => response.text()); const keyValue = crypto.createPublicKey(keyText); this.keyCache.set(keyId, keyValue.export({ format: 'pem', type: 'spki' })); @@ -108,26 +217,61 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers { } } -export type AwsAlbProviderOptions = {}; +export type AwsAlbProviderOptions = { + /** + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. + */ + authHandler?: AuthHandler; -export const createAwsAlbProvider = (_options?: AwsAlbProviderOptions) => { + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + signIn: { + /** + * Maps an auth result to a Backstage identity for the user. + */ + resolver: SignInResolver; + }; +}; + +export const createAwsAlbProvider = (options?: AwsAlbProviderOptions) => { return ({ - logger, - catalogApi, config, - identityResolver, + tokenIssuer, + catalogApi, + logger, }: AuthProviderFactoryOptions) => { const region = config.getString('region'); const issuer = config.getOptionalString('iss'); - if (identityResolver !== undefined) { - return new AwsAlbAuthProvider(logger, catalogApi, { - region, - issuer, - identityResolutionCallback: identityResolver, - }); + + if (options?.signIn.resolver === undefined) { + throw new Error( + 'SignInResolver is required to use this authentication provider', + ); } - throw new Error( - 'Identity resolver is required to use this authentication provider', - ); + + const catalogIdentityClient = new CatalogIdentityClient({ + catalogApi, + tokenIssuer, + }); + + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile }) => ({ + profile: makeProfileInfo(fullProfile), + }); + + const signInResolver = options?.signIn.resolver; + + return new AwsAlbAuthProvider({ + region, + issuer, + signInResolver, + authHandler, + tokenIssuer, + catalogIdentityClient, + logger, + }); }; }; diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 90466a8094..e63616ab91 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -21,6 +21,7 @@ export * from './microsoft'; export * from './oauth2'; export * from './okta'; export * from './bitbucket'; +export * from './aws-alb'; export { factories as defaultAuthProviderFactories } from './factories'; diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index 6f6df5d904..7f7b841c68 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -119,19 +119,6 @@ export interface AuthProviderRouteHandlers { logout?(req: express.Request, res: express.Response): Promise; } -/** - * EXPERIMENTAL - this will almost certainly break in a future release. - * - * Used to resolve an identity from auth information in some auth providers. - */ -export type ExperimentalIdentityResolver = ( - /** - * An object containing information specific to the auth provider. - */ - payload: object, - catalogApi: CatalogApi, -) => Promise>; - export type AuthProviderFactoryOptions = { providerId: string; globalConfig: AuthProviderConfig; @@ -140,7 +127,6 @@ export type AuthProviderFactoryOptions = { tokenIssuer: TokenIssuer; discovery: PluginEndpointDiscovery; catalogApi: CatalogApi; - identityResolver?: ExperimentalIdentityResolver; }; export type AuthProviderFactory = ( From 0cfeea8f8f350e406c9a3257defcd5f79ef642e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Frinnstr=C3=B6m?= Date: Wed, 6 Oct 2021 08:09:57 +0200 Subject: [PATCH 11/54] Add changeset and update docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Frinnström --- .changeset/breezy-dolphins-lay.md | 8 ++ .../docs/tutorials/aws-alb-aad-oidc-auth.md | 76 +++++++++++++------ 2 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 .changeset/breezy-dolphins-lay.md diff --git a/.changeset/breezy-dolphins-lay.md b/.changeset/breezy-dolphins-lay.md new file mode 100644 index 0000000000..0197979531 --- /dev/null +++ b/.changeset/breezy-dolphins-lay.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +AWS-ALB: update provider to the latest changes described [here](https://backstage.io/docs/auth/identity-resolver). + +This removes the `ExperimentalIdentityResolver` type in favor of `SignInResolver` and `AuthHandler`. +The AWS ALB provider can now be configured in the same way as the Google provider in the example. diff --git a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md index b121bab25f..6b1b8cb4e7 100644 --- a/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md +++ b/contrib/docs/tutorials/aws-alb-aad-oidc-auth.md @@ -100,17 +100,16 @@ const app = createApp({ ### Backend -When using ALB auth it is not possible to leverage the built-in auth config discovery mechanism implemented in the app created by default; bespoke logic needs to be implemented. +When using ALB auth you can configure it as described [here](https://backstage.io/docs/auth/identity-resolver). -- replace the content of `packages/backend/plugin/auth.ts` with the below +- replace the content of `packages/backend/plugin/auth.ts` with the below and tweak it according to your needs. ```ts import { createRouter, - AuthResponse, - AuthProviderFactoryOptions, - defaultAuthProviderFactories, + createAwsAlbProvider, } from '@backstage/plugin-auth-backend'; +import { Router } from 'express'; import { PluginEnvironment } from '../types'; export default async function createPlugin({ @@ -118,30 +117,59 @@ export default async function createPlugin({ database, config, discovery, -}: PluginEnvironment) { - const identityResolver = (payload: any): Promise> => { - return Promise.resolve({ - providerInfo: {}, - profile: { - email: payload.email, - displayName: payload.name, - picture: payload.picture, - }, - backstageIdentity: { - id: payload.email, - }, - }); - }; - const providerFactories = { - awsalb: (options: AuthProviderFactoryOptions) => - defaultAuthProviderFactories.awsalb({ ...options, identityResolver }), - }; +}: PluginEnvironment): Promise { return await createRouter({ logger, config, database, discovery, - providerFactories, + providerFactories: { + awsalb: createAwsAlbProvider({ + authHandler: async ({ fullProfile }) => { + let email: string | undefined = undefined; + if (fullProfile.emails && fullProfile.emails.length > 0) { + const [firstEmail] = fullProfile.emails; + email = firstEmail.value; + } + + let picture: string | undefined = undefined; + if (fullProfile.photos && fullProfile.photos.length > 0) { + const [firstPhoto] = fullProfile.photos; + picture = firstPhoto.value; + } + + const displayName: string | undefined = + fullProfile.displayName ?? fullProfile.username ?? fullProfile.id; + + return { + profile: { + email, + picture, + displayName, + }, + }; + }, + signIn: { + resolver: async ({ profile: { email } }, ctx) => { + const [id] = email?.split('@') ?? ''; + // Fetch from an external system that returns entity claims like: + // ['user:default/breanna.davison', ...] + const ent = [`user:default/${id}`]; + + // Resolve group membership from the Backstage catalog + const fullEnt = + await ctx.catalogIdentityClient.resolveCatalogMembership({ + entityRefs: [id].concat(ent), + logger: ctx.logger, + }); + const token = await ctx.tokenIssuer.issueToken({ + claims: { sub: id, ent: fullEnt }, + }); + return { id, token }; + }, + }, + }), + }, }); } ``` From f29ad9fe6d08e63acd8c335b8448fa7f6f22283c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Frinnstr=C3=B6m?= Date: Wed, 6 Oct 2021 08:13:19 +0200 Subject: [PATCH 12/54] Update api-report.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Frinnström --- plugins/auth-backend/api-report.md | 30 +++++++++++++++---- .../src/providers/aws-alb/provider.ts | 13 ++++---- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 0b9f0eb8c4..0bdd73f3e8 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -3,6 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// + import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -32,7 +34,6 @@ export type AuthProviderFactoryOptions = { tokenIssuer: TokenIssuer; discovery: PluginEndpointDiscovery; catalogApi: CatalogApi; - identityResolver?: ExperimentalIdentityResolver; }; // Warning: (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag @@ -74,6 +75,16 @@ export type AuthResponse = { backstageIdentity?: BackstageIdentity; }; +// Warning: (ae-missing-release-tag) "AwsAlbProviderOptions" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type AwsAlbProviderOptions = { + authHandler?: AuthHandler; + signIn: { + resolver: SignInResolver; + }; +}; + // Warning: (ae-missing-release-tag) "BackstageIdentity" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -135,6 +146,13 @@ export const bitbucketUserIdSignInResolver: SignInResolver // @public (undocumented) export const bitbucketUsernameSignInResolver: SignInResolver; +// Warning: (ae-missing-release-tag) "createAwsAlbProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const createAwsAlbProvider: ( + options?: AwsAlbProviderOptions | undefined, +) => AuthProviderFactory; + // Warning: (ae-missing-release-tag) "createBitbucketProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -526,9 +544,9 @@ export type WebMessageResponse = // // src/identity/types.d.ts:25:5 - (ae-forgotten-export) The symbol "TokenParams" needs to be exported by the entry point index.d.ts // src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts -// src/providers/bitbucket/provider.d.ts:61:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts -// src/providers/bitbucket/provider.d.ts:69:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts -// src/providers/types.d.ts:109:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts -// src/providers/types.d.ts:115:5 - (ae-forgotten-export) The symbol "ExperimentalIdentityResolver" needs to be exported by the entry point index.d.ts -// src/providers/types.d.ts:132:8 - (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative +// src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts +// src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AwsAlbResult" needs to be exported by the entry point index.d.ts +// src/providers/aws-alb/provider.d.ts:85:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:99:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts +// src/providers/types.d.ts:121:8 - (tsdoc-missing-deprecation-message) The @deprecated block must include a deprecation message, e.g. describing the recommended alternative ``` diff --git a/plugins/auth-backend/src/providers/aws-alb/provider.ts b/plugins/auth-backend/src/providers/aws-alb/provider.ts index 04b192d562..ac5db5e69f 100644 --- a/plugins/auth-backend/src/providers/aws-alb/provider.ts +++ b/plugins/auth-backend/src/providers/aws-alb/provider.ts @@ -15,7 +15,7 @@ */ import { AuthHandler, - AuthProviderFactoryOptions, + AuthProviderFactory, AuthProviderRouteHandlers, AuthResponse, SignInResolver, @@ -235,13 +235,10 @@ export type AwsAlbProviderOptions = { }; }; -export const createAwsAlbProvider = (options?: AwsAlbProviderOptions) => { - return ({ - config, - tokenIssuer, - catalogApi, - logger, - }: AuthProviderFactoryOptions) => { +export const createAwsAlbProvider = ( + options?: AwsAlbProviderOptions, +): AuthProviderFactory => { + return ({ config, tokenIssuer, catalogApi, logger }) => { const region = config.getString('region'); const issuer = config.getOptionalString('iss'); From 86f9a418bfd3e30b59e6a8af228ee864941f7cec Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Wed, 6 Oct 2021 17:33:51 +0200 Subject: [PATCH 13/54] Add GA to plugin marketplace. Signed-off-by: Eric Peterson --- .../plugins/backstage-analytics-module-ga.yaml | 9 +++++++++ microsite/static/img/ga-icon.png | Bin 0 -> 34270 bytes 2 files changed, 9 insertions(+) create mode 100644 microsite/data/plugins/backstage-analytics-module-ga.yaml create mode 100644 microsite/static/img/ga-icon.png diff --git a/microsite/data/plugins/backstage-analytics-module-ga.yaml b/microsite/data/plugins/backstage-analytics-module-ga.yaml new file mode 100644 index 0000000000..362f03bdad --- /dev/null +++ b/microsite/data/plugins/backstage-analytics-module-ga.yaml @@ -0,0 +1,9 @@ +--- +title: "Analytics Module: Google Analytics" +author: Spotify +authorUrl: https://github.com/spotify +category: Analytics +description: Track usage of your Backstage instance using Google Analytics. +documentation: https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +iconUrl: img/ga-icon.png +npmPackageName: '@backstage/plugin-analytics-module-ga' diff --git a/microsite/static/img/ga-icon.png b/microsite/static/img/ga-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8eaebd06aa7cdd2625d785c273da8282cb3017a7 GIT binary patch literal 34270 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 G*#7~FX=la& literal 0 HcmV?d00001 From 79d0c0f4c45a209dcef176efac932e4c93fd7cdd Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Wed, 6 Oct 2021 17:39:42 +0200 Subject: [PATCH 14/54] Remove existing GA 'integration' docs. Signed-off-by: Eric Peterson --- .../google-analytics/installation.md | 23 ------------------- microsite/sidebars.json | 5 ---- mkdocs.yml | 2 -- 3 files changed, 30 deletions(-) delete mode 100644 docs/integrations/google-analytics/installation.md diff --git a/docs/integrations/google-analytics/installation.md b/docs/integrations/google-analytics/installation.md deleted file mode 100644 index d6c23cc4b0..0000000000 --- a/docs/integrations/google-analytics/installation.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -id: installation -title: Google Analytics Installation -sidebar_label: Installation -# prettier-ignore -description: Adding Google Analytics to Your App ---- - -There is a basic -[Google Analytics](https://marketingplatform.google.com/about/analytics/) -integration built into Backstage. You can enable it by adding the following to -your app configuration: - -```yaml -app: - googleAnalyticsTrackingId: UA-000000-0 -``` - -Replace the tracking ID with the one generated for you after signing up for the -Google Analytics service. - -The default behavior is only to send a pageview hit. To record more, review the -[Google Analytics developer documentation](https://developers.google.com/analytics/devguides/collection/gtagjs). diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 9cf3b204c2..1f578e0cbe 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -146,11 +146,6 @@ "label": "GitLab", "ids": ["integrations/gitlab/locations"] }, - { - "type": "subcategory", - "label": "Google Analytics", - "ids": ["integrations/google-analytics/installation"] - }, { "type": "subcategory", "label": "Google GCS", diff --git a/mkdocs.yml b/mkdocs.yml index 366411c7c4..364d44905f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -97,8 +97,6 @@ nav: - Org Data: 'integrations/github/org.md' - GitLab: - Locations: 'integrations/gitlab/locations.md' - - Google Analytics: - - Installation: 'integrations/google-analytics/installation.md' - Google GCS: - Locations: 'integrations/google-cloud-storage/locations.md' - LDAP: From b00e5643854e909dab97c61cc1fca8470b9d9523 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 7 Oct 2021 09:53:10 +0200 Subject: [PATCH 15/54] Document the Analytics API. Signed-off-by: Eric Peterson --- docs/plugins/analytics.md | 298 ++++++++++++++++++++++++++++++++++ docs/plugins/observability.md | 5 - microsite/sidebars.json | 1 + mkdocs.yml | 1 + 4 files changed, 300 insertions(+), 5 deletions(-) create mode 100644 docs/plugins/analytics.md diff --git a/docs/plugins/analytics.md b/docs/plugins/analytics.md new file mode 100644 index 0000000000..0b3d5dfd9c --- /dev/null +++ b/docs/plugins/analytics.md @@ -0,0 +1,298 @@ +--- +id: analytics +title: Plugin Analytics +description: Measuring usage of your Backstage instance. +--- + +Setting up, maintaining, and iterating on an instance of Backstage can be a +large investment. To help measure return on this investment, Backstage comes +with an event-based Analytics API that grants app integrators the flexibility to +collect and analyze Backstage usage in the analytics tool of their choice, while +providing plugin developers a standard interface for instrumenting key user +interactions. + +## Concepts + +- **Events** consist of, at a minimum, an `action` (like `click`) and a + `subject` (like `thing that was clicked on`). +- **Attributes** represent additional dimensional data (in the form of key/value + pairs) that may be provided on an event-by-event basis. To continue the above + example, the URL a user clicked to might look like `{ "to": "/a/page" }`. +- **Context** represents the broader context in which an event took place. By + default, information like `pluginId`, `extension`, and `routeRef` are + provided. + +This composition of events aims to allow analysis at different levels of detail, +enabling very granular questions (like "what is the most clicked on thing on a +particular route") as well as very high-level questions (like "what is the most +used plugin in my Backstage instance") to be answered. + +## Supported Analytics Tools + +While all that's needed to consume and forward these events to an analytics tool +is a concrete implementation of [AnalyticsApi][analytics-api-type], common +integrations are packaged and provided as plugins. Find your analytics tool of +choice below. + +| Analytics Tool | Support Status | +| ---------------------- | -------------- | +| [Google Analytics][ga] | Yes ✅ | + +To suggest an integration, please [open an issue][add-tool] for the analytics +tool your organization uses. Or jump to [Writing Integrations][int-howto] to +learn how to contribute the integration yourself! + +[ga]: + https://github.com/backstage/backstage/blob/master/plugins/analytics-module-ga/README.md +[add-tool]: + https://github.com/backstage/backstage/issues/new?assignees=&labels=plugin&template=plugin_template.md&title=%5BAnalytics+Module%5D+THE+ANALYTICS+TOOL+TO+INTEGRATE +[int-howto]: #writing-integrations +[analytics-api-type]: + https://backstage.io/docs/reference/core-plugin-api.analyticsapi + +## Key Events + +The following table summarizes events that, depending on the plugins you have +installed, may be captured. + +| Action | Provided By | Subject | +| ---------- | -------------- | ----------------------------------------- | +| `navigate` | Backstage Core | The URL of the page that was navigated to | +| `click` | Backstage Core | The text of the link that was clicked on | + +If there is an event you'd like to see captured, please [open an +issue][add-event] describing the event you want to see and the questions it +would help you answer. Or jump to [Capturing Events][event-howto] to learn how +to contribute the instrumentation yourself! + +_OSS plugin maintainers: feel free to document your events in the table above._ + +[add-event]: +https://github.com/backstage/backstage/issues/new?assignees=&labels=enhancement&template=feature_template.md&title=[Analytics%20Event]:%20THE+EVENT+TO+CAPTURE +[event-howto]: #capturing-events + +## Writing Integrations + +Analytics event forwarding is implemented as a Backstage utility API. Just as +you might provide a custom API implementation for errors or SCM Authentication, +you can provide one for analytics. + +The provided API need only provide a single method `captureEvent`, which takes +an `AnalyticsEvent` object. + +```ts +import { + analyticsApiRef, + AnalyticsEvent, + AnyApiFactory, + createApiFactory, +} from '@backstage/core-plugin-api'; + +export const apis: AnyApiFactory[] = [ + createApiFactory(analyticsApiRef, { + captureEvent: (event: AnalyticsEvent) => { + window._AcmeAnalyticsQ.push(event); + }, + }), +]; +``` + +In reality, you would likely want to encapsulate instantiation logic and pull +some details from configuration. A more complete example might look like: + +```ts +import { + AnalyticsApi, + analyticsApiRef, + AnalyticsEvent, + AnyApiFactory, + configApiRef, + createApiFactory, +} from '@backstage/core-plugin-api'; +import { AcmeAnalytics } from 'acme-analytics'; + +class AcmeAnalytics implements AnalyticsApi { + private constructor(accountId: number) { + AcmeAnalytics.init(accountId); + } + + static fromConfig(config) { + const accountId = config.getString('app.analytics.acme.id'); + return new AcmeAnalytics(accountId); + } + + captureEvent(event: AnalyticsEvent) { + const { action, ...rest } = event; + AcmeAnalytics.send(action, rest); + } +} + +export const apis: AnyApiFactory[] = [ + createApiFactory({ + api: analyticsApiRef, + deps: { configApi: configApiRef }, + factory: ({ configApi }) => AcmeAnalytics.fromConfig(configApi), + }), +]; +``` + +If you are integrating with an analytics service (as opposed to an internal +tool), consider contributing your API implementation as a plugin! + +By convention, such packages should be named +`@backstage/analytics-module-[name]`, and any configuration should be keyed +under `app.analytics.[name]`. + +## Capturing Events + +To instrument an event in a component, start by retrieving an analytics tracker +using the `useAnalytics()` hook provided by `@backstage/core-plugin-api`. The +tracker includes a `captureEvent` method which takes an `action` and a `subject` +as arguments. + +```ts +import { useAnalytics } from '@backstage/core-plugin-api'; + +const analytics = useAnalytics(); +analytics.captureEvent('deploy', serviceName); +``` + +### Providing Extra Attributes + +Additional dimensional `attributes` as well as a numeric `value` can be provided +on a third `options` argument if/when relevant for the event: + +```ts +analytics.captureEvent('merge', pullRequestName, { + value: pullRequestAgeInMinutes, + attributes: { + org, + repo, + }, +}); +``` + +In the above example, an event resembling the following object would be +captured: + +```json +{ + "action": "merge", + "subject": "Name of Pull Request", + "value": 60, + "attributes": { + "org": "some-org", + "repo": "some-repo" + } +} +``` + +### Providing Context for Events + +The `attributes` option is good for capturing details available to you within +the component that you're instrumenting. For capturing metadata only available +further up the react tree, or to help app integrators aggregate distinct events +by some common value, use an ``. + +```tsx +import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api'; + +const MyComponent = ({ value }) => { + const analytics = useAnalytics(); + const handleClick = () => analytics.captureEvent('check', value); + return ; +}; + +const MyWrapper = () => { + return ( + + + + ); +}; +``` + +In the above example, clicking on `` would result in an analytics +event resembling: + +```json +{ + "action": "check", + "subject": "Some Value", + "context": { + "segment": "xyz" + } +} +``` + +Note that, for brevity in the example above, the context keys provided by +Backstage core (`pluginId`, `extension`, and `routeRef`) have been omitted. In +reality, those details would be included alongside any additional context +provided by you. + +Analytics contexts can be nested; their values are merged down the react tree, +allowing keys to be overwritten. + +### Event Naming Considerations + +An event is split into its constituent parts to enable analysis at various +levels of granularity. In order to maintain this flexibility at analysis-time, +it's important to keep each of these levels of detail disaggregated. + +- Avoid providing an overly specific `action`. For example, instead of + `filterEntityTable`, consider just using `filter` as the action, and allowing + `EntityTable` to be specified as part of the event's `context` (most likely + automatically as part of the `extension` in which the `filter` event was + captured). + +- On the flip side, when adding `attributes` to an event, look at existing + events and see if the data you are capturing matches the intention, type, or + even the content of _their_ `attributes`. For instance, it may be common for + events that involve the Catalog to add details like entity `name`, `kind`, + and/or `namespace` as `attributes`. Using the same keys in your event will + ensure that events instrumented across plugins can easily be aggregated. + +### Unit Testing Event Capture + +The `@backstage/test-utils` package includes a `MockAnalyticsApi` implementation +that you can use in your unit tests to spy on and make assertions about any +analytics events captured. + +Use it like this: + +```tsx +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { analyticsApiRef } from '@backstage/core-plugin-api'; +import { MockAnalyticsApi, wrapInTestApp } from '@backstage/test-utils'; +import { render, fireEvent, waitFor } from '@testing-library/react'; + +describe('SomeComponent', () => { + it('should capture event on click', () => { + // Use the Mock Analytics API to spy on event captures. + const apiSpy = new MockAnalyticsApi(); + + // Render the component being tested + const { getByText } = render( + wrapInTestApp( + + + , + ), + ); + + // Fire the event that triggers event capture. + fireEvent.click(getByText('some component text')); + + // Assert that the event was captured with the expected data. + await waitFor(() => { + expect(apiSpy.getEvents()[0]).toMatchObject({ + action: 'expected action', + subject: 'expected subject'', + attributes: { + foo: 'bar', + }, + }); + }); + }); +}); +``` diff --git a/docs/plugins/observability.md b/docs/plugins/observability.md index b5a6a0ba60..d8778f07f8 100644 --- a/docs/plugins/observability.md +++ b/docs/plugins/observability.md @@ -8,11 +8,6 @@ description: Adding Observability to Your Plugin This article briefly describes the observability options that are available to a Backstage integrator. -## Google Analytics - -See how to install Google Analytics in your app -[here](../integrations/google-analytics/installation.md) - ## Datadog RUM Events See how to install Datadog Events in your app diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 1f578e0cbe..c82586e14a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -165,6 +165,7 @@ "plugins/structure-of-a-plugin", "plugins/integrating-plugin-into-software-catalog", "plugins/composability", + "plugins/analytics", { "type": "subcategory", "label": "Backends and APIs", diff --git a/mkdocs.yml b/mkdocs.yml index 364d44905f..8747d5ad8b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -110,6 +110,7 @@ nav: - Plugin Development: 'plugins/plugin-development.md' - Integrate into the Software Catalog: 'plugins/integrating-plugin-into-software-catalog.md' - Composability System: 'plugins/composability.md' + - Plugin Analytics: 'plugins/analytics.md' - Backends and APIs: - Proxying: 'plugins/proxying.md' - Backend plugin: 'plugins/backend-plugin.md' From 3cc66fd08721c67ac7f49c7186efc592a6e94656 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Thu, 7 Oct 2021 20:47:08 +0200 Subject: [PATCH 16/54] Prettier Signed-off-by: Eric Peterson --- microsite/data/plugins/backstage-analytics-module-ga.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/backstage-analytics-module-ga.yaml b/microsite/data/plugins/backstage-analytics-module-ga.yaml index 362f03bdad..0a03ec20e8 100644 --- a/microsite/data/plugins/backstage-analytics-module-ga.yaml +++ b/microsite/data/plugins/backstage-analytics-module-ga.yaml @@ -1,5 +1,5 @@ --- -title: "Analytics Module: Google Analytics" +title: 'Analytics Module: Google Analytics' author: Spotify authorUrl: https://github.com/spotify category: Analytics From 4130394b7943eea1291d555c31b3e38e751b7821 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 8 Oct 2021 15:09:30 +0200 Subject: [PATCH 17/54] Update link wording Signed-off-by: Johan Haals --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ad17ff17f9..99d63cf0b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -129,7 +129,7 @@ In general, changesets are not needed for the documentation, build utilities, co 1. Run `yarn changeset` 2. Select which packages you want to include a changeset for 3. Select impact of change that you're introducing, using `minor` for breaking changes and `patch` otherwise. We do not use `major` changes while packages are at version `0.x`. -4. Explain your changes in the generated changeset. Examples of a well written changeset can be found [here](https://backstage.io/docs/getting-started/contributors#writing-changesets). +4. Explain your changes in the generated changeset. See [examples of well written changesets](https://backstage.io/docs/getting-started/contributors#writing-changesets). 5. Add generated changeset to Git 6. Push the commit with your changeset to the branch associated with your PR 7. Accept our gratitude for making the release process easier on the maintainers From af092b088409c45171b1408c147d1aed9345f27d Mon Sep 17 00:00:00 2001 From: PhakornKiong Date: Sat, 9 Oct 2021 22:08:58 +0800 Subject: [PATCH 18/54] fix sidebar from being pushed out of alignment Signed-off-by: PhakornKiong --- microsite/static/css/custom.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/microsite/static/css/custom.css b/microsite/static/css/custom.css index ac432372b1..139fa7b1d2 100644 --- a/microsite/static/css/custom.css +++ b/microsite/static/css/custom.css @@ -1217,3 +1217,7 @@ code { .medium-zoom-image { z-index: 10000; } + +h3.collapsible span.arrow { + margin-right: 4px; +} From b7c05854715be73891bd7156f27ea0be5321741f Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 9 Oct 2021 13:14:26 -0500 Subject: [PATCH 19/54] Added pull requests to backend Signed-off-by: Andre Wanlin --- .changeset/stale-seahorses-sell.md | 5 ++ plugins/azure-devops-backend/api-report.md | 24 ++++++ .../src/api/AzureDevOpsApi.test.ts | 74 ++++++++++++++++--- .../src/api/AzureDevOpsApi.ts | 62 +++++++++++++++- plugins/azure-devops-backend/src/api/index.ts | 2 +- plugins/azure-devops-backend/src/api/types.ts | 14 ++++ plugins/azure-devops-backend/src/index.ts | 2 +- .../src/service/router.test.ts | 72 +++++++++++++++++- .../src/service/router.ts | 16 ++++ 9 files changed, 252 insertions(+), 19 deletions(-) create mode 100644 .changeset/stale-seahorses-sell.md diff --git a/.changeset/stale-seahorses-sell.md b/.changeset/stale-seahorses-sell.md new file mode 100644 index 0000000000..2fa52b109f --- /dev/null +++ b/.changeset/stale-seahorses-sell.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops-backend': patch +--- + +Expands the Azure DevOps backend plugin to provide pull request data to be used by the front end plugin diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 4ad2f833d7..511127f1e8 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -10,6 +10,7 @@ import { Config } from '@backstage/config'; import express from 'express'; import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { Logger as Logger_2 } from 'winston'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { WebApi } from 'azure-devops-node-api'; // Warning: (ae-missing-release-tag) "AzureDevOpsApi" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -29,6 +30,13 @@ export class AzureDevOpsApi { repoName: string, ): Promise; // (undocumented) + getPullRequests( + projectName: string, + repoName: string, + top: number, + status: PullRequestStatus, + ): Promise; + // (undocumented) getRepoBuilds( projectName: string, repoName: string, @@ -41,6 +49,22 @@ export class AzureDevOpsApi { // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// Warning: (ae-missing-release-tag) "PullRequest" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type PullRequest = { + pullRequestId?: number; + repoName?: string; + title?: string; + createdBy?: string; + creationDate?: Date; + sourceRefName?: string; + targetRefName?: string; + status?: PullRequestStatus; + isDraft?: boolean; + link: string; +}; + // Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index 25022b4c02..3a3662ec56 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -13,17 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { repoBuildFromBuild } from './AzureDevOpsApi'; -import { RepoBuild } from './types'; +import { mappedPullRequest, mappedRepoBuild } from './AzureDevOpsApi'; +import { PullRequest, RepoBuild } from './types'; import { Build, BuildResult, BuildStatus, DefinitionReference, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { + GitPullRequest, + PullRequestStatus, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { GitRepository } from 'azure-devops-node-api/interfaces/TfvcInterfaces'; +import { IdentityRef } from 'azure-devops-node-api/interfaces/common/VSSInterfaces'; describe('AzureDevOpsApi', () => { - describe('repoBuildFromBuild', () => { + describe('mappedRepoBuild', () => { it('should return RepoBuild from Build', () => { const inputBuildDefinition: DefinitionReference = { name: 'My Build Definition', @@ -57,11 +63,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with no Build definition name', () => { + describe('mappedRepoBuild with no Build definition name', () => { it('should return RepoBuild with only Build Number for title', () => { const inputLinks: any = { web: { @@ -91,11 +97,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with undefined status', () => { + describe('mappedRepoBuild with undefined status', () => { it('should return BuildStatus of None for status', () => { const inputLinks: any = { web: { @@ -125,11 +131,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with undefined result', () => { + describe('mappedRepoBuild with undefined result', () => { it('should return BuildResult of None for result', () => { const inputLinks: any = { web: { @@ -159,11 +165,11 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); }); }); - describe('repoBuildFromBuild with undefined link', () => { + describe('mappedRepoBuild with undefined link', () => { it('should return empty string for link', () => { const inputBuild: Build = { id: 1, @@ -187,7 +193,51 @@ describe('AzureDevOpsApi', () => { source: 'refs/heads/develop (f4f78b31)', }; - expect(repoBuildFromBuild(inputBuild)).toEqual(outputRepoBuild); + expect(mappedRepoBuild(inputBuild)).toEqual(outputRepoBuild); + }); + }); + + describe('mappedPullRequest', () => { + it('should return PullRequest from GitPullRequest', () => { + const inputGitRepository: GitRepository = { + name: 'super-feature-repo', + }; + + const inputIdentityRef: IdentityRef = { + displayName: 'Jane Doe', + }; + + const inputPullRequest: GitPullRequest = { + pullRequestId: 7181, + repository: inputGitRepository, + title: 'My Awesome New Feature', + createdBy: inputIdentityRef, + creationDate: new Date('2020-09-12T06:10:23.9325232Z'), + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + }; + + const inputBaseUrl = + 'https://host.com/myOrg/_git/super-feature-repo/pullrequest'; + + const outputPullRequest: PullRequest = { + pullRequestId: 7181, + repoName: 'super-feature-repo', + title: 'My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: new Date('2020-09-12T06:10:23.9325232Z'), + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181', + }; + + expect(mappedPullRequest(inputPullRequest, inputBaseUrl)).toEqual( + outputPullRequest, + ); }); }); }); diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 3da4dff11d..3d4320da6a 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -16,12 +16,17 @@ import { Logger } from 'winston'; import { WebApi } from 'azure-devops-node-api'; -import { RepoBuild } from './types'; import { Build, BuildResult, BuildStatus, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { + GitPullRequest, + GitPullRequestSearchCriteria, + PullRequestStatus, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequest, RepoBuild } from './types'; export class AzureDevOpsApi { constructor( @@ -88,14 +93,47 @@ export class AzureDevOpsApi { ); const repoBuilds: RepoBuild[] = buildList.map(build => { - return repoBuildFromBuild(build); + return mappedRepoBuild(build); }); return repoBuilds; } + + async getPullRequests( + projectName: string, + repoName: string, + top: number, + status: PullRequestStatus, + ) { + if (this.logger) { + this.logger.debug( + `Calling Azure DevOps REST API, getting up to ${top} Pull Requests for Repository ${repoName} for Project ${projectName}`, + ); + } + + const gitRepository = await this.getGitRepository(projectName, repoName); + const client = await this.webApi.getGitApi(); + const searchCriteria: GitPullRequestSearchCriteria = { + status: status, + }; + const gitPullRequests = await client.getPullRequests( + gitRepository.id as string, + searchCriteria, + projectName, + undefined, + undefined, + top, + ); + const linkBaseUrl = `${this.webApi.serverUrl}/${projectName}/_git/${repoName}/pullrequest`; + const pullRequests: PullRequest[] = gitPullRequests.map(gitPullRequest => { + return mappedPullRequest(gitPullRequest, linkBaseUrl); + }); + + return pullRequests; + } } -export function repoBuildFromBuild(build: Build) { +export function mappedRepoBuild(build: Build) { return { id: build.id, title: [build.definition?.name, build.buildNumber] @@ -108,3 +146,21 @@ export function repoBuildFromBuild(build: Build) { source: `${build.sourceBranch} (${build.sourceVersion?.substr(0, 8)})`, }; } + +export function mappedPullRequest( + pullRequest: GitPullRequest, + linkBaseUrl: string, +) { + return { + pullRequestId: pullRequest.pullRequestId, + repoName: pullRequest.repository?.name, + title: pullRequest.title, + createdBy: pullRequest.createdBy?.displayName, + creationDate: pullRequest.creationDate, + sourceRefName: pullRequest.sourceRefName, + targetRefName: pullRequest.targetRefName, + status: pullRequest.status, + isDraft: pullRequest.isDraft, + link: `${linkBaseUrl}/${pullRequest.pullRequestId}`, + }; +} diff --git a/plugins/azure-devops-backend/src/api/index.ts b/plugins/azure-devops-backend/src/api/index.ts index 8faf37fe4e..893f0f4f2c 100644 --- a/plugins/azure-devops-backend/src/api/index.ts +++ b/plugins/azure-devops-backend/src/api/index.ts @@ -15,4 +15,4 @@ */ export { AzureDevOpsApi } from './AzureDevOpsApi'; -export type { RepoBuild } from './types'; +export type { RepoBuild, PullRequest } from './types'; diff --git a/plugins/azure-devops-backend/src/api/types.ts b/plugins/azure-devops-backend/src/api/types.ts index b3562f76c6..aaa9d8dcbd 100644 --- a/plugins/azure-devops-backend/src/api/types.ts +++ b/plugins/azure-devops-backend/src/api/types.ts @@ -18,6 +18,7 @@ import { BuildResult, BuildStatus, } from 'azure-devops-node-api/interfaces/BuildInterfaces'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; export type RepoBuild = { id?: number; @@ -28,3 +29,16 @@ export type RepoBuild = { queueTime?: Date; source: string; }; + +export type PullRequest = { + pullRequestId?: number; + repoName?: string; + title?: string; + createdBy?: string; + creationDate?: Date; + sourceRefName?: string; + targetRefName?: string; + status?: PullRequestStatus; + isDraft?: boolean; + link: string; +}; diff --git a/plugins/azure-devops-backend/src/index.ts b/plugins/azure-devops-backend/src/index.ts index a7004ec73e..ff4d3973bf 100644 --- a/plugins/azure-devops-backend/src/index.ts +++ b/plugins/azure-devops-backend/src/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ export { AzureDevOpsApi } from './api'; -export type { RepoBuild } from './api'; +export type { RepoBuild, PullRequest } from './api'; export * from './service/router'; diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts index a66b86f418..e6444bb9d3 100644 --- a/plugins/azure-devops-backend/src/service/router.test.ts +++ b/plugins/azure-devops-backend/src/service/router.test.ts @@ -20,8 +20,11 @@ import express from 'express'; import request from 'supertest'; import { AzureDevOpsApi } from '../api'; import { createRouter } from './router'; -import { RepoBuild } from '../api/types'; -import { GitRepository } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequest, RepoBuild } from '../api/types'; +import { + GitRepository, + PullRequestStatus, +} from 'azure-devops-node-api/interfaces/GitInterfaces'; import { Build, BuildResult, @@ -37,6 +40,7 @@ describe('createRouter', () => { getGitRepository: jest.fn(), getBuildList: jest.fn(), getRepoBuilds: jest.fn(), + getPullRequests: jest.fn(), } as any; const router = await createRouter({ azureDevOpsApi, @@ -193,4 +197,68 @@ describe('createRouter', () => { expect(response.body).toEqual(repoBuilds); }); }); + + describe('GET /pull-requests/:projectName/:repoName', () => { + it('fetches a list of pull requests', async () => { + const firstPullRequest: PullRequest = { + pullRequestId: 7181, + repoName: 'super-feature-repo', + title: 'My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: undefined, + sourceRefName: 'refs/heads/topic/super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7181', + }; + + const secondPullRequest: PullRequest = { + pullRequestId: 7182, + repoName: 'super-feature-repo', + title: 'Refactoring My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: undefined, + sourceRefName: 'refs/heads/topic/refactor-super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7182', + }; + + const thirdPullRequest: PullRequest = { + pullRequestId: 7183, + repoName: 'super-feature-repo', + title: 'Bug Fix for My Awesome New Feature', + createdBy: 'Jane Doe', + creationDate: undefined, + sourceRefName: 'refs/heads/topic/fix-super-awesome-feature', + targetRefName: 'refs/heads/main', + status: PullRequestStatus.Active, + isDraft: false, + link: 'https://host.com/myOrg/_git/super-feature-repo/pullrequest/7183', + }; + + const pullRequests: PullRequest[] = [ + firstPullRequest, + secondPullRequest, + thirdPullRequest, + ]; + + azureDevOpsApi.getPullRequests.mockResolvedValueOnce(pullRequests); + + const response = await request(app) + .get('/pull-requests/myProject/myRepo') + .query({ top: '50', status: 1 }); + + expect(azureDevOpsApi.getPullRequests).toHaveBeenCalledWith( + 'myProject', + 'myRepo', + 50, + 1, + ); + expect(response.status).toEqual(200); + expect(response.body).toEqual(pullRequests); + }); + }); }); diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts index 1fc86128e6..62ac06afe4 100644 --- a/plugins/azure-devops-backend/src/service/router.ts +++ b/plugins/azure-devops-backend/src/service/router.ts @@ -21,6 +21,7 @@ import { Logger } from 'winston'; import { Config } from '@backstage/config'; import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; import { AzureDevOpsApi } from '../api'; +import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; const DEFAULT_TOP: number = 10; @@ -84,6 +85,21 @@ export async function createRouter( res.status(200).json(gitRepository); }); + router.get('/pull-requests/:projectName/:repoName', async (req, res) => { + const { projectName, repoName } = req.params; + const top = req.query.top ? Number(req.query.top) : DEFAULT_TOP; + const status = req.query.status + ? Number(req.query.status) + : PullRequestStatus.Active; + const gitPullRequest = await azureDevOpsApi.getPullRequests( + projectName, + repoName, + top, + status, + ); + res.status(200).json(gitPullRequest); + }); + router.use(errorHandler()); return router; } From c1acc473573a4459ccafb283415753bb17009ecc Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 9 Oct 2021 15:36:51 -0500 Subject: [PATCH 20/54] Added Unique Name Signed-off-by: Andre Wanlin --- plugins/azure-devops-backend/api-report.md | 1 + plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts | 2 ++ plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts | 1 + plugins/azure-devops-backend/src/api/types.ts | 1 + 4 files changed, 5 insertions(+) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 511127f1e8..34477a71ca 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -56,6 +56,7 @@ export type PullRequest = { pullRequestId?: number; repoName?: string; title?: string; + uniqueName?: string; createdBy?: string; creationDate?: Date; sourceRefName?: string; diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts index 3a3662ec56..fd9aa169d5 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.test.ts @@ -205,6 +205,7 @@ describe('AzureDevOpsApi', () => { const inputIdentityRef: IdentityRef = { displayName: 'Jane Doe', + uniqueName: 'DOMAINjdoe', }; const inputPullRequest: GitPullRequest = { @@ -226,6 +227,7 @@ describe('AzureDevOpsApi', () => { pullRequestId: 7181, repoName: 'super-feature-repo', title: 'My Awesome New Feature', + uniqueName: 'DOMAINjdoe', createdBy: 'Jane Doe', creationDate: new Date('2020-09-12T06:10:23.9325232Z'), sourceRefName: 'refs/heads/topic/super-awesome-feature', diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 3d4320da6a..991a68fcee 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -155,6 +155,7 @@ export function mappedPullRequest( pullRequestId: pullRequest.pullRequestId, repoName: pullRequest.repository?.name, title: pullRequest.title, + uniqueName: pullRequest.createdBy?.uniqueName, createdBy: pullRequest.createdBy?.displayName, creationDate: pullRequest.creationDate, sourceRefName: pullRequest.sourceRefName, diff --git a/plugins/azure-devops-backend/src/api/types.ts b/plugins/azure-devops-backend/src/api/types.ts index aaa9d8dcbd..4b2a0f7988 100644 --- a/plugins/azure-devops-backend/src/api/types.ts +++ b/plugins/azure-devops-backend/src/api/types.ts @@ -34,6 +34,7 @@ export type PullRequest = { pullRequestId?: number; repoName?: string; title?: string; + uniqueName?: string; createdBy?: string; creationDate?: Date; sourceRefName?: string; From 16f044cb6be037650c62e66979f64a856b1a76c3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 9 Oct 2021 13:42:27 +0200 Subject: [PATCH 21/54] cli: update backend eslint config to allow __dirname in tests Signed-off-by: Patrik Oldsberg --- .changeset/smart-seahorses-ring.md | 5 +++++ packages/cli/config/eslint.backend.js | 17 +++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 .changeset/smart-seahorses-ring.md diff --git a/.changeset/smart-seahorses-ring.md b/.changeset/smart-seahorses-ring.md new file mode 100644 index 0000000000..a5ae156afd --- /dev/null +++ b/.changeset/smart-seahorses-ring.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Update default backend ESLint configuration to allow usage of `__dirname` in tests. diff --git a/packages/cli/config/eslint.backend.js b/packages/cli/config/eslint.backend.js index 99767f5d79..a97f26f8da 100644 --- a/packages/cli/config/eslint.backend.js +++ b/packages/cli/config/eslint.backend.js @@ -14,6 +14,15 @@ * limitations under the License. */ +const globalRestrictedSyntax = [ + { + message: + 'Default import from winston is not allowed, import `* as winston` instead.', + selector: + 'ImportDeclaration[source.value="winston"] ImportDefaultSpecifier', + }, +]; + module.exports = { extends: [ '@spotify/eslint-config-base', @@ -69,17 +78,12 @@ module.exports = { // Avoid default import from winston as it breaks at runtime 'no-restricted-syntax': [ 'error', - { - message: - 'Default import from winston is not allowed, import `* as winston` instead.', - selector: - 'ImportDeclaration[source.value="winston"] ImportDefaultSpecifier', - }, { message: "`__dirname` doesn't refer to the same dir in production builds, try `resolvePackagePath()` from `@backstage/backend-common` instead.", selector: 'Identifier[name="__dirname"]', }, + ...globalRestrictedSyntax, ], }, overrides: [ @@ -103,6 +107,7 @@ module.exports = { bundledDependencies: true, }, ], + 'no-restricted-syntax': ['error', ...globalRestrictedSyntax], }, }, ], From f25b8459cdd303d7610061117b99b04aa3bae007 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 9 Oct 2021 13:52:20 +0200 Subject: [PATCH 22/54] update tests to be runnable from the project root Signed-off-by: Patrik Oldsberg --- .../src/reading/AwsS3UrlReader.test.ts | 24 +++-------------- .../src/reading/AzureUrlReader.test.ts | 4 +-- .../src/reading/BitbucketUrlReader.test.ts | 26 +++++-------------- .../src/reading/GithubUrlReader.test.ts | 14 ++-------- .../src/reading/GitlabUrlReader.test.ts | 4 +-- packages/cli-common/src/paths.test.ts | 2 ++ packages/integration/src/azure/config.test.ts | 10 ++++--- .../integration/src/bitbucket/config.test.ts | 10 ++++--- .../integration/src/github/config.test.ts | 10 ++++--- .../integration/src/gitlab/config.test.ts | 10 ++++--- .../AwsS3DiscoveryProcessor.test.ts | 9 ++----- .../processors/FileReaderProcessor.test.ts | 8 +----- .../next/ConfigLocationEntityProvider.test.ts | 6 +---- .../actions/builtin/fetch/template.test.ts | 14 ++++++---- 14 files changed, 54 insertions(+), 97 deletions(-) diff --git a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts index 80f59fe182..0bc0e4f97f 100644 --- a/packages/backend-common/src/reading/AwsS3UrlReader.test.ts +++ b/packages/backend-common/src/reading/AwsS3UrlReader.test.ts @@ -135,13 +135,7 @@ describe('AwsS3UrlReader', () => { 'getObject', Buffer.from( require('fs').readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'awsS3', - 'awsS3-mock-object.yaml', - ), + path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), ), ), ); @@ -187,13 +181,7 @@ describe('AwsS3UrlReader', () => { 'getObject', Buffer.from( require('fs').readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'awsS3', - 'awsS3-mock-object.yaml', - ), + path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), ), ), ); @@ -249,13 +237,7 @@ describe('AwsS3UrlReader', () => { 'getObject', Buffer.from( require('fs').readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'awsS3', - 'awsS3-mock-object.yaml', - ), + path.resolve(__dirname, '__fixtures__/awsS3/awsS3-mock-object.yaml'), ), ), ); diff --git a/packages/backend-common/src/reading/AzureUrlReader.test.ts b/packages/backend-common/src/reading/AzureUrlReader.test.ts index e014fa2e75..59968a18e9 100644 --- a/packages/backend-common/src/reading/AzureUrlReader.test.ts +++ b/packages/backend-common/src/reading/AzureUrlReader.test.ts @@ -152,7 +152,7 @@ describe('AzureUrlReader', () => { describe('readTree', () => { const repoBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'mock-main.zip'), + path.resolve(__dirname, '__fixtures__/mock-main.zip'), ); const processor = new AzureUrlReader( @@ -264,7 +264,7 @@ describe('AzureUrlReader', () => { describe('search', () => { const repoBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'mock-main.zip'), + path.resolve(__dirname, '__fixtures__/mock-main.zip'), ); const processor = new AzureUrlReader( diff --git a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts index ef61452163..5bbf3725ea 100644 --- a/packages/backend-common/src/reading/BitbucketUrlReader.test.ts +++ b/packages/backend-common/src/reading/BitbucketUrlReader.test.ts @@ -104,20 +104,13 @@ describe('BitbucketUrlReader', () => { describe('readTree', () => { const repoBuffer = fs.readFileSync( path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-repo-with-commit-hash.tar.gz', + __dirname, + '__fixtures__/bitbucket-repo-with-commit-hash.tar.gz', ), ); const privateBitbucketRepoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-server-repo.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/bitbucket-server-repo.tar.gz'), ); beforeEach(() => { @@ -298,10 +291,8 @@ describe('BitbucketUrlReader', () => { describe('search hosted', () => { const repoBuffer = fs.readFileSync( path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-repo-with-commit-hash.tar.gz', + __dirname, + '__fixtures__/bitbucket-repo-with-commit-hash.tar.gz', ), ); @@ -386,12 +377,7 @@ describe('BitbucketUrlReader', () => { describe('search private', () => { const privateBitbucketRepoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'bitbucket-server-repo.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/bitbucket-server-repo.tar.gz'), ); beforeEach(() => { diff --git a/packages/backend-common/src/reading/GithubUrlReader.test.ts b/packages/backend-common/src/reading/GithubUrlReader.test.ts index f9717a862a..e70444cf2c 100644 --- a/packages/backend-common/src/reading/GithubUrlReader.test.ts +++ b/packages/backend-common/src/reading/GithubUrlReader.test.ts @@ -254,12 +254,7 @@ describe('GithubUrlReader', () => { describe('readTree', () => { const repoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'backstage-mock-etag123.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/backstage-mock-etag123.tar.gz'), ); const reposGithubApiResponse = { @@ -540,12 +535,7 @@ describe('GithubUrlReader', () => { describe('search', () => { const repoBuffer = fs.readFileSync( - path.resolve( - 'src', - 'reading', - '__fixtures__', - 'backstage-mock-etag123.tar.gz', - ), + path.resolve(__dirname, '__fixtures__/backstage-mock-etag123.tar.gz'), ); const githubTreeContents: GhTreeResponse['tree'] = [ diff --git a/packages/backend-common/src/reading/GitlabUrlReader.test.ts b/packages/backend-common/src/reading/GitlabUrlReader.test.ts index 51b211e698..331150bde3 100644 --- a/packages/backend-common/src/reading/GitlabUrlReader.test.ts +++ b/packages/backend-common/src/reading/GitlabUrlReader.test.ts @@ -223,7 +223,7 @@ describe('GitlabUrlReader', () => { describe('readTree', () => { const archiveBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'gitlab-archive.tar.gz'), + path.resolve(__dirname, '__fixtures__/gitlab-archive.tar.gz'), ); const projectGitlabApiResponse = { @@ -495,7 +495,7 @@ describe('GitlabUrlReader', () => { describe('search', () => { const archiveBuffer = fs.readFileSync( - path.resolve('src', 'reading', '__fixtures__', 'gitlab-archive.tar.gz'), + path.resolve(__dirname, '__fixtures__/gitlab-archive.tar.gz'), ); const projectGitlabApiResponse = { diff --git a/packages/cli-common/src/paths.test.ts b/packages/cli-common/src/paths.test.ts index 894060f2e8..5c1cf8af08 100644 --- a/packages/cli-common/src/paths.test.ts +++ b/packages/cli-common/src/paths.test.ts @@ -46,6 +46,8 @@ describe('paths', () => { const dir = resolvePath(__dirname, '..'); const root = resolvePath(__dirname, '../../..'); + jest.spyOn(process, 'cwd').mockReturnValue(dir); + const paths = findPaths(__dirname); expect(paths.ownDir).toBe(dir); diff --git a/packages/integration/src/azure/config.test.ts b/packages/integration/src/azure/config.test.ts index 385086fbcf..353065a030 100644 --- a/packages/integration/src/azure/config.test.ts +++ b/packages/integration/src/azure/config.test.ts @@ -31,15 +31,17 @@ describe('readAzureIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/integration/src/bitbucket/config.test.ts b/packages/integration/src/bitbucket/config.test.ts index b887566e35..5440b174c8 100644 --- a/packages/integration/src/bitbucket/config.test.ts +++ b/packages/integration/src/bitbucket/config.test.ts @@ -31,15 +31,17 @@ describe('readBitbucketIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/integration/src/github/config.test.ts b/packages/integration/src/github/config.test.ts index eb084b14a9..73efb4f81a 100644 --- a/packages/integration/src/github/config.test.ts +++ b/packages/integration/src/github/config.test.ts @@ -31,15 +31,17 @@ describe('readGitHubIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/packages/integration/src/gitlab/config.test.ts b/packages/integration/src/gitlab/config.test.ts index 1308704e9d..db03285206 100644 --- a/packages/integration/src/gitlab/config.test.ts +++ b/packages/integration/src/gitlab/config.test.ts @@ -31,15 +31,17 @@ describe('readGitLabIntegrationConfig', () => { data: Partial, ): Promise { const fullSchema = await loadConfigSchema({ - dependencies: [require('../../package.json').name], + dependencies: ['@backstage/integration'], }); const serializedSchema = fullSchema.serialize() as { - schemas: { path: string }[]; + schemas: { value: { properties?: { integrations?: object } } }[]; }; const schema = await loadConfigSchema({ serialized: { - ...serializedSchema, // grab the schema from this package only - schemas: serializedSchema.schemas.filter(s => s.path === 'config.d.ts'), + ...serializedSchema, // only include schemas that apply to integrations + schemas: serializedSchema.schemas.filter( + s => s.value?.properties?.integrations, + ), }, }); const processed = schema.process( diff --git a/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts index 435ad368c7..064a2ad724 100644 --- a/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/AwsS3DiscoveryProcessor.test.ts @@ -37,13 +37,8 @@ AWSMock.mock( Buffer.from( require('fs').readFileSync( path.resolve( - 'src', - 'ingestion', - 'processors', - '__fixtures__', - 'fileReaderProcessor', - 'awsS3', - 'awsS3-mock-object.txt', + __dirname, + '__fixtures__/fileReaderProcessor/awsS3/awsS3-mock-object.txt', ), ), ), diff --git a/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts b/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts index 41c7e4f732..8a5f202e5e 100644 --- a/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/FileReaderProcessor.test.ts @@ -23,13 +23,7 @@ import { import path from 'path'; describe('FileReaderProcessor', () => { - const fixturesRoot = path.join( - 'src', - 'ingestion', - 'processors', - '__fixtures__', - 'fileReaderProcessor', - ); + const fixturesRoot = path.join(__dirname, '__fixtures__/fileReaderProcessor'); it('should load from file', async () => { const processor = new FileReaderProcessor(); diff --git a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts b/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts index ef3e47eb8b..0663ad8d04 100644 --- a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts +++ b/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { resolvePackagePath } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import path from 'path'; import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; @@ -44,10 +43,7 @@ describe('ConfigLocationEntityProvider', () => { { entity: expect.objectContaining({ spec: { - target: path.join( - resolvePackagePath('@backstage/plugin-catalog-backend'), - './lols.yaml', - ), + target: path.join(process.cwd(), 'lols.yaml'), type: 'file', }, }), diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts index d676af56ca..4befec5a81 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -15,10 +15,14 @@ */ import os from 'os'; -import { join as joinPath, resolve as resolvePath } from 'path'; +import { join as joinPath } from 'path'; import fs from 'fs-extra'; import mockFs from 'mock-fs'; -import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { + getVoidLogger, + resolvePackagePath, + UrlReader, +} from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; import { PassThrough } from 'stream'; import { fetchContents } from './helpers'; @@ -30,9 +34,9 @@ jest.mock('./helpers', () => ({ })); const aBinaryFile = fs.readFileSync( - resolvePath( - 'src', - '../fixtures/test-nested-template/public/react-logo192.png', + resolvePackagePath( + '@backstage/plugin-scaffolder-backend', + 'fixtures/test-nested-template/public/react-logo192.png', ), ); From 668df8d33f0817fd99d543f3822014ce104b01e3 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 9 Oct 2021 13:57:41 +0200 Subject: [PATCH 23/54] cli: set project display name when running mmultiple projects Signed-off-by: Patrik Oldsberg --- packages/cli/config/jest.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/jest.js b/packages/cli/config/jest.js index 80b3e84cda..c687616a7d 100644 --- a/packages/cli/config/jest.js +++ b/packages/cli/config/jest.js @@ -18,7 +18,7 @@ const fs = require('fs-extra'); const path = require('path'); const glob = require('util').promisify(require('glob')); -async function getProjectConfig(targetPath) { +async function getProjectConfig(targetPath, displayName) { const configJsPath = path.resolve(targetPath, 'jest.config.js'); const configTsPath = path.resolve(targetPath, 'jest.config.ts'); // If the package has it's own jest config, we use that instead. @@ -73,6 +73,7 @@ async function getProjectConfig(targetPath) { const transformModulePattern = transformModules && `(?!${transformModules})`; const options = { + displayName, rootDir: path.resolve(targetPath, 'src'), coverageDirectory: path.resolve(targetPath, 'coverage'), collectCoverageFrom: ['**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'], @@ -143,7 +144,7 @@ async function getRootConfig() { const packageData = await fs.readJson(packagePath); const testScript = packageData.scripts && packageData.scripts.test; if (testScript && testScript.includes('backstage-cli test')) { - return await getProjectConfig(projectPath); + return await getProjectConfig(projectPath, packageData.name); } return undefined; From ef5bf4235aa54ff380b4536c06a2df71ab8a6f55 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 11 Oct 2021 10:13:25 +0200 Subject: [PATCH 24/54] Add `` component Signed-off-by: Oliver Sand --- .changeset/lemon-actors-wait.md | 15 +++++++++ packages/app/src/components/home/HomePage.tsx | 21 ++++++------ .../WelcomeTitle/WelcomeTitle.test.tsx | 32 +++++++++++++++++++ .../WelcomeTitle/WelcomeTitle.tsx | 32 +++++++++++++++++++ .../homePageComponents/WelcomeTitle/index.ts | 16 ++++++++++ .../locales/goodAfternoon.locales.json | 0 .../locales/goodEvening.locales.json | 0 .../locales/goodMorning.locales.json | 0 .../WelcomeTitle}/timeUtil.js | 0 .../WelcomeTitle}/timeUtil.test.js | 0 plugins/home/src/index.ts | 1 + plugins/home/src/plugin.ts | 9 ++++++ 12 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 .changeset/lemon-actors-wait.md create mode 100644 plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx create mode 100644 plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx create mode 100644 plugins/home/src/homePageComponents/WelcomeTitle/index.ts rename plugins/{welcome/src/utils => home/src/homePageComponents/WelcomeTitle}/locales/goodAfternoon.locales.json (100%) rename plugins/{welcome/src/utils => home/src/homePageComponents/WelcomeTitle}/locales/goodEvening.locales.json (100%) rename plugins/{welcome/src/utils => home/src/homePageComponents/WelcomeTitle}/locales/goodMorning.locales.json (100%) rename plugins/{welcome/src/utils => home/src/homePageComponents/WelcomeTitle}/timeUtil.js (100%) rename plugins/{welcome/src/utils => home/src/homePageComponents/WelcomeTitle}/timeUtil.test.js (100%) diff --git a/.changeset/lemon-actors-wait.md b/.changeset/lemon-actors-wait.md new file mode 100644 index 0000000000..e98adc891e --- /dev/null +++ b/.changeset/lemon-actors-wait.md @@ -0,0 +1,15 @@ +--- +'@backstage/plugin-home': patch +--- + +Adds a `` component that shows a playful greeting on the home page. +To use it, pass it to the home page header: + +```typescript + +

} pageTitleOverride="Home"> + +
+ … + +``` diff --git a/packages/app/src/components/home/HomePage.tsx b/packages/app/src/components/home/HomePage.tsx index ebb486790d..33ac1a7e8c 100644 --- a/packages/app/src/components/home/HomePage.tsx +++ b/packages/app/src/components/home/HomePage.tsx @@ -14,25 +14,26 @@ * limitations under the License. */ -import React from 'react'; -import Grid from '@material-ui/core/Grid'; -import { - HomePageRandomJoke, - ComponentAccordion, - ComponentTabs, - ComponentTab, -} from '@backstage/plugin-home'; import { Content, Header, - Page, HomepageTimer, + Page, } from '@backstage/core-components'; +import { + ComponentAccordion, + ComponentTab, + ComponentTabs, + HomePageRandomJoke, + WelcomeTitle, +} from '@backstage/plugin-home'; import { HomePageSearchBar } from '@backstage/plugin-search'; +import Grid from '@material-ui/core/Grid'; +import React from 'react'; export const HomePage = () => ( -
+
} pageTitleOverride="Home">
diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx new file mode 100644 index 0000000000..839ecdbae1 --- /dev/null +++ b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.test.tsx @@ -0,0 +1,32 @@ +/* + * 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 { renderInTestApp } from '@backstage/test-utils'; +import React from 'react'; +import { WelcomeTitle } from './WelcomeTitle'; + +describe('', () => { + afterEach(() => jest.resetAllMocks()); + + test('should greet user', async () => { + jest + .spyOn(global.Date, 'now') + .mockImplementation(() => new Date('1970-01-01T23:00:00').valueOf()); + + const { getByText } = await renderInTestApp(); + + expect(getByText(/Get some rest, Guest/)).toBeInTheDocument(); + }); +}); diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx new file mode 100644 index 0000000000..04ec7e1673 --- /dev/null +++ b/plugins/home/src/homePageComponents/WelcomeTitle/WelcomeTitle.tsx @@ -0,0 +1,32 @@ +/* + * 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 { identityApiRef, useApi } from '@backstage/core-plugin-api'; +import { Tooltip } from '@material-ui/core'; +import React, { useMemo } from 'react'; +import { getTimeBasedGreeting } from './timeUtil'; + +export const WelcomeTitle = () => { + const identityApi = useApi(identityApiRef); + const profile = identityApi.getProfile(); + const userId = identityApi.getUserId(); + const greeting = useMemo(() => getTimeBasedGreeting(), []); + + return ( + + {`${greeting.greeting}, ${profile.displayName || userId}!`} + + ); +}; diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/index.ts b/plugins/home/src/homePageComponents/WelcomeTitle/index.ts new file mode 100644 index 0000000000..ca237511e7 --- /dev/null +++ b/plugins/home/src/homePageComponents/WelcomeTitle/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { WelcomeTitle } from './WelcomeTitle'; diff --git a/plugins/welcome/src/utils/locales/goodAfternoon.locales.json b/plugins/home/src/homePageComponents/WelcomeTitle/locales/goodAfternoon.locales.json similarity index 100% rename from plugins/welcome/src/utils/locales/goodAfternoon.locales.json rename to plugins/home/src/homePageComponents/WelcomeTitle/locales/goodAfternoon.locales.json diff --git a/plugins/welcome/src/utils/locales/goodEvening.locales.json b/plugins/home/src/homePageComponents/WelcomeTitle/locales/goodEvening.locales.json similarity index 100% rename from plugins/welcome/src/utils/locales/goodEvening.locales.json rename to plugins/home/src/homePageComponents/WelcomeTitle/locales/goodEvening.locales.json diff --git a/plugins/welcome/src/utils/locales/goodMorning.locales.json b/plugins/home/src/homePageComponents/WelcomeTitle/locales/goodMorning.locales.json similarity index 100% rename from plugins/welcome/src/utils/locales/goodMorning.locales.json rename to plugins/home/src/homePageComponents/WelcomeTitle/locales/goodMorning.locales.json diff --git a/plugins/welcome/src/utils/timeUtil.js b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.js similarity index 100% rename from plugins/welcome/src/utils/timeUtil.js rename to plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.js diff --git a/plugins/welcome/src/utils/timeUtil.test.js b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.js similarity index 100% rename from plugins/welcome/src/utils/timeUtil.test.js rename to plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.js diff --git a/plugins/home/src/index.ts b/plugins/home/src/index.ts index 0dacda7a01..0004cf5a23 100644 --- a/plugins/home/src/index.ts +++ b/plugins/home/src/index.ts @@ -27,6 +27,7 @@ export { ComponentAccordion, ComponentTabs, ComponentTab, + WelcomeTitle, } from './plugin'; export { SettingsModal } from './components'; export { createCardExtension } from './extensions'; diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 46df84c1bf..503418a42c 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -60,6 +60,15 @@ export const ComponentTab = homePlugin.provide( }), ); +export const WelcomeTitle = homePlugin.provide( + createComponentExtension({ + component: { + lazy: () => + import('./homePageComponents/WelcomeTitle').then(m => m.WelcomeTitle), + }, + }), +); + export const HomePageRandomJoke = homePlugin.provide( createCardExtension<{ defaultCategory?: 'any' | 'programming' }>({ title: 'Random Joke', From e5d79ade955d4b932c00f485d77700cb7f5d6ae8 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 11 Oct 2021 10:21:02 +0200 Subject: [PATCH 25/54] Convert timeUtil to typescript Signed-off-by: Oliver Sand --- .../{timeUtil.test.js => timeUtil.test.ts} | 26 +++++++++++-------- .../WelcomeTitle/{timeUtil.js => timeUtil.ts} | 10 +++---- 2 files changed, 20 insertions(+), 16 deletions(-) rename plugins/home/src/homePageComponents/WelcomeTitle/{timeUtil.test.js => timeUtil.test.ts} (55%) rename plugins/home/src/homePageComponents/WelcomeTitle/{timeUtil.js => timeUtil.ts} (83%) diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.js b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.ts similarity index 55% rename from plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.js rename to plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.ts index 2abb03152f..77eb6677ca 100644 --- a/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.js +++ b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.test.ts @@ -16,16 +16,20 @@ import { getTimeBasedGreeting } from './timeUtil'; -it('has greeting and language', () => { - const greeting = getTimeBasedGreeting(); - expect(greeting).toHaveProperty('greeting'); - expect(greeting).toHaveProperty('language'); -}); +describe('getTimeBasedGreeting', () => { + afterEach(() => jest.resetAllMocks()); -it('greets late at night', () => { - jest - .spyOn(global.Date, 'now') - .mockImplementationOnce(() => new Date('1970-01-01T23:00:00').valueOf()); - const greeting = getTimeBasedGreeting(); - expect(greeting.greeting).toBe('Get some rest'); + it('has greeting and language', () => { + const greeting = getTimeBasedGreeting(); + expect(greeting).toHaveProperty('greeting'); + expect(greeting).toHaveProperty('language'); + }); + + it('greets late at night', () => { + jest + .spyOn(global.Date, 'now') + .mockImplementationOnce(() => new Date('1970-01-01T23:00:00').valueOf()); + const greeting = getTimeBasedGreeting(); + expect(greeting.greeting).toBe('Get some rest'); + }); }); diff --git a/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.js b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.ts similarity index 83% rename from plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.js rename to plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.ts index 93f4379d8e..8df7703b44 100644 --- a/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.js +++ b/plugins/home/src/homePageComponents/WelcomeTitle/timeUtil.ts @@ -18,12 +18,12 @@ import goodMorning from './locales/goodMorning.locales.json'; import goodAfternoon from './locales/goodAfternoon.locales.json'; import goodEvening from './locales/goodEvening.locales.json'; -// Select a large random integer at startup, to prevent the greetings to change every time the user -// navigates. +// Select a large random integer at startup, to prevent the greetings to change +// every time the user navigates. const greetingRandomSeed = Math.floor(Math.random() * 1000000); -export function getTimeBasedGreeting() { - const random = array => array[greetingRandomSeed % array.length]; +export function getTimeBasedGreeting(): { language: string; greeting: string } { + const random = (array: string[]) => array[greetingRandomSeed % array.length]; const currentHour = new Date(Date.now()).getHours(); if (currentHour >= 23) { @@ -32,7 +32,7 @@ export function getTimeBasedGreeting() { greeting: 'Get some rest', }; } - const timeOfDay = hour => { + const timeOfDay = (hour: number): { [language: string]: string } => { if (hour < 12) return goodMorning; if (hour < 17) return goodAfternoon; return goodEvening; From a593c5feb4939205870867a83be09dfba2a911bf Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 11 Oct 2021 10:39:11 +0200 Subject: [PATCH 26/54] Update api report Signed-off-by: Oliver Sand --- plugins/home/api-report.md | 3 +++ plugins/home/src/plugin.ts | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/plugins/home/api-report.md b/plugins/home/api-report.md index db7631290d..a1cd98124a 100644 --- a/plugins/home/api-report.md +++ b/plugins/home/api-report.md @@ -122,6 +122,9 @@ export const SettingsModal: ({ children: JSX.Element; }) => JSX.Element; +// @public +export const WelcomeTitle: () => JSX.Element; + // Warnings were encountered during analysis: // // src/extensions.d.ts:16:5 - (ae-forgotten-export) The symbol "ComponentParts" needs to be exported by the entry point index.d.ts diff --git a/plugins/home/src/plugin.ts b/plugins/home/src/plugin.ts index 503418a42c..56849cb6d0 100644 --- a/plugins/home/src/plugin.ts +++ b/plugins/home/src/plugin.ts @@ -60,6 +60,11 @@ export const ComponentTab = homePlugin.provide( }), ); +/** + * A component to display a playful greeting for the user. + * + * @public + */ export const WelcomeTitle = homePlugin.provide( createComponentExtension({ component: { From 87b2d5ad88d1a16a8044b7e2ddb0c95a294e4ca1 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 11 Oct 2021 11:19:34 +0200 Subject: [PATCH 27/54] Fix `` to display only the selected tab, not the other way around Signed-off-by: Oliver Sand --- .changeset/two-foxes-marry.md | 5 ++ .../ComponentTabs/ComponentTabs.test.tsx | 76 +++++++++++++++++++ .../ComponentTabs/ComponentTabs.tsx | 5 +- 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 .changeset/two-foxes-marry.md create mode 100644 plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx diff --git a/.changeset/two-foxes-marry.md b/.changeset/two-foxes-marry.md new file mode 100644 index 0000000000..a6ac7df666 --- /dev/null +++ b/.changeset/two-foxes-marry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Fix `` to display only the selected tab, not the other way around. diff --git a/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx new file mode 100644 index 0000000000..96ab37a8aa --- /dev/null +++ b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.test.tsx @@ -0,0 +1,76 @@ +/* + * 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 { render } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { ComponentTabs } from './ComponentTabs'; + +describe('', () => { + test('should render tabs without exploding', () => { + const { getByText } = render( + <>ContentA, + }, + { + label: 'TabB', + Component: () => <>ContentB, + }, + ]} + />, + ); + + expect(getByText('TabA')).toBeInTheDocument(); + expect(getByText('TabB')).toBeInTheDocument(); + + expect(getByText('ContentA')).toBeInTheDocument(); + expect(getByText('ContentB')).toHaveStyle({ + display: 'none', + }); + }); + + test('should switch tab on click', () => { + const { getByText } = render( + <>ContentA, + }, + { + label: 'TabB', + Component: () => <>ContentB, + }, + ]} + />, + ); + + expect(getByText('ContentB')).toHaveStyle({ + display: 'none', + }); + + userEvent.click(getByText('TabB')); + + expect(getByText('ContentA')).toHaveStyle({ + display: 'none', + }); + }); +}); diff --git a/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx index f404a80980..d6d3561fe1 100644 --- a/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx +++ b/plugins/home/src/componentRenderers/ComponentTabs/ComponentTabs.tsx @@ -44,7 +44,10 @@ export const ComponentTabs = ({ ))} {tabs.map(({ Component }, idx) => ( -
+
))} From d0dba0a448f4ad23928e09b018f1fa7e41787e7a Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 11 Oct 2021 11:26:49 +0200 Subject: [PATCH 28/54] place example in indented code block Signed-off-by: Johan Haals --- docs/getting-started/contributors.md | 34 ++++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/docs/getting-started/contributors.md b/docs/getting-started/contributors.md index 7b612ccd0d..ed56d63cab 100644 --- a/docs/getting-started/contributors.md +++ b/docs/getting-started/contributors.md @@ -207,20 +207,24 @@ getEntity is now a function that returns a Promise. #### Good -**BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is -passed to the router. + --- + '@backstage/catalog': patch + --- -These changes are **required** to `packages/backend/src/plugins/catalog.ts` + **BREAKING** The catalog createRouter now requires that a `FluxCapacitor` is + passed to the router. -```diff -+ import { FluxCapacitor } from '@backstage/time'; -+ const fluxCapacitor = new FluxCapacitor(); - return await createRouter({ - entitiesCatalog, - locationAnalyzer, - locationService, -+ fluxCapacitor, - logger: env.logger, - config: env.config, - }); -``` + These changes are **required** to `packages/backend/src/plugins/catalog.ts` + + ```diff + + import { FluxCapacitor } from '@backstage/time'; + + const fluxCapacitor = new FluxCapacitor(); + return await createRouter({ + entitiesCatalog, + locationAnalyzer, + locationService, + + fluxCapacitor, + logger: env.logger, + config: env.config, + }); + ``` From a20cbf00d2774df64edb892e97c276d29acd6263 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 11 Oct 2021 11:10:31 +0200 Subject: [PATCH 29/54] core-components: lazy load syntax highlighter Signed-off-by: Patrik Oldsberg --- .changeset/silent-candles-remember.md | 5 ++ .../components/CodeSnippet/CodeSnippet.tsx | 84 +++++++++++-------- 2 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 .changeset/silent-candles-remember.md diff --git a/.changeset/silent-candles-remember.md b/.changeset/silent-candles-remember.md new file mode 100644 index 0000000000..99061bbc1d --- /dev/null +++ b/.changeset/silent-candles-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +The syntax highlighting library used by the `CodeSnippet` component is now lazy loaded. diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx index 3ce041a819..f8a7d2ff9e 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx @@ -14,12 +14,56 @@ * limitations under the License. */ -import React from 'react'; -import SyntaxHighlighter from 'react-syntax-highlighter'; -import { docco, dark } from 'react-syntax-highlighter/dist/cjs/styles/hljs'; +import React, { lazy, Suspense } from 'react'; import { useTheme } from '@material-ui/core'; import { BackstageTheme } from '@backstage/theme'; import { CopyTextButton } from '../CopyTextButton'; +import { Progress } from '../Progress'; + +const LazySyntaxHighlighter = lazy(async () => { + const [{ default: SyntaxHighlighter }, { docco, dark }] = await Promise.all([ + import('react-syntax-highlighter'), + import('react-syntax-highlighter/dist/cjs/styles/hljs'), + ]); + + function LazyHighlighter(props: CodeSnippetProps) { + const { + text, + language, + showLineNumbers = false, + highlightedNumbers, + customStyle, + } = props; + const theme = useTheme(); + const mode = theme.palette.type === 'dark' ? dark : docco; + const highlightColor = + theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; + + return ( + + highlightedNumbers?.includes(lineNumber) + ? { + style: { + backgroundColor: highlightColor, + }, + } + : {} + } + > + {text} + + ); + } + + return { default: LazyHighlighter }; +}); /** * Properties for {@link CodeSnippet} @@ -68,38 +112,12 @@ export interface CodeSnippetProps { * providing consistent theming and copy code button */ export function CodeSnippet(props: CodeSnippetProps) { - const { - text, - language, - showLineNumbers = false, - showCopyCodeButton = false, - highlightedNumbers, - customStyle, - } = props; - const theme = useTheme(); - const mode = theme.palette.type === 'dark' ? dark : docco; - const highlightColor = theme.palette.type === 'dark' ? '#256bf3' : '#e6ffed'; + const { text, showCopyCodeButton = false } = props; return (
- - highlightedNumbers?.includes(lineNumber) - ? { - style: { - backgroundColor: highlightColor, - }, - } - : {} - } - > - {text} - + }> + + {showCopyCodeButton && (
From ce487aef24b2c031f747abeb8930d8e1fb6753bd Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 11 Oct 2021 14:38:18 +0200 Subject: [PATCH 30/54] Remove unused dependency on @octokit/openapi-types Signed-off-by: Johan Haals --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index 03a502fee2..cebd2e245a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "devDependencies": { "@types/webpack": "^5.28.0", "@changesets/cli": "^2.14.0", - "@octokit/openapi-types": "^2.2.0", "@spotify/prettier-config": "^11.0.0", "command-exists": "^1.2.9", "concurrently": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index e6babc2742..31cd4a60e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4965,11 +4965,6 @@ "@octokit/types" "^6.12.2" btoa-lite "^1.0.0" -"@octokit/openapi-types@^2.2.0": - version "2.2.0" - resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-2.2.0.tgz#123e0438a0bc718ccdac3b5a2e69b3dd00daa85b" - integrity sha512-274lNUDonw10kT8wHg8fCcUc1ZjZHbWv0/TbAwb0ojhBQqZYc1cQ/4yqTVTtPMDeZ//g7xVEYe/s3vURkRghPg== - "@octokit/openapi-types@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.2.tgz#065ce49b338043ec7f741316ce06afd4d459d944" From e2e1620bb51c3566f58d98bc933eac4f0cbbbbdc Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Mon, 11 Oct 2021 14:52:43 +0200 Subject: [PATCH 31/54] Remove unused dependencies Signed-off-by: Johan Haals --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index cebd2e245a..9c0f41ca53 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "lerna": "^4.0.0", "lint-staged": "^11.1.2", "prettier": "^2.2.1", - "recursive-readdir": "^2.2.2", "shx": "^0.3.2", "yarn-lock-check": "^1.0.5" }, From 56bd5372563a863c824e25b3515475edeb7bcdc5 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 11 Oct 2021 21:55:42 +0200 Subject: [PATCH 32/54] [Search] add search api how to guide + search bar custom placeholder (#7535) --- .changeset/search-gentle-chairs-grab.md | 5 ++ docs/features/search/how-to-guides.md | 46 +++++++++++++++++++ microsite/sidebars.json | 3 +- mkdocs.yml | 1 + plugins/search/api-report.md | 8 +++- .../SearchBar/SearchBar.stories.tsx | 17 +++++++ .../components/SearchBar/SearchBar.test.tsx | 20 ++++++++ .../src/components/SearchBar/SearchBar.tsx | 8 +++- 8 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 .changeset/search-gentle-chairs-grab.md create mode 100644 docs/features/search/how-to-guides.md diff --git a/.changeset/search-gentle-chairs-grab.md b/.changeset/search-gentle-chairs-grab.md new file mode 100644 index 0000000000..f3e2cf3cec --- /dev/null +++ b/.changeset/search-gentle-chairs-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search': patch +--- + +SearchBar component to accept optional placeholder prop diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md new file mode 100644 index 0000000000..b6ea6ae364 --- /dev/null +++ b/docs/features/search/how-to-guides.md @@ -0,0 +1,46 @@ +--- +id: how-to-guides +title: Search "HOW TO" guides +sidebar_label: "HOW TO" guides +description: Search "HOW TO" guides +--- + +## How to implement your own Search API + +The Search plugin provides implementation of one primary API by default: the +[SearchApi](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L35), +which is responsible for talking to the search-backend to query search results. + +There may be occasions where you need to implement this API yourself, to +customize it to your own needs - for example if you have your own search backend +that you want to talk to. The purpose of this guide is to walk you through how +to do that in two steps. + +1. Implement the `SearchApi` + [interface](https://github.com/backstage/backstage/blob/db2666b980853c281b8fe77905d7639c5d255f13/plugins/search/src/apis.ts#L31) + according to your needs. + +```typescript +export class SearchClient implements SearchApi { + // your implementation +} +``` + +2. Override the API ref `searchApiRef` with your new implemented API in the + `App.tsx` using `ApiFactories`. + [Read more about App APIs](https://backstage.io/docs/api/utility-apis#app-apis). + +```typescript +const app = createApp({ + apis: [ + // SearchApi + createApiFactory({ + api: searchApiRef, + deps: { discovery: discoveryApiRef }, + factory({ discovery }) { + return new SearchClient({ discoveryApi: discovery }); + }, + }), + ], +}); +``` diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 8b8b359fdc..bb9fd5783a 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -90,7 +90,8 @@ "features/search/getting-started", "features/search/concepts", "features/search/architecture", - "features/search/search-engines" + "features/search/search-engines", + "features/search/how-to-guides" ] }, { diff --git a/mkdocs.yml b/mkdocs.yml index b668ffdb15..7376893586 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -68,6 +68,7 @@ nav: - Concepts: 'features/search/concepts.md' - Search Architecture: 'features/search/architecture.md' - Search Engines: 'features/search/search-engines.md' + - HOW TO guides: 'features/search/how-to-guides.md' - TechDocs: - Overview: 'features/techdocs/README.md' - Getting Started: 'features/techdocs/getting-started.md' diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 169972d02d..b29149805c 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/api-report.md @@ -79,7 +79,11 @@ export const searchApiRef: ApiRef; // Warning: (ae-missing-release-tag) "SearchBar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; +export const SearchBar: ({ + className, + debounceTime, + placeholder, +}: Props) => JSX.Element; // Warning: (ae-missing-release-tag) "SearchBarNext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -87,9 +91,11 @@ export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; export const SearchBarNext: ({ className, debounceTime, + placeholder, }: { className?: string | undefined; debounceTime?: number | undefined; + placeholder?: string | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "SearchContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx index fa2ba0d94e..2c990e512c 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.stories.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.stories.tsx @@ -45,3 +45,20 @@ export const Default = () => { ); }; + +export const CustomPlaceholder = () => { + return ( + + {/* @ts-ignore (defaultValue requires more than what is used here) */} + + + + + + + + + + + ); +}; diff --git a/plugins/search/src/components/SearchBar/SearchBar.test.tsx b/plugins/search/src/components/SearchBar/SearchBar.test.tsx index de8ea9a5a7..3a11daa9fa 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.test.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.test.tsx @@ -65,6 +65,26 @@ describe('SearchBar', () => { await waitFor(() => { expect(screen.getByRole('textbox', { name })).toBeInTheDocument(); + expect( + screen.getByPlaceholderText('Search in Mock title'), + ).toBeInTheDocument(); + }); + }); + + it('Renders with custom placeholder', async () => { + render( + + + + + , + , + ); + + await waitFor(() => { + expect( + screen.getByPlaceholderText('This is a custom placeholder'), + ).toBeInTheDocument(); }); }); diff --git a/plugins/search/src/components/SearchBar/SearchBar.tsx b/plugins/search/src/components/SearchBar/SearchBar.tsx index 214e22520c..53802899eb 100644 --- a/plugins/search/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search/src/components/SearchBar/SearchBar.tsx @@ -89,9 +89,14 @@ export const SearchBarBase = ({ type Props = { className?: string; debounceTime?: number; + placeholder?: string; }; -export const SearchBar = ({ className, debounceTime = 0 }: Props) => { +export const SearchBar = ({ + className, + debounceTime = 0, + placeholder, +}: Props) => { const { term, setTerm } = useSearch(); const [value, setValue] = useState(term); @@ -113,6 +118,7 @@ export const SearchBar = ({ className, debounceTime = 0 }: Props) => { value={value} onChange={handleQuery} onClear={handleClear} + placeholder={placeholder} /> ); }; From ac7edbb437ad3af875a843a15360b4a6cadea54f Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Mon, 11 Oct 2021 15:08:53 -0500 Subject: [PATCH 33/54] Refactoring based on feedback Signed-off-by: Andre Wanlin --- plugins/azure-devops-backend/api-report.md | 5 +++-- .../azure-devops-backend/src/api/AzureDevOpsApi.ts | 14 +++++++------- plugins/azure-devops-backend/src/api/types.ts | 5 +++++ .../src/service/router.test.ts | 3 +-- plugins/azure-devops-backend/src/service/router.ts | 8 ++++++-- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/azure-devops-backend/api-report.md b/plugins/azure-devops-backend/api-report.md index 34477a71ca..74b4ac5b23 100644 --- a/plugins/azure-devops-backend/api-report.md +++ b/plugins/azure-devops-backend/api-report.md @@ -29,12 +29,13 @@ export class AzureDevOpsApi { projectName: string, repoName: string, ): Promise; + // Warning: (ae-forgotten-export) The symbol "PullRequestOptions" needs to be exported by the entry point index.d.ts + // // (undocumented) getPullRequests( projectName: string, repoName: string, - top: number, - status: PullRequestStatus, + options: PullRequestOptions, ): Promise; // (undocumented) getRepoBuilds( diff --git a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts index 991a68fcee..89e32aa2f3 100644 --- a/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts +++ b/plugins/azure-devops-backend/src/api/AzureDevOpsApi.ts @@ -24,9 +24,8 @@ import { import { GitPullRequest, GitPullRequestSearchCriteria, - PullRequestStatus, } from 'azure-devops-node-api/interfaces/GitInterfaces'; -import { PullRequest, RepoBuild } from './types'; +import { PullRequest, PullRequestOptions, RepoBuild } from './types'; export class AzureDevOpsApi { constructor( @@ -102,8 +101,7 @@ export class AzureDevOpsApi { async getPullRequests( projectName: string, repoName: string, - top: number, - status: PullRequestStatus, + options: PullRequestOptions, ) { if (this.logger) { this.logger.debug( @@ -114,7 +112,7 @@ export class AzureDevOpsApi { const gitRepository = await this.getGitRepository(projectName, repoName); const client = await this.webApi.getGitApi(); const searchCriteria: GitPullRequestSearchCriteria = { - status: status, + status: options.status, }; const gitPullRequests = await client.getPullRequests( gitRepository.id as string, @@ -122,9 +120,11 @@ export class AzureDevOpsApi { projectName, undefined, undefined, - top, + options.top, ); - const linkBaseUrl = `${this.webApi.serverUrl}/${projectName}/_git/${repoName}/pullrequest`; + const linkBaseUrl = `${this.webApi.serverUrl}/${encodeURIComponent( + projectName, + )}/_git/${encodeURIComponent(repoName)}/pullrequest`; const pullRequests: PullRequest[] = gitPullRequests.map(gitPullRequest => { return mappedPullRequest(gitPullRequest, linkBaseUrl); }); diff --git a/plugins/azure-devops-backend/src/api/types.ts b/plugins/azure-devops-backend/src/api/types.ts index 4b2a0f7988..a53f501f26 100644 --- a/plugins/azure-devops-backend/src/api/types.ts +++ b/plugins/azure-devops-backend/src/api/types.ts @@ -43,3 +43,8 @@ export type PullRequest = { isDraft?: boolean; link: string; }; + +export type PullRequestOptions = { + top: number; + status: PullRequestStatus; +}; diff --git a/plugins/azure-devops-backend/src/service/router.test.ts b/plugins/azure-devops-backend/src/service/router.test.ts index e6444bb9d3..4143291409 100644 --- a/plugins/azure-devops-backend/src/service/router.test.ts +++ b/plugins/azure-devops-backend/src/service/router.test.ts @@ -254,8 +254,7 @@ describe('createRouter', () => { expect(azureDevOpsApi.getPullRequests).toHaveBeenCalledWith( 'myProject', 'myRepo', - 50, - 1, + { status: 1, top: 50 }, ); expect(response.status).toEqual(200); expect(response.body).toEqual(pullRequests); diff --git a/plugins/azure-devops-backend/src/service/router.ts b/plugins/azure-devops-backend/src/service/router.ts index 62ac06afe4..0929d5cde9 100644 --- a/plugins/azure-devops-backend/src/service/router.ts +++ b/plugins/azure-devops-backend/src/service/router.ts @@ -22,6 +22,7 @@ import { Config } from '@backstage/config'; import { getPersonalAccessTokenHandler, WebApi } from 'azure-devops-node-api'; import { AzureDevOpsApi } from '../api'; import { PullRequestStatus } from 'azure-devops-node-api/interfaces/GitInterfaces'; +import { PullRequestOptions } from '../api/types'; const DEFAULT_TOP: number = 10; @@ -91,11 +92,14 @@ export async function createRouter( const status = req.query.status ? Number(req.query.status) : PullRequestStatus.Active; + const pullRequestOptions: PullRequestOptions = { + top: top, + status: status, + }; const gitPullRequest = await azureDevOpsApi.getPullRequests( projectName, repoName, - top, - status, + pullRequestOptions, ); res.status(200).json(gitPullRequest); }); From 395020b17c6b91ea0a1a6ad28464ea48cae2fb05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Gomez?= Date: Mon, 11 Oct 2021 22:31:45 +0200 Subject: [PATCH 34/54] Add Grafana plugin to the marketplace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kévin Gomez --- microsite/data/plugins/grafana.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 microsite/data/plugins/grafana.yaml diff --git a/microsite/data/plugins/grafana.yaml b/microsite/data/plugins/grafana.yaml new file mode 100644 index 0000000000..ff4e33e3df --- /dev/null +++ b/microsite/data/plugins/grafana.yaml @@ -0,0 +1,13 @@ +--- +title: Grafana +author: K-Phoen +authorUrl: https://github.com/K-Phoen +category: Monitoring +description: Associate alerts and dashboards to components. +documentation: https://github.com/K-Phoen/backstage-plugin-grafana/ +iconUrl: https://avatars.githubusercontent.com/u/7195757?s=200&v=4 +npmPackageName: '@k-phoen/backstage-plugin-grafana' +tags: + - dashboards + - monitoring + - alerting From 5091a36ca623e74b44585471ed7c75dead3c8e34 Mon Sep 17 00:00:00 2001 From: Daniel Ortiz Lira Date: Thu, 30 Sep 2021 18:40:14 -0500 Subject: [PATCH 35/54] Expose SystemDiagramCard to material-ui overrides Signed-off-by: Daniel Ortiz Lira --- .../SystemDiagramCard/SystemDiagramCard.tsx | 54 +++++++++++-------- .../src/components/SystemDiagramCard/index.ts | 1 + 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx index b3326abaab..9e46fabefb 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx @@ -46,28 +46,38 @@ import { import { useApi, useRouteRef } from '@backstage/core-plugin-api'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ - domainNode: { - fill: theme.palette.primary.main, - stroke: theme.palette.border, - }, - systemNode: { - fill: 'coral', - stroke: theme.palette.border, - }, - componentNode: { - fill: 'yellowgreen', - stroke: theme.palette.border, - }, - apiNode: { - fill: theme.palette.gold, - stroke: theme.palette.border, - }, - resourceNode: { - fill: 'grey', - stroke: theme.palette.border, - }, -})); +export type SystemDiagramCardClassKey = + | 'domainNode' + | 'systemNode' + | 'componentNode' + | 'apiNode' + | 'resourceNode'; + +const useStyles = makeStyles( + (theme: BackstageTheme) => ({ + domainNode: { + fill: theme.palette.primary.main, + stroke: theme.palette.border, + }, + systemNode: { + fill: 'coral', + stroke: theme.palette.border, + }, + componentNode: { + fill: 'yellowgreen', + stroke: theme.palette.border, + }, + apiNode: { + fill: theme.palette.gold, + stroke: theme.palette.border, + }, + resourceNode: { + fill: 'grey', + stroke: theme.palette.border, + }, + }), + { name: 'PluginCatalogSystemDiagramCard' }, +); // Simplifies the diagram output by hiding the default namespace and kind function readableEntityName( diff --git a/plugins/catalog/src/components/SystemDiagramCard/index.ts b/plugins/catalog/src/components/SystemDiagramCard/index.ts index 4c1427c838..bc9ed6309d 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/index.ts +++ b/plugins/catalog/src/components/SystemDiagramCard/index.ts @@ -15,3 +15,4 @@ */ export { SystemDiagramCard } from './SystemDiagramCard'; +export type { SystemDiagramCardClassKey } from './SystemDiagramCard'; From a4bb6dc73b0e5f26af3ba72264c68408dd51d6f3 Mon Sep 17 00:00:00 2001 From: Daniel Ortiz Lira Date: Thu, 30 Sep 2021 18:40:54 -0500 Subject: [PATCH 36/54] Expose EntityLinksEmptyState to material-ui overrides Signed-off-by: Daniel Ortiz Lira --- .../EntityLinksCard/EntityLinksEmptyState.tsx | 19 ++++++++++++------- .../src/components/EntityLinksCard/index.ts | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx index b0e8ee5d74..0da92a9f03 100644 --- a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx @@ -26,13 +26,18 @@ const ENTITY_YAML = `metadata: title: My Dashboard icon: dashboard`; -const useStyles = makeStyles(theme => ({ - code: { - borderRadius: 6, - margin: `${theme.spacing(2)}px 0px`, - background: theme.palette.type === 'dark' ? '#444' : '#fff', - }, -})); +export type EntityLinksEmptyStateClassKey = 'code'; + +const useStyles = makeStyles( + theme => ({ + code: { + borderRadius: 6, + margin: `${theme.spacing(2)}px 0px`, + background: theme.palette.type === 'dark' ? '#444' : '#fff', + }, + }), + { name: 'PluginCatalogEntityLinksEmptyState' }, +); export const EntityLinksEmptyState = () => { const classes = useStyles(); diff --git a/plugins/catalog/src/components/EntityLinksCard/index.ts b/plugins/catalog/src/components/EntityLinksCard/index.ts index 04c30c0f68..fc53904a47 100644 --- a/plugins/catalog/src/components/EntityLinksCard/index.ts +++ b/plugins/catalog/src/components/EntityLinksCard/index.ts @@ -15,3 +15,4 @@ */ export { EntityLinksCard } from './EntityLinksCard'; +export type { EntityLinksEmptyStateClassKey } from './EntityLinksEmptyState'; From 241a9fe67a895fbf07da3303d8bb53b2a7de30b8 Mon Sep 17 00:00:00 2001 From: Daniel Ortiz Lira Date: Tue, 5 Oct 2021 10:54:24 -0500 Subject: [PATCH 37/54] Expose overridableComponents type from the package root Signed-off-by: Daniel Ortiz Lira --- plugins/catalog/src/index.ts | 1 + plugins/catalog/src/overridableComponents.ts | 31 ++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 plugins/catalog/src/overridableComponents.ts diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 54f4da5e7e..5a8ee84fa8 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -33,6 +33,7 @@ export * from './components/EntityProcessingErrorsPanel'; export * from './components/EntityPageLayout'; export * from './components/EntitySwitch'; export * from './components/FilteredEntityLayout'; +export * from './overridableComponents'; export { Router } from './components/Router'; export { CatalogEntityPage, diff --git a/plugins/catalog/src/overridableComponents.ts b/plugins/catalog/src/overridableComponents.ts new file mode 100644 index 0000000000..caa923692c --- /dev/null +++ b/plugins/catalog/src/overridableComponents.ts @@ -0,0 +1,31 @@ +/* + * 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 { Overrides } from '@material-ui/core/styles/overrides'; +import { StyleRules } from '@material-ui/core/styles/withStyles'; + +import { EntityLinksEmptyStateClassKey } from './components/EntityLinksCard'; +import { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; + +type PluginCatalogComponentsNameToClassKey = { + PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; + PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; +}; + +export type BackstageOverrides = Overrides & { + [Name in keyof PluginCatalogComponentsNameToClassKey]?: Partial< + StyleRules + >; +}; From 3ddb4d93532b2b38af97f5a83af000e1ef593d29 Mon Sep 17 00:00:00 2001 From: Daniel Ortiz Lira Date: Tue, 5 Oct 2021 10:58:12 -0500 Subject: [PATCH 38/54] Build api reports Signed-off-by: Daniel Ortiz Lira --- plugins/catalog/api-report.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 4937a3eb86..a404dbab89 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -23,10 +23,12 @@ import { IconComponent } from '@backstage/core-plugin-api'; import { IdentityApi } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { Location as Location_2 } from '@backstage/catalog-model'; +import { Overrides } from '@material-ui/core/styles/overrides'; import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; +import { StyleRules } from '@material-ui/core/styles/withStyles'; import { TableColumn } from '@backstage/core-components'; import { TableProps } from '@backstage/core-components'; import { TabProps } from '@material-ui/core'; @@ -55,6 +57,16 @@ export const AboutField: ({ children, }: Props_2) => JSX.Element; +// Warning: (ae-forgotten-export) The symbol "PluginCatalogComponentsNameToClassKey" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "BackstageOverrides" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type BackstageOverrides = Overrides & { + [Name in keyof PluginCatalogComponentsNameToClassKey]?: Partial< + StyleRules + >; +}; + // Warning: (ae-missing-release-tag) "CatalogClientWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public From 93a13dfb422705fca0946182bb9e7ede9ff5609c Mon Sep 17 00:00:00 2001 From: Daniel Ortiz Lira Date: Mon, 11 Oct 2021 16:30:50 -0500 Subject: [PATCH 39/54] Add changeset for @backstage/plugin-catalog package Signed-off-by: Daniel Ortiz Lira --- .changeset/large-pots-invent.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/large-pots-invent.md diff --git a/.changeset/large-pots-invent.md b/.changeset/large-pots-invent.md new file mode 100644 index 0000000000..269b0bd288 --- /dev/null +++ b/.changeset/large-pots-invent.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Support `material-ui` overrides in SystemDiagramCard and EmptityLinksEmptyState components From b4bdb1f1fa94b87a261bf7b40fcf1ae0b9d5d5e7 Mon Sep 17 00:00:00 2001 From: Daniel Ortiz Lira Date: Mon, 11 Oct 2021 18:30:32 -0500 Subject: [PATCH 40/54] Fix api-reports warnings Signed-off-by: Daniel Ortiz Lira --- plugins/catalog/api-report.md | 20 ++++++++++++++++--- .../EntityLinksCard/EntityLinksEmptyState.tsx | 1 + .../SystemDiagramCard/SystemDiagramCard.tsx | 1 + plugins/catalog/src/index.ts | 3 +++ plugins/catalog/src/overridableComponents.ts | 4 +++- 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index a404dbab89..2be7fc8575 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -57,9 +57,6 @@ export const AboutField: ({ children, }: Props_2) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "PluginCatalogComponentsNameToClassKey" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "BackstageOverrides" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type BackstageOverrides = Overrides & { [Name in keyof PluginCatalogComponentsNameToClassKey]?: Partial< @@ -335,6 +332,9 @@ export const EntityLinksCard: ({ variant?: 'gridItem' | undefined; }) => JSX.Element; +// @public (undocumented) +export type EntityLinksEmptyStateClassKey = 'code'; + // Warning: (ae-missing-release-tag) "EntityListContainer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -436,6 +436,12 @@ export function isNamespace(namespace: string): (entity: Entity) => boolean; // @public (undocumented) export const isOrphan: (entity: Entity) => boolean; +// @public (undocumented) +export type PluginCatalogComponentsNameToClassKey = { + PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; + PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; +}; + // Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @deprecated (undocumented) @@ -445,6 +451,14 @@ export const Router: ({ EntityPage?: React_2.ComponentType<{}> | undefined; }) => JSX.Element; +// @public (undocumented) +export type SystemDiagramCardClassKey = + | 'domainNode' + | 'systemNode' + | 'componentNode' + | 'apiNode' + | 'resourceNode'; + // Warnings were encountered during analysis: // // src/components/CatalogTable/CatalogTable.d.ts:10:5 - (ae-forgotten-export) The symbol "CatalogTableProps" needs to be exported by the entry point index.d.ts diff --git a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx index 0da92a9f03..54325ffb5d 100644 --- a/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx +++ b/plugins/catalog/src/components/EntityLinksCard/EntityLinksEmptyState.tsx @@ -26,6 +26,7 @@ const ENTITY_YAML = `metadata: title: My Dashboard icon: dashboard`; +/** @public */ export type EntityLinksEmptyStateClassKey = 'code'; const useStyles = makeStyles( diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx index 9e46fabefb..984ae9f982 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx @@ -46,6 +46,7 @@ import { import { useApi, useRouteRef } from '@backstage/core-plugin-api'; +/** @public */ export type SystemDiagramCardClassKey = | 'domainNode' | 'systemNode' diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts index 5a8ee84fa8..d804bbaf83 100644 --- a/plugins/catalog/src/index.ts +++ b/plugins/catalog/src/index.ts @@ -51,3 +51,6 @@ export { EntityLinksCard, EntitySystemDiagramCard, } from './plugin'; + +export type { EntityLinksEmptyStateClassKey } from './components/EntityLinksCard'; +export type { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; diff --git a/plugins/catalog/src/overridableComponents.ts b/plugins/catalog/src/overridableComponents.ts index caa923692c..e76aafbb68 100644 --- a/plugins/catalog/src/overridableComponents.ts +++ b/plugins/catalog/src/overridableComponents.ts @@ -19,11 +19,13 @@ import { StyleRules } from '@material-ui/core/styles/withStyles'; import { EntityLinksEmptyStateClassKey } from './components/EntityLinksCard'; import { SystemDiagramCardClassKey } from './components/SystemDiagramCard'; -type PluginCatalogComponentsNameToClassKey = { +/** @public */ +export type PluginCatalogComponentsNameToClassKey = { PluginCatalogEntityLinksEmptyState: EntityLinksEmptyStateClassKey; PluginCatalogSystemDiagramCard: SystemDiagramCardClassKey; }; +/** @public */ export type BackstageOverrides = Overrides & { [Name in keyof PluginCatalogComponentsNameToClassKey]?: Partial< StyleRules From 805970befeb2be056cb14ac7ac3ee619d97f9afe Mon Sep 17 00:00:00 2001 From: anisorigin <89754895+anisorigin@users.noreply.github.com> Date: Tue, 12 Oct 2021 12:45:20 +1000 Subject: [PATCH 41/54] change appId: 1 to app id. --- docs/plugins/github-apps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/plugins/github-apps.md b/docs/plugins/github-apps.md index ef0b21d902..1be1502b6b 100644 --- a/docs/plugins/github-apps.md +++ b/docs/plugins/github-apps.md @@ -60,7 +60,7 @@ The YAML file must include the following information. Please note that the indentation for the `privateKey` is required. ```yaml -appId: 1 +appId: app id clientId: client id clientSecret: client secret webhookSecret: webhook secret @@ -95,7 +95,7 @@ If you want to limit the GitHub app installations visible to backstage you may optionally include the `allowedInstallationOwners` option. ```yaml -appId: 1 +appId: app id allowedInstallationOwners: ['GlobexCorp'] clientId: client id clientSecret: client secret From d03ee85ff83f3480c79162372b50a22658f77297 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Mon, 11 Oct 2021 14:45:42 +0200 Subject: [PATCH 42/54] Bump `swagger-ui-react` to `^4.0.0-rc.3` Signed-off-by: Dominik Henneke --- .changeset/dirty-balloons-develop.md | 5 + plugins/api-docs/package.json | 2 +- yarn.lock | 271 ++++++++++----------------- 3 files changed, 109 insertions(+), 169 deletions(-) create mode 100644 .changeset/dirty-balloons-develop.md diff --git a/.changeset/dirty-balloons-develop.md b/.changeset/dirty-balloons-develop.md new file mode 100644 index 0000000000..6dd9005cca --- /dev/null +++ b/.changeset/dirty-balloons-develop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Bump `swagger-ui-react` to `^4.0.0-rc.3`. diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index be3fdf7229..9a89837102 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -49,7 +49,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4", - "swagger-ui-react": "^3.37.2" + "swagger-ui-react": "^4.0.0-rc.3" }, "devDependencies": { "@backstage/cli": "^0.7.15", diff --git a/yarn.lock b/yarn.lock index 1b7572044d..1a0caad7e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2105,7 +2105,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.2.0": +"@babel/runtime@^7.12.1", "@babel/runtime@^7.2.0": version "7.15.4" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== @@ -3686,18 +3686,6 @@ underscore "^1.9.1" ws "^7.3.1" -"@kyleshockey/object-assign-deep@^0.4.2": - version "0.4.2" - resolved "https://registry.npmjs.org/@kyleshockey/object-assign-deep/-/object-assign-deep-0.4.2.tgz#84900f0eefc372798f4751b5262830b8208922ec" - integrity sha1-hJAPDu/DcnmPR1G1JigwuCCJIuw= - -"@kyleshockey/xml@^1.0.2": - version "1.0.2" - resolved "https://registry.npmjs.org/@kyleshockey/xml/-/xml-1.0.2.tgz#81fad3d7c33da2ba2639db095db3db24c2921f70" - integrity sha512-iMo32MPLcI9cPxs3YL5kmKxKgDmkSZDCFEqIT5eRk7d/Ll8r4X3SwGYSigzALd6+RHWlFEmjL1QyaQ15xDZFlw== - dependencies: - stream "^0.0.2" - "@lerna/add@4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@lerna/add/-/add-4.0.0.tgz#c36f57d132502a57b9e7058d1548b7a565ef183f" @@ -6802,6 +6790,14 @@ dependencies: highlight.js "^10.1.0" +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^5.0.0": version "5.1.0" resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" @@ -7310,6 +7306,16 @@ "@types/react" "*" immutable ">=3.8.2" +"@types/react-redux@^7.1.16": + version "7.1.19" + resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.19.tgz#477bd0a9b01bae6d6bf809418cdfa7d3c16d4c62" + integrity sha512-L37dSCT0aoJnCgpR8Iuginlbxoh7qhWOXiaDqEsxVMrER1CmVhFD+63NxgJeT4pkmEM28oX0NH4S4f+sXHTZjA== + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + "@types/react-sparklines@^1.7.0": version "1.7.0" resolved "https://registry.npmjs.org/@types/react-sparklines/-/react-sparklines-1.7.0.tgz#f956d0f7b0e746ad445ce1cd250fe81f8a384684" @@ -7368,13 +7374,6 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@16.4.6": - version "16.4.6" - resolved "https://registry.npmjs.org/@types/react/-/react-16.4.6.tgz#5024957c6bcef4f02823accf5974faba2e54fada" - integrity sha512-9LDZdhsuKSc+DjY65SjBkA958oBWcTWSVWAd2cD9XqKBjhGw1KzAkRhWRw2eIsXvaIE/TOTjjKMFVC+JA1iU4g== - dependencies: - csstype "^2.2.0" - "@types/recharts@^1.8.14", "@types/recharts@^1.8.15": version "1.8.19" resolved "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.19.tgz#047f72cf4c25df545aa1085fe3a085e58a2483c1" @@ -11426,14 +11425,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.5.1: - version "15.7.0" - resolved "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz#7499d7ca2e69bb51d13faf59bd04f0c65a1d6c1e" - integrity sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng== - dependencies: - loose-envify "^1.3.1" - object-assign "^4.1.1" - create-react-context@0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" @@ -11790,7 +11781,7 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: +csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: version "2.6.17" resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== @@ -12872,11 +12863,6 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emitter-component@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz#065e2dbed6959bf470679edabeaf7981d1003ab6" - integrity sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY= - emittery@^0.7.1: version "0.7.1" resolved "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" @@ -13983,11 +13969,6 @@ fecha@^4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -fetch-blob@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" - integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -14300,10 +14281,10 @@ fork-ts-checker-webpack-plugin@^6.0.4: semver "^7.3.2" tapable "^1.0.0" -form-data-encoder@1.4.3, form-data-encoder@^1.0.1: - version "1.4.3" - resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.4.3.tgz#b0975dbe5795676f211c0ff98e473eaf52b0db4a" - integrity sha512-ARLR/jJaj3+tlKkO7h1uvvjQcD6xCiKyg42hcG5Q4jv8uDa1IMPs81bM3BwI8BrqVEQxF9pX6tx0iLIzAvr31Q== +form-data-encoder@^1.4.3: + version "1.6.0" + resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.6.0.tgz#9dd1f479836c1b1b47201667c68f8daafa800943" + integrity sha512-P97AVaOB8hZaniiKK3f46zxQcchQXI8EgBnX+2+719gLv5ZbDSf3J1XtIuAQ8xbGLU4vZYhy7xwhFtK8U5u9Nw== form-data@4.0.0, form-data@^4.0.0: version "4.0.0" @@ -14346,14 +14327,13 @@ format@^0.2.0: resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs= -formdata-node@^3.6.2: - version "3.7.0" - resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-3.7.0.tgz#b58942e617639a0ebea57227cfff8332a4020760" - integrity sha512-O3y7XoWwE4zRvI5e1yVPDHyDGtEgGEI10KxTbQeMbEEt+imR7uxbL5Z4BCaHz5M09d1pkrFaqeYc8beAce4VSw== +formdata-node@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.3.0.tgz#77be2add9092cbd1e1f4d35bc3293a89be117a04" + integrity sha512-TwqhWUZd2jB5l0kUhhcy1XYNsXq46NH6k60zmiu7xsxMztul+cCMuPSAQrSDV62zznhBKJdA9O+zeWj5i5Pbfg== dependencies: - fetch-blob "2.1.2" - form-data-encoder "1.4.3" node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.1" formidable@^1.2.0, formidable@^1.2.2: version "1.2.2" @@ -15503,10 +15483,10 @@ highlight.js@^10.1.0, highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@^ resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== -highlight.js@~10.4.0: - version "10.4.1" - resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" - integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== +highlight.js@~10.7.0: + version "10.7.3" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== history@^5.0.0: version "5.0.0" @@ -15963,7 +15943,7 @@ immer@^9.0.1: resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73" integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ== -immutable@>=3.8.2, immutable@^3.8.1, immutable@^3.8.2, immutable@^3.x.x: +immutable@>=3.8.2, immutable@^3.8.2, immutable@^3.x.x: version "3.8.2" resolved "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM= @@ -16220,7 +16200,7 @@ into-stream@^5.1.1: from2 "^2.3.0" p-is-promise "^3.0.0" -invariant@^2.0.0, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: +invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -16443,7 +16423,7 @@ is-docker@^2.0.0, is-docker@^2.1.1: resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== -is-dom@^1.0.0, is-dom@^1.0.9: +is-dom@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== @@ -18580,11 +18560,6 @@ lodash-es@^4.17.15: resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash-es@^4.2.1: - version "4.17.15" - resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" - integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -18780,7 +18755,7 @@ lodash@4.17.15: resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.0, lodash@~4.17.15, lodash@~4.17.4: +lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.0, lodash@~4.17.15, lodash@~4.17.4: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -18885,12 +18860,12 @@ lowercase-keys@^2.0.0: integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== lowlight@^1.14.0, lowlight@^1.17.0: - version "1.17.0" - resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.17.0.tgz#a1143b2fba8239df8cd5893f9fe97aaf8465af4a" - integrity sha512-vmtBgYKD+QVNy7tIa7ulz5d//Il9R4MooOVh4nkOf9R9Cb/Dk5TXMSTieg/vDulkBkIWj59/BIlyFQxT9X1oAQ== + version "1.20.0" + resolved "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" + integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== dependencies: fault "^1.0.0" - highlight.js "~10.4.0" + highlight.js "~10.7.0" lru-cache@^4.0.1, lru-cache@^4.1.3: version "4.1.5" @@ -21525,11 +21500,6 @@ pend@~1.2.0: resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - integrity sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU= - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -22718,7 +22688,7 @@ raf-schd@^4.0.2: resolved "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== -raf@^3.1.0, raf@^3.4.0, raf@^3.4.1: +raf@^3.4.0, raf@^3.4.1: version "3.4.1" resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== @@ -22837,10 +22807,10 @@ react-copy-to-clipboard@5.0.3: copy-to-clipboard "^3" prop-types "^15.5.8" -react-debounce-input@^3.2.3: - version "3.2.3" - resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.3.tgz#9e8c69771a621c81e8fe36b45ade49a95059cd87" - integrity sha512-7Bfjm9sxrtgB+IsSrdXoo4CVqKg7CbWC68dNhr8q7ZmY6C0AqtR524//SenHQWT+eeSG9DmSLWNWCUFSyaaWSQ== +react-debounce-input@=3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/react-debounce-input/-/react-debounce-input-3.2.4.tgz#8204373a6498776536a2fcc7e467d054c3b729d4" + integrity sha512-fX70bNj0fLEYO2Zcvuh7eh9wOUQ29GIx6r8IxIJlc0i0mpUH++9ax0BhfAYfzndADli3RAMROrZQ014J01owrg== dependencies: lodash.debounce "^4" prop-types "^15.7.2" @@ -22996,23 +22966,12 @@ react-immutable-proptypes@2.2.0: dependencies: invariant "^2.2.2" -react-immutable-pure-component@^1.1.1: - version "1.2.3" - resolved "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-1.2.3.tgz#fa33638df68cfe9f73ccbee1d5861c17f3053f86" - integrity sha512-kNy2A/fDrSuR8TKwB+4ynmItmp1vgF87tWxxfmadwDYo2J3ANipHqTjDIBvJvJ7libvuh76jIbvmK0krjtKH1g== - optionalDependencies: - "@types/react" "16.4.6" +react-immutable-pure-component@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/react-immutable-pure-component/-/react-immutable-pure-component-2.2.2.tgz#3014d3e20cd5a7a4db73b81f1f1464f4d351684b" + integrity sha512-vkgoMJUDqHZfXXnjVlG3keCxSO/U6WeDQ5/Sl0GK2cH8TOxEzQ5jXqDXHEL/jqk6fsNxV05oH5kD7VNMUE2k+A== -react-inspector@^2.3.0: - version "2.3.1" - resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-2.3.1.tgz#f0eb7f520669b545b441af9d38ec6d706e5f649c" - integrity sha512-tUUK7t3KWgZEIUktOYko5Ic/oYwvjEvQUFAGC1UeMeDaQ5za2yZFtItJa2RTwBJB//NxPr000WQK6sEbqC6y0Q== - dependencies: - babel-runtime "^6.26.0" - is-dom "^1.0.9" - prop-types "^15.6.1" - -react-inspector@^5.1.0: +react-inspector@^5.1.0, react-inspector@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8" integrity sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg== @@ -23021,7 +22980,7 @@ react-inspector@^5.1.0: is-dom "^1.0.0" prop-types "^15.0.0" -react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -23067,15 +23026,6 @@ react-markdown@^5.0.2: unist-util-visit "^2.0.0" xtend "^4.0.1" -react-motion@^0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316" - integrity sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ== - dependencies: - performance-now "^0.2.0" - prop-types "^15.5.8" - raf "^3.1.0" - react-popper-tooltip@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/react-popper-tooltip/-/react-popper-tooltip-3.1.1.tgz#329569eb7b287008f04fcbddb6370452ad3f9eac" @@ -23093,28 +23043,17 @@ react-popper@^2.2.4: react-fast-compare "^3.0.1" warning "^4.0.2" -react-redux@=4.4.10: - version "4.4.10" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-4.4.10.tgz#ad57bd1db00c2d0aa7db992b360ce63dd0b80ec5" - integrity sha512-tjL0Bmpkj75Td0k+lXlF8Fc8a9GuXFv/3ahUOCXExWs/jhsKiQeTffdH0j5byejCGCRL4tvGFYlrwBF1X/Aujg== +react-redux@^7.1.1, react-redux@^7.2.4: + version "7.2.5" + resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.5.tgz#213c1b05aa1187d9c940ddfc0b29450957f6a3b8" + integrity sha512-Dt29bNyBsbQaysp6s/dN0gUodcq+dVKKER8Qv82UrpeygwYeX1raTtil7O/fftw/rFqzaf6gJhDZRkkZnn6bjg== dependencies: - create-react-class "^15.5.1" - hoist-non-react-statics "^3.3.0" - invariant "^2.0.0" - lodash "^4.17.11" + "@babel/runtime" "^7.12.1" + "@types/react-redux" "^7.1.16" + hoist-non-react-statics "^3.3.2" loose-envify "^1.4.0" prop-types "^15.7.2" - -react-redux@^7.1.1: - version "7.2.0" - resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" - integrity sha512-EvCAZYGfOLqwV7gh849xy9/pt55rJXPwmYvI4lilPM5rUT/1NxuuN59ipdBksRVSvz0KInbPnp4IfoXJXCqiDA== - dependencies: - "@babel/runtime" "^7.5.5" - hoist-non-react-statics "^3.3.0" - loose-envify "^1.4.0" - prop-types "^15.7.2" - react-is "^16.9.0" + react-is "^16.13.1" react-refresh@^0.8.3: version "0.8.3" @@ -23196,7 +23135,7 @@ react-syntax-highlighter@^13.5.3: prismjs "^1.21.0" refractor "^3.1.0" -react-syntax-highlighter@^15.4.3: +react-syntax-highlighter@^15.4.3, react-syntax-highlighter@^15.4.4: version "15.4.4" resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz#dc9043f19e7bd063ff3ea78986d22a6eaa943b2a" integrity sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw== @@ -23609,22 +23548,17 @@ reduce-function-call@^1.0.1: dependencies: balanced-match "^1.0.0" -redux-immutable@3.1.0: - version "3.1.0" - resolved "https://registry.npmjs.org/redux-immutable/-/redux-immutable-3.1.0.tgz#cafbd686e0711261119b9c28960935dc47a49d0a" - integrity sha1-yvvWhuBxEmERm5wolgk13EeknQo= - dependencies: - immutable "^3.8.1" +redux-immutable@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3" + integrity sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM= -redux@=3.7.2: - version "3.7.2" - resolved "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" - integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== +redux@^4.0.0, redux@^4.1.0: + version "4.1.1" + resolved "https://registry.npmjs.org/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47" + integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw== dependencies: - lodash "^4.2.1" - lodash-es "^4.2.1" - loose-envify "^1.1.0" - symbol-observable "^1.0.3" + "@babel/runtime" "^7.9.2" redux@^4.0.4: version "4.0.5" @@ -25430,13 +25364,6 @@ stream-transform@^2.0.1: dependencies: mixme "^0.3.1" -stream@^0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz#7f5363f057f6592c5595f00bc80a27f5cec1f0ef" - integrity sha1-f1Nj8Ff2WSxVlfALyAon9c7B8O8= - dependencies: - emitter-component "^1.1.1" - streamsearch@0.1.2, streamsearch@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -25893,10 +25820,10 @@ svgo@^1.0.0, svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -swagger-client@^3.15.0: - version "3.15.0" - resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.15.0.tgz#ba9de5830a8baf40ec5a867e815fa4588d974fa3" - integrity sha512-8Ki4bVbT+bl8hGmy3vR89wktZhVLXKNUk8CoUvfwn7Rq45bZ15c9UQXVr4VU6hP3f9diWJGJs0bKZR9pPOq4ZA== +swagger-client@^3.16.1: + version "3.16.1" + resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.16.1.tgz#df86c9d407ab52c00cb356e714b0ec732bb3ad40" + integrity sha512-BcNRQzXHRGuXfhN0f80ptlr+bSaPvXwo8+gWbpmTnbKdAjcWOKAWwUx7rgGHjTKZh0qROr/GX9xOZIY8LrBuTg== dependencies: "@babel/runtime-corejs3" "^7.11.2" btoa "^1.2.1" @@ -25905,24 +25832,22 @@ swagger-client@^3.15.0: cross-fetch "^3.1.4" deep-extend "~0.6.0" fast-json-patch "^3.0.0-1" - form-data-encoder "^1.0.1" - formdata-node "^3.6.2" + form-data-encoder "^1.4.3" + formdata-node "^4.0.0" js-yaml "^4.1.0" - lodash "^4.17.19" + lodash "^4.17.21" qs "^6.9.4" querystring-browser "^1.0.4" traverse "~0.6.6" url "~0.11.0" -swagger-ui-react@^3.37.2: - version "3.52.0" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-3.52.0.tgz#edf85fed58d55677fc49807ff7d67e43e2d5bf44" - integrity sha512-JBw2Omif7i8pFIW8PHsoHZDXhn1LhiPmzxl6NRYGaoCxKDi+HQAQthvZ3piJbDQ8vS3EjtlVh7lkBn/RtrkPww== +swagger-ui-react@^4.0.0-rc.3: + version "4.0.0-rc.3" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-4.0.0-rc.3.tgz#393f424daf55272dd36737be654d978daf870d3d" + integrity sha512-Mo3+NvwLbbPy+ZJZoQkc3UudevSM03SHTZqwZOI7EY4KyMgqeet3xElnAUGZC94GqBZTstU0rf/znkgdaNo9qg== dependencies: "@babel/runtime-corejs3" "^7.14.7" "@braintree/sanitize-url" "^5.0.2" - "@kyleshockey/object-assign-deep" "^0.4.2" - "@kyleshockey/xml" "^1.0.2" base64-js "^1.5.1" classnames "^2.3.1" css.escape "1.5.1" @@ -25937,21 +25862,21 @@ swagger-ui-react@^3.37.2: prop-types "^15.7.2" randombytes "^2.1.0" react-copy-to-clipboard "5.0.3" - react-debounce-input "^3.2.3" + react-debounce-input "=3.2.4" react-immutable-proptypes "2.2.0" - react-immutable-pure-component "^1.1.1" - react-inspector "^2.3.0" - react-motion "^0.5.2" - react-redux "=4.4.10" - react-syntax-highlighter "^15.4.3" - redux "=3.7.2" - redux-immutable "3.1.0" + react-immutable-pure-component "^2.2.0" + react-inspector "^5.1.1" + react-redux "^7.2.4" + react-syntax-highlighter "^15.4.4" + redux "^4.1.0" + redux-immutable "^4.0.0" remarkable "^2.0.1" reselect "^4.0.0" serialize-error "^8.1.0" sha.js "^2.4.11" - swagger-client "^3.15.0" - url-parse "^1.5.1" + swagger-client "^3.16.1" + url-parse "^1.5.3" + xml "=1.0.1" xml-but-prettier "^1.0.1" zenscroll "^4.0.2" @@ -25962,7 +25887,7 @@ swap-case@^2.0.2: dependencies: tslib "^2.0.3" -symbol-observable@1.2.0, symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@1.2.0, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -27252,7 +27177,7 @@ url-parse-lax@^3.0.0: dependencies: prepend-http "^2.0.0" -url-parse@^1.4.3, url-parse@^1.5.1: +url-parse@^1.4.3, url-parse@^1.5.3: version "1.5.3" resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== @@ -27664,6 +27589,11 @@ web-namespaces@^1.0.0: resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +web-streams-polyfill@4.0.0-beta.1: + version "4.0.0-beta.1" + resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.1.tgz#3b19b9817374b7cee06d374ba7eeb3aeb80e8c95" + integrity sha512-3ux37gEX670UUphBF9AMCq8XM6iQ8Ac6A+DSRRjDoRBm1ufCkaCDdNVbaqq60PsEkdNlLKrGtv/YBP4EJXqNtQ== + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -28230,6 +28160,11 @@ xml2js@^0.4.11, xml2js@^0.4.19, xml2js@^0.4.23: sax ">=0.6.0" xmlbuilder "~11.0.0" +xml@=1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" + integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU= + xmlbuilder@^15.1.1: version "15.1.1" resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" From 3ea4a8f8320b58cb8e39229d72e8fce482fd550f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 12 Oct 2021 10:07:00 +0200 Subject: [PATCH 43/54] catalog-import: fix test + bump CodeSnippet change to minor Signed-off-by: Patrik Oldsberg --- .changeset/silent-candles-remember.md | 4 ++-- .../PreviewCatalogInfoComponent.test.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.changeset/silent-candles-remember.md b/.changeset/silent-candles-remember.md index 99061bbc1d..51700c4582 100644 --- a/.changeset/silent-candles-remember.md +++ b/.changeset/silent-candles-remember.md @@ -1,5 +1,5 @@ --- -'@backstage/core-components': patch +'@backstage/core-components': minor --- -The syntax highlighting library used by the `CodeSnippet` component is now lazy loaded. +The syntax highlighting library used by the `CodeSnippet` component is now lazy loaded. This most likely has no effect on existing code, but may break tests as the content of the `CodeSnippet` is now rendered asynchronously. diff --git a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx index f0844c265c..2618fab57a 100644 --- a/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx +++ b/plugins/catalog-import/src/components/StepPrepareCreatePullRequest/PreviewCatalogInfoComponent.test.tsx @@ -46,7 +46,7 @@ const entities: Entity[] = [ describe('', () => { it('renders without exploding', async () => { - const { getByText } = render( + const { getByText, findByText } = render( ', () => { ); const repositoryUrl = getByText('http://my-repository/a/catalog-info.yaml'); - const kindText = getByText('Kind_2'); + const kindText = await findByText('Kind_2'); expect(repositoryUrl).toBeInTheDocument(); expect(repositoryUrl).toBeVisible(); expect(kindText).toBeInTheDocument(); From f9001cefb691a0f832806db9c8dfde00c52afdb3 Mon Sep 17 00:00:00 2001 From: Dominik Henneke Date: Tue, 12 Oct 2021 09:58:36 +0200 Subject: [PATCH 44/54] Fix the dev page and add a new example Signed-off-by: Dominik Henneke --- plugins/api-docs/dev/index.tsx | 21 +++++++- .../dev/invalid-language-example-api.yaml | 48 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 plugins/api-docs/dev/invalid-language-example-api.yaml diff --git a/plugins/api-docs/dev/index.tsx b/plugins/api-docs/dev/index.tsx index e60647e7b9..633a285b98 100644 --- a/plugins/api-docs/dev/index.tsx +++ b/plugins/api-docs/dev/index.tsx @@ -15,29 +15,34 @@ */ import { ApiEntity, Entity } from '@backstage/catalog-model'; +import { Content, Header, Page } from '@backstage/core-components'; import { createDevApp } from '@backstage/dev-utils'; +import { CatalogEntityPage } from '@backstage/plugin-catalog'; import { catalogApiRef, EntityProvider } from '@backstage/plugin-catalog-react'; import React from 'react'; import { apiDocsConfigRef, + apiDocsPlugin, ApiExplorerPage, defaultDefinitionWidgets, EntityApiDefinitionCard, } from '../src'; import asyncapiApiEntity from './asyncapi-example-api.yaml'; import graphqlApiEntity from './graphql-example-api.yaml'; +import invalidLanguageApiEntity from './invalid-language-example-api.yaml'; import openapiApiEntity from './openapi-example-api.yaml'; import otherApiEntity from './other-example-api.yaml'; -import { Content, Header, Page } from '@backstage/core-components'; const mockEntities = [ openapiApiEntity, asyncapiApiEntity, graphqlApiEntity, + invalidLanguageApiEntity, otherApiEntity, ] as unknown as Entity[]; createDevApp() + .registerPlugin(apiDocsPlugin) .registerApi({ api: catalogApiRef, deps: {}, @@ -65,6 +70,7 @@ createDevApp() }; }, }) + .addPage({ element: }) .addPage({ title: 'API Explorer', element: }) .addPage({ title: 'OpenAPI', @@ -105,6 +111,19 @@ createDevApp() ), }) + .addPage({ + title: 'Invalid Language', + element: ( + +
+ + + + + + + ), + }) .addPage({ title: 'Other', element: ( diff --git a/plugins/api-docs/dev/invalid-language-example-api.yaml b/plugins/api-docs/dev/invalid-language-example-api.yaml new file mode 100644 index 0000000000..01b8ae9457 --- /dev/null +++ b/plugins/api-docs/dev/invalid-language-example-api.yaml @@ -0,0 +1,48 @@ +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: hello-world + description: Hello World example for gRPC +spec: + type: my-invalid-language + lifecycle: deprecated + owner: team-c + definition: | + // Copyright 2015 gRPC 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. + + syntax = "proto3"; + + option java_multiple_files = true; + option java_package = "io.grpc.examples.helloworld"; + option java_outer_classname = "HelloWorldProto"; + option objc_class_prefix = "HLW"; + + package helloworld; + + // The greeting service definition. + service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + + // The request message containing the user's name. + message HelloRequest { + string name = 1; + } + + // The response message containing the greetings + message HelloReply { + string message = 1; + } From 75bc878221fa77a8bcec678c90079a39c1dfdcb0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 11 Oct 2021 14:13:43 +0200 Subject: [PATCH 45/54] core-components,core-app-api,theme: avoid importing all of @mui/core Signed-off-by: Patrik Oldsberg --- .changeset/happy-hotels-explain.md | 7 +++++ .../core-app-api/src/app/AppThemeProvider.tsx | 3 ++- packages/core-components/.eslintrc.js | 17 ++++++++++++ packages/core-components/api-report.md | 20 +++++++------- .../components/AlertDisplay/AlertDisplay.tsx | 3 ++- .../src/components/Avatar/Avatar.tsx | 8 ++---- .../src/components/Button/Button.stories.tsx | 16 +++++------- .../src/components/Button/Button.tsx | 5 ++-- .../src/components/Chip/Chip.stories.tsx | 2 +- .../components/CodeSnippet/CodeSnippet.tsx | 2 +- .../CopyTextButton/CopyTextButton.tsx | 3 ++- .../components/CreateButton/CreateButton.tsx | 4 ++- .../src/components/Dialog/Dialog.stories.tsx | 16 +++++------- .../DismissableBanner.stories.tsx | 3 ++- .../DismissableBanner/DismissableBanner.tsx | 2 +- .../src/components/Drawer/Drawer.stories.tsx | 14 ++++------ .../EmptyState/EmptyState.stories.tsx | 2 +- .../components/EmptyState/EmptyState.test.tsx | 2 +- .../src/components/EmptyState/EmptyState.tsx | 4 ++- .../components/EmptyState/EmptyStateImage.tsx | 2 +- .../MissingAnnotationEmptyState.tsx | 4 ++- .../src/components/ErrorPanel/ErrorPanel.tsx | 5 +++- .../FeatureCalloutCircular.tsx | 4 ++- .../HeaderIconLinkRow/HeaderIconLinkRow.tsx | 2 +- .../HeaderIconLinkRow/IconLinkVertical.tsx | 3 ++- .../HorizontalScrollGrid.test.tsx | 2 +- .../HorizontalScrollGrid.tsx | 4 ++- .../src/components/Lifecycle/Lifecycle.tsx | 2 +- .../src/components/Link/Link.tsx | 5 ++-- .../MarkdownContent/MarkdownContent.tsx | 2 +- .../LoginRequestListItem.tsx | 15 +++++------ .../OAuthRequestDialog/OAuthRequestDialog.tsx | 17 +++++------- .../OverflowTooltip.stories.tsx | 2 +- .../OverflowTooltip/OverflowTooltip.tsx | 3 ++- .../src/components/Progress/Progress.tsx | 4 ++- .../src/components/ProgressBars/Gauge.tsx | 2 +- .../ProgressBars/GaugeCard.stories.tsx | 2 +- .../src/components/ProgressBars/GaugeCard.tsx | 2 +- .../components/ProgressBars/LinearGauge.tsx | 3 ++- .../ResponseErrorPanel/ResponseErrorPanel.tsx | 5 +++- .../src/components/Select/Select.tsx | 18 ++++++------- .../Select/static/ClosedDropdown.tsx | 3 ++- .../Select/static/OpenedDropdown.tsx | 3 ++- .../SimpleStepper/SimpleStepper.stories.tsx | 2 +- .../SimpleStepper/SimpleStepper.tsx | 2 +- .../SimpleStepper/SimpleStepperFooter.tsx | 3 ++- .../SimpleStepper/SimpleStepperStep.tsx | 12 ++++----- .../src/components/Status/Status.tsx | 2 +- .../StructuredMetadataTable/MetadataTable.tsx | 10 +++---- .../StructuredMetadataTable.stories.tsx | 2 +- .../StructuredMetadataTable.tsx | 7 ++++- .../SupportButton/SupportButton.tsx | 26 +++++++++---------- .../components/TabbedLayout/TabbedLayout.tsx | 2 +- .../src/components/TabbedLayout/types.ts | 2 +- .../src/components/Table/Filters.tsx | 3 ++- .../src/components/Table/SubvalueCell.tsx | 2 +- .../src/components/Table/Table.stories.tsx | 2 +- .../src/components/Table/Table.tsx | 10 +++---- .../src/components/Tabs/Tab.tsx | 3 ++- .../src/components/Tabs/TabBar.tsx | 3 ++- .../src/components/Tabs/TabIcon.tsx | 3 ++- .../src/components/Tabs/Tabs.tsx | 2 +- .../src/components/TrendLine/TrendLine.tsx | 2 +- .../WarningPanel/WarningPanel.stories.tsx | 4 ++- .../WarningPanel/WarningPanel.test.tsx | 2 +- .../components/WarningPanel/WarningPanel.tsx | 16 +++++------- .../src/layout/BottomLink/BottomLink.tsx | 4 ++- .../Breadcrumbs/Breadcrumbs.stories.tsx | 7 ++++- .../layout/Breadcrumbs/Breadcrumbs.test.tsx | 2 +- .../src/layout/Breadcrumbs/Breadcrumbs.tsx | 16 +++++------- .../src/layout/Content/Content.tsx | 2 +- .../layout/ContentHeader/ContentHeader.tsx | 3 ++- .../src/layout/ErrorPage/ErrorPage.tsx | 4 ++- .../src/layout/ErrorPage/MicDrop.tsx | 2 +- .../src/layout/Header/Header.tsx | 6 ++++- .../HeaderActionMenu/HeaderActionMenu.tsx | 15 +++++------ .../src/layout/HeaderLabel/HeaderLabel.tsx | 5 +++- .../src/layout/HeaderTabs/HeaderTabs.test.tsx | 3 ++- .../src/layout/HeaderTabs/HeaderTabs.tsx | 4 ++- .../HomepageTimer/HomepageTimer.test.tsx | 2 +- .../src/layout/InfoCard/InfoCard.stories.tsx | 4 ++- .../src/layout/InfoCard/InfoCard.tsx | 16 +++++------- .../src/layout/ItemCard/ItemCard.stories.tsx | 14 +++++----- .../src/layout/ItemCard/ItemCard.tsx | 15 +++++------ .../src/layout/ItemCard/ItemCardGrid.test.tsx | 2 +- .../src/layout/ItemCard/ItemCardGrid.tsx | 7 ++++- .../layout/ItemCard/ItemCardHeader.test.tsx | 3 ++- .../src/layout/ItemCard/ItemCardHeader.tsx | 8 ++---- .../src/layout/Page/Page.stories.tsx | 6 ++++- .../core-components/src/layout/Page/Page.tsx | 2 +- .../src/layout/Sidebar/Bar.tsx | 3 ++- .../src/layout/Sidebar/Intro.tsx | 5 +++- .../src/layout/Sidebar/Items.test.tsx | 2 +- .../src/layout/Sidebar/Items.tsx | 12 +++------ .../src/layout/Sidebar/Page.tsx | 2 +- .../src/layout/SignInPage/SignInPage.tsx | 4 ++- .../src/layout/SignInPage/auth0Provider.tsx | 4 ++- .../src/layout/SignInPage/commonProvider.tsx | 3 ++- .../src/layout/SignInPage/customProvider.tsx | 14 +++++----- .../src/layout/SignInPage/guestProvider.tsx | 3 ++- .../src/layout/SignInPage/styles.tsx | 3 ++- .../layout/TabbedCard/TabbedCard.stories.tsx | 3 ++- .../src/layout/TabbedCard/TabbedCard.tsx | 18 +++++-------- packages/theme/src/baseTheme.ts | 2 +- plugins/catalog-react/api-report.md | 4 +-- 105 files changed, 326 insertions(+), 277 deletions(-) create mode 100644 .changeset/happy-hotels-explain.md diff --git a/.changeset/happy-hotels-explain.md b/.changeset/happy-hotels-explain.md new file mode 100644 index 0000000000..ede18c3187 --- /dev/null +++ b/.changeset/happy-hotels-explain.md @@ -0,0 +1,7 @@ +--- +'@backstage/core-app-api': patch +'@backstage/core-components': patch +'@backstage/theme': patch +--- + +Internal refactor to avoid importing all of `@material-ui/core`. diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx index 4e283a1d00..4e3a8487a6 100644 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ b/packages/core-app-api/src/app/AppThemeProvider.tsx @@ -15,7 +15,8 @@ */ import React, { useMemo, useEffect, useState, PropsWithChildren } from 'react'; -import { ThemeProvider, CssBaseline } from '@material-ui/core'; +import { ThemeProvider } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; import { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api'; import { useObservable } from 'react-use'; diff --git a/packages/core-components/.eslintrc.js b/packages/core-components/.eslintrc.js index d592a653c8..65a93d3660 100644 --- a/packages/core-components/.eslintrc.js +++ b/packages/core-components/.eslintrc.js @@ -1,8 +1,25 @@ +const base = require('@backstage/cli/config/eslint'); +const [, baseRestrictedImports] = base.rules['no-restricted-imports']; + module.exports = { extends: [require.resolve('@backstage/cli/config/eslint')], rules: { // TODO: add prop types to JS and remove 'react/prop-types': 0, 'jest/expect-expect': 0, + 'no-restricted-imports': [ + 2, + { + ...baseRestrictedImports, + paths: [ + { + // Importing the entire MUI icons packages kills build performance as the list of icons is huge. + name: '@material-ui/core', + message: "Please import '@material-ui/core/...' instead.", + }, + ...baseRestrictedImports.paths, + ], + }, + ], }, }; diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 4a94135b4a..3d9559632f 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -8,9 +8,8 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; -import { ButtonProps as ButtonProps_2 } from '@material-ui/core'; -import { CardHeaderProps } from '@material-ui/core'; +import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; +import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; @@ -21,9 +20,10 @@ import { default as dagre_2 } from 'dagre'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; -import { LinearProgressProps } from '@material-ui/core'; -import { LinkProps as LinkProps_2 } from '@material-ui/core'; +import { LinearProgressProps } from '@material-ui/core/LinearProgress'; +import { LinkProps as LinkProps_2 } from '@material-ui/core/Link'; import { LinkProps as LinkProps_3 } from 'react-router-dom'; +import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import { MaterialTableProps } from '@material-table/core'; import { NavLinkProps } from 'react-router-dom'; import { Overrides } from '@material-ui/core/styles/overrides'; @@ -37,14 +37,14 @@ import { SessionApi } from '@backstage/core-plugin-api'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; import { SparklinesProps } from 'react-sparklines'; -import { StyledComponentProps } from '@material-ui/core'; +import { StyledComponentProps } from '@material-ui/core/styles'; import { StyleRules } from '@material-ui/styles'; import { StyleRules as StyleRules_2 } from '@material-ui/core/styles/withStyles'; -import { TabProps } from '@material-ui/core'; +import { TabProps } from '@material-ui/core/Tab'; import { TextTruncateProps } from 'react-text-truncate'; -import { Theme } from '@material-ui/core'; -import { TooltipProps } from '@material-ui/core'; -import { WithStyles } from '@material-ui/core'; +import { Theme } from '@material-ui/core/styles'; +import { TooltipProps } from '@material-ui/core/Tooltip'; +import { WithStyles } from '@material-ui/core/styles'; // Warning: (ae-missing-release-tag) "AlertDisplay" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx b/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx index f43dbd08dd..3a7ec392ee 100644 --- a/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx +++ b/packages/core-components/src/components/AlertDisplay/AlertDisplay.tsx @@ -15,7 +15,8 @@ */ import React, { useEffect, useState } from 'react'; -import { Snackbar, IconButton } from '@material-ui/core'; +import Snackbar from '@material-ui/core/Snackbar'; +import IconButton from '@material-ui/core/IconButton'; import CloseIcon from '@material-ui/icons/Close'; import { Alert } from '@material-ui/lab'; import { AlertMessage, useApi, alertApiRef } from '@backstage/core-plugin-api'; diff --git a/packages/core-components/src/components/Avatar/Avatar.tsx b/packages/core-components/src/components/Avatar/Avatar.tsx index 0156c58b92..32ce92b46b 100644 --- a/packages/core-components/src/components/Avatar/Avatar.tsx +++ b/packages/core-components/src/components/Avatar/Avatar.tsx @@ -14,12 +14,8 @@ * limitations under the License. */ import React, { CSSProperties } from 'react'; -import { - Avatar as MaterialAvatar, - createStyles, - makeStyles, - Theme, -} from '@material-ui/core'; +import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import MaterialAvatar from '@material-ui/core/Avatar'; import { extractInitials, stringToColor } from './utils'; export type AvatarClassKey = 'avatar'; diff --git a/packages/core-components/src/components/Button/Button.stories.tsx b/packages/core-components/src/components/Button/Button.stories.tsx index 3451a77765..32c3a98bcc 100644 --- a/packages/core-components/src/components/Button/Button.stories.tsx +++ b/packages/core-components/src/components/Button/Button.stories.tsx @@ -17,15 +17,13 @@ import React, { ComponentType } from 'react'; import { Button } from './Button'; import { useLocation } from 'react-router-dom'; import { createRouteRef, useRouteRef } from '@backstage/core-plugin-api'; -import { - Divider, - Link, - List, - ListItem, - ListItemText, - Typography, - Button as MaterialButton, -} from '@material-ui/core'; +import Divider from '@material-ui/core/Divider'; +import Link from '@material-ui/core/Link'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import MaterialButton from '@material-ui/core/Button'; import { wrapInTestApp } from '@backstage/test-utils'; const routeRef = createRouteRef({ diff --git a/packages/core-components/src/components/Button/Button.tsx b/packages/core-components/src/components/Button/Button.tsx index f2fc9f93f9..5c0ba3e956 100644 --- a/packages/core-components/src/components/Button/Button.tsx +++ b/packages/core-components/src/components/Button/Button.tsx @@ -14,10 +14,9 @@ * limitations under the License. */ -import { - Button as MaterialButton, +import MaterialButton, { ButtonProps as MaterialButtonProps, -} from '@material-ui/core'; +} from '@material-ui/core/Button'; import React from 'react'; import { Link, LinkProps } from '../Link'; diff --git a/packages/core-components/src/components/Chip/Chip.stories.tsx b/packages/core-components/src/components/Chip/Chip.stories.tsx index ff52a81c16..f5cdfd3f16 100644 --- a/packages/core-components/src/components/Chip/Chip.stories.tsx +++ b/packages/core-components/src/components/Chip/Chip.stories.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { Chip } from '@material-ui/core'; +import Chip from '@material-ui/core/Chip'; export default { title: 'Data Display/Chip', diff --git a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx index f8a7d2ff9e..3aa2f42984 100644 --- a/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx +++ b/packages/core-components/src/components/CodeSnippet/CodeSnippet.tsx @@ -15,7 +15,7 @@ */ import React, { lazy, Suspense } from 'react'; -import { useTheme } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { CopyTextButton } from '../CopyTextButton'; import { Progress } from '../Progress'; diff --git a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx index 2d2fb4006a..3986b8fa43 100644 --- a/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx +++ b/packages/core-components/src/components/CopyTextButton/CopyTextButton.tsx @@ -15,7 +15,8 @@ */ import { errorApiRef, useApi } from '@backstage/core-plugin-api'; -import { IconButton, Tooltip } from '@material-ui/core'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; import CopyIcon from '@material-ui/icons/FileCopy'; import React, { MouseEventHandler, useEffect, useState } from 'react'; import { useCopyToClipboard } from 'react-use'; diff --git a/packages/core-components/src/components/CreateButton/CreateButton.tsx b/packages/core-components/src/components/CreateButton/CreateButton.tsx index c6b9b7abed..074ee9fc9f 100644 --- a/packages/core-components/src/components/CreateButton/CreateButton.tsx +++ b/packages/core-components/src/components/CreateButton/CreateButton.tsx @@ -15,7 +15,9 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { Button, IconButton, useMediaQuery } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import React from 'react'; import { Link as RouterLink, LinkProps } from 'react-router-dom'; import AddCircleOutline from '@material-ui/icons/AddCircleOutline'; diff --git a/packages/core-components/src/components/Dialog/Dialog.stories.tsx b/packages/core-components/src/components/Dialog/Dialog.stories.tsx index b4c0a7da57..200124ce9d 100644 --- a/packages/core-components/src/components/Dialog/Dialog.stories.tsx +++ b/packages/core-components/src/components/Dialog/Dialog.stories.tsx @@ -14,15 +14,13 @@ * limitations under the License. */ -import { - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - IconButton, - Typography, -} from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import CloseIcon from '@material-ui/icons/Close'; import React, { useState } from 'react'; diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx index 583413eb35..a64514be61 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.stories.tsx @@ -16,7 +16,8 @@ import React from 'react'; import { DismissableBanner } from './DismissableBanner'; -import { Link, Typography } from '@material-ui/core'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; import { ApiProvider, ApiRegistry, WebStorage } from '@backstage/core-app-api'; import { ErrorApi, diff --git a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx index 3d229a5d54..be8e613b61 100644 --- a/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx +++ b/packages/core-components/src/components/DismissableBanner/DismissableBanner.tsx @@ -18,7 +18,7 @@ import React, { ReactNode, useState, useEffect } from 'react'; import { useApi, storageApiRef } from '@backstage/core-plugin-api'; import { useObservable } from 'react-use'; import classNames from 'classnames'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import Snackbar from '@material-ui/core/Snackbar'; import SnackbarContent from '@material-ui/core/SnackbarContent'; diff --git a/packages/core-components/src/components/Drawer/Drawer.stories.tsx b/packages/core-components/src/components/Drawer/Drawer.stories.tsx index 2538af855e..7e1c8a728f 100644 --- a/packages/core-components/src/components/Drawer/Drawer.stories.tsx +++ b/packages/core-components/src/components/Drawer/Drawer.stories.tsx @@ -15,15 +15,11 @@ */ import React, { useState } from 'react'; -import { - Drawer, - Button, - Typography, - makeStyles, - IconButton, - createStyles, - Theme, -} from '@material-ui/core'; +import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; +import IconButton from '@material-ui/core/IconButton'; import Close from '@material-ui/icons/Close'; export default { diff --git a/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx b/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx index ed4dbe2101..53314497fb 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.stories.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { EmptyState } from './EmptyState'; -import { Button } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; import { MissingAnnotationEmptyState } from './MissingAnnotationEmptyState'; export default { diff --git a/packages/core-components/src/components/EmptyState/EmptyState.test.tsx b/packages/core-components/src/components/EmptyState/EmptyState.test.tsx index 2f4e4e883d..620e8d4d31 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.test.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { EmptyState } from './EmptyState'; import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; -import { Button } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; describe('', () => { it('render EmptyState component with type annotaion is missing', async () => { diff --git a/packages/core-components/src/components/EmptyState/EmptyState.tsx b/packages/core-components/src/components/EmptyState/EmptyState.tsx index f3fc3b2399..a7298c4192 100644 --- a/packages/core-components/src/components/EmptyState/EmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyState.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { makeStyles, Typography, Grid } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; import { EmptyStateImage } from './EmptyStateImage'; export type EmptyStateClassKey = 'root' | 'action' | 'imageContainer'; diff --git a/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx b/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx index 012fb04e76..f82d8e6e57 100644 --- a/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx +++ b/packages/core-components/src/components/EmptyState/EmptyStateImage.tsx @@ -19,7 +19,7 @@ import missingAnnotation from './assets/missingAnnotation.svg'; import noInformation from './assets/noInformation.svg'; import createComponent from './assets/createComponent.svg'; import noBuild from './assets/noBuild.svg'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; type Props = { missing: 'field' | 'info' | 'content' | 'data'; diff --git a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx index 6f20f7c8e6..61131bad9b 100644 --- a/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx +++ b/packages/core-components/src/components/EmptyState/MissingAnnotationEmptyState.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Button, makeStyles, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; +import Typography from '@material-ui/core/Typography'; import { BackstageTheme } from '@backstage/theme'; import { Link } from '../Link'; import { EmptyState } from './EmptyState'; diff --git a/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx b/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx index 22f28a7a73..3ef35d6eaa 100644 --- a/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx +++ b/packages/core-components/src/components/ErrorPanel/ErrorPanel.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -import { List, ListItem, ListItemText, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; import React, { PropsWithChildren } from 'react'; import { CopyTextButton } from '../CopyTextButton'; import { WarningPanel } from '../WarningPanel'; diff --git a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx index 7c4b9736b7..3152d701c8 100644 --- a/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx +++ b/packages/core-components/src/components/FeatureDiscovery/FeatureCalloutCircular.tsx @@ -14,7 +14,9 @@ * limitations under the License. */ -import { ClickAwayListener, makeStyles, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import Typography from '@material-ui/core/Typography'; import React, { PropsWithChildren, useCallback, diff --git a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx index c4508eb9bc..4e83976a96 100644 --- a/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx +++ b/packages/core-components/src/components/HeaderIconLinkRow/HeaderIconLinkRow.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; import { IconLinkVertical, IconLinkVerticalProps } from './IconLinkVertical'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; export type HeaderIconLinkRowClassKey = 'links'; diff --git a/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx b/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx index 532ba2f959..421147753b 100644 --- a/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx +++ b/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; import classnames from 'classnames'; -import { makeStyles, Link } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; import LinkIcon from '@material-ui/icons/Link'; import { Link as RouterLink } from '../Link'; diff --git a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx index 080e48919b..a3ef93d9fa 100644 --- a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx +++ b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.test.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { fireEvent } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; import { HorizontalScrollGrid } from './HorizontalScrollGrid'; -import { Grid } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; describe('', () => { beforeEach(() => { diff --git a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx index 88b61d56bb..483d27d657 100644 --- a/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx +++ b/packages/core-components/src/components/HorizontalScrollGrid/HorizontalScrollGrid.tsx @@ -18,7 +18,9 @@ import React, { PropsWithChildren } from 'react'; import classNames from 'classnames'; import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'; import ChevronRightIcon from '@material-ui/icons/ChevronRight'; -import { Grid, IconButton, makeStyles, Theme } from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; +import IconButton from '@material-ui/core/IconButton'; const generateGradientStops = (themeType: 'dark' | 'light') => { // 97% corresponds to the theme.palette.background.default for the light theme diff --git a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx index 76e63854f5..f553129927 100644 --- a/packages/core-components/src/components/Lifecycle/Lifecycle.tsx +++ b/packages/core-components/src/components/Lifecycle/Lifecycle.tsx @@ -16,7 +16,7 @@ import React from 'react'; import CSS from 'csstype'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; type Props = CSS.Properties & { shorthand?: boolean; diff --git a/packages/core-components/src/components/Link/Link.tsx b/packages/core-components/src/components/Link/Link.tsx index d20773a8bb..8866150f86 100644 --- a/packages/core-components/src/components/Link/Link.tsx +++ b/packages/core-components/src/components/Link/Link.tsx @@ -15,10 +15,9 @@ */ import { useAnalytics } from '@backstage/core-plugin-api'; -import { - Link as MaterialLink, +import MaterialLink, { LinkProps as MaterialLinkProps, -} from '@material-ui/core'; +} from '@material-ui/core/Link'; import React, { ElementType } from 'react'; import { Link as RouterLink, diff --git a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx index 65be789bb4..eacd9adc8a 100644 --- a/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx +++ b/packages/core-components/src/components/MarkdownContent/MarkdownContent.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import ReactMarkdown from 'react-markdown'; import gfm from 'remark-gfm'; import React from 'react'; diff --git a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx index 7f7f51abba..b911a9a1d9 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx @@ -14,15 +14,12 @@ * limitations under the License. */ -import { - ListItem, - ListItemAvatar, - ListItemText, - makeStyles, - Typography, - Theme, - Button, -} from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemText from '@material-ui/core/ListItemText'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import React, { useState } from 'react'; import { PendingAuthRequest } from '@backstage/core-plugin-api'; diff --git a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx index 73e276fce9..5378fa6c15 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/OAuthRequestDialog.tsx @@ -14,16 +14,13 @@ * limitations under the License. */ -import { - Dialog, - DialogActions, - DialogContent, - DialogTitle, - List, - makeStyles, - Theme, - Button, -} from '@material-ui/core'; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import List from '@material-ui/core/List'; +import Button from '@material-ui/core/Button'; import React, { useMemo, useState } from 'react'; import { useObservable } from 'react-use'; import LoginRequestListItem from './LoginRequestListItem'; diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx index 0882a63e9c..a022b3b5c2 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.stories.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Box } from '@material-ui/core'; +import Box from '@material-ui/core/Box'; import React from 'react'; import { OverflowTooltip } from './OverflowTooltip'; diff --git a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx index a06930cdfa..fb21813f89 100644 --- a/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx +++ b/packages/core-components/src/components/OverflowTooltip/OverflowTooltip.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { makeStyles, Tooltip, TooltipProps } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Tooltip, { TooltipProps } from '@material-ui/core/Tooltip'; import React, { useState } from 'react'; import TextTruncate, { TextTruncateProps } from 'react-text-truncate'; diff --git a/packages/core-components/src/components/Progress/Progress.tsx b/packages/core-components/src/components/Progress/Progress.tsx index 00ad532b5e..f199c5640f 100644 --- a/packages/core-components/src/components/Progress/Progress.tsx +++ b/packages/core-components/src/components/Progress/Progress.tsx @@ -15,7 +15,9 @@ */ import React, { useState, useEffect, PropsWithChildren } from 'react'; -import { LinearProgress, LinearProgressProps } from '@material-ui/core'; +import LinearProgress, { + LinearProgressProps, +} from '@material-ui/core/LinearProgress'; export function Progress(props: PropsWithChildren) { const [isVisible, setIsVisible] = useState(false); diff --git a/packages/core-components/src/components/ProgressBars/Gauge.tsx b/packages/core-components/src/components/ProgressBars/Gauge.tsx index 2699f8a289..2f6973e4d8 100644 --- a/packages/core-components/src/components/ProgressBars/Gauge.tsx +++ b/packages/core-components/src/components/ProgressBars/Gauge.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles, useTheme } from '@material-ui/core'; +import { makeStyles, useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { Circle } from 'rc-progress'; import React from 'react'; diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx index da81100a99..ad3323f8d3 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.stories.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import { GaugeCard } from './GaugeCard'; -import { Grid } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; import { MemoryRouter } from 'react-router'; const linkInfo = { title: 'Go to XYZ Location', link: '#' }; diff --git a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx index aec63add11..5c5a825921 100644 --- a/packages/core-components/src/components/ProgressBars/GaugeCard.tsx +++ b/packages/core-components/src/components/ProgressBars/GaugeCard.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { InfoCard, InfoCardVariants } from '../../layout/InfoCard'; import { BottomLinkProps } from '../../layout/BottomLink'; import { Gauge } from './Gauge'; diff --git a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx index cf8c022852..afc93fd337 100644 --- a/packages/core-components/src/components/ProgressBars/LinearGauge.tsx +++ b/packages/core-components/src/components/ProgressBars/LinearGauge.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Tooltip, useTheme } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; +import Tooltip from '@material-ui/core/Tooltip'; // @ts-ignore import { Line } from 'rc-progress'; import { BackstageTheme } from '@backstage/theme'; diff --git a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx index 37143000a3..68d7360400 100644 --- a/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx +++ b/packages/core-components/src/components/ResponseErrorPanel/ResponseErrorPanel.tsx @@ -15,7 +15,10 @@ */ import { ResponseError } from '@backstage/errors'; -import { Divider, ListItem, ListItemText, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemText from '@material-ui/core/ListItemText'; import React from 'react'; import { CodeSnippet } from '../CodeSnippet'; import { CopyTextButton } from '../CopyTextButton'; diff --git a/packages/core-components/src/components/Select/Select.tsx b/packages/core-components/src/components/Select/Select.tsx index 23ed2cf62c..a0e3aba94b 100644 --- a/packages/core-components/src/components/Select/Select.tsx +++ b/packages/core-components/src/components/Select/Select.tsx @@ -14,16 +14,14 @@ * limitations under the License. */ -import { - Checkbox, - Chip, - ClickAwayListener, - FormControl, - InputBase, - MenuItem, - Select, - Typography, -} from '@material-ui/core'; +import Checkbox from '@material-ui/core/Checkbox'; +import Chip from '@material-ui/core/Chip'; +import ClickAwayListener from '@material-ui/core/ClickAwayListener'; +import FormControl from '@material-ui/core/FormControl'; +import InputBase from '@material-ui/core/InputBase'; +import MenuItem from '@material-ui/core/MenuItem'; +import Select from '@material-ui/core/Select'; +import Typography from '@material-ui/core/Typography'; import { createStyles, makeStyles, diff --git a/packages/core-components/src/components/Select/static/ClosedDropdown.tsx b/packages/core-components/src/components/Select/static/ClosedDropdown.tsx index 61a24abcef..eb93f5a354 100644 --- a/packages/core-components/src/components/Select/static/ClosedDropdown.tsx +++ b/packages/core-components/src/components/Select/static/ClosedDropdown.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import { SvgIcon, makeStyles, createStyles } from '@material-ui/core'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import SvgIcon from '@material-ui/core/SvgIcon'; export type ClosedDropdownClassKey = 'icon'; diff --git a/packages/core-components/src/components/Select/static/OpenedDropdown.tsx b/packages/core-components/src/components/Select/static/OpenedDropdown.tsx index eb86a34fe0..617288ded6 100644 --- a/packages/core-components/src/components/Select/static/OpenedDropdown.tsx +++ b/packages/core-components/src/components/Select/static/OpenedDropdown.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import { SvgIcon, makeStyles, createStyles } from '@material-ui/core'; +import { makeStyles, createStyles } from '@material-ui/core/styles'; +import SvgIcon from '@material-ui/core/SvgIcon'; export type OpenedDropdownClassKey = 'icon'; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx index 9d958f766d..aa6846bc8f 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TextField } from '@material-ui/core'; +import TextField from '@material-ui/core/TextField'; import React, { useState } from 'react'; import { SimpleStepper } from './SimpleStepper'; import { SimpleStepperStep } from './SimpleStepperStep'; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx index 9ffb63c085..4a1a4ab919 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepper.tsx @@ -20,7 +20,7 @@ import React, { useEffect, PropsWithChildren, } from 'react'; -import { Stepper as MuiStepper } from '@material-ui/core'; +import MuiStepper from '@material-ui/core/Stepper'; type InternalState = { stepperLength: number; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx index 270099a104..5f4bdbf0c6 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepperFooter.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React, { useContext, ReactNode, PropsWithChildren } from 'react'; -import { Button, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; import { StepActions } from './types'; import { VerticalStepperContext } from './SimpleStepper'; diff --git a/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx b/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx index 0696a1256a..bb46163105 100644 --- a/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx +++ b/packages/core-components/src/components/SimpleStepper/SimpleStepperStep.tsx @@ -14,13 +14,11 @@ * limitations under the License. */ import React, { PropsWithChildren } from 'react'; -import { - Step as MuiStep, - StepContent, - StepLabel, - Typography, - makeStyles, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import MuiStep from '@material-ui/core/Step'; +import StepContent from '@material-ui/core/StepContent'; +import StepLabel from '@material-ui/core/StepLabel'; +import Typography from '@material-ui/core/Typography'; import { SimpleStepperFooter } from './SimpleStepperFooter'; import { StepProps } from './types'; diff --git a/packages/core-components/src/components/Status/Status.tsx b/packages/core-components/src/components/Status/Status.tsx index a6215d6127..a855fe3ebe 100644 --- a/packages/core-components/src/components/Status/Status.tsx +++ b/packages/core-components/src/components/Status/Status.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import classNames from 'classnames'; import React, { PropsWithChildren } from 'react'; diff --git a/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx index 3c869f88c3..2f5dc0b6b5 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/MetadataTable.tsx @@ -16,15 +16,15 @@ import React from 'react'; import { - Table, - TableBody, - TableCell, - TableRow, withStyles, createStyles, WithStyles, Theme, -} from '@material-ui/core'; +} from '@material-ui/core/styles'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableRow from '@material-ui/core/TableRow'; export type MetadataTableTitleCellClassKey = 'root'; diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx index 458c431b27..f48e959073 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.stories.tsx @@ -15,7 +15,7 @@ */ import React, { PropsWithChildren } from 'react'; import { InfoCard } from '../../layout/InfoCard'; -import { Grid } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; import { StructuredMetadataTable } from './StructuredMetadataTable'; const cardContentStyle = { heightX: 200, width: 500 }; diff --git a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx index abaa662159..9ef7890820 100644 --- a/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx +++ b/packages/core-components/src/components/StructuredMetadataTable/StructuredMetadataTable.tsx @@ -15,7 +15,12 @@ */ import React, { Fragment, ReactElement } from 'react'; -import { withStyles, createStyles, WithStyles, Theme } from '@material-ui/core'; +import { + withStyles, + createStyles, + WithStyles, + Theme, +} from '@material-ui/core/styles'; import startCase from 'lodash/startCase'; import { diff --git a/packages/core-components/src/components/SupportButton/SupportButton.tsx b/packages/core-components/src/components/SupportButton/SupportButton.tsx index bee2928314..841bfe9429 100644 --- a/packages/core-components/src/components/SupportButton/SupportButton.tsx +++ b/packages/core-components/src/components/SupportButton/SupportButton.tsx @@ -16,20 +16,18 @@ import { useApp } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { - Box, - Button, - DialogActions, - IconButton, - List, - ListItem, - ListItemIcon, - ListItemText, - makeStyles, - Popover, - Typography, - useMediaQuery, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; +import Box from '@material-ui/core/Box'; +import Button from '@material-ui/core/Button'; +import DialogActions from '@material-ui/core/DialogActions'; +import IconButton from '@material-ui/core/IconButton'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText from '@material-ui/core/ListItemText'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; import React, { MouseEventHandler, useState } from 'react'; import { SupportItem, SupportItemLink, useSupportConfig } from '../../hooks'; import { HelpIcon } from '../../icons'; diff --git a/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx b/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx index 03f012b78f..26e70e33b5 100644 --- a/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx +++ b/packages/core-components/src/components/TabbedLayout/TabbedLayout.tsx @@ -23,7 +23,7 @@ import React, { ReactNode, } from 'react'; import { RoutedTabs } from './RoutedTabs'; -import { TabProps } from '@material-ui/core'; +import { TabProps } from '@material-ui/core/Tab'; type SubRoute = { path: string; diff --git a/packages/core-components/src/components/TabbedLayout/types.ts b/packages/core-components/src/components/TabbedLayout/types.ts index b70ab3da0a..59e26305bd 100644 --- a/packages/core-components/src/components/TabbedLayout/types.ts +++ b/packages/core-components/src/components/TabbedLayout/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TabProps } from '@material-ui/core'; +import { TabProps } from '@material-ui/core/Tab'; import * as React from 'react'; export type SubRoute = { diff --git a/packages/core-components/src/components/Table/Filters.tsx b/packages/core-components/src/components/Table/Filters.tsx index 7498138751..0f4143d442 100644 --- a/packages/core-components/src/components/Table/Filters.tsx +++ b/packages/core-components/src/components/Table/Filters.tsx @@ -16,7 +16,8 @@ import React, { useEffect, useState } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { Button, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Button from '@material-ui/core/Button'; import { Select } from '../Select'; import { SelectProps } from '../Select/Select'; diff --git a/packages/core-components/src/components/Table/SubvalueCell.tsx b/packages/core-components/src/components/Table/SubvalueCell.tsx index 43fb54fca8..2564e8e4eb 100644 --- a/packages/core-components/src/components/Table/SubvalueCell.tsx +++ b/packages/core-components/src/components/Table/SubvalueCell.tsx @@ -16,7 +16,7 @@ import React from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; export type SubvalueCellClassKey = 'value' | 'subvalue'; diff --git a/packages/core-components/src/components/Table/Table.stories.tsx b/packages/core-components/src/components/Table/Table.stories.tsx index 27fd33f32c..7785764b22 100644 --- a/packages/core-components/src/components/Table/Table.stories.tsx +++ b/packages/core-components/src/components/Table/Table.stories.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import React from 'react'; import { Link } from '../Link'; import { SubvalueCell, Table, TableColumn } from '.'; diff --git a/packages/core-components/src/components/Table/Table.tsx b/packages/core-components/src/components/Table/Table.tsx index 61e9cc7969..f61811938e 100644 --- a/packages/core-components/src/components/Table/Table.tsx +++ b/packages/core-components/src/components/Table/Table.tsx @@ -15,13 +15,9 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { - IconButton, - makeStyles, - Typography, - useTheme, - withStyles, -} from '@material-ui/core'; +import { makeStyles, useTheme, withStyles } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; +import Typography from '@material-ui/core/Typography'; // Material-table is not using the standard icons available in in material-ui. https://github.com/mbrn/material-table/issues/51 import AddBox from '@material-ui/icons/AddBox'; import ArrowUpward from '@material-ui/icons/ArrowUpward'; diff --git a/packages/core-components/src/components/Tabs/Tab.tsx b/packages/core-components/src/components/Tabs/Tab.tsx index 7b284940f1..571640d970 100644 --- a/packages/core-components/src/components/Tabs/Tab.tsx +++ b/packages/core-components/src/components/Tabs/Tab.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Tab, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Tab from '@material-ui/core/Tab'; import { BackstageTheme } from '@backstage/theme'; interface StyledTabProps { diff --git a/packages/core-components/src/components/Tabs/TabBar.tsx b/packages/core-components/src/components/Tabs/TabBar.tsx index d0febebc6a..d2f0250fd4 100644 --- a/packages/core-components/src/components/Tabs/TabBar.tsx +++ b/packages/core-components/src/components/Tabs/TabBar.tsx @@ -15,7 +15,8 @@ */ import React, { PropsWithChildren } from 'react'; -import { Tabs, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Tabs from '@material-ui/core/Tabs'; import { BackstageTheme } from '@backstage/theme'; interface StyledTabsProps { diff --git a/packages/core-components/src/components/Tabs/TabIcon.tsx b/packages/core-components/src/components/Tabs/TabIcon.tsx index 37b38217a3..9743469aa7 100644 --- a/packages/core-components/src/components/Tabs/TabIcon.tsx +++ b/packages/core-components/src/components/Tabs/TabIcon.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { IconButton, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import IconButton from '@material-ui/core/IconButton'; import { BackstageTheme } from '@backstage/theme'; interface StyledIconProps { diff --git a/packages/core-components/src/components/Tabs/Tabs.tsx b/packages/core-components/src/components/Tabs/Tabs.tsx index d60f3ff639..31bf1d2027 100644 --- a/packages/core-components/src/components/Tabs/Tabs.tsx +++ b/packages/core-components/src/components/Tabs/Tabs.tsx @@ -16,7 +16,7 @@ import React, { useRef, useEffect, MutableRefObject, useState } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { AppBar } from '@material-ui/core'; +import AppBar from '@material-ui/core/AppBar'; import { makeStyles } from '@material-ui/core/styles'; import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore'; import NavigateNextIcon from '@material-ui/icons/NavigateNext'; diff --git a/packages/core-components/src/components/TrendLine/TrendLine.tsx b/packages/core-components/src/components/TrendLine/TrendLine.tsx index cb44d2b36b..b59d748c2c 100644 --- a/packages/core-components/src/components/TrendLine/TrendLine.tsx +++ b/packages/core-components/src/components/TrendLine/TrendLine.tsx @@ -21,7 +21,7 @@ import { SparklinesLineProps, SparklinesProps, } from 'react-sparklines'; -import { useTheme } from '@material-ui/core'; +import { useTheme } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; function color(data: number[], theme: BackstageTheme): string | undefined { diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx index bab831e905..838cb02a76 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.stories.tsx @@ -16,7 +16,9 @@ import React from 'react'; import { WarningPanel } from './WarningPanel'; -import { Button, Link, Typography } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; export default { title: 'Feedback/Warning Panel', diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx index 4ae6423333..25cf642c28 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.test.tsx @@ -17,7 +17,7 @@ import React from 'react'; import { fireEvent, screen } from '@testing-library/react'; import { renderInTestApp } from '@backstage/test-utils'; -import { Typography } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; import { WarningPanel, WarningProps } from './WarningPanel'; diff --git a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx index bf845617c9..0cf250497b 100644 --- a/packages/core-components/src/components/WarningPanel/WarningPanel.tsx +++ b/packages/core-components/src/components/WarningPanel/WarningPanel.tsx @@ -14,16 +14,12 @@ * limitations under the License. */ import { BackstageTheme } from '@backstage/theme'; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Grid, - makeStyles, - Typography, - darken, - lighten, -} from '@material-ui/core'; +import { makeStyles, darken, lighten } from '@material-ui/core/styles'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; import ErrorOutline from '@material-ui/icons/ErrorOutline'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import React from 'react'; diff --git a/packages/core-components/src/layout/BottomLink/BottomLink.tsx b/packages/core-components/src/layout/BottomLink/BottomLink.tsx index cb168600e5..e097724385 100644 --- a/packages/core-components/src/layout/BottomLink/BottomLink.tsx +++ b/packages/core-components/src/layout/BottomLink/BottomLink.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Divider, Typography, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Divider from '@material-ui/core/Divider'; +import Typography from '@material-ui/core/Typography'; import ArrowIcon from '@material-ui/icons/ArrowForward'; import { BackstageTheme } from '@backstage/theme'; import Box from '@material-ui/core/Box'; diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx index 9716471d9f..12bd757aa3 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.stories.tsx @@ -13,7 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Box, List, ListItem, Popover, Typography } from '@material-ui/core'; + +import Box from '@material-ui/core/Box'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; import ExpandLessIcon from '@material-ui/icons/ExpandLess'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import React, { Fragment } from 'react'; diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx index 4c8d707501..12125bafc9 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.test.tsx @@ -15,7 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { Typography } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { Link } from '../../components/Link'; diff --git a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx index 26304f9e2b..4423c5c0b7 100644 --- a/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/core-components/src/layout/Breadcrumbs/Breadcrumbs.tsx @@ -14,15 +14,13 @@ * limitations under the License. */ -import { - Box, - Breadcrumbs as MaterialBreadcrumbs, - List, - ListItem, - Popover, - Typography, - withStyles, -} from '@material-ui/core'; +import { withStyles } from '@material-ui/core/styles'; +import Box from '@material-ui/core/Box'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import Popover from '@material-ui/core/Popover'; +import Typography from '@material-ui/core/Typography'; +import MaterialBreadcrumbs from '@material-ui/core/Breadcrumbs'; import React, { ComponentProps, Fragment } from 'react'; type Props = ComponentProps; diff --git a/packages/core-components/src/layout/Content/Content.tsx b/packages/core-components/src/layout/Content/Content.tsx index 588c9fd630..122eb07e2a 100644 --- a/packages/core-components/src/layout/Content/Content.tsx +++ b/packages/core-components/src/layout/Content/Content.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import classNames from 'classnames'; -import { Theme, makeStyles } from '@material-ui/core'; +import { Theme, makeStyles } from '@material-ui/core/styles'; export type BackstageContentClassKey = 'root' | 'stretch' | 'noPadding'; diff --git a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx index c3faec64a2..b4a80ac4b1 100644 --- a/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx +++ b/packages/core-components/src/layout/ContentHeader/ContentHeader.tsx @@ -19,7 +19,8 @@ */ import React, { PropsWithChildren, ReactNode } from 'react'; -import { Typography, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import { Helmet } from 'react-helmet'; export type ContentHeaderClassKey = diff --git a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx index 79aaaebfc6..46257f85bb 100644 --- a/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx +++ b/packages/core-components/src/layout/ErrorPage/ErrorPage.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Typography, Link, Grid } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import Link from '@material-ui/core/Link'; +import Grid from '@material-ui/core/Grid'; import { makeStyles } from '@material-ui/core/styles'; import { BackstageTheme } from '@backstage/theme'; import { MicDrop } from './MicDrop'; diff --git a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx index 5500994e4f..efa101b23b 100644 --- a/packages/core-components/src/layout/ErrorPage/MicDrop.tsx +++ b/packages/core-components/src/layout/ErrorPage/MicDrop.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import MicDropSvgUrl from './mic-drop.svg'; const useStyles = makeStyles( diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 535e3c4ed8..aed9e9f8e4 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -16,7 +16,11 @@ import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { Box, Grid, makeStyles, Tooltip, Typography } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Box from '@material-ui/core/Box'; +import Grid from '@material-ui/core/Grid'; +import Tooltip from '@material-ui/core/Tooltip'; +import Typography from '@material-ui/core/Typography'; import React, { CSSProperties, PropsWithChildren, ReactNode } from 'react'; import { Helmet } from 'react-helmet'; import { Link } from '../../components/Link'; diff --git a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx index 5243a65498..d8dc398ce3 100644 --- a/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx +++ b/packages/core-components/src/layout/HeaderActionMenu/HeaderActionMenu.tsx @@ -15,15 +15,14 @@ */ import React, { Fragment, ReactElement, ComponentType } from 'react'; -import { - IconButton, - List, - ListItem, - ListItemIcon, - ListItemText, - Popover, +import IconButton from '@material-ui/core/IconButton'; +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemText, { ListItemTextProps, -} from '@material-ui/core'; +} from '@material-ui/core/ListItemText'; +import Popover from '@material-ui/core/Popover'; import { VerticalMenuIcon } from './VerticalMenuIcon'; type ActionItemProps = { diff --git a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx index 5071971f48..3b9bde0f62 100644 --- a/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx +++ b/packages/core-components/src/layout/HeaderLabel/HeaderLabel.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -import { Link, makeStyles, Typography, Grid } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; import React from 'react'; export type HeaderLabelClassKey = 'root' | 'label' | 'value'; diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx index e6762de90e..2501785701 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.test.tsx @@ -17,7 +17,8 @@ import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; import { HeaderTabs } from '.'; -import { Badge, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; const mockTabs = [ { id: 'overview', label: 'Overview' }, diff --git a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx index b34af3eb83..dd740d1c53 100644 --- a/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx +++ b/packages/core-components/src/layout/HeaderTabs/HeaderTabs.tsx @@ -18,7 +18,9 @@ // This is just a temporary solution to implementing tabs for now import React, { useState, useEffect } from 'react'; -import { makeStyles, Tabs, Tab as TabUI, TabProps } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import TabUI, { TabProps } from '@material-ui/core/Tab'; +import Tabs from '@material-ui/core/Tabs'; export type HeaderTabsClassKey = | 'tabsWrapper' diff --git a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx index eda877884e..1d4bf6954a 100644 --- a/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx +++ b/packages/core-components/src/layout/HomepageTimer/HomepageTimer.test.tsx @@ -18,7 +18,7 @@ import { renderWithEffects } from '@backstage/test-utils'; import { HomepageTimer } from './HomepageTimer'; import React from 'react'; import { lightTheme } from '@backstage/theme'; -import { ThemeProvider } from '@material-ui/core'; +import { ThemeProvider } from '@material-ui/core/styles'; import { ApiProvider, ApiRegistry, diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx index cf012a129a..8dd8272f5a 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.stories.tsx @@ -13,7 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid, Typography } from '@material-ui/core'; + +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; import React, { PropsWithChildren } from 'react'; import { MemoryRouter } from 'react-router'; import { InfoCard } from './InfoCard'; diff --git a/packages/core-components/src/layout/InfoCard/InfoCard.tsx b/packages/core-components/src/layout/InfoCard/InfoCard.tsx index a3094b792e..0b9c4c7dcf 100644 --- a/packages/core-components/src/layout/InfoCard/InfoCard.tsx +++ b/packages/core-components/src/layout/InfoCard/InfoCard.tsx @@ -15,16 +15,12 @@ */ import React, { ReactNode } from 'react'; -import { - Card, - CardActions, - CardContent, - CardHeader, - CardHeaderProps, - Divider, - withStyles, - makeStyles, -} from '@material-ui/core'; +import { withStyles, makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader, { CardHeaderProps } from '@material-ui/core/CardHeader'; +import Divider from '@material-ui/core/Divider'; import classNames from 'classnames'; import { ErrorBoundary, ErrorBoundaryProps } from '../ErrorBoundary'; import { BottomLink, BottomLinkProps } from '../BottomLink'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx index 7cd67ff00d..1d336fc5f5 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.stories.tsx @@ -14,14 +14,12 @@ * limitations under the License. */ -import { - Card, - CardActions, - CardContent, - CardMedia, - makeStyles, - Typography, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Typography from '@material-ui/core/Typography'; import React from 'react'; import { MemoryRouter } from 'react-router'; import { Button } from '../../components'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCard.tsx b/packages/core-components/src/layout/ItemCard/ItemCard.tsx index 81a88d2c09..82426c14e1 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCard.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCard.tsx @@ -13,14 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { - Box, - Card, - CardActions, - CardContent, - CardMedia, - Chip, -} from '@material-ui/core'; + +import Box from '@material-ui/core/Box'; +import Card from '@material-ui/core/Card'; +import CardActions from '@material-ui/core/CardActions'; +import CardContent from '@material-ui/core/CardContent'; +import CardMedia from '@material-ui/core/CardMedia'; +import Chip from '@material-ui/core/Chip'; import React, { ReactNode } from 'react'; import { Button } from '../../components'; import { ItemCardHeader } from './ItemCardHeader'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx index 30fdb415e5..f8759ffd17 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.test.tsx @@ -15,7 +15,7 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { Card } from '@material-ui/core'; +import Card from '@material-ui/core/Card'; import { screen } from '@testing-library/react'; import React from 'react'; import { ItemCardGrid } from './ItemCardGrid'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx index 739398252e..b4fe504c31 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardGrid.tsx @@ -14,7 +14,12 @@ * limitations under the License. */ -import { createStyles, makeStyles, Theme, WithStyles } from '@material-ui/core'; +import { + createStyles, + makeStyles, + Theme, + WithStyles, +} from '@material-ui/core/styles'; import React from 'react'; export type ItemCardGridClassKey = 'root'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx index 41b150a900..a401dfb4a5 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.test.tsx @@ -15,7 +15,8 @@ */ import { renderInTestApp } from '@backstage/test-utils'; -import { Card, CardMedia } from '@material-ui/core'; +import Card from '@material-ui/core/Card'; +import CardMedia from '@material-ui/core/CardMedia'; import { screen } from '@testing-library/react'; import React from 'react'; import { ItemCardHeader } from './ItemCardHeader'; diff --git a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx index 7ad6732e02..c604f24e3f 100644 --- a/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx +++ b/packages/core-components/src/layout/ItemCard/ItemCardHeader.tsx @@ -14,12 +14,8 @@ * limitations under the License. */ -import { - createStyles, - makeStyles, - Typography, - WithStyles, -} from '@material-ui/core'; +import { createStyles, makeStyles, WithStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import React from 'react'; import { BackstageTheme } from '@backstage/theme'; diff --git a/packages/core-components/src/layout/Page/Page.stories.tsx b/packages/core-components/src/layout/Page/Page.stories.tsx index 3ad9ecfe72..7386d198a3 100644 --- a/packages/core-components/src/layout/Page/Page.stories.tsx +++ b/packages/core-components/src/layout/Page/Page.stories.tsx @@ -14,7 +14,11 @@ * limitations under the License. */ -import { Box, Chip, Grid, Link, Typography } from '@material-ui/core'; +import Box from '@material-ui/core/Box'; +import Chip from '@material-ui/core/Chip'; +import Grid from '@material-ui/core/Grid'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; import React, { useState } from 'react'; import { MemoryRouter } from 'react-router'; import { createApp } from '@backstage/core-app-api'; diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 37d6c663f2..57653d23b5 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, ThemeProvider } from '@material-ui/core'; +import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; export type PageClassKey = 'root'; diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 750ec6bbdc..e6fa3820ad 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ -import { makeStyles, useMediaQuery } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import clsx from 'clsx'; import React, { useRef, useState, useContext, PropsWithChildren } from 'react'; import { sidebarConfig, SidebarContext } from './config'; diff --git a/packages/core-components/src/layout/Sidebar/Intro.tsx b/packages/core-components/src/layout/Sidebar/Intro.tsx index 70b952588d..8086898e64 100644 --- a/packages/core-components/src/layout/Sidebar/Intro.tsx +++ b/packages/core-components/src/layout/Sidebar/Intro.tsx @@ -16,7 +16,10 @@ import React, { useContext, useState } from 'react'; import { useLocalStorage } from 'react-use'; -import { Link, Typography, makeStyles, Collapse } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Link from '@material-ui/core/Link'; +import Typography from '@material-ui/core/Typography'; +import Collapse from '@material-ui/core/Collapse'; import CloseIcon from '@material-ui/icons/Close'; import { BackstageTheme } from '@backstage/theme'; import { diff --git a/packages/core-components/src/layout/Sidebar/Items.test.tsx b/packages/core-components/src/layout/Sidebar/Items.test.tsx index 89d2a2c294..48fcd98666 100644 --- a/packages/core-components/src/layout/Sidebar/Items.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.test.tsx @@ -23,7 +23,7 @@ import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import { Sidebar } from './Bar'; import { SidebarItem } from './Items'; import { renderHook } from '@testing-library/react-hooks'; -import { hexToRgb, makeStyles } from '@material-ui/core'; +import { hexToRgb, makeStyles } from '@material-ui/core/styles'; const useStyles = makeStyles({ spotlight: { diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 8bd80b6825..8a66e182f6 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -16,14 +16,10 @@ import { IconComponent } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { - Badge, - makeStyles, - styled, - TextField, - Theme, - Typography, -} from '@material-ui/core'; +import { makeStyles, styled, Theme } from '@material-ui/core/styles'; +import Badge from '@material-ui/core/Badge'; +import TextField from '@material-ui/core/TextField'; +import Typography from '@material-ui/core/Typography'; import { CreateCSSProperties } from '@material-ui/core/styles/withStyles'; import SearchIcon from '@material-ui/icons/Search'; import clsx from 'clsx'; diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index c375112321..cc50c5af6b 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import React, { createContext, PropsWithChildren, diff --git a/packages/core-components/src/layout/SignInPage/SignInPage.tsx b/packages/core-components/src/layout/SignInPage/SignInPage.tsx index 910fc6a7bf..bd4fe37480 100644 --- a/packages/core-components/src/layout/SignInPage/SignInPage.tsx +++ b/packages/core-components/src/layout/SignInPage/SignInPage.tsx @@ -20,7 +20,9 @@ import { SignInPageProps, useApi, } from '@backstage/core-plugin-api'; -import { Button, Grid, Typography } from '@material-ui/core'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; import React, { useState } from 'react'; import { useMount } from 'react-use'; import { Progress } from '../../components/Progress'; diff --git a/packages/core-components/src/layout/SignInPage/auth0Provider.tsx b/packages/core-components/src/layout/SignInPage/auth0Provider.tsx index 78043cb3fb..19836827c9 100644 --- a/packages/core-components/src/layout/SignInPage/auth0Provider.tsx +++ b/packages/core-components/src/layout/SignInPage/auth0Provider.tsx @@ -15,7 +15,9 @@ */ import React from 'react'; -import { Grid, Typography, Button } from '@material-ui/core'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; import { diff --git a/packages/core-components/src/layout/SignInPage/commonProvider.tsx b/packages/core-components/src/layout/SignInPage/commonProvider.tsx index 3160bd171b..8df5faf34e 100644 --- a/packages/core-components/src/layout/SignInPage/commonProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/commonProvider.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Typography, Button } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { ProviderComponent, diff --git a/packages/core-components/src/layout/SignInPage/customProvider.tsx b/packages/core-components/src/layout/SignInPage/customProvider.tsx index 46fed6b5ae..3be375ed3d 100644 --- a/packages/core-components/src/layout/SignInPage/customProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/customProvider.tsx @@ -16,14 +16,12 @@ import React from 'react'; import { useForm, UseFormRegisterReturn } from 'react-hook-form'; -import { - Typography, - Button, - FormControl, - TextField, - FormHelperText, - makeStyles, -} from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; +import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; +import FormHelperText from '@material-ui/core/FormHelperText'; import isEmpty from 'lodash/isEmpty'; import { InfoCard } from '../InfoCard/InfoCard'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; diff --git a/packages/core-components/src/layout/SignInPage/guestProvider.tsx b/packages/core-components/src/layout/SignInPage/guestProvider.tsx index 8673c17ffa..e91d9cbc75 100644 --- a/packages/core-components/src/layout/SignInPage/guestProvider.tsx +++ b/packages/core-components/src/layout/SignInPage/guestProvider.tsx @@ -15,7 +15,8 @@ */ import React from 'react'; -import { Typography, Button } from '@material-ui/core'; +import Typography from '@material-ui/core/Typography'; +import Button from '@material-ui/core/Button'; import { InfoCard } from '../InfoCard/InfoCard'; import { GridItem } from './styles'; import { ProviderComponent, ProviderLoader, SignInProvider } from './types'; diff --git a/packages/core-components/src/layout/SignInPage/styles.tsx b/packages/core-components/src/layout/SignInPage/styles.tsx index 55db36229b..6cf5ded226 100644 --- a/packages/core-components/src/layout/SignInPage/styles.tsx +++ b/packages/core-components/src/layout/SignInPage/styles.tsx @@ -14,7 +14,8 @@ * limitations under the License. */ import React from 'react'; -import { Grid, makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; export type SignInPageClassKey = 'container' | 'item'; diff --git a/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx b/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx index 3214d26653..77fff64fb5 100644 --- a/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx +++ b/packages/core-components/src/layout/TabbedCard/TabbedCard.stories.tsx @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid } from '@material-ui/core'; + +import Grid from '@material-ui/core/Grid'; import React, { PropsWithChildren, useState } from 'react'; import { MemoryRouter } from 'react-router'; import { CardTab, TabbedCard } from './TabbedCard'; diff --git a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx index e9905663b2..f9ec755e00 100644 --- a/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx +++ b/packages/core-components/src/layout/TabbedCard/TabbedCard.tsx @@ -20,17 +20,13 @@ import React, { ReactNode, PropsWithChildren, } from 'react'; -import { - Card, - CardContent, - CardHeader, - Divider, - withStyles, - makeStyles, - Tabs, - Tab, - TabProps, -} from '@material-ui/core'; +import { withStyles, makeStyles } from '@material-ui/core/styles'; +import Card from '@material-ui/core/Card'; +import CardContent from '@material-ui/core/CardContent'; +import CardHeader from '@material-ui/core/CardHeader'; +import Divider from '@material-ui/core/Divider'; +import Tabs from '@material-ui/core/Tabs'; +import Tab, { TabProps } from '@material-ui/core/Tab'; import { BottomLink, BottomLinkProps } from '../BottomLink'; import { ErrorBoundary, ErrorBoundaryProps } from '../ErrorBoundary'; diff --git a/packages/theme/src/baseTheme.ts b/packages/theme/src/baseTheme.ts index b2d964c4db..4a45516a55 100644 --- a/packages/theme/src/baseTheme.ts +++ b/packages/theme/src/baseTheme.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createTheme as createMuiTheme } from '@material-ui/core'; +import { createTheme as createMuiTheme } from '@material-ui/core/styles'; import { darken, lighten } from '@material-ui/core/styles/colorManipulator'; import { Overrides } from '@material-ui/core/styles/overrides'; diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index 7f50e87e03..ce7282276e 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -257,13 +257,14 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< | 'children' | 'key' | 'id' + | 'className' | 'classes' + | 'innerRef' | 'defaultChecked' | 'defaultValue' | 'suppressContentEditableWarning' | 'suppressHydrationWarning' | 'accessKey' - | 'className' | 'contentEditable' | 'contextMenu' | 'draggable' @@ -504,7 +505,6 @@ export const EntityRefLink: React_2.ForwardRefExoticComponent< | 'onTransitionEndCapture' | 'component' | 'variant' - | 'innerRef' | 'download' | 'href' | 'hrefLang' From a6767ed28942e7f4f2d1cea55f242e3fad7a5a01 Mon Sep 17 00:00:00 2001 From: James Turley Date: Tue, 12 Oct 2021 11:19:32 +0100 Subject: [PATCH 46/54] Add GoCardless to ADOPTERS.md --- ADOPTERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADOPTERS.md b/ADOPTERS.md index 5fafdb5da4..21d1419a45 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -55,3 +55,4 @@ | [Santagostino](https://santagostino.it) | [@santagostino](https://github.com/santagostino) | Developer portal, gateway to our infrastructure, documentation, service catalog and internal tooling. | | [Peak](https://peak.ai) | [Luke Beamish](https://github.com/lukebeamish-peak) | Developer portal for all internal engineers to access documentation and tooling. | | [Gelato](https://gelato.com/) | [Dmitry Makarenko](https://github.com/dmitry-makarenko-gelato) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal and third-party systems🚀. | +| [GoCardless](https://gocardless.com/) | [James Turley](https://github.com/tragiclifestories) | Developer portal: documentation, service templates, org structure, service catalog, plugins for integration with internal systems. | \ No newline at end of file From ac7f177291a7169c4e4de9be242146d227cde6ad Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 10:27:28 +0200 Subject: [PATCH 47/54] catalog: Move database, processing and stitching out of next Signed-off-by: Johan Haals --- .../database/DefaultProcessingDatabase.test.ts | 2 +- .../{next => }/database/DefaultProcessingDatabase.ts | 2 +- .../src/{next => }/database/conversion.test.ts | 0 .../src/{next => }/database/conversion.ts | 0 .../src/{next => }/database/metrics.ts | 2 +- .../src/{next => }/database/migrations.ts | 0 .../src/{next => }/database/tables.ts | 0 .../catalog-backend/src/{next => }/database/types.ts | 0 .../catalog-backend/src/{next => }/database/util.ts | 0 .../src/next/ConfigLocationEntityProvider.ts | 2 +- .../src/next/DefaultCatalogProcessingEngine.test.ts | 6 +++--- .../src/next/DefaultCatalogProcessingEngine.ts | 6 +++--- .../src/next/DefaultLocationService.test.ts | 2 +- .../src/next/DefaultLocationService.ts | 2 +- .../src/next/DefaultLocationStore.test.ts | 2 +- .../catalog-backend/src/next/DefaultLocationStore.ts | 4 ++-- .../src/next/DefaultRefreshService.test.ts | 12 ++++++------ .../src/next/DefaultRefreshService.ts | 2 +- .../catalog-backend/src/next/NextCatalogBuilder.ts | 8 ++++---- .../src/next/NextEntitiesCatalog.test.ts | 4 ++-- .../catalog-backend/src/next/NextEntitiesCatalog.ts | 2 +- plugins/catalog-backend/src/next/index.ts | 4 ++-- plugins/catalog-backend/src/next/types.ts | 2 +- .../DefaultCatalogProcessingOrchestrator.test.ts | 6 +++--- .../DefaultCatalogProcessingOrchestrator.ts | 6 +++--- .../processing/ProcessorCacheManager.test.ts | 2 +- .../{next => }/processing/ProcessorCacheManager.ts | 4 ++-- .../processing/ProcessorOutputCollector.ts | 4 ++-- .../src/{next => }/processing/index.ts | 0 .../src/{next => }/processing/types.ts | 0 .../src/{next => }/processing/util.ts | 0 .../src/{next => }/stitching/Stitcher.test.ts | 0 .../src/{next => }/stitching/Stitcher.ts | 0 .../{next => }/stitching/buildEntitySearch.test.ts | 0 .../src/{next => }/stitching/buildEntitySearch.ts | 0 .../src/{next => }/stitching/index.ts | 0 .../catalog-backend/src/{next => }/stitching/util.ts | 0 37 files changed, 43 insertions(+), 43 deletions(-) rename plugins/catalog-backend/src/{next => }/database/DefaultProcessingDatabase.test.ts (99%) rename plugins/catalog-backend/src/{next => }/database/DefaultProcessingDatabase.ts (99%) rename plugins/catalog-backend/src/{next => }/database/conversion.test.ts (100%) rename plugins/catalog-backend/src/{next => }/database/conversion.ts (100%) rename plugins/catalog-backend/src/{next => }/database/metrics.ts (97%) rename plugins/catalog-backend/src/{next => }/database/migrations.ts (100%) rename plugins/catalog-backend/src/{next => }/database/tables.ts (100%) rename plugins/catalog-backend/src/{next => }/database/types.ts (100%) rename plugins/catalog-backend/src/{next => }/database/util.ts (100%) rename plugins/catalog-backend/src/{next => }/processing/DefaultCatalogProcessingOrchestrator.test.ts (97%) rename plugins/catalog-backend/src/{next => }/processing/DefaultCatalogProcessingOrchestrator.ts (98%) rename plugins/catalog-backend/src/{next => }/processing/ProcessorCacheManager.test.ts (98%) rename plugins/catalog-backend/src/{next => }/processing/ProcessorCacheManager.ts (96%) rename plugins/catalog-backend/src/{next => }/processing/ProcessorOutputCollector.ts (96%) rename plugins/catalog-backend/src/{next => }/processing/index.ts (100%) rename plugins/catalog-backend/src/{next => }/processing/types.ts (100%) rename plugins/catalog-backend/src/{next => }/processing/util.ts (100%) rename plugins/catalog-backend/src/{next => }/stitching/Stitcher.test.ts (100%) rename plugins/catalog-backend/src/{next => }/stitching/Stitcher.ts (100%) rename plugins/catalog-backend/src/{next => }/stitching/buildEntitySearch.test.ts (100%) rename plugins/catalog-backend/src/{next => }/stitching/buildEntitySearch.ts (100%) rename plugins/catalog-backend/src/{next => }/stitching/index.ts (100%) rename plugins/catalog-backend/src/{next => }/stitching/util.ts (100%) diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts similarity index 99% rename from plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts rename to plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts index 5f509747f3..e77d2cde7b 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.test.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts @@ -28,7 +28,7 @@ import { DbRefreshStateRow, DbRelationsRow, } from './tables'; -import { createRandomRefreshInterval } from '../refresh'; +import { createRandomRefreshInterval } from '../next/refresh'; import { timestampToDateTime } from './conversion'; import { generateStableHash } from './util'; diff --git a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts similarity index 99% rename from plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts rename to plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts index b47154a46f..fd16ca5384 100644 --- a/plugins/catalog-backend/src/next/database/DefaultProcessingDatabase.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts @@ -33,7 +33,7 @@ import { UpdateEntityCacheOptions, } from './types'; import { DeferredEntity } from '../processing/types'; -import { RefreshIntervalFunction } from '../refresh'; +import { RefreshIntervalFunction } from '../next/refresh'; import { rethrowError, timestampToDateTime } from './conversion'; import { initDatabaseMetrics } from './metrics'; import { diff --git a/plugins/catalog-backend/src/next/database/conversion.test.ts b/plugins/catalog-backend/src/database/conversion.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/conversion.test.ts rename to plugins/catalog-backend/src/database/conversion.test.ts diff --git a/plugins/catalog-backend/src/next/database/conversion.ts b/plugins/catalog-backend/src/database/conversion.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/conversion.ts rename to plugins/catalog-backend/src/database/conversion.ts diff --git a/plugins/catalog-backend/src/next/database/metrics.ts b/plugins/catalog-backend/src/database/metrics.ts similarity index 97% rename from plugins/catalog-backend/src/next/database/metrics.ts rename to plugins/catalog-backend/src/database/metrics.ts index 4ad319ed10..0f1ad54820 100644 --- a/plugins/catalog-backend/src/next/database/metrics.ts +++ b/plugins/catalog-backend/src/database/metrics.ts @@ -15,7 +15,7 @@ */ import { Knex } from 'knex'; -import { createGaugeMetric } from '../metrics'; +import { createGaugeMetric } from '../next/metrics'; import { DbRefreshStateRow, DbRelationsRow, DbLocationsRow } from './tables'; export function initDatabaseMetrics(knex: Knex) { diff --git a/plugins/catalog-backend/src/next/database/migrations.ts b/plugins/catalog-backend/src/database/migrations.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/migrations.ts rename to plugins/catalog-backend/src/database/migrations.ts diff --git a/plugins/catalog-backend/src/next/database/tables.ts b/plugins/catalog-backend/src/database/tables.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/tables.ts rename to plugins/catalog-backend/src/database/tables.ts diff --git a/plugins/catalog-backend/src/next/database/types.ts b/plugins/catalog-backend/src/database/types.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/types.ts rename to plugins/catalog-backend/src/database/types.ts diff --git a/plugins/catalog-backend/src/next/database/util.ts b/plugins/catalog-backend/src/database/util.ts similarity index 100% rename from plugins/catalog-backend/src/next/database/util.ts rename to plugins/catalog-backend/src/database/util.ts diff --git a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts b/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts index 5deca09944..4f1c93da3e 100644 --- a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts +++ b/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts @@ -16,7 +16,7 @@ import { Config } from '@backstage/config'; import path from 'path'; -import { getEntityLocationRef } from './processing/util'; +import { getEntityLocationRef } from '../processing/util'; import { EntityProvider, EntityProviderConnection } from './types'; import { locationSpecToLocationEntity } from './util'; diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts index 430d7f251f..2bae899514 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts +++ b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts @@ -18,10 +18,10 @@ import { getVoidLogger } from '@backstage/backend-common'; import { Hash } from 'crypto'; import { DateTime } from 'luxon'; import waitForExpect from 'wait-for-expect'; -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; -import { CatalogProcessingOrchestrator } from './processing/types'; -import { Stitcher } from './stitching/Stitcher'; +import { CatalogProcessingOrchestrator } from '../processing/types'; +import { Stitcher } from '../stitching/Stitcher'; describe('DefaultCatalogProcessingEngine', () => { const db = { diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts index 346a912009..3fb870230b 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts @@ -23,13 +23,13 @@ import { serializeError } from '@backstage/errors'; import { Hash } from 'crypto'; import stableStringify from 'fast-json-stable-stringify'; import { Logger } from 'winston'; -import { ProcessingDatabase, RefreshStateItem } from './database/types'; +import { ProcessingDatabase, RefreshStateItem } from '../database/types'; import { createCounterMetric, createSummaryMetric } from './metrics'; import { CatalogProcessingOrchestrator, EntityProcessingResult, -} from './processing/types'; -import { Stitcher } from './stitching/Stitcher'; +} from '../processing/types'; +import { Stitcher } from '../stitching/Stitcher'; import { startTaskPipeline } from './TaskPipeline'; import { CatalogProcessingEngine, diff --git a/plugins/catalog-backend/src/next/DefaultLocationService.test.ts b/plugins/catalog-backend/src/next/DefaultLocationService.test.ts index 78dd9c1009..a089989d20 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationService.test.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationService.test.ts @@ -15,7 +15,7 @@ */ import { DefaultLocationService } from './DefaultLocationService'; -import { CatalogProcessingOrchestrator } from './processing/types'; +import { CatalogProcessingOrchestrator } from '../processing/types'; import { LocationStore } from './types'; describe('DefaultLocationServiceTest', () => { diff --git a/plugins/catalog-backend/src/next/DefaultLocationService.ts b/plugins/catalog-backend/src/next/DefaultLocationService.ts index 678ce9791c..325a2a3612 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationService.ts @@ -23,7 +23,7 @@ import { import { CatalogProcessingOrchestrator, DeferredEntity, -} from './processing/types'; +} from '../processing/types'; import { LocationService, LocationStore } from './types'; import { locationSpecToMetadataName } from './util'; diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts b/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts index f3cefeb14f..b9d129de5f 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts @@ -15,7 +15,7 @@ */ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { v4 as uuid } from 'uuid'; -import { applyDatabaseMigrations } from './database/migrations'; +import { applyDatabaseMigrations } from '../database/migrations'; import { DefaultLocationStore } from './DefaultLocationStore'; describe('DefaultLocationStore', () => { diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.ts b/plugins/catalog-backend/src/next/DefaultLocationStore.ts index c9ffe9a682..fdada36737 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/next/DefaultLocationStore.ts @@ -18,8 +18,8 @@ import { Location, LocationSpec } from '@backstage/catalog-model'; import { ConflictError, NotFoundError } from '@backstage/errors'; import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; -import { DbLocationsRow } from './database/tables'; -import { getEntityLocationRef } from './processing/util'; +import { DbLocationsRow } from '../database/tables'; +import { getEntityLocationRef } from '../processing/util'; import { EntityProvider, EntityProviderConnection, diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts b/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts index 7b6a181375..42838f39da 100644 --- a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts +++ b/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts @@ -19,16 +19,16 @@ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { createHash } from 'crypto'; import { Knex } from 'knex'; import { Logger } from 'winston'; -import { applyDatabaseMigrations } from './database/migrations'; -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; +import { applyDatabaseMigrations } from '../database/migrations'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { DbRefreshStateReferencesRow, DbRefreshStateRow, -} from './database/tables'; -import { ProcessingDatabase } from './database/types'; +} from '../database/tables'; +import { ProcessingDatabase } from '../database/types'; import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; -import { EntityProcessingRequest } from './processing/types'; -import { Stitcher } from './stitching/Stitcher'; +import { EntityProcessingRequest } from '../processing/types'; +import { Stitcher } from '../stitching/Stitcher'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { v4 as uuid } from 'uuid'; import { DefaultRefreshService } from './DefaultRefreshService'; diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.ts b/plugins/catalog-backend/src/next/DefaultRefreshService.ts index cfc7fa220b..3b982a0e46 100644 --- a/plugins/catalog-backend/src/next/DefaultRefreshService.ts +++ b/plugins/catalog-backend/src/next/DefaultRefreshService.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { RefreshOptions, RefreshService } from './types'; export class DefaultRefreshService implements RefreshService { diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index 8ca93d4bd7..79f6a75143 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -65,14 +65,14 @@ import { LocationService, } from '../next/types'; import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; -import { DefaultProcessingDatabase } from './database/DefaultProcessingDatabase'; -import { applyDatabaseMigrations } from './database/migrations'; +import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; +import { applyDatabaseMigrations } from '../database/migrations'; import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; import { DefaultLocationService } from './DefaultLocationService'; import { DefaultLocationStore } from './DefaultLocationStore'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; -import { DefaultCatalogProcessingOrchestrator } from './processing/DefaultCatalogProcessingOrchestrator'; -import { Stitcher } from './stitching/Stitcher'; +import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatalogProcessingOrchestrator'; +import { Stitcher } from '../stitching/Stitcher'; import { createRandomRefreshInterval, RefreshIntervalFunction, diff --git a/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts index 47e92445e9..082aa47df5 100644 --- a/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts @@ -18,12 +18,12 @@ import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; -import { applyDatabaseMigrations } from './database/migrations'; +import { applyDatabaseMigrations } from '../database/migrations'; import { DbFinalEntitiesRow, DbRefreshStateReferencesRow, DbRefreshStateRow, -} from './database/tables'; +} from '../database/tables'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; describe('NextEntitiesCatalog', () => { diff --git a/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts index 4fde078a8b..0891be045d 100644 --- a/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts @@ -30,7 +30,7 @@ import { DbRefreshStateRow, DbSearchRow, DbPageInfo, -} from './database/tables'; +} from '../database/tables'; function parsePagination(input?: EntityPagination): { limit?: number; diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/next/index.ts index bbc68651ea..055478e6f3 100644 --- a/plugins/catalog-backend/src/next/index.ts +++ b/plugins/catalog-backend/src/next/index.ts @@ -18,10 +18,10 @@ export type { CatalogEnvironment } from './NextCatalogBuilder'; export { NextCatalogBuilder } from './NextCatalogBuilder'; export { createNextRouter } from './NextRouter'; export type { NextRouterOptions } from './NextRouter'; -export * from './processing'; +export * from '../processing'; export { createRandomRefreshInterval } from './refresh'; export type { RefreshIntervalFunction } from './refresh'; -export * from './stitching'; +export * from '../stitching'; export type { EntityProvider, EntityProviderConnection, diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/next/types.ts index 4131b5835b..18deca42df 100644 --- a/plugins/catalog-backend/src/next/types.ts +++ b/plugins/catalog-backend/src/next/types.ts @@ -15,7 +15,7 @@ */ import { Entity, Location, LocationSpec } from '@backstage/catalog-model'; -import { DeferredEntity } from './processing/types'; +import { DeferredEntity } from '../processing/types'; export interface LocationService { createLocation( diff --git a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts similarity index 97% rename from plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.test.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts index 098f3ac14d..5ae4d58ec7 100644 --- a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.test.ts @@ -30,10 +30,10 @@ import { CatalogProcessorEmit, CatalogProcessorParser, results, -} from '../../ingestion'; -import { CatalogRulesEnforcer } from '../../ingestion/CatalogRules'; +} from '../ingestion'; +import { CatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator'; -import { defaultEntityDataParser } from '../../ingestion/processors/util/parse'; +import { defaultEntityDataParser } from '../ingestion/processors/util/parse'; import { ConfigReader } from '@backstage/config'; class FooBarProcessor implements CatalogProcessor { diff --git a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts similarity index 98% rename from plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts index 7a3977bcca..e973dcbf0c 100644 --- a/plugins/catalog-backend/src/next/processing/DefaultCatalogProcessingOrchestrator.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingOrchestrator.ts @@ -31,8 +31,8 @@ import { Logger } from 'winston'; import { CatalogProcessor, CatalogProcessorParser, -} from '../../ingestion/processors'; -import * as results from '../../ingestion/processors/results'; +} from '../ingestion/processors'; +import * as results from '../ingestion/processors/results'; import { CatalogProcessingOrchestrator, EntityProcessingRequest, @@ -48,7 +48,7 @@ import { validateEntityEnvelope, isObject, } from './util'; -import { CatalogRulesEnforcer } from '../../ingestion/CatalogRules'; +import { CatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { ProcessorCacheManager } from './ProcessorCacheManager'; type Context = { diff --git a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.test.ts b/plugins/catalog-backend/src/processing/ProcessorCacheManager.test.ts similarity index 98% rename from plugins/catalog-backend/src/next/processing/ProcessorCacheManager.test.ts rename to plugins/catalog-backend/src/processing/ProcessorCacheManager.test.ts index 44c96da1ca..e7f9fb313b 100644 --- a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.test.ts +++ b/plugins/catalog-backend/src/processing/ProcessorCacheManager.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { CatalogProcessor } from '../../ingestion/processors'; +import { CatalogProcessor } from '../ingestion/processors'; import { ProcessorCacheManager } from './ProcessorCacheManager'; class MyProcessor implements CatalogProcessor { diff --git a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.ts b/plugins/catalog-backend/src/processing/ProcessorCacheManager.ts similarity index 96% rename from plugins/catalog-backend/src/next/processing/ProcessorCacheManager.ts rename to plugins/catalog-backend/src/processing/ProcessorCacheManager.ts index 16945b7cc9..378974e33e 100644 --- a/plugins/catalog-backend/src/next/processing/ProcessorCacheManager.ts +++ b/plugins/catalog-backend/src/processing/ProcessorCacheManager.ts @@ -15,8 +15,8 @@ */ import { JsonObject, JsonValue } from '@backstage/config'; -import { CatalogProcessor } from '../../ingestion/processors'; -import { CatalogProcessorCache } from '../../ingestion/processors/types'; +import { CatalogProcessor } from '../ingestion/processors'; +import { CatalogProcessorCache } from '../ingestion/processors/types'; import { isObject } from './util'; class SingleProcessorSubCache implements CatalogProcessorCache { diff --git a/plugins/catalog-backend/src/next/processing/ProcessorOutputCollector.ts b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts similarity index 96% rename from plugins/catalog-backend/src/next/processing/ProcessorOutputCollector.ts rename to plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts index 507def964a..9ff4c9240c 100644 --- a/plugins/catalog-backend/src/next/processing/ProcessorOutputCollector.ts +++ b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts @@ -22,8 +22,8 @@ import { stringifyLocationReference, } from '@backstage/catalog-model'; import { Logger } from 'winston'; -import { CatalogProcessorResult } from '../../ingestion'; -import { locationSpecToLocationEntity } from '../util'; +import { CatalogProcessorResult } from '../ingestion'; +import { locationSpecToLocationEntity } from '../next/util'; import { DeferredEntity } from './types'; import { getEntityLocationRef, diff --git a/plugins/catalog-backend/src/next/processing/index.ts b/plugins/catalog-backend/src/processing/index.ts similarity index 100% rename from plugins/catalog-backend/src/next/processing/index.ts rename to plugins/catalog-backend/src/processing/index.ts diff --git a/plugins/catalog-backend/src/next/processing/types.ts b/plugins/catalog-backend/src/processing/types.ts similarity index 100% rename from plugins/catalog-backend/src/next/processing/types.ts rename to plugins/catalog-backend/src/processing/types.ts diff --git a/plugins/catalog-backend/src/next/processing/util.ts b/plugins/catalog-backend/src/processing/util.ts similarity index 100% rename from plugins/catalog-backend/src/next/processing/util.ts rename to plugins/catalog-backend/src/processing/util.ts diff --git a/plugins/catalog-backend/src/next/stitching/Stitcher.test.ts b/plugins/catalog-backend/src/stitching/Stitcher.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/Stitcher.test.ts rename to plugins/catalog-backend/src/stitching/Stitcher.test.ts diff --git a/plugins/catalog-backend/src/next/stitching/Stitcher.ts b/plugins/catalog-backend/src/stitching/Stitcher.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/Stitcher.ts rename to plugins/catalog-backend/src/stitching/Stitcher.ts diff --git a/plugins/catalog-backend/src/next/stitching/buildEntitySearch.test.ts b/plugins/catalog-backend/src/stitching/buildEntitySearch.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/buildEntitySearch.test.ts rename to plugins/catalog-backend/src/stitching/buildEntitySearch.test.ts diff --git a/plugins/catalog-backend/src/next/stitching/buildEntitySearch.ts b/plugins/catalog-backend/src/stitching/buildEntitySearch.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/buildEntitySearch.ts rename to plugins/catalog-backend/src/stitching/buildEntitySearch.ts diff --git a/plugins/catalog-backend/src/next/stitching/index.ts b/plugins/catalog-backend/src/stitching/index.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/index.ts rename to plugins/catalog-backend/src/stitching/index.ts diff --git a/plugins/catalog-backend/src/next/stitching/util.ts b/plugins/catalog-backend/src/stitching/util.ts similarity index 100% rename from plugins/catalog-backend/src/next/stitching/util.ts rename to plugins/catalog-backend/src/stitching/util.ts From 37d131ab2f3a7ee4b60f021db23f70b906125d6c Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 10:33:00 +0200 Subject: [PATCH 48/54] Remove unused context package Signed-off-by: Johan Haals --- .../src/next/Context/BackgroundContext.ts | 26 ------------ .../src/next/Context/ContextWithValue.ts | 39 ------------------ .../src/next/Context/TransactionValue.test.ts | 37 ----------------- .../src/next/Context/TransactionValue.ts | 40 ------------------- .../catalog-backend/src/next/Context/index.ts | 21 ---------- .../catalog-backend/src/next/Context/types.ts | 23 ----------- 6 files changed, 186 deletions(-) delete mode 100644 plugins/catalog-backend/src/next/Context/BackgroundContext.ts delete mode 100644 plugins/catalog-backend/src/next/Context/ContextWithValue.ts delete mode 100644 plugins/catalog-backend/src/next/Context/TransactionValue.test.ts delete mode 100644 plugins/catalog-backend/src/next/Context/TransactionValue.ts delete mode 100644 plugins/catalog-backend/src/next/Context/index.ts delete mode 100644 plugins/catalog-backend/src/next/Context/types.ts diff --git a/plugins/catalog-backend/src/next/Context/BackgroundContext.ts b/plugins/catalog-backend/src/next/Context/BackgroundContext.ts deleted file mode 100644 index c41fa8e3c5..0000000000 --- a/plugins/catalog-backend/src/next/Context/BackgroundContext.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 { Context, ContextKey } from './types'; - -/** - * A base Context implementation that does not hold any value. - */ -export class BackgroundContext implements Context { - getContextValue(key: ContextKey): T { - return key.defaultValue; - } -} diff --git a/plugins/catalog-backend/src/next/Context/ContextWithValue.ts b/plugins/catalog-backend/src/next/Context/ContextWithValue.ts deleted file mode 100644 index cc88c03679..0000000000 --- a/plugins/catalog-backend/src/next/Context/ContextWithValue.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 { Context, ContextKey } from './types'; - -/** - * A Context implementation that holds a single value, optionally extending an existing context. - */ -export class ContextWithValue implements Context { - static create(parent: Context, key: ContextKey, value: unknown) { - return new ContextWithValue(parent, key, value); - } - - private constructor( - private readonly parent: Context, - private readonly key: ContextKey, - private readonly value: unknown, - ) {} - - getContextValue(key: ContextKey): T { - if (this.key === key) { - return this.value as T; - } - return this.parent.getContextValue(key); - } -} diff --git a/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts b/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts deleted file mode 100644 index 20165eff2f..0000000000 --- a/plugins/catalog-backend/src/next/Context/TransactionValue.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 { TransactionValue } from './TransactionValue'; -import { Knex } from 'knex'; -import { BackgroundContext } from './BackgroundContext'; - -describe('TransactionValue Context', () => { - it('should be able to store tx values and retrieve them from a context', () => { - const tx = {} as Knex.Transaction; - const ctx = new BackgroundContext(); - - const nextCtx = TransactionValue.in(ctx, tx); - - expect(TransactionValue.from(nextCtx)).toBe(tx); - }); - - it('should throw when there is no tx value in the context', () => { - const ctx = new BackgroundContext(); - - expect(() => TransactionValue.from(ctx)).toThrow( - /No transaction available in context/, - ); - }); -}); diff --git a/plugins/catalog-backend/src/next/Context/TransactionValue.ts b/plugins/catalog-backend/src/next/Context/TransactionValue.ts deleted file mode 100644 index af069c6d8e..0000000000 --- a/plugins/catalog-backend/src/next/Context/TransactionValue.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 { Context, ContextKey } from './types'; -import { Knex } from 'knex'; -import { ContextWithValue } from './ContextWithValue'; - -const transactionContextKey = new ContextKey( - undefined, -); - -/** - * TransactionValue handles the wrapping of a knex transaction in a Context. - */ -export class TransactionValue { - static in(parent: Context, tx: Knex.Transaction) { - return ContextWithValue.create(parent, transactionContextKey, tx); - } - - static from(context: Context): Knex.Transaction { - const transaction = context.getContextValue(transactionContextKey); - if (!transaction) { - throw new Error(`No transaction available in context`); - } - return transaction; - } -} diff --git a/plugins/catalog-backend/src/next/Context/index.ts b/plugins/catalog-backend/src/next/Context/index.ts deleted file mode 100644 index 822edba478..0000000000 --- a/plugins/catalog-backend/src/next/Context/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * 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 { BackgroundContext } from './BackgroundContext'; -export { ContextWithValue } from './ContextWithValue'; -export { TransactionValue } from './TransactionValue'; -export { ContextKey } from './types'; -export type { Context } from './types'; diff --git a/plugins/catalog-backend/src/next/Context/types.ts b/plugins/catalog-backend/src/next/Context/types.ts deleted file mode 100644 index 062bc884e9..0000000000 --- a/plugins/catalog-backend/src/next/Context/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 class ContextKey { - constructor(readonly defaultValue: T) {} -} - -export interface Context { - getContextValue(key: ContextKey): T; -} From 54e5cd14aced55f6d92ce207fce6dcf9aef653be Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 10:46:45 +0200 Subject: [PATCH 49/54] Move EntityProviders to providers Signed-off-by: Johan Haals --- plugins/catalog-backend/src/next/NextCatalogBuilder.ts | 4 ++-- .../{next => providers}/ConfigLocationEntityProvider.test.ts | 2 +- .../src/{next => providers}/ConfigLocationEntityProvider.ts | 4 ++-- .../src/{next => providers}/DefaultLocationStore.test.ts | 0 .../src/{next => providers}/DefaultLocationStore.ts | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) rename plugins/catalog-backend/src/{next => providers}/ConfigLocationEntityProvider.test.ts (98%) rename plugins/catalog-backend/src/{next => providers}/ConfigLocationEntityProvider.ts (93%) rename plugins/catalog-backend/src/{next => providers}/DefaultLocationStore.test.ts (100%) rename plugins/catalog-backend/src/{next => providers}/DefaultLocationStore.ts (98%) diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index 79f6a75143..964cf4538a 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -64,12 +64,12 @@ import { EntityProvider, LocationService, } from '../next/types'; -import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; +import { ConfigLocationEntityProvider } from '../providers/ConfigLocationEntityProvider'; import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { applyDatabaseMigrations } from '../database/migrations'; import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; import { DefaultLocationService } from './DefaultLocationService'; -import { DefaultLocationStore } from './DefaultLocationStore'; +import { DefaultLocationStore } from '../providers/DefaultLocationStore'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatalogProcessingOrchestrator'; import { Stitcher } from '../stitching/Stitcher'; diff --git a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts similarity index 98% rename from plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts rename to plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts index 0663ad8d04..a44c803c5f 100644 --- a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.test.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts @@ -17,7 +17,7 @@ import { ConfigReader } from '@backstage/config'; import path from 'path'; import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; -import { EntityProviderConnection } from './types'; +import { EntityProviderConnection } from '../next/types'; describe('ConfigLocationEntityProvider', () => { it('should apply mutation with the correct paths in the config', async () => { diff --git a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts similarity index 93% rename from plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts rename to plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts index 4f1c93da3e..ce88488d8f 100644 --- a/plugins/catalog-backend/src/next/ConfigLocationEntityProvider.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts @@ -17,8 +17,8 @@ import { Config } from '@backstage/config'; import path from 'path'; import { getEntityLocationRef } from '../processing/util'; -import { EntityProvider, EntityProviderConnection } from './types'; -import { locationSpecToLocationEntity } from './util'; +import { EntityProvider, EntityProviderConnection } from '../next/types'; +import { locationSpecToLocationEntity } from '../next/util'; export class ConfigLocationEntityProvider implements EntityProvider { constructor(private readonly config: Config) {} diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.test.ts b/plugins/catalog-backend/src/providers/DefaultLocationStore.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/DefaultLocationStore.test.ts rename to plugins/catalog-backend/src/providers/DefaultLocationStore.test.ts diff --git a/plugins/catalog-backend/src/next/DefaultLocationStore.ts b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts similarity index 98% rename from plugins/catalog-backend/src/next/DefaultLocationStore.ts rename to plugins/catalog-backend/src/providers/DefaultLocationStore.ts index fdada36737..5498b7105e 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts @@ -24,8 +24,8 @@ import { EntityProvider, EntityProviderConnection, LocationStore, -} from './types'; -import { locationSpecToLocationEntity } from './util'; +} from '../next/types'; +import { locationSpecToLocationEntity } from '../next/util'; export class DefaultLocationStore implements LocationStore, EntityProvider { private _connection: EntityProviderConnection | undefined; From ee8d55e02b507098c4a9382faa892171c7c9a96b Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 11:21:40 +0200 Subject: [PATCH 50/54] Move providers and processing to separate folders Signed-off-by: Johan Haals --- .../catalog-backend/src/database/metrics.ts | 2 +- plugins/catalog-backend/src/index.ts | 2 ++ .../src/next/DefaultRefreshService.test.ts | 2 +- .../src/next/NextCatalogBuilder.ts | 10 +++---- plugins/catalog-backend/src/next/index.ts | 4 --- plugins/catalog-backend/src/next/types.ts | 19 ------------ .../DefaultCatalogProcessingEngine.test.ts | 2 +- .../DefaultCatalogProcessingEngine.ts | 6 ++-- .../{next => processing}/TaskPipeline.test.ts | 0 .../src/{next => processing}/TaskPipeline.ts | 0 .../catalog-backend/src/processing/index.ts | 1 + .../catalog-backend/src/processing/types.ts | 5 ++++ .../ConfigLocationEntityProvider.test.ts | 2 +- .../providers/ConfigLocationEntityProvider.ts | 2 +- .../src/providers/DefaultLocationStore.ts | 8 ++--- .../catalog-backend/src/providers/index.ts | 21 +++++++++++++ .../catalog-backend/src/providers/types.ts | 30 +++++++++++++++++++ .../src/{next => util}/metrics.ts | 0 18 files changed, 74 insertions(+), 42 deletions(-) rename plugins/catalog-backend/src/{next => processing}/DefaultCatalogProcessingEngine.test.ts (99%) rename plugins/catalog-backend/src/{next => processing}/DefaultCatalogProcessingEngine.ts (99%) rename plugins/catalog-backend/src/{next => processing}/TaskPipeline.test.ts (100%) rename plugins/catalog-backend/src/{next => processing}/TaskPipeline.ts (100%) create mode 100644 plugins/catalog-backend/src/providers/index.ts create mode 100644 plugins/catalog-backend/src/providers/types.ts rename plugins/catalog-backend/src/{next => util}/metrics.ts (100%) diff --git a/plugins/catalog-backend/src/database/metrics.ts b/plugins/catalog-backend/src/database/metrics.ts index 0f1ad54820..b6c4e248dc 100644 --- a/plugins/catalog-backend/src/database/metrics.ts +++ b/plugins/catalog-backend/src/database/metrics.ts @@ -15,7 +15,7 @@ */ import { Knex } from 'knex'; -import { createGaugeMetric } from '../next/metrics'; +import { createGaugeMetric } from '../util/metrics'; import { DbRefreshStateRow, DbRelationsRow, DbLocationsRow } from './tables'; export function initDatabaseMetrics(knex: Knex) { diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 4d84330f27..0c72926210 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -26,3 +26,5 @@ export * from './legacy'; export * from './search'; export * from './util'; export * from './next'; +export * from './processing'; +export * from './providers'; diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts b/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts index 42838f39da..0ea475a528 100644 --- a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts +++ b/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts @@ -26,7 +26,7 @@ import { DbRefreshStateRow, } from '../database/tables'; import { ProcessingDatabase } from '../database/types'; -import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; +import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine'; import { EntityProcessingRequest } from '../processing/types'; import { Stitcher } from '../stitching/Stitcher'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index 964cf4538a..869af1581f 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -59,15 +59,12 @@ import { } from '../ingestion/processors/PlaceholderProcessor'; import { defaultEntityDataParser } from '../ingestion/processors/util/parse'; import { LocationAnalyzer } from '../ingestion/types'; -import { - CatalogProcessingEngine, - EntityProvider, - LocationService, -} from '../next/types'; +import { EntityProvider } from '../providers/types'; +import { CatalogProcessingEngine } from '../processing/types'; import { ConfigLocationEntityProvider } from '../providers/ConfigLocationEntityProvider'; import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { applyDatabaseMigrations } from '../database/migrations'; -import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; +import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine'; import { DefaultLocationService } from './DefaultLocationService'; import { DefaultLocationStore } from '../providers/DefaultLocationStore'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; @@ -82,6 +79,7 @@ import { DefaultRefreshService } from './DefaultRefreshService'; import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { Config } from '@backstage/config'; import { Logger } from 'winston'; +import { LocationService } from './types'; export type CatalogEnvironment = { logger: Logger; diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/next/index.ts index 055478e6f3..cc3ad0b979 100644 --- a/plugins/catalog-backend/src/next/index.ts +++ b/plugins/catalog-backend/src/next/index.ts @@ -23,10 +23,6 @@ export { createRandomRefreshInterval } from './refresh'; export type { RefreshIntervalFunction } from './refresh'; export * from '../stitching'; export type { - EntityProvider, - EntityProviderConnection, - EntityProviderMutation, - CatalogProcessingEngine, LocationService, LocationStore, RefreshOptions, diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/next/types.ts index 18deca42df..4e3d52e205 100644 --- a/plugins/catalog-backend/src/next/types.ts +++ b/plugins/catalog-backend/src/next/types.ts @@ -15,7 +15,6 @@ */ import { Entity, Location, LocationSpec } from '@backstage/catalog-model'; -import { DeferredEntity } from '../processing/types'; export interface LocationService { createLocation( @@ -34,11 +33,6 @@ export interface LocationStore { deleteLocation(id: string): Promise; } -export interface CatalogProcessingEngine { - start(): Promise; - stop(): Promise; -} - /** * Options for requesting a refresh of entities in the catalog. * @@ -60,16 +54,3 @@ export interface RefreshService { */ refresh(options: RefreshOptions): Promise; } - -export type EntityProviderMutation = - | { type: 'full'; entities: DeferredEntity[] } - | { type: 'delta'; added: DeferredEntity[]; removed: DeferredEntity[] }; - -export interface EntityProviderConnection { - applyMutation(mutation: EntityProviderMutation): Promise; -} - -export interface EntityProvider { - getProviderName(): string; - connect(connection: EntityProviderConnection): Promise; -} diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts similarity index 99% rename from plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts index 2bae899514..044c3e7c4b 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.test.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.test.ts @@ -20,7 +20,7 @@ import { DateTime } from 'luxon'; import waitForExpect from 'wait-for-expect'; import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { DefaultCatalogProcessingEngine } from './DefaultCatalogProcessingEngine'; -import { CatalogProcessingOrchestrator } from '../processing/types'; +import { CatalogProcessingOrchestrator } from './types'; import { Stitcher } from '../stitching/Stitcher'; describe('DefaultCatalogProcessingEngine', () => { diff --git a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts similarity index 99% rename from plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts rename to plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 3fb870230b..d0faff25d5 100644 --- a/plugins/catalog-backend/src/next/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -24,19 +24,19 @@ import { Hash } from 'crypto'; import stableStringify from 'fast-json-stable-stringify'; import { Logger } from 'winston'; import { ProcessingDatabase, RefreshStateItem } from '../database/types'; -import { createCounterMetric, createSummaryMetric } from './metrics'; +import { createCounterMetric, createSummaryMetric } from '../util/metrics'; import { + CatalogProcessingEngine, CatalogProcessingOrchestrator, EntityProcessingResult, } from '../processing/types'; import { Stitcher } from '../stitching/Stitcher'; import { startTaskPipeline } from './TaskPipeline'; import { - CatalogProcessingEngine, EntityProvider, EntityProviderConnection, EntityProviderMutation, -} from './types'; +} from '../providers/types'; const CACHE_TTL = 5; diff --git a/plugins/catalog-backend/src/next/TaskPipeline.test.ts b/plugins/catalog-backend/src/processing/TaskPipeline.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/TaskPipeline.test.ts rename to plugins/catalog-backend/src/processing/TaskPipeline.test.ts diff --git a/plugins/catalog-backend/src/next/TaskPipeline.ts b/plugins/catalog-backend/src/processing/TaskPipeline.ts similarity index 100% rename from plugins/catalog-backend/src/next/TaskPipeline.ts rename to plugins/catalog-backend/src/processing/TaskPipeline.ts diff --git a/plugins/catalog-backend/src/processing/index.ts b/plugins/catalog-backend/src/processing/index.ts index 9e03f18f28..7b60a1670b 100644 --- a/plugins/catalog-backend/src/processing/index.ts +++ b/plugins/catalog-backend/src/processing/index.ts @@ -16,6 +16,7 @@ export type { CatalogProcessingOrchestrator, + CatalogProcessingEngine, EntityProcessingRequest, EntityProcessingResult, DeferredEntity, diff --git a/plugins/catalog-backend/src/processing/types.ts b/plugins/catalog-backend/src/processing/types.ts index 8b533714ce..4b16ccef32 100644 --- a/plugins/catalog-backend/src/processing/types.ts +++ b/plugins/catalog-backend/src/processing/types.ts @@ -44,3 +44,8 @@ export type DeferredEntity = { entity: Entity; locationKey?: string; }; + +export interface CatalogProcessingEngine { + start(): Promise; + stop(): Promise; +} diff --git a/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts index a44c803c5f..0663ad8d04 100644 --- a/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.test.ts @@ -17,7 +17,7 @@ import { ConfigReader } from '@backstage/config'; import path from 'path'; import { ConfigLocationEntityProvider } from './ConfigLocationEntityProvider'; -import { EntityProviderConnection } from '../next/types'; +import { EntityProviderConnection } from './types'; describe('ConfigLocationEntityProvider', () => { it('should apply mutation with the correct paths in the config', async () => { diff --git a/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts index ce88488d8f..e497515035 100644 --- a/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts @@ -17,7 +17,7 @@ import { Config } from '@backstage/config'; import path from 'path'; import { getEntityLocationRef } from '../processing/util'; -import { EntityProvider, EntityProviderConnection } from '../next/types'; +import { EntityProvider, EntityProviderConnection } from './types'; import { locationSpecToLocationEntity } from '../next/util'; export class ConfigLocationEntityProvider implements EntityProvider { diff --git a/plugins/catalog-backend/src/providers/DefaultLocationStore.ts b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts index 5498b7105e..fdd33a422d 100644 --- a/plugins/catalog-backend/src/providers/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts @@ -20,12 +20,10 @@ import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; import { DbLocationsRow } from '../database/tables'; import { getEntityLocationRef } from '../processing/util'; -import { - EntityProvider, - EntityProviderConnection, - LocationStore, -} from '../next/types'; +import { EntityProvider, EntityProviderConnection } from './types'; + import { locationSpecToLocationEntity } from '../next/util'; +import { LocationStore } from '../next'; export class DefaultLocationStore implements LocationStore, EntityProvider { private _connection: EntityProviderConnection | undefined; diff --git a/plugins/catalog-backend/src/providers/index.ts b/plugins/catalog-backend/src/providers/index.ts new file mode 100644 index 0000000000..3b4a1c4b7e --- /dev/null +++ b/plugins/catalog-backend/src/providers/index.ts @@ -0,0 +1,21 @@ +/* + * 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 type { + EntityProvider, + EntityProviderConnection, + EntityProviderMutation, +} from './types'; diff --git a/plugins/catalog-backend/src/providers/types.ts b/plugins/catalog-backend/src/providers/types.ts new file mode 100644 index 0000000000..90c5f13215 --- /dev/null +++ b/plugins/catalog-backend/src/providers/types.ts @@ -0,0 +1,30 @@ +/* + * 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 { DeferredEntity } from '../processing'; + +export type EntityProviderMutation = + | { type: 'full'; entities: DeferredEntity[] } + | { type: 'delta'; added: DeferredEntity[]; removed: DeferredEntity[] }; + +export interface EntityProviderConnection { + applyMutation(mutation: EntityProviderMutation): Promise; +} + +export interface EntityProvider { + getProviderName(): string; + connect(connection: EntityProviderConnection): Promise; +} diff --git a/plugins/catalog-backend/src/next/metrics.ts b/plugins/catalog-backend/src/util/metrics.ts similarity index 100% rename from plugins/catalog-backend/src/next/metrics.ts rename to plugins/catalog-backend/src/util/metrics.ts From 9d3ff6680f2fe0cf26da2bcb16d3436cba6331a4 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 11:49:21 +0200 Subject: [PATCH 51/54] Move router and *Service to service folder Signed-off-by: Johan Haals --- plugins/catalog-backend/src/index.ts | 1 + .../src/legacy/service/router.test.ts | 2 +- .../src/legacy/service/router.ts | 2 +- .../src/next/NextCatalogBuilder.ts | 8 +-- plugins/catalog-backend/src/next/index.ts | 11 +---- plugins/catalog-backend/src/next/types.ts | 34 +------------ .../DefaultLocationService.test.ts | 2 +- .../DefaultLocationService.ts | 5 +- .../DefaultRefreshService.test.ts | 0 .../DefaultRefreshService.ts | 0 .../src/{next => service}/NextRouter.test.ts | 2 +- .../src/{next => service}/NextRouter.ts | 0 plugins/catalog-backend/src/service/index.ts | 19 +++++++ plugins/catalog-backend/src/service/types.ts | 49 +++++++++++++++++++ 14 files changed, 82 insertions(+), 53 deletions(-) rename plugins/catalog-backend/src/{next => service}/DefaultLocationService.test.ts (99%) rename plugins/catalog-backend/src/{next => service}/DefaultLocationService.ts (95%) rename plugins/catalog-backend/src/{next => service}/DefaultRefreshService.test.ts (100%) rename plugins/catalog-backend/src/{next => service}/DefaultRefreshService.ts (100%) rename plugins/catalog-backend/src/{next => service}/NextRouter.test.ts (99%) rename plugins/catalog-backend/src/{next => service}/NextRouter.ts (100%) create mode 100644 plugins/catalog-backend/src/service/index.ts create mode 100644 plugins/catalog-backend/src/service/types.ts diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 0c72926210..5fe0ca2483 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -28,3 +28,4 @@ export * from './util'; export * from './next'; export * from './processing'; export * from './providers'; +export * from './service'; diff --git a/plugins/catalog-backend/src/legacy/service/router.test.ts b/plugins/catalog-backend/src/legacy/service/router.test.ts index d15d3799b5..edc7d852ac 100644 --- a/plugins/catalog-backend/src/legacy/service/router.test.ts +++ b/plugins/catalog-backend/src/legacy/service/router.test.ts @@ -25,7 +25,7 @@ import { LocationResponse, LocationsCatalog } from '../catalog/types'; import { HigherOrderOperation } from '../ingestion/types'; import { createRouter } from './router'; import { basicEntityFilter } from '../../service/request'; -import { RefreshService } from '../../next'; +import { RefreshService } from '../../service'; describe('createRouter readonly disabled', () => { let entitiesCatalog: jest.Mocked>; diff --git a/plugins/catalog-backend/src/legacy/service/router.ts b/plugins/catalog-backend/src/legacy/service/router.ts index 4dfa9e49f9..e7d097a9c7 100644 --- a/plugins/catalog-backend/src/legacy/service/router.ts +++ b/plugins/catalog-backend/src/legacy/service/router.ts @@ -34,7 +34,7 @@ import { RefreshService, LocationService, RefreshOptions, -} from '../../next/types'; +} from '../../service/types'; import { basicEntityFilter, parseEntityFilterParams, diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index 869af1581f..e1723ff627 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -65,7 +65,7 @@ import { ConfigLocationEntityProvider } from '../providers/ConfigLocationEntityP import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { applyDatabaseMigrations } from '../database/migrations'; import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine'; -import { DefaultLocationService } from './DefaultLocationService'; +import { DefaultLocationService } from '../service/DefaultLocationService'; import { DefaultLocationStore } from '../providers/DefaultLocationStore'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatalogProcessingOrchestrator'; @@ -74,12 +74,12 @@ import { createRandomRefreshInterval, RefreshIntervalFunction, } from './refresh'; -import { createNextRouter } from './NextRouter'; -import { DefaultRefreshService } from './DefaultRefreshService'; +import { createNextRouter } from '../service/NextRouter'; +import { DefaultRefreshService } from '../service/DefaultRefreshService'; import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { Config } from '@backstage/config'; import { Logger } from 'winston'; -import { LocationService } from './types'; +import { LocationService } from '../service/types'; export type CatalogEnvironment = { logger: Logger; diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/next/index.ts index cc3ad0b979..c3fa4316f5 100644 --- a/plugins/catalog-backend/src/next/index.ts +++ b/plugins/catalog-backend/src/next/index.ts @@ -16,15 +16,6 @@ export type { CatalogEnvironment } from './NextCatalogBuilder'; export { NextCatalogBuilder } from './NextCatalogBuilder'; -export { createNextRouter } from './NextRouter'; -export type { NextRouterOptions } from './NextRouter'; -export * from '../processing'; export { createRandomRefreshInterval } from './refresh'; export type { RefreshIntervalFunction } from './refresh'; -export * from '../stitching'; -export type { - LocationService, - LocationStore, - RefreshOptions, - RefreshService, -} from './types'; +export type { LocationStore } from './types'; diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/next/types.ts index 4e3d52e205..b5888a5a3a 100644 --- a/plugins/catalog-backend/src/next/types.ts +++ b/plugins/catalog-backend/src/next/types.ts @@ -14,17 +14,7 @@ * limitations under the License. */ -import { Entity, Location, LocationSpec } from '@backstage/catalog-model'; - -export interface LocationService { - createLocation( - spec: LocationSpec, - dryRun: boolean, - ): Promise<{ location: Location; entities: Entity[]; exists?: boolean }>; - listLocations(): Promise; - getLocation(id: string): Promise; - deleteLocation(id: string): Promise; -} +import { Location, LocationSpec } from '@backstage/catalog-model'; export interface LocationStore { createLocation(spec: LocationSpec): Promise; @@ -32,25 +22,3 @@ export interface LocationStore { getLocation(id: string): Promise; deleteLocation(id: string): Promise; } - -/** - * Options for requesting a refresh of entities in the catalog. - * - * @public - */ -export type RefreshOptions = { - /** The reference to a single entity that should be refreshed */ - entityRef: string; -}; - -/** - * A service that manages refreshes of entities in the catalog. - * - * @public - */ -export interface RefreshService { - /** - * Request a refresh of entities in the catalog. - */ - refresh(options: RefreshOptions): Promise; -} diff --git a/plugins/catalog-backend/src/next/DefaultLocationService.test.ts b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts similarity index 99% rename from plugins/catalog-backend/src/next/DefaultLocationService.test.ts rename to plugins/catalog-backend/src/service/DefaultLocationService.test.ts index a089989d20..8d9dee0703 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts @@ -16,7 +16,7 @@ import { DefaultLocationService } from './DefaultLocationService'; import { CatalogProcessingOrchestrator } from '../processing/types'; -import { LocationStore } from './types'; +import { LocationStore } from '../next/types'; describe('DefaultLocationServiceTest', () => { const orchestrator: jest.Mocked = { diff --git a/plugins/catalog-backend/src/next/DefaultLocationService.ts b/plugins/catalog-backend/src/service/DefaultLocationService.ts similarity index 95% rename from plugins/catalog-backend/src/next/DefaultLocationService.ts rename to plugins/catalog-backend/src/service/DefaultLocationService.ts index 325a2a3612..bc066823db 100644 --- a/plugins/catalog-backend/src/next/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.ts @@ -24,8 +24,9 @@ import { CatalogProcessingOrchestrator, DeferredEntity, } from '../processing/types'; -import { LocationService, LocationStore } from './types'; -import { locationSpecToMetadataName } from './util'; +import { LocationService } from './types'; +import { LocationStore } from '../next/types'; +import { locationSpecToMetadataName } from '../next/util'; export class DefaultLocationService implements LocationService { constructor( diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.test.ts b/plugins/catalog-backend/src/service/DefaultRefreshService.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/DefaultRefreshService.test.ts rename to plugins/catalog-backend/src/service/DefaultRefreshService.test.ts diff --git a/plugins/catalog-backend/src/next/DefaultRefreshService.ts b/plugins/catalog-backend/src/service/DefaultRefreshService.ts similarity index 100% rename from plugins/catalog-backend/src/next/DefaultRefreshService.ts rename to plugins/catalog-backend/src/service/DefaultRefreshService.ts diff --git a/plugins/catalog-backend/src/next/NextRouter.test.ts b/plugins/catalog-backend/src/service/NextRouter.test.ts similarity index 99% rename from plugins/catalog-backend/src/next/NextRouter.test.ts rename to plugins/catalog-backend/src/service/NextRouter.test.ts index 489e7efdb5..57911c74fb 100644 --- a/plugins/catalog-backend/src/next/NextRouter.test.ts +++ b/plugins/catalog-backend/src/service/NextRouter.test.ts @@ -22,7 +22,7 @@ import express from 'express'; import request from 'supertest'; import { EntitiesCatalog } from '../catalog'; import { LocationService, RefreshService } from './types'; -import { basicEntityFilter } from '../service/request'; +import { basicEntityFilter } from './request'; import { createNextRouter } from './NextRouter'; describe('createNextRouter readonly disabled', () => { diff --git a/plugins/catalog-backend/src/next/NextRouter.ts b/plugins/catalog-backend/src/service/NextRouter.ts similarity index 100% rename from plugins/catalog-backend/src/next/NextRouter.ts rename to plugins/catalog-backend/src/service/NextRouter.ts diff --git a/plugins/catalog-backend/src/service/index.ts b/plugins/catalog-backend/src/service/index.ts new file mode 100644 index 0000000000..f47d796693 --- /dev/null +++ b/plugins/catalog-backend/src/service/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 type { LocationService, RefreshService, RefreshOptions } from './types'; +export { createNextRouter } from './NextRouter'; +export type { NextRouterOptions } from './NextRouter'; diff --git a/plugins/catalog-backend/src/service/types.ts b/plugins/catalog-backend/src/service/types.ts new file mode 100644 index 0000000000..eedc688bb3 --- /dev/null +++ b/plugins/catalog-backend/src/service/types.ts @@ -0,0 +1,49 @@ +/* + * 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 { Entity, LocationSpec, Location } from '@backstage/catalog-model'; + +export interface LocationService { + createLocation( + spec: LocationSpec, + dryRun: boolean, + ): Promise<{ location: Location; entities: Entity[]; exists?: boolean }>; + listLocations(): Promise; + getLocation(id: string): Promise; + deleteLocation(id: string): Promise; +} + +/** + * Options for requesting a refresh of entities in the catalog. + * + * @public + */ +export type RefreshOptions = { + /** The reference to a single entity that should be refreshed */ + entityRef: string; +}; + +/** + * A service that manages refreshes of entities in the catalog. + * + * @public + */ +export interface RefreshService { + /** + * Request a refresh of entities in the catalog. + */ + refresh(options: RefreshOptions): Promise; +} From ccbcab2a42ee582eb896b20f7bf1d586ee497553 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 13:24:29 +0200 Subject: [PATCH 52/54] Move refresh to processing Signed-off-by: Johan Haals --- .../src/database/DefaultProcessingDatabase.test.ts | 2 +- .../catalog-backend/src/database/DefaultProcessingDatabase.ts | 2 +- plugins/catalog-backend/src/next/NextCatalogBuilder.ts | 2 +- plugins/catalog-backend/src/next/index.ts | 2 -- plugins/catalog-backend/src/processing/index.ts | 3 +++ plugins/catalog-backend/src/{next => processing}/refresh.ts | 0 6 files changed, 6 insertions(+), 5 deletions(-) rename plugins/catalog-backend/src/{next => processing}/refresh.ts (100%) diff --git a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts index e77d2cde7b..00b3de2fc7 100644 --- a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.test.ts @@ -28,7 +28,7 @@ import { DbRefreshStateRow, DbRelationsRow, } from './tables'; -import { createRandomRefreshInterval } from '../next/refresh'; +import { createRandomRefreshInterval } from '../processing/refresh'; import { timestampToDateTime } from './conversion'; import { generateStableHash } from './util'; diff --git a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts index fd16ca5384..9785ac3935 100644 --- a/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts +++ b/plugins/catalog-backend/src/database/DefaultProcessingDatabase.ts @@ -33,7 +33,7 @@ import { UpdateEntityCacheOptions, } from './types'; import { DeferredEntity } from '../processing/types'; -import { RefreshIntervalFunction } from '../next/refresh'; +import { RefreshIntervalFunction } from '../processing/refresh'; import { rethrowError, timestampToDateTime } from './conversion'; import { initDatabaseMetrics } from './metrics'; import { diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts index e1723ff627..2150c10756 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/next/NextCatalogBuilder.ts @@ -73,7 +73,7 @@ import { Stitcher } from '../stitching/Stitcher'; import { createRandomRefreshInterval, RefreshIntervalFunction, -} from './refresh'; +} from '../processing/refresh'; import { createNextRouter } from '../service/NextRouter'; import { DefaultRefreshService } from '../service/DefaultRefreshService'; import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules'; diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/next/index.ts index c3fa4316f5..c37ebf4ff7 100644 --- a/plugins/catalog-backend/src/next/index.ts +++ b/plugins/catalog-backend/src/next/index.ts @@ -16,6 +16,4 @@ export type { CatalogEnvironment } from './NextCatalogBuilder'; export { NextCatalogBuilder } from './NextCatalogBuilder'; -export { createRandomRefreshInterval } from './refresh'; -export type { RefreshIntervalFunction } from './refresh'; export type { LocationStore } from './types'; diff --git a/plugins/catalog-backend/src/processing/index.ts b/plugins/catalog-backend/src/processing/index.ts index 7b60a1670b..dc8b07eceb 100644 --- a/plugins/catalog-backend/src/processing/index.ts +++ b/plugins/catalog-backend/src/processing/index.ts @@ -22,3 +22,6 @@ export type { DeferredEntity, } from './types'; export { DefaultCatalogProcessingOrchestrator } from './DefaultCatalogProcessingOrchestrator'; + +export { createRandomRefreshInterval } from './refresh'; +export type { RefreshIntervalFunction } from './refresh'; diff --git a/plugins/catalog-backend/src/next/refresh.ts b/plugins/catalog-backend/src/processing/refresh.ts similarity index 100% rename from plugins/catalog-backend/src/next/refresh.ts rename to plugins/catalog-backend/src/processing/refresh.ts From 9a6ad82fbae03a668c1cb86f667b0c28a917e9dc Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 13:41:33 +0200 Subject: [PATCH 53/54] Restructure NextcatalogBuilder, util and NextEntitiesCatalog Signed-off-by: Johan Haals --- plugins/catalog-backend/src/index.ts | 1 - .../src/legacy/service/CatalogBuilder.test.ts | 2 +- .../src/legacy/service/CatalogBuilder.ts | 2 +- plugins/catalog-backend/src/next/index.ts | 19 --------------- plugins/catalog-backend/src/next/types.ts | 24 ------------------- .../processing/ProcessorOutputCollector.ts | 2 +- .../providers/ConfigLocationEntityProvider.ts | 2 +- .../src/providers/DefaultLocationStore.ts | 5 ++-- .../service/DefaultLocationService.test.ts | 2 +- .../src/service/DefaultLocationService.ts | 5 ++-- .../{next => service}/NextCatalogBuilder.ts | 8 +++---- .../NextEntitiesCatalog.test.ts | 0 .../{next => service}/NextEntitiesCatalog.ts | 0 plugins/catalog-backend/src/service/index.ts | 9 ++++++- plugins/catalog-backend/src/service/types.ts | 7 ++++++ .../src/{next/util.ts => util/conversion.ts} | 0 16 files changed, 28 insertions(+), 60 deletions(-) delete mode 100644 plugins/catalog-backend/src/next/index.ts delete mode 100644 plugins/catalog-backend/src/next/types.ts rename plugins/catalog-backend/src/{next => service}/NextCatalogBuilder.ts (98%) rename plugins/catalog-backend/src/{next => service}/NextEntitiesCatalog.test.ts (100%) rename plugins/catalog-backend/src/{next => service}/NextEntitiesCatalog.ts (100%) rename plugins/catalog-backend/src/{next/util.ts => util/conversion.ts} (100%) diff --git a/plugins/catalog-backend/src/index.ts b/plugins/catalog-backend/src/index.ts index 5fe0ca2483..7ad888bee4 100644 --- a/plugins/catalog-backend/src/index.ts +++ b/plugins/catalog-backend/src/index.ts @@ -25,7 +25,6 @@ export * from './ingestion'; export * from './legacy'; export * from './search'; export * from './util'; -export * from './next'; export * from './processing'; export * from './providers'; export * from './service'; diff --git a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts index 1b366130ed..926aa67635 100644 --- a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts +++ b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.test.ts @@ -23,7 +23,7 @@ import { DatabaseManager } from '../database'; import { CatalogProcessorParser } from '../../ingestion'; import * as result from '../../ingestion/processors/results'; import { CatalogBuilder } from './CatalogBuilder'; -import { CatalogEnvironment } from '../../next'; +import { CatalogEnvironment } from '../../service'; const dummyEntity = { apiVersion: 'backstage.io/v1alpha1', diff --git a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts index 1a151b77d6..b8e45c7cf3 100644 --- a/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/legacy/service/CatalogBuilder.ts @@ -64,7 +64,7 @@ import { } from '../../ingestion/processors/PlaceholderProcessor'; import { defaultEntityDataParser } from '../../ingestion/processors/util/parse'; import { LocationAnalyzer } from '../../ingestion/types'; -import { CatalogEnvironment, NextCatalogBuilder } from '../../next'; +import { CatalogEnvironment, NextCatalogBuilder } from '../../service'; /** * A builder that helps wire up all of the component parts of the catalog. diff --git a/plugins/catalog-backend/src/next/index.ts b/plugins/catalog-backend/src/next/index.ts deleted file mode 100644 index c37ebf4ff7..0000000000 --- a/plugins/catalog-backend/src/next/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * 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 type { CatalogEnvironment } from './NextCatalogBuilder'; -export { NextCatalogBuilder } from './NextCatalogBuilder'; -export type { LocationStore } from './types'; diff --git a/plugins/catalog-backend/src/next/types.ts b/plugins/catalog-backend/src/next/types.ts deleted file mode 100644 index b5888a5a3a..0000000000 --- a/plugins/catalog-backend/src/next/types.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 { Location, LocationSpec } from '@backstage/catalog-model'; - -export interface LocationStore { - createLocation(spec: LocationSpec): Promise; - listLocations(): Promise; - getLocation(id: string): Promise; - deleteLocation(id: string): Promise; -} diff --git a/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts index 9ff4c9240c..dfaa25418b 100644 --- a/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts +++ b/plugins/catalog-backend/src/processing/ProcessorOutputCollector.ts @@ -23,7 +23,7 @@ import { } from '@backstage/catalog-model'; import { Logger } from 'winston'; import { CatalogProcessorResult } from '../ingestion'; -import { locationSpecToLocationEntity } from '../next/util'; +import { locationSpecToLocationEntity } from '../util/conversion'; import { DeferredEntity } from './types'; import { getEntityLocationRef, diff --git a/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts index e497515035..389cd8d9ff 100644 --- a/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts +++ b/plugins/catalog-backend/src/providers/ConfigLocationEntityProvider.ts @@ -18,7 +18,7 @@ import { Config } from '@backstage/config'; import path from 'path'; import { getEntityLocationRef } from '../processing/util'; import { EntityProvider, EntityProviderConnection } from './types'; -import { locationSpecToLocationEntity } from '../next/util'; +import { locationSpecToLocationEntity } from '../util/conversion'; export class ConfigLocationEntityProvider implements EntityProvider { constructor(private readonly config: Config) {} diff --git a/plugins/catalog-backend/src/providers/DefaultLocationStore.ts b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts index fdd33a422d..9214cd0bc1 100644 --- a/plugins/catalog-backend/src/providers/DefaultLocationStore.ts +++ b/plugins/catalog-backend/src/providers/DefaultLocationStore.ts @@ -21,9 +21,8 @@ import { v4 as uuid } from 'uuid'; import { DbLocationsRow } from '../database/tables'; import { getEntityLocationRef } from '../processing/util'; import { EntityProvider, EntityProviderConnection } from './types'; - -import { locationSpecToLocationEntity } from '../next/util'; -import { LocationStore } from '../next'; +import { locationSpecToLocationEntity } from '../util/conversion'; +import { LocationStore } from '../service'; export class DefaultLocationStore implements LocationStore, EntityProvider { private _connection: EntityProviderConnection | undefined; diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.test.ts b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts index 8d9dee0703..a089989d20 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.test.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.test.ts @@ -16,7 +16,7 @@ import { DefaultLocationService } from './DefaultLocationService'; import { CatalogProcessingOrchestrator } from '../processing/types'; -import { LocationStore } from '../next/types'; +import { LocationStore } from './types'; describe('DefaultLocationServiceTest', () => { const orchestrator: jest.Mocked = { diff --git a/plugins/catalog-backend/src/service/DefaultLocationService.ts b/plugins/catalog-backend/src/service/DefaultLocationService.ts index bc066823db..2166341935 100644 --- a/plugins/catalog-backend/src/service/DefaultLocationService.ts +++ b/plugins/catalog-backend/src/service/DefaultLocationService.ts @@ -24,9 +24,8 @@ import { CatalogProcessingOrchestrator, DeferredEntity, } from '../processing/types'; -import { LocationService } from './types'; -import { LocationStore } from '../next/types'; -import { locationSpecToMetadataName } from '../next/util'; +import { LocationService, LocationStore } from './types'; +import { locationSpecToMetadataName } from '../util/conversion'; export class DefaultLocationService implements LocationService { constructor( diff --git a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts b/plugins/catalog-backend/src/service/NextCatalogBuilder.ts similarity index 98% rename from plugins/catalog-backend/src/next/NextCatalogBuilder.ts rename to plugins/catalog-backend/src/service/NextCatalogBuilder.ts index 2150c10756..60a1668f37 100644 --- a/plugins/catalog-backend/src/next/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/NextCatalogBuilder.ts @@ -65,7 +65,7 @@ import { ConfigLocationEntityProvider } from '../providers/ConfigLocationEntityP import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { applyDatabaseMigrations } from '../database/migrations'; import { DefaultCatalogProcessingEngine } from '../processing/DefaultCatalogProcessingEngine'; -import { DefaultLocationService } from '../service/DefaultLocationService'; +import { DefaultLocationService } from './DefaultLocationService'; import { DefaultLocationStore } from '../providers/DefaultLocationStore'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; import { DefaultCatalogProcessingOrchestrator } from '../processing/DefaultCatalogProcessingOrchestrator'; @@ -74,12 +74,12 @@ import { createRandomRefreshInterval, RefreshIntervalFunction, } from '../processing/refresh'; -import { createNextRouter } from '../service/NextRouter'; -import { DefaultRefreshService } from '../service/DefaultRefreshService'; +import { createNextRouter } from './NextRouter'; +import { DefaultRefreshService } from './DefaultRefreshService'; import { DefaultCatalogRulesEnforcer } from '../ingestion/CatalogRules'; import { Config } from '@backstage/config'; import { Logger } from 'winston'; -import { LocationService } from '../service/types'; +import { LocationService } from './types'; export type CatalogEnvironment = { logger: Logger; diff --git a/plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts similarity index 100% rename from plugins/catalog-backend/src/next/NextEntitiesCatalog.test.ts rename to plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts diff --git a/plugins/catalog-backend/src/next/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts similarity index 100% rename from plugins/catalog-backend/src/next/NextEntitiesCatalog.ts rename to plugins/catalog-backend/src/service/NextEntitiesCatalog.ts diff --git a/plugins/catalog-backend/src/service/index.ts b/plugins/catalog-backend/src/service/index.ts index f47d796693..5345f79628 100644 --- a/plugins/catalog-backend/src/service/index.ts +++ b/plugins/catalog-backend/src/service/index.ts @@ -14,6 +14,13 @@ * limitations under the License. */ -export type { LocationService, RefreshService, RefreshOptions } from './types'; +export type { + LocationService, + RefreshService, + RefreshOptions, + LocationStore, +} from './types'; export { createNextRouter } from './NextRouter'; export type { NextRouterOptions } from './NextRouter'; +export type { CatalogEnvironment } from './NextCatalogBuilder'; +export { NextCatalogBuilder } from './NextCatalogBuilder'; diff --git a/plugins/catalog-backend/src/service/types.ts b/plugins/catalog-backend/src/service/types.ts index eedc688bb3..05d3d00f4d 100644 --- a/plugins/catalog-backend/src/service/types.ts +++ b/plugins/catalog-backend/src/service/types.ts @@ -47,3 +47,10 @@ export interface RefreshService { */ refresh(options: RefreshOptions): Promise; } + +export interface LocationStore { + createLocation(spec: LocationSpec): Promise; + listLocations(): Promise; + getLocation(id: string): Promise; + deleteLocation(id: string): Promise; +} diff --git a/plugins/catalog-backend/src/next/util.ts b/plugins/catalog-backend/src/util/conversion.ts similarity index 100% rename from plugins/catalog-backend/src/next/util.ts rename to plugins/catalog-backend/src/util/conversion.ts From 55ff928d5017c832e2203f4fc5197ff8157436b4 Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 12 Oct 2021 13:45:04 +0200 Subject: [PATCH 54/54] Add changeset Signed-off-by: Johan Haals --- .changeset/six-pumpkins-lie.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/six-pumpkins-lie.md diff --git a/.changeset/six-pumpkins-lie.md b/.changeset/six-pumpkins-lie.md new file mode 100644 index 0000000000..7f28582d91 --- /dev/null +++ b/.changeset/six-pumpkins-lie.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +This change refactors the internal package structure to remove the `next` catalog folder that was used during the implementation and testing phase of the new catalog engine. The implementation is now the default and is therefore restructured to no longer be packaged under `next/`. This refactor does not change catalog imports from other parts of the project.