diff --git a/.changeset/dull-cloths-act.md b/.changeset/dull-cloths-act.md
new file mode 100644
index 0000000000..e49ee5a289
--- /dev/null
+++ b/.changeset/dull-cloths-act.md
@@ -0,0 +1,6 @@
+---
+'@backstage/plugin-scaffolder-react': minor
+'@backstage/plugin-scaffolder': minor
+---
+
+Added information about the `entityRef` and `taskId` to the analytics events whenever is possible.
diff --git a/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx b/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx
index a70b822d13..74ade33344 100644
--- a/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx
+++ b/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx
@@ -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}
{templates.map(({ template, additionalLinks }) => (
-
+ >
+
+
))}
diff --git a/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx b/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx
index a0070765fe..58b74dff9d 100644
--- a/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx
+++ b/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx
@@ -22,7 +22,7 @@ import {
} from '@backstage/plugin-scaffolder-common';
import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react';
import Typography from '@material-ui/core/Typography';
-import { ComponentType, useCallback } from 'react';
+import { ComponentType, useCallback, useEffect } from 'react';
import { TemplateGroup } from '../TemplateGroup/TemplateGroup';
@@ -58,12 +58,17 @@ export const TemplateGroups = (props: TemplateGroupsProps) => {
[onTemplateSelected],
);
+ useEffect(() => {
+ if (error) {
+ errorApi.post(error);
+ }
+ }, [error, errorApi]);
+
if (loading) {
return ;
}
if (error) {
- errorApi.post(error);
return null;
}
diff --git a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx
index 2aa4ee9b2d..527009037d 100644
--- a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx
+++ b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx
@@ -97,13 +97,14 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => {
async (formState: Record) => {
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,
+ attributes: {
+ templateSteps: sortedManifest?.steps?.length ?? 0,
+ },
});
},
- [onCreate, analytics, templateName, minutesSaved],
+ [onCreate, analytics, minutesSaved, sortedManifest],
);
useEffect(() => {
diff --git a/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts b/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts
index be6f581f87..cac696a745 100644
--- a/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts
+++ b/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts
@@ -16,6 +16,7 @@
import cloneDeep from 'lodash/cloneDeep';
import { useApi, featureFlagsApiRef } from '@backstage/core-plugin-api';
import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react';
+import { useMemo } from 'react';
/**
* Returns manifest of software templates with steps without a featureFlag tag.
@@ -28,49 +29,51 @@ export const useFilteredSchemaProperties = (
const featureFlagKey = 'backstage:featureFlag';
const featureFlagApi = useApi(featureFlagsApiRef);
- if (!manifest) {
- return undefined;
- }
-
- const filteredSteps = manifest?.steps
- .filter(step => {
- const featureFlag = step.schema[featureFlagKey];
- return (
- typeof featureFlag !== 'string' || featureFlagApi.isActive(featureFlag)
- );
- })
- .map(step => {
- const filteredStep = cloneDeep(step);
- const removedPropertyKeys: Array = [];
- if (filteredStep.schema.properties) {
- filteredStep.schema.properties = Object.fromEntries(
- Object.entries(filteredStep.schema.properties).filter(
- ([key, value]) => {
- if (value[featureFlagKey]) {
- if (featureFlagApi.isActive(value[featureFlagKey])) {
- return true;
- }
-
- removedPropertyKeys.push(key);
- return false;
- }
- return true;
- },
- ),
+ return useMemo(() => {
+ if (!manifest) {
+ return undefined;
+ }
+ const filteredSteps = manifest?.steps
+ .filter(step => {
+ const featureFlag = step.schema[featureFlagKey];
+ return (
+ typeof featureFlag !== 'string' ||
+ featureFlagApi.isActive(featureFlag)
);
+ })
+ .map(step => {
+ const filteredStep = cloneDeep(step);
+ const removedPropertyKeys: Array = [];
+ if (filteredStep.schema.properties) {
+ filteredStep.schema.properties = Object.fromEntries(
+ Object.entries(filteredStep.schema.properties).filter(
+ ([key, value]) => {
+ if (value[featureFlagKey]) {
+ if (featureFlagApi.isActive(value[featureFlagKey])) {
+ return true;
+ }
- // remove the feature flag property key from required if they are not active
- filteredStep.schema.required = Array.isArray(
- filteredStep.schema.required,
- )
- ? filteredStep.schema.required?.filter(
- r => !removedPropertyKeys.includes(r as string),
- )
- : filteredStep.schema.required;
- }
+ removedPropertyKeys.push(key);
+ return false;
+ }
+ return true;
+ },
+ ),
+ );
- return filteredStep;
- });
+ // remove the feature flag property key from required if they are not active
+ filteredStep.schema.required = Array.isArray(
+ filteredStep.schema.required,
+ )
+ ? filteredStep.schema.required?.filter(
+ r => !removedPropertyKeys.includes(r as string),
+ )
+ : filteredStep.schema.required;
+ }
- return { ...manifest, steps: filteredSteps };
+ return filteredStep;
+ });
+
+ return { ...manifest, steps: filteredSteps };
+ }, [manifest, featureFlagApi]);
};
diff --git a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx
index d3a02931de..6836259dd8 100644
--- a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx
+++ b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx
@@ -21,7 +21,7 @@ import {
renderInTestApp,
TestApiRegistry,
} from '@backstage/test-utils';
-import { act, fireEvent } from '@testing-library/react';
+import { fireEvent, waitFor } from '@testing-library/react';
import {
ScaffolderApi,
scaffolderApiRef,
@@ -127,14 +127,10 @@ describe('TemplateWizardPage', () => {
});
// Go to the final page
- await act(async () => {
- fireEvent.click(await findByRole('button', { name: 'Review' }));
- });
+ fireEvent.click(await findByRole('button', { name: 'Review' }));
// Create the software
- await act(async () => {
- fireEvent.click(await findByRole('button', { name: 'Create' }));
- });
+ fireEvent.click(await findByRole('button', { name: 'Create' }));
// The "Next Step" button should have fired an event
expect(analyticsApi.captureEvent).toHaveBeenCalledWith(
@@ -148,15 +144,20 @@ describe('TemplateWizardPage', () => {
);
// And the "Create" button should have fired an event
- expect(analyticsApi.captureEvent).toHaveBeenCalledWith(
- expect.objectContaining({
- action: 'create',
- subject: 'expected-name',
- context: expect.objectContaining({
- entityRef: 'template:default/test',
+ await waitFor(() =>
+ expect(analyticsApi.captureEvent).toHaveBeenCalledWith(
+ expect.objectContaining({
+ action: 'create',
+ subject: 'Task has been created',
+ attributes: {
+ templateSteps: 1,
+ },
+ context: expect.objectContaining({
+ entityRef: 'template:default/test',
+ }),
+ value: 120,
}),
- value: 120,
- }),
+ ),
);
});
diff --git a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx
index 9a912f265f..c15795de2c 100644
--- a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx
+++ b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx
@@ -26,6 +26,7 @@ import MoreVert from '@material-ui/icons/MoreVert';
import { SyntheticEvent, useState } from 'react';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { scaffolderTranslationRef } from '../../../translation';
+import { Link } from '@backstage/core-components';
const useStyles = makeStyles(theme => ({
button: {
@@ -82,7 +83,7 @@ export function TemplateWizardPageContextMenu(
transformOrigin={{ vertical: 'top', horizontal: 'right' }}
>
-