scaffolder: refine analytics events data
Signed-off-by: Vincenzo Scamporlino <vincenzos@spotify.com>
This commit is contained in:
@@ -22,7 +22,7 @@ import {
|
||||
} from '@backstage/core-components';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { TemplateCardProps, TemplateCard } from '../TemplateCard';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { AnalyticsContext, IconComponent } from '@backstage/core-plugin-api';
|
||||
|
||||
/**
|
||||
* The props for the {@link TemplateGroup} component.
|
||||
@@ -69,12 +69,18 @@ export const TemplateGroup = (props: TemplateGroupProps) => {
|
||||
{titleComponent}
|
||||
<ItemCardGrid>
|
||||
{templates.map(({ template, additionalLinks }) => (
|
||||
<Card
|
||||
<AnalyticsContext
|
||||
attributes={{
|
||||
entityRef: stringifyEntityRef(template),
|
||||
}}
|
||||
key={stringifyEntityRef(template)}
|
||||
additionalLinks={additionalLinks}
|
||||
template={template}
|
||||
onSelected={onSelected}
|
||||
/>
|
||||
>
|
||||
<Card
|
||||
additionalLinks={additionalLinks}
|
||||
template={template}
|
||||
onSelected={onSelected}
|
||||
/>
|
||||
</AnalyticsContext>
|
||||
))}
|
||||
</ItemCardGrid>
|
||||
</Content>
|
||||
|
||||
@@ -97,13 +97,11 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => {
|
||||
async (formState: Record<string, JsonValue>) => {
|
||||
await onCreate(formState);
|
||||
|
||||
const name =
|
||||
typeof formState.name === 'string' ? formState.name : undefined;
|
||||
analytics.captureEvent('create', name ?? templateName ?? 'unknown', {
|
||||
analytics.captureEvent('create', 'Task has been created', {
|
||||
value: minutesSaved,
|
||||
});
|
||||
},
|
||||
[onCreate, analytics, templateName, minutesSaved],
|
||||
[onCreate, analytics, minutesSaved],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -29,11 +29,8 @@ import Toc from '@material-ui/icons/Toc';
|
||||
import ControlPointIcon from '@material-ui/icons/ControlPoint';
|
||||
import MoreVert from '@material-ui/icons/MoreVert';
|
||||
import { SyntheticEvent, useState } from 'react';
|
||||
import { useAnalytics, useApi } from '@backstage/core-plugin-api';
|
||||
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
|
||||
import { usePermission } from '@backstage/plugin-permission-react';
|
||||
import {
|
||||
taskCancelPermission,
|
||||
taskReadPermission,
|
||||
taskCreatePermission,
|
||||
} from '@backstage/plugin-scaffolder-common/alpha';
|
||||
@@ -50,7 +47,8 @@ type ContextMenuProps = {
|
||||
onStartOver?: () => void;
|
||||
onToggleLogs?: (state: boolean) => void;
|
||||
onToggleButtonBar?: (state: boolean) => void;
|
||||
taskId?: string;
|
||||
isCancelButtonDisabled: boolean;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles<Theme, { fontColor: string }>(() => ({
|
||||
@@ -70,27 +68,13 @@ export const ContextMenu = (props: ContextMenuProps) => {
|
||||
onStartOver,
|
||||
onToggleLogs,
|
||||
onToggleButtonBar,
|
||||
taskId,
|
||||
} = props;
|
||||
const { getPageTheme } = useTheme();
|
||||
const pageTheme = getPageTheme({ themeId: 'website' });
|
||||
const classes = useStyles({ fontColor: pageTheme.fontColor });
|
||||
const scaffolderApi = useApi(scaffolderApiRef);
|
||||
const analytics = useAnalytics();
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>();
|
||||
const { t } = useTranslationRef(scaffolderTranslationRef);
|
||||
|
||||
const [{ status: cancelStatus }, { execute: cancel }] = useAsync(async () => {
|
||||
if (taskId) {
|
||||
analytics.captureEvent('cancelled', 'Template has been cancelled');
|
||||
await scaffolderApi.cancelTask(taskId);
|
||||
}
|
||||
});
|
||||
|
||||
const { allowed: canCancelTask } = usePermission({
|
||||
permission: taskCancelPermission,
|
||||
});
|
||||
|
||||
const { allowed: canReadTask } = usePermission({
|
||||
permission: taskReadPermission,
|
||||
});
|
||||
@@ -171,12 +155,8 @@ export const ContextMenu = (props: ContextMenuProps) => {
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={cancel}
|
||||
disabled={
|
||||
!cancelEnabled ||
|
||||
cancelStatus !== 'not-executed' ||
|
||||
!canCancelTask
|
||||
}
|
||||
onClick={props.onCancel}
|
||||
disabled={props.isCancelButtonDisabled}
|
||||
data-testid="cancel-task"
|
||||
>
|
||||
<ListItemIcon>
|
||||
|
||||
@@ -32,7 +32,12 @@ import {
|
||||
useTaskEventStream,
|
||||
} from '@backstage/plugin-scaffolder-react';
|
||||
import { selectedTemplateRouteRef } from '../../routes';
|
||||
import { useAnalytics, useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
AnalyticsContext,
|
||||
useAnalytics,
|
||||
useApi,
|
||||
useRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import qs from 'qs';
|
||||
import { ContextMenu } from './ContextMenu';
|
||||
import {
|
||||
@@ -51,6 +56,7 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { scaffolderTranslationRef } from '../../translation';
|
||||
import { entityPresentationApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { default as reactUseAsync } from 'react-use/esm/useAsync';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
contentWrapper: {
|
||||
@@ -82,6 +88,36 @@ export const OngoingTask = (props: {
|
||||
}>;
|
||||
}) => {
|
||||
// todo(blam): check that task Id actually exists, and that it's valid. otherwise redirect to something more useful.
|
||||
const { taskId } = useParams();
|
||||
const taskStream = useTaskEventStream(taskId!);
|
||||
const { namespace, name } =
|
||||
taskStream.task?.spec.templateInfo?.entity?.metadata ?? {};
|
||||
|
||||
return (
|
||||
<AnalyticsContext
|
||||
attributes={{
|
||||
entityRef:
|
||||
name &&
|
||||
stringifyEntityRef({
|
||||
kind: 'template',
|
||||
namespace,
|
||||
name,
|
||||
}),
|
||||
taskId,
|
||||
}}
|
||||
>
|
||||
<Page themeId="website">
|
||||
<OngoingTaskContent {...props} />
|
||||
</Page>
|
||||
</AnalyticsContext>
|
||||
);
|
||||
};
|
||||
|
||||
function OngoingTaskContent(props: {
|
||||
TemplateOutputsComponent?: ComponentType<{
|
||||
output?: ScaffolderTaskOutput;
|
||||
}>;
|
||||
}) {
|
||||
const { taskId } = useParams();
|
||||
const templateRouteRef = useRouteRef(selectedTemplateRouteRef);
|
||||
const navigate = useNavigate();
|
||||
@@ -183,7 +219,7 @@ export const OngoingTask = (props: {
|
||||
templateRouteRef,
|
||||
]);
|
||||
|
||||
const [{ status: _ }, { execute: triggerRetry }] = useAsync(async () => {
|
||||
const [, { execute: triggerRetry }] = useAsync(async () => {
|
||||
if (taskId) {
|
||||
analytics.captureEvent('retried', 'Template has been retried');
|
||||
await scaffolderApi.retry?.(taskId);
|
||||
@@ -202,9 +238,11 @@ export const OngoingTask = (props: {
|
||||
const Outputs = props.TemplateOutputsComponent ?? DefaultTemplateOutputs;
|
||||
|
||||
const cancelEnabled = !(taskStream.cancelled || taskStream.completed);
|
||||
const isCancelButtonDisabled =
|
||||
!cancelEnabled || cancelStatus !== 'not-executed' || !canCancelTask;
|
||||
|
||||
return (
|
||||
<Page themeId="website">
|
||||
<>
|
||||
<Header
|
||||
pageTitleOverride={
|
||||
presentation
|
||||
@@ -231,7 +269,8 @@ export const OngoingTask = (props: {
|
||||
onRetry={triggerRetry}
|
||||
onToggleLogs={setLogVisibleState}
|
||||
onToggleButtonBar={setButtonBarVisibleState}
|
||||
taskId={taskId}
|
||||
onCancel={triggerCancel}
|
||||
isCancelButtonDisabled={isCancelButtonDisabled}
|
||||
/>
|
||||
</Header>
|
||||
<Content className={classes.contentWrapper}>
|
||||
@@ -316,6 +355,6 @@ export const OngoingTask = (props: {
|
||||
</Paper>
|
||||
) : null}
|
||||
</Content>
|
||||
</Page>
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user