Merge branch 'master' into feat/BCKSTG-93-paragraphs

Signed-off-by: Carlos Esteban Lopez Jaramillo <luchillo17@gmail.com>
This commit is contained in:
Carlos Esteban Lopez Jaramillo
2023-01-11 13:31:50 -05:00
committed by GitHub
102 changed files with 1585 additions and 623 deletions
@@ -23,6 +23,7 @@ import {
ListItemText,
makeStyles,
} from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { parseEntityRef } from '@backstage/catalog-model';
import { Link } from '@backstage/core-components';
import { useAnalytics } from '@backstage/core-plugin-api';
@@ -83,7 +84,8 @@ export function AdrSearchResultListItem(props: {
</Link>
}
secondary={
<span
<Typography
component="span"
style={{
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
@@ -100,7 +102,7 @@ export function AdrSearchResultListItem(props: {
) : (
result.text
)}
</span>
</Typography>
}
/>
<Box>
@@ -15,6 +15,7 @@
*/
import { ApiEntity } from '@backstage/catalog-model';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { apiDocsConfigRef } from '../../config';
import { useApi } from '@backstage/core-plugin-api';
@@ -28,5 +29,5 @@ export const ApiTypeTitle = (props: { apiEntity: ApiEntity }) => {
const definition = config.getApiDefinitionWidget(apiEntity);
const type = definition ? definition.title : apiEntity.spec.type;
return <span>{type}</span>;
return <Typography component="span">{type}</Typography>;
};
@@ -42,34 +42,34 @@ export const getBuildResultComponent = (result: number | undefined) => {
switch (result) {
case BuildResult.Succeeded:
return (
<span>
<Typography component="span">
<StatusOK /> Succeeded
</span>
</Typography>
);
case BuildResult.PartiallySucceeded:
return (
<span>
<Typography component="span">
<StatusWarning /> Partially Succeeded
</span>
</Typography>
);
case BuildResult.Failed:
return (
<span>
<Typography component="span">
<StatusError /> Failed
</span>
</Typography>
);
case BuildResult.Canceled:
return (
<span>
<Typography component="span">
<StatusAborted /> Canceled
</span>
</Typography>
);
case BuildResult.None:
default:
return (
<span>
<Typography component="span">
<StatusWarning /> Unknown
</span>
</Typography>
);
}
};
@@ -81,36 +81,36 @@ export const getBuildStateComponent = (
switch (status) {
case BuildStatus.InProgress:
return (
<span>
<Typography component="span">
<StatusRunning /> In Progress
</span>
</Typography>
);
case BuildStatus.Completed:
return getBuildResultComponent(result);
case BuildStatus.Cancelling:
return (
<span>
<Typography component="span">
<StatusAborted /> Cancelling
</span>
</Typography>
);
case BuildStatus.Postponed:
return (
<span>
<Typography component="span">
<StatusPending /> Postponed
</span>
</Typography>
);
case BuildStatus.NotStarted:
return (
<span>
<Typography component="span">
<StatusAborted /> Not Started
</span>
</Typography>
);
case BuildStatus.None:
default:
return (
<span>
<Typography component="span">
<StatusWarning /> Unknown
</span>
</Typography>
);
}
};
@@ -16,6 +16,7 @@
import { Avatar, Link } from '@backstage/core-components';
import { Card, CardContent, CardHeader } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { AutoCompleteIcon } from '../AutoCompleteIcon';
import { DashboardPullRequest } from '@backstage/plugin-azure-devops-common';
@@ -78,9 +79,9 @@ export const PullRequestCard = ({
: undefined;
const subheader = (
<span>
<Typography component="span">
{repoLink} · {creationDate}
</span>
</Typography>
);
const avatar = (
@@ -20,6 +20,7 @@ import {
PolicyType,
} from '@backstage/plugin-azure-devops-common';
import { styled, withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import CancelIcon from '@material-ui/icons/Cancel';
import GroupWorkIcon from '@material-ui/icons/GroupWork';
@@ -91,6 +92,7 @@ export const PullRequestCardPolicy = ({
policy,
}: PullRequestCardPolicyProps) => (
<PullRequestCardPolicyContainer>
{getPolicyIcon(policy)} <span>{policy.text}</span>
{getPolicyIcon(policy)}{' '}
<Typography component="span">{policy.text}</Typography>
</PullRequestCardPolicyContainer>
);
@@ -33,6 +33,7 @@ import PublicIcon from '@material-ui/icons/Public';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import StartIcon from '@material-ui/icons/PlayArrow';
import StopIcon from '@material-ui/icons/Stop';
import Typography from '@material-ui/core/Typography';
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
import { DateTime } from 'luxon';
import { useApi } from '@backstage/core-plugin-api';
@@ -51,7 +52,8 @@ const State = ({ value }: { value: States }) => {
};
return (
<Box display="flex" alignItems="center">
<span
<Typography
component="span"
style={{
display: 'block',
width: '8px',
@@ -16,6 +16,7 @@
import React from 'react';
import { Entity } from '@backstage/catalog-model';
import Typography from '@material-ui/core/Typography';
import { Autocomplete } from '@material-ui/lab';
import { TextField, makeStyles } from '@material-ui/core';
@@ -49,7 +50,9 @@ export const ProjectSelector = ({
defaultValue={defaultValue}
options={catalogEntities}
getOptionLabel={option => option?.metadata?.name}
renderOption={option => <span>{option?.metadata?.name}</span>}
renderOption={option => (
<Typography component="span">{option?.metadata?.name}</Typography>
)}
renderInput={params => <TextField {...params} label={label} />}
onChange={(_, data) => {
onChange(data!);
@@ -577,13 +577,15 @@ export class IncrementalIngestionDatabaseManager {
.update('ingestion_mark_id', markId)
.whereIn('ref', existingRefsArray);
await tx('ingestion_mark_entities').insert(
newRefs.map(ref => ({
id: v4(),
ingestion_mark_id: markId,
ref,
})),
);
if (newRefs.length > 0) {
await tx('ingestion_mark_entities').insert(
newRefs.map(ref => ({
id: v4(),
ingestion_mark_id: markId,
ref,
})),
);
}
});
}
@@ -125,11 +125,15 @@ export const EntityPeekAheadPopover = (props: EntityPeekAheadPopoverProps) => {
return (
<>
<span onMouseEnter={debouncedHandleMouseEnter}>
<span data-testid="trigger" {...bindHover(popupState)}>
<Typography component="span" onMouseEnter={debouncedHandleMouseEnter}>
<Typography
component="span"
data-testid="trigger"
{...bindHover(popupState)}
>
{children}
</span>
</span>
</Typography>
</Typography>
{isHovered && (
<HoverPopover
PaperProps={{
@@ -22,6 +22,7 @@ import {
MenuItem,
MenuList,
Popover,
Tooltip,
} from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import BugReportIcon from '@material-ui/icons/BugReport';
@@ -114,19 +115,21 @@ export function EntityContextMenu(props: EntityContextMenuProps) {
return (
<>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
aria-expanded={!!anchorEl}
role="button"
onClick={onOpen}
data-testid="menu-button"
className={classes.button}
id="long-menu"
>
<MoreVert />
</IconButton>
<Tooltip title="More" arrow>
<IconButton
aria-label="more"
aria-controls="long-menu"
aria-haspopup="true"
aria-expanded={!!anchorEl}
role="button"
onClick={onOpen}
data-testid="menu-button"
className={classes.button}
id="long-menu"
>
<MoreVert />
</IconButton>
</Tooltip>
<Popover
open={Boolean(anchorEl)}
onClose={onClose}
@@ -118,9 +118,9 @@ export function StatusChart(props: StatusChartProps) {
: percentOrCount;
return [
<span>
<Typography component="span">
{label}: {valueText}
</span>,
</Typography>,
null,
];
};
@@ -18,6 +18,7 @@ import React, { CSSProperties } from 'react';
import { DateTime, Duration } from 'luxon';
import humanizeDuration from 'humanize-duration';
import { capitalize } from 'lodash';
import Typography from '@material-ui/core/Typography';
const infoText: CSSProperties = { color: 'InfoText' };
@@ -60,11 +61,19 @@ function formatDateTimeShort(milliseconds: number) {
}
export function labelFormatter(epoch: number) {
return <span style={infoText}>{formatDateTimeShort(epoch)}</span>;
return (
<Typography component="span" style={infoText}>
{formatDateTimeShort(epoch)}
</Typography>
);
}
export function labelFormatterWithoutTime(epoch: number) {
return <span style={infoText}>{formatDateShort(epoch)}</span>;
return (
<Typography component="span" style={infoText}>
{formatDateShort(epoch)}
</Typography>
);
}
export function tickFormatterX(epoch: number) {
@@ -88,12 +97,12 @@ export function tickFormatterY(duration: number) {
export function tooltipValueFormatter(durationOrCount: number, name: string) {
return [
<span style={infoText}>
<Typography component="span" style={infoText}>
{capitalize(name)}:{' '}
{name.endsWith(' count')
? durationOrCount
: formatDuration(durationOrCount)}
</span>,
</Typography>,
null,
];
}
@@ -36,9 +36,9 @@ export const AlertInsightsHeader = ({
<ScrollAnchor id={DefaultNavigation.AlertInsightsHeader} />
<Typography variant="h4" align="center">
{title}{' '}
<span role="img" aria-label="direct-hit">
<Typography component="span" role="img" aria-label="direct-hit">
🎯
</span>
</Typography>
</Typography>
<Typography className={classes.h6Subtle} align="center" gutterBottom>
{subtitle}
@@ -16,6 +16,7 @@
import React from 'react';
import classnames from 'classnames';
import Typography from '@material-ui/core/Typography';
import { CurrencyType, Duration, GrowthType } from '../../types';
import { ChangeStatistic } from '@backstage/plugin-cost-insights-common';
import { rateOf } from '../../utils/currency';
@@ -59,48 +60,52 @@ export const CostGrowth = (props: CostGrowthProps) => {
});
if (engineers < engineerThreshold) {
return <span className={classes}>Negligible</span>;
return (
<Typography component="span" className={classes}>
Negligible
</Typography>
);
}
if (currency.kind === CurrencyType.USD) {
// Do not display percentage if ratio cannot be calculated
if (isNaN(ratio)) {
return (
<span className={classes}>
<Typography component="span" className={classes}>
~{currency.prefix}
{formatCurrency(converted)}
</span>
</Typography>
);
}
return (
<span className={classes}>
<Typography component="span" className={classes}>
{formatPercent(ratio)} or ~{currency.prefix}
{formatCurrency(converted)}
</span>
</Typography>
);
}
if (amount < 1) {
return (
<span className={classes}>
<Typography component="span" className={classes}>
less than {indefiniteArticleOf(['a', 'an'], currency.unit)}
</span>
</Typography>
);
}
// Do not display percentage if ratio cannot be calculated
if (isNaN(ratio)) {
return (
<span className={classes}>
<Typography component="span" className={classes}>
~{formatCurrency(converted, currency.unit)}
</span>
</Typography>
);
}
return (
<span className={classes}>
<Typography component="span" className={classes}>
{formatPercent(ratio)} or ~{formatCurrency(converted, currency.unit)}
</span>
</Typography>
);
};
@@ -46,9 +46,9 @@ const CostInsightsHeaderNoData = ({
return (
<>
<Typography variant="h4" align="center" gutterBottom>
<span role="img" aria-label="flushed-face">
<Typography component="span" role="img" aria-label="flushed-face">
😳
</span>{' '}
</Typography>{' '}
Well this is awkward
</Typography>
<Typography className={classes.h6Subtle} align="center" gutterBottom>
@@ -76,9 +76,9 @@ const CostInsightsHeaderAlerts = ({
return (
<>
<Typography variant="h4" align="center" gutterBottom>
<span role="img" aria-label="magnifying-glass">
<Typography component="span" role="img" aria-label="magnifying-glass">
🔎
</span>{' '}
</Typography>{' '}
You have {alerts} thing{alerts > 1 && 's'} to look into
</Typography>
<Typography className={classes.h6Subtle} align="center" gutterBottom>
@@ -101,9 +101,9 @@ const CostInsightsHeaderNoAlerts = ({
return (
<>
<Typography variant="h4" gutterBottom align="center">
<span role="img" aria-label="thumbs-up">
<Typography component="span" role="img" aria-label="thumbs-up">
👍
</span>{' '}
</Typography>{' '}
Your team is doing great
</Typography>
<Typography className={classes.h6Subtle} align="center" gutterBottom>
@@ -120,9 +120,9 @@ export const CostInsightsHeaderNoGroups = () => {
return (
<>
<Typography variant="h4" align="center" gutterBottom>
<span role="img" aria-label="flushed-face">
<Typography component="span" role="img" aria-label="flushed-face">
😳
</span>{' '}
</Typography>{' '}
Well this is awkward
</Typography>
<Typography className={classes.h6Subtle} align="center" gutterBottom>
@@ -22,6 +22,7 @@ import {
Select,
SelectProps,
} from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { Currency, CurrencyType } from '../../types';
import { findAlways } from '../../utils/assert';
import { useSelectStyles as useStyles } from '../../utils/styles';
@@ -73,9 +74,9 @@ export const CurrencySelect = ({
key={c.kind || NULL_VALUE}
value={c.kind || NULL_VALUE}
>
<span role="img" aria-label={c.label}>
<Typography component="span" role="img" aria-label={c.label}>
{c.label}
</span>
</Typography>
</MenuItem>
))}
</Select>
@@ -59,9 +59,14 @@ export const LegendItem = (props: PropsWithChildren<LegendItemProps>) => {
}
placement="top-start"
>
<span role="img" aria-label="help" className={classes.helpIcon}>
<Typography
component="span"
role="img"
aria-label="help"
className={classes.helpIcon}
>
<HelpOutlineOutlinedIcon fontSize="small" />
</span>
</Typography>
</Tooltip>
)}
</Box>
@@ -30,9 +30,9 @@ export const WhyCostsMatter = () => {
<Grid item>
<Typography variant="h6">
Sustainability{' '}
<span role="img" aria-label="globe">
<Typography component="span" role="img" aria-label="globe">
🌎
</span>
</Typography>
</Typography>
<Typography>
Reducing cloud usage improves our carbon footprint.
@@ -44,9 +44,13 @@ export const WhyCostsMatter = () => {
<Grid item>
<Typography variant="h6">
Revenue{' '}
<span role="img" aria-label="money-with-wings">
<Typography
component="span"
role="img"
aria-label="money-with-wings"
>
💸
</span>
</Typography>
</Typography>
<Typography>
Keeping cloud costs well-tuned prevents infrastructure from eating
@@ -59,9 +63,9 @@ export const WhyCostsMatter = () => {
<Grid item>
<Typography variant="h6">
Innovation{' '}
<span role="img" aria-label="medal">
<Typography component="span" role="img" aria-label="medal">
🥇
</span>
</Typography>
</Typography>
<Typography>
The more we save, the more we can reinvest in speed and
@@ -16,6 +16,7 @@
import React from 'react';
import pluralize from 'pluralize';
import Typography from '@material-ui/core/Typography';
import { KubernetesMigrationAlertCard } from '../components';
import { CostInsightsApi } from '../../api';
import {
@@ -91,11 +92,11 @@ export class KubernetesMigrationAlert implements KubernetesMigrationApi {
get title() {
return (
<span>
<Typography component="span">
Consider migrating{' '}
{pluralize('service', this.data.services.length, true)} to Kubernetes{' '}
<Lifecycle shorthand />
</span>
</Typography>
);
}
@@ -192,7 +192,10 @@ export const ServiceDetailsCard = () => {
{!showServiceDetails && !loading && (
<div className={classes.warning}>
<WarningIcon />
&nbsp;&nbsp;<span>This service does not exist in FireHydrant.</span>
&nbsp;&nbsp;
<Typography component="span">
This service does not exist in FireHydrant.
</Typography>
</div>
)}
{showServiceDetails && (
@@ -251,7 +254,7 @@ export const ServiceDetailsCard = () => {
<AddIcon className={classes.icon} />
</Box>
<Box>
<span>Declare an incident</span>
<Typography component="span">Declare an incident</Typography>
</Box>
</Box>
</MaterialButton>
@@ -267,7 +270,7 @@ export const ServiceDetailsCard = () => {
<WhatshotIcon className={classes.icon} />
</Box>
<Box>
<span>View all incidents</span>
<Typography component="span">View all incidents</Typography>
</Box>
</Box>
</MaterialButton>
@@ -284,7 +287,7 @@ export const ServiceDetailsCard = () => {
<NotesIcon className={classes.icon} />
</Box>
<Box>
<span>View Service Details</span>
<Typography component="span">View Service Details</Typography>
</Box>
</Box>
</MaterialButton>
@@ -18,6 +18,7 @@ import { useEntity } from '@backstage/plugin-catalog-react';
import { Grid, Tooltip } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import { DateTime } from 'luxon';
import React, { PropsWithChildren } from 'react';
import useAsync from 'react-use/lib/useAsync';
@@ -199,7 +200,9 @@ export const FossaCard = (props: { variant?: InfoCardVariants }) => {
<Grid item className={classes.lastAnalyzed}>
Based on {value.dependencyCount} Dependencies on branch{' '}
<Tooltip title="The default branch can be changed by a FOSSA admin.">
<span className={classes.branch}>{value.projectDefaultBranch}</span>
<Typography className={classes.branch}>
{value.projectDefaultBranch}
</Typography>
</Tooltip>
.
</Grid>
@@ -27,6 +27,7 @@ import {
getEntityRelations,
} from '@backstage/plugin-catalog-react';
import { Tooltip } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { Skeleton } from '@material-ui/lab';
import { DateTime } from 'luxon';
import * as React from 'react';
@@ -140,11 +141,11 @@ const columns: TableColumn<FossaRow>[] = [
DateTime.DATETIME_MED,
)}
>
<span>
<Typography component="span">
{DateTime.fromISO(details.timestamp).toRelative({
locale: 'en',
})}
</span>
</Typography>
</Tooltip>
)
);
@@ -39,11 +39,17 @@ import { Link as RouterLink } from 'react-router-dom';
const LongText = ({ text, max }: { text: string; max: number }) => {
if (text.length < max) {
return <span>{text}</span>;
return (
<Typography color="primary" component="span">
{text}
</Typography>
);
}
return (
<Tooltip title={text}>
<span>{text.slice(0, max)}...</span>
<Typography color="primary" component="span">
{text.slice(0, max)}...
</Typography>
</Tooltip>
);
};
@@ -16,6 +16,7 @@
import React, { ReactNode } from 'react';
import { grey } from '@material-ui/core/colors';
import Typography from '@material-ui/core/Typography';
import CallSplitIcon from '@material-ui/icons/CallSplit';
import ChatIcon from '@material-ui/icons/Chat';
import DynamicFeedIcon from '@material-ui/icons/DynamicFeed';
@@ -35,29 +36,31 @@ export const Differ = ({ current, next, icon }: DifferProps) => {
return (
<>
{icon && (
<span>
<Typography component="span">
<Icon icon={icon} />{' '}
</span>
</Typography>
)}
{!!current && (
<span
<Typography
component="span"
data-testid={TEST_IDS.components.differ.current}
style={{ color: grey[700] }}
>
{current ?? 'None'}
</span>
</Typography>
)}
{current && next && <span>{' → '}</span>}
{current && next && <Typography component="span">{' → '}</Typography>}
{next && (
<span
<Typography
component="span"
data-testid={TEST_IDS.components.differ.next}
style={{ fontWeight: 'bold' }}
>
{next}
</span>
</Typography>
)}
</>
);
@@ -50,8 +50,9 @@ export function LinearProgressWithLabel(props: {
const CompletionEmoji = () => {
if (status === STATUSES.ONGOING) return null;
if (status === STATUSES.FAILURE) return <span>{` ${ICONS.FAILURE} `}</span>;
return <span>{` ${ICONS.SUCCESS} `}</span>;
if (status === STATUSES.FAILURE)
return <Typography component="span">{` ${ICONS.FAILURE} `}</Typography>;
return <Typography component="span">{` ${ICONS.SUCCESS} `}</Typography>;
};
return (
@@ -42,6 +42,7 @@ describe('Info', () => {
expect(await findByText(mockReleaseBranch.name)).toMatchInlineSnapshot(`
<span
class="MuiTypography-root MuiTypography-body1"
data-testid="grm--differ-next"
style="font-weight: bold;"
>
@@ -53,6 +54,7 @@ describe('Info', () => {
await findByText(`${mockCalverProject.owner}/${mockCalverProject.repo}`),
).toMatchInlineSnapshot(`
<span
class="MuiTypography-root MuiTypography-body1"
data-testid="grm--differ-next"
style="font-weight: bold;"
>
@@ -63,6 +65,7 @@ describe('Info', () => {
expect(await findByText(mockCalverProject.versioningStrategy))
.toMatchInlineSnapshot(`
<span
class="MuiTypography-root MuiTypography-body1"
data-testid="grm--differ-next"
style="font-weight: bold;"
>
@@ -73,6 +76,7 @@ describe('Info', () => {
expect(await findByText(mockReleaseCandidateCalver.tagName))
.toMatchInlineSnapshot(`
<span
class="MuiTypography-root MuiTypography-body1"
data-testid="grm--differ-next"
style="font-weight: bold;"
>
@@ -75,12 +75,13 @@ describe('useResponseSteps', () => {
"message": <b>
Something went wrong
<span
<WithStyles(ForwardRef(Typography))
aria-label="fire"
component="span"
role="img"
>
🔥
</span>
</WithStyles(ForwardRef(Typography))>
</b>,
"secondaryMessage": "Error message: :(",
},
@@ -108,12 +109,13 @@ describe('useResponseSteps', () => {
"message": <b>
Something went wrong
<span
<WithStyles(ForwardRef(Typography))
aria-label="fire"
component="span"
role="img"
>
🔥
</span>
</WithStyles(ForwardRef(Typography))>
</b>,
"secondaryMessage": "Error message: unknown",
},
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import Typography from '@material-ui/core/Typography';
import React, { useState } from 'react';
import { ResponseStep } from '../types/types';
@@ -30,9 +31,9 @@ export function useResponseSteps() {
message: (
<b>
Something went wrong{' '}
<span role="img" aria-label="fire">
<Typography component="span" role="img" aria-label="fire">
🔥
</span>
</Typography>
</b>
),
secondaryMessage: `Error message: ${
@@ -19,6 +19,7 @@ import {
useApi,
} from '@backstage/core-plugin-api';
import { Tooltip } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import React, { useEffect, useMemo } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { getTimeBasedGreeting } from './timeUtil';
@@ -43,9 +44,9 @@ export const WelcomeTitle = () => {
return (
<Tooltip title={greeting.language}>
<span>{`${greeting.greeting}${
<Typography component="span">{`${greeting.greeting}${
profile?.displayName ? `, ${profile?.displayName}` : ''
}!`}</span>
}!`}</Typography>
</Tooltip>
);
};
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import React, { useState, useEffect } from 'react';
import Typography from '@material-ui/core/Typography';
import { Website, lighthouseApiRef } from '../../api';
import useInterval from 'react-use/lib/useInterval';
import {
@@ -112,7 +113,9 @@ export const AuditListTable = ({ items }: { items: Website[] }) => {
lastReport: (
<>
<AuditStatusIcon audit={website.lastAudit} />{' '}
<span>{website.lastAudit.status.toLocaleUpperCase('en-US')}</span>
<Typography component="span">
{website.lastAudit.status.toLocaleUpperCase('en-US')}
</Typography>
</>
),
lastAuditTriggered: formatTime(website.lastAudit.timeCreated),
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import React from 'react';
import Typography from '@material-ui/core/Typography';
import { Audit, AuditCompleted, LighthouseCategoryId } from '../../api';
import { useWebsiteForEntity } from '../../hooks/useWebsiteForEntity';
import AuditStatusIcon from '../AuditStatusIcon';
@@ -52,7 +53,7 @@ const LighthouseCategoryScoreStatus = (props: { score: number }) => {
</>
);
default:
return <span>N/A</span>;
return <Typography component="span">N/A</Typography>;
}
};
@@ -128,6 +128,12 @@ export function createFetchRailsAction(options: {
description: 'Skip Action Text gem',
type: 'boolean',
},
skipActiveRecord: {
title: 'skipActiveRecord',
description: 'Skip Active Record files',
type: 'boolean',
},
force: {
title: 'force',
description: 'Overwrite files that already exist',
@@ -33,6 +33,7 @@ describe('railsArgumentResolver', () => {
[{ skipActionMailbox: true }, ['--skip-action-mailbox']],
[{ skipActiveStorage: true }, ['--skip-active-storage']],
[{ skipActionText: true }, ['--skip-action-text']],
[{ skipActiveRecord: true }, ['--skip-active-record']],
[{ force: true }, ['--force']],
[{ webpacker: 'vue' }, ['--webpack', 'vue']],
[{ database: 'postgresql' }, ['--database', 'postgresql']],
@@ -57,6 +57,7 @@ export type RailsRunOptions = {
skipBundle?: boolean;
skipTest?: boolean;
skipWebpackInstall?: boolean;
skipActiveRecord?: boolean;
template?: string;
webpacker?: Webpacker;
};
@@ -84,6 +85,10 @@ export const railsArgumentResolver = (
argumentsToRun.push('--skip-webpack-install');
}
if (options?.skipActiveRecord) {
argumentsToRun.push('--skip-active-record');
}
if (options?.skipTest) {
argumentsToRun.push('--skip-test');
}
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
@@ -0,0 +1,160 @@
# scaffolder-backend-module-sentry
Welcome to the Sentry Module for Scaffolder.
Here you can find all Sentry related features to improve your scaffolder:
## Getting started
You need to configure the action in your backend:
## From your Backstage root directory
```bash
# From your Backstage root directory
yarn add --cwd packages/backend @backstage/plugin-scaffolder-backend-module-sentry
```
Configure the action (you can check
the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to
see all options):
```typescript
const actions = [
createSentryCreateProjectAction({
integrations,
reader: env.reader,
containerRunner,
}),
];
return await createRouter({
containerRunner,
catalogClient,
actions,
logger: env.logger,
config: env.config,
database: env.database,
reader: env.reader,
});
```
You need to define your Sentry API Token in your `app-config.yaml`:
```yaml
scaffolder:
sentry:
token: ${SENTRY_TOKEN}
```
After that you can use the action in your template:
```yaml
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: sentry-demo
title: Sentry template
description: scaffolder sentry app
spec:
owner: backstage/techdocs-core
type: service
parameters:
- title: Fill in some steps
required:
- name
- owner
properties:
name:
title: Name
type: string
description: Unique name of the component
ui:autofocus: true
ui:options:
rows: 5
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
catalogFilter:
kind: Group
system:
title: System
type: string
description: System of the component
ui:field: EntityPicker
ui:options:
catalogFilter:
kind: System
defaultKind: System
- title: Choose a location
required:
- repoUrl
- dryRun
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
dryRun:
title: Only perform a dry run, don't publish anything
type: boolean
default: false
steps:
- id: fetch
name: Fetch
action: fetch:template
input:
url: https://github.com/TEMPLATE
values:
name: ${{ parameters.name }}
- id: create-sentry-project
if: ${{ parameters.dryRun !== true }}
name: Create Sentry Project
action: sentry:create-project
input:
organizationSlug: ORG-SLUG
teamSlug: TEAM-SLUG
name: ${{ parameters.name }}
- id: publish
if: ${{ parameters.dryRun !== true }}
name: Publish
action: publish:github
input:
allowedHosts:
- github.com
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}
- id: register
if: ${{ parameters.dryRun !== true }}
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'
- name: Results
if: ${{ parameters.dryRun }}
action: debug:log
input:
listWorkspace: true
output:
links:
- title: Repository
url: ${{ steps['publish'].output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps['register'].output.entityRef }}
```
@@ -0,0 +1,21 @@
## API Report File for "@backstage/plugin-scaffolder-backend-module-sentry"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { Config } from '@backstage/config';
import { TemplateAction } from '@backstage/plugin-scaffolder-backend';
// @public
export function createSentryCreateProjectAction(options: {
config: Config;
}): TemplateAction<{
organizationSlug: string;
teamSlug: string;
name: string;
slug?: string | undefined;
authToken?: string | undefined;
}>;
// (No @packageDocumentation comment for this package)
```
@@ -0,0 +1,48 @@
{
"name": "@backstage/plugin-scaffolder-backend-module-sentry",
"version": "0.0.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin"
},
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack"
},
"dependencies": {
"@backstage/config": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/integration": "workspace:^",
"@backstage/plugin-scaffolder-backend": "workspace:^"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@backstage/core-app-api": "workspace:^",
"@backstage/dev-utils": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"@types/node": "*",
"cross-fetch": "^3.1.5",
"msw": "^0.49.0"
},
"files": [
"dist"
]
}
@@ -0,0 +1,122 @@
/*
* 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 { createTemplateAction } from '@backstage/plugin-scaffolder-backend';
import { InputError } from '@backstage/errors';
import { Config } from '@backstage/config';
/**
* Creates the `sentry:craete-project` Scaffolder action.
*
* @remarks
*
* See {@link https://backstage.io/docs/features/software-templates/writing-custom-actions}.
*
* @param options - Configuration of the Sentry API.
* @public
*/
export function createSentryCreateProjectAction(options: { config: Config }) {
const { config } = options;
return createTemplateAction<{
organizationSlug: string;
teamSlug: string;
name: string;
slug?: string;
authToken?: string;
}>({
id: 'sentry:project:create',
schema: {
input: {
required: ['organizationSlug', 'teamSlug', 'name'],
type: 'object',
properties: {
organizationSlug: {
title: 'The slug of the organization the team belongs to',
type: 'string',
},
teamSlug: {
title: 'The slug of the team to create a new project for',
type: 'string',
},
name: {
title: 'The name for the new project',
type: 'string',
},
slug: {
title:
'Optional slug for the new project. If not provided a slug is generated from the name',
type: 'string',
},
authToken: {
title:
'authenticate via bearer auth token. Requires scope: project:write',
type: 'string',
},
},
},
},
async handler(ctx) {
const { organizationSlug, teamSlug, name, slug, authToken } = ctx.input;
const body: any = {
name: name,
};
if (slug) {
body.slug = slug;
}
const token = authToken
? authToken
: config.getOptionalString('scaffolder.sentry.token');
if (!token) {
throw new InputError(`No valid sentry token given`);
}
const response = await fetch(
`https://sentry.io/api/0/teams/${organizationSlug}/${teamSlug}/projects/`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
},
);
const contentType = response.headers.get('content-type');
if (contentType !== 'application/json') {
throw new InputError(
`Unexpected Sentry Response Type: ${await response.text()}`,
);
}
const code = response.status;
const result = await response.json();
if (code !== 201) {
throw new InputError(`Sentry Response was: ${await result.detail}`);
}
ctx.output('id', result.id);
ctx.output('result', result);
},
});
}
@@ -0,0 +1,17 @@
/*
* 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 { createSentryCreateProjectAction } from './actions/createProject';
@@ -28,6 +28,7 @@ import {
Box,
Divider,
} from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { Link } from '@backstage/core-components';
/**
@@ -85,7 +86,8 @@ export const DefaultResultListItemComponent = ({
</Link>
}
secondary={
<span
<Typography
component="span"
style={{
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
@@ -102,7 +104,7 @@ export const DefaultResultListItemComponent = ({
) : (
result.text
)}
</span>
</Typography>
}
/>
{secondaryAction && <Box alignItems="flex-end">{secondaryAction}</Box>}
@@ -26,6 +26,7 @@ import {
Paper,
useTheme,
} from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import LaunchIcon from '@material-ui/icons/Launch';
import { makeStyles } from '@material-ui/core/styles';
import {
@@ -131,7 +132,9 @@ export const Modal = ({ toggleModal }: SearchModalProps) => {
}}
to={`${getSearchLink()}?query=${term}`}
>
<span className={classes.viewResultsLink}>View Full Results</span>
<Typography component="span" className={classes.viewResultsLink}>
View Full Results
</Typography>
<LaunchIcon color="primary" />
</Link>
</Grid>
@@ -16,6 +16,7 @@
import { BackstageTheme } from '@backstage/theme';
import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import React from 'react';
const useStyles = makeStyles((theme: BackstageTheme) => {
@@ -29,5 +30,9 @@ const useStyles = makeStyles((theme: BackstageTheme) => {
export const Value = (props: { value?: string }) => {
const classes = useStyles();
return <span className={classes.value}>{props.value}</span>;
return (
<Typography component="span" className={classes.value}>
{props.value}
</Typography>
);
};
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { ClassNameMap } from '@material-ui/core/styles/withStyles';
import Typography from '@material-ui/core/Typography';
import React from 'react';
import { WithLink } from '../../utils/components';
import { RadarDescription } from '../RadarDescription';
@@ -50,17 +51,21 @@ export const RadarLegendLink = ({
if (description) {
return (
<>
<span
<Typography
component="span"
className={classes.entryLink}
onClick={handleClickOpen}
role="button"
tabIndex={0}
onKeyPress={toggle}
>
<span className={active ? classes.activeEntry : classes.entry}>
<Typography
component="span"
className={active ? classes.activeEntry : classes.entry}
>
{title}
</span>
</span>
</Typography>
</Typography>
{open && (
<RadarDescription
open={open}
@@ -75,9 +80,12 @@ export const RadarLegendLink = ({
}
return (
<WithLink url={url} className={classes.entryLink}>
<span className={active ? classes.activeEntry : classes.entry}>
<Typography
component="span"
className={active ? classes.activeEntry : classes.entry}
>
{title}
</span>
</Typography>
</WithLink>
);
};
@@ -22,6 +22,7 @@ import {
ListItemText,
makeStyles,
} from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import { Link } from '@backstage/core-components';
import { useAnalytics } from '@backstage/core-plugin-api';
import { ResultHighlight } from '@backstage/plugin-search-common';
@@ -137,7 +138,8 @@ export const TechDocsSearchResultListItem = (
</LinkWrapper>
}
secondary={
<span
<Typography
component="span"
style={{
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
@@ -154,7 +156,7 @@ export const TechDocsSearchResultListItem = (
) : (
result.text
)}
</span>
</Typography>
}
/>
);
@@ -89,32 +89,30 @@ export const ProviderSettingsItem = (props: {
primary={title}
secondary={
<Tooltip placement="top" arrow title={description}>
<span>
<Grid container spacing={6}>
<Grid item>
<ProviderSettingsAvatar size={48} picture={profile.picture} />
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography
variant="subtitle1"
color="textPrimary"
gutterBottom
>
{profile.displayName}
</Typography>
<Typography variant="body2" color="textSecondary">
{profile.email}
</Typography>
<Typography variant="body2" color="textSecondary">
{description}
</Typography>
</Grid>
<Grid container spacing={6}>
<Grid item>
<ProviderSettingsAvatar size={48} picture={profile.picture} />
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography
variant="subtitle1"
color="textPrimary"
gutterBottom
>
{profile.displayName}
</Typography>
<Typography variant="body2" color="textSecondary">
{profile.email}
</Typography>
<Typography variant="body2" color="textSecondary">
{description}
</Typography>
</Grid>
</Grid>
</Grid>
</span>
</Grid>
</Tooltip>
}
secondaryTypographyProps={{ noWrap: true, style: { width: '80%' } }}