mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(API): Return null deviation on insights summary if previous period has no data (#14193)
This commit is contained in:
committed by
GitHub
parent
41b1797a25
commit
ffc0a596e0
@@ -15,27 +15,27 @@ export type InsightsSummaryUnit = z.infer<typeof insightsSummaryUnitSchema>;
|
|||||||
export const insightsSummaryDataSchemas = {
|
export const insightsSummaryDataSchemas = {
|
||||||
total: z.object({
|
total: z.object({
|
||||||
value: z.number(),
|
value: z.number(),
|
||||||
deviation: z.number(),
|
deviation: z.union([z.null(), z.number()]),
|
||||||
unit: z.literal('count'),
|
unit: z.literal('count'),
|
||||||
}),
|
}),
|
||||||
failed: z.object({
|
failed: z.object({
|
||||||
value: z.number(),
|
value: z.number(),
|
||||||
deviation: z.number(),
|
deviation: z.union([z.null(), z.number()]),
|
||||||
unit: z.literal('count'),
|
unit: z.literal('count'),
|
||||||
}),
|
}),
|
||||||
failureRate: z.object({
|
failureRate: z.object({
|
||||||
value: z.number(),
|
value: z.number(),
|
||||||
deviation: z.number(),
|
deviation: z.union([z.null(), z.number()]),
|
||||||
unit: z.literal('ratio'),
|
unit: z.literal('ratio'),
|
||||||
}),
|
}),
|
||||||
timeSaved: z.object({
|
timeSaved: z.object({
|
||||||
value: z.number(),
|
value: z.number(),
|
||||||
deviation: z.number(),
|
deviation: z.union([z.null(), z.number()]),
|
||||||
unit: z.literal('time'),
|
unit: z.literal('time'),
|
||||||
}),
|
}),
|
||||||
averageRunTime: z.object({
|
averageRunTime: z.object({
|
||||||
value: z.number(),
|
value: z.number(),
|
||||||
deviation: z.number(),
|
deviation: z.union([z.null(), z.number()]),
|
||||||
unit: z.literal('time'),
|
unit: z.literal('time'),
|
||||||
}),
|
}),
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@@ -34,15 +34,15 @@ describe('InsightsController', () => {
|
|||||||
|
|
||||||
// ASSERT
|
// ASSERT
|
||||||
expect(response).toEqual({
|
expect(response).toEqual({
|
||||||
total: { deviation: 0, unit: 'count', value: 0 },
|
total: { deviation: null, unit: 'count', value: 0 },
|
||||||
failed: { deviation: 0, unit: 'count', value: 0 },
|
failed: { deviation: null, unit: 'count', value: 0 },
|
||||||
failureRate: { deviation: 0, unit: 'ratio', value: 0 },
|
failureRate: { deviation: null, unit: 'ratio', value: 0 },
|
||||||
averageRunTime: { deviation: 0, unit: 'time', value: 0 },
|
averageRunTime: { deviation: null, unit: 'time', value: 0 },
|
||||||
timeSaved: { deviation: 0, unit: 'time', value: 0 },
|
timeSaved: { deviation: null, unit: 'time', value: 0 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the insights summary with deviation = current if insights exist only for current period', async () => {
|
it('should return the insights summary with null deviation if insights exist only for current period', async () => {
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
insightsByPeriodRepository.getPreviousAndCurrentPeriodTypeAggregates.mockResolvedValue([
|
insightsByPeriodRepository.getPreviousAndCurrentPeriodTypeAggregates.mockResolvedValue([
|
||||||
{ period: 'current', type: TypeToNumber.success, total_value: 20 },
|
{ period: 'current', type: TypeToNumber.success, total_value: 20 },
|
||||||
@@ -56,11 +56,11 @@ describe('InsightsController', () => {
|
|||||||
|
|
||||||
// ASSERT
|
// ASSERT
|
||||||
expect(response).toEqual({
|
expect(response).toEqual({
|
||||||
total: { deviation: 30, unit: 'count', value: 30 },
|
total: { deviation: null, unit: 'count', value: 30 },
|
||||||
failed: { deviation: 10, unit: 'count', value: 10 },
|
failed: { deviation: null, unit: 'count', value: 10 },
|
||||||
failureRate: { deviation: 0.33, unit: 'ratio', value: 0.33 },
|
failureRate: { deviation: null, unit: 'ratio', value: 0.33 },
|
||||||
averageRunTime: { deviation: 10, unit: 'time', value: 10 },
|
averageRunTime: { deviation: null, unit: 'time', value: 10 },
|
||||||
timeSaved: { deviation: 10, unit: 'time', value: 10 },
|
timeSaved: { deviation: null, unit: 'time', value: 10 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -784,4 +784,21 @@ describe('getInsightsSummary', () => {
|
|||||||
total: { deviation: 3, unit: 'count', value: 4 },
|
total: { deviation: 3, unit: 'count', value: 4 },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('no data for previous period should return null deviation', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
// last 7 days
|
||||||
|
await createCompactedInsightsEvent(workflow, {
|
||||||
|
type: 'success',
|
||||||
|
value: 1,
|
||||||
|
periodUnit: 'day',
|
||||||
|
periodStart: DateTime.utc(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
const summary = await insightsService.getInsightsSummary();
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(Object.values(summary).map((v) => v.deviation)).toEqual([null, null, null, null, null]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -265,32 +265,36 @@ export class InsightsService {
|
|||||||
const currentTimeSaved = getValueByType('current', 'time_saved_min');
|
const currentTimeSaved = getValueByType('current', 'time_saved_min');
|
||||||
const previousTimeSaved = getValueByType('previous', 'time_saved_min');
|
const previousTimeSaved = getValueByType('previous', 'time_saved_min');
|
||||||
|
|
||||||
|
// If the previous period has no executions, we discard deviation
|
||||||
|
const getDeviation = (current: number, previous: number) =>
|
||||||
|
previousTotal === 0 ? null : current - previous;
|
||||||
|
|
||||||
// Return the formatted result
|
// Return the formatted result
|
||||||
const result: InsightsSummary = {
|
const result: InsightsSummary = {
|
||||||
averageRunTime: {
|
averageRunTime: {
|
||||||
value: currentAvgRuntime,
|
value: currentAvgRuntime,
|
||||||
unit: 'time',
|
unit: 'time',
|
||||||
deviation: currentAvgRuntime - previousAvgRuntime,
|
deviation: getDeviation(currentAvgRuntime, previousAvgRuntime),
|
||||||
},
|
},
|
||||||
failed: {
|
failed: {
|
||||||
value: currentFailures,
|
value: currentFailures,
|
||||||
unit: 'count',
|
unit: 'count',
|
||||||
deviation: currentFailures - previousFailures,
|
deviation: getDeviation(currentFailures, previousFailures),
|
||||||
},
|
},
|
||||||
failureRate: {
|
failureRate: {
|
||||||
value: currentFailureRate,
|
value: currentFailureRate,
|
||||||
unit: 'ratio',
|
unit: 'ratio',
|
||||||
deviation: currentFailureRate - previousFailureRate,
|
deviation: getDeviation(currentFailureRate, previousFailureRate),
|
||||||
},
|
},
|
||||||
timeSaved: {
|
timeSaved: {
|
||||||
value: currentTimeSaved,
|
value: currentTimeSaved,
|
||||||
unit: 'time',
|
unit: 'time',
|
||||||
deviation: currentTimeSaved - previousTimeSaved,
|
deviation: getDeviation(currentTimeSaved, previousTimeSaved),
|
||||||
},
|
},
|
||||||
total: {
|
total: {
|
||||||
value: currentTotal,
|
value: currentTotal,
|
||||||
unit: 'count',
|
unit: 'count',
|
||||||
deviation: currentTotal - previousTotal,
|
deviation: getDeviation(currentTotal, previousTotal),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,15 @@ describe('InsightsSummary', () => {
|
|||||||
{ id: 'averageRunTime', value: 2.5, deviation: 0.5, unit: 's' },
|
{ id: 'averageRunTime', value: 2.5, deviation: 0.5, unit: 's' },
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{ id: 'total', value: 525, deviation: null, unit: '' },
|
||||||
|
{ id: 'failed', value: 14, deviation: null, unit: '' },
|
||||||
|
{ id: 'failureRate', value: 1.9, deviation: null, unit: '%' },
|
||||||
|
{ id: 'timeSaved', value: 55.55555555555556, deviation: null, unit: 'h' },
|
||||||
|
{ id: 'averageRunTime', value: 2.5, deviation: null, unit: 's' },
|
||||||
|
],
|
||||||
|
],
|
||||||
])('should render the summary correctly', (summary) => {
|
])('should render the summary correctly', (summary) => {
|
||||||
const { html } = renderComponent({
|
const { html } = renderComponent({
|
||||||
props: {
|
props: {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ const getImpactStyle = (id: keyof InsightsSummary, value: number) => {
|
|||||||
<em
|
<em
|
||||||
>{{ smartDecimal(value) }} <i>{{ unit }}</i></em
|
>{{ smartDecimal(value) }} <i>{{ unit }}</i></em
|
||||||
>
|
>
|
||||||
<small :class="getImpactStyle(id, deviation)">
|
<small v-if="deviation !== null" :class="getImpactStyle(id, deviation)">
|
||||||
<N8nIcon
|
<N8nIcon
|
||||||
:class="[$style.icon, getImpactStyle(id, deviation)]"
|
:class="[$style.icon, getImpactStyle(id, deviation)]"
|
||||||
:icon="deviation === 0 ? 'caret-right' : deviation > 0 ? 'caret-up' : 'caret-down'"
|
:icon="deviation === 0 ? 'caret-right' : deviation > 0 ? 'caret-up' : 'caret-down'"
|
||||||
|
|||||||
@@ -73,3 +73,26 @@ exports[`InsightsSummary > should render the summary correctly 4`] = `
|
|||||||
</ul>
|
</ul>
|
||||||
</div>"
|
</div>"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`InsightsSummary > should render the summary correctly 5`] = `
|
||||||
|
"<div class="insights">
|
||||||
|
<h3 class="n8n-heading text-light size-small bold mb-xs mb-xs">Production executions for the last 7 days</h3>
|
||||||
|
<ul data-test-id="insights-summary-tabs">
|
||||||
|
<li data-test-id="insights-summary-tab-total">
|
||||||
|
<p><strong>Total</strong><span><em>525 <i></i></em><!--v-if--></span></p>
|
||||||
|
</li>
|
||||||
|
<li data-test-id="insights-summary-tab-failed">
|
||||||
|
<p><strong>Failed</strong><span><em>14 <i></i></em><!--v-if--></span></p>
|
||||||
|
</li>
|
||||||
|
<li data-test-id="insights-summary-tab-failureRate">
|
||||||
|
<p><strong>Failure rate</strong><span><em>1.9 <i>%</i></em><!--v-if--></span></p>
|
||||||
|
</li>
|
||||||
|
<li data-test-id="insights-summary-tab-timeSaved">
|
||||||
|
<p><strong>Time saved</strong><span><em>55.56 <i>h</i></em><!--v-if--></span></p>
|
||||||
|
</li>
|
||||||
|
<li data-test-id="insights-summary-tab-averageRunTime">
|
||||||
|
<p><strong>Avg. run time</strong><span><em>2.5 <i>s</i></em><!--v-if--></span></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export type InsightsSummaryDisplay = Array<
|
|||||||
[K in keyof InsightsDisplayUnits]: {
|
[K in keyof InsightsDisplayUnits]: {
|
||||||
id: K;
|
id: K;
|
||||||
value: number;
|
value: number;
|
||||||
deviation: number;
|
deviation: number | null;
|
||||||
unit: InsightsDisplayUnits[K];
|
unit: InsightsDisplayUnits[K];
|
||||||
};
|
};
|
||||||
}[keyof InsightsDisplayUnits]
|
}[keyof InsightsDisplayUnits]
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ export const transformInsightsSummary = (data: InsightsSummary | null): Insights
|
|||||||
? INSIGHTS_SUMMARY_ORDER.map((key) => ({
|
? INSIGHTS_SUMMARY_ORDER.map((key) => ({
|
||||||
id: key,
|
id: key,
|
||||||
value: transformInsightsValues[key]?.(data[key].value) ?? data[key].value,
|
value: transformInsightsValues[key]?.(data[key].value) ?? data[key].value,
|
||||||
deviation: transformInsightsValues[key]?.(data[key].deviation) ?? data[key].deviation,
|
deviation: data[key].deviation
|
||||||
|
? (transformInsightsValues[key]?.(data[key].deviation) ?? data[key].deviation)
|
||||||
|
: null,
|
||||||
unit: INSIGHTS_UNIT_MAPPING[key],
|
unit: INSIGHTS_UNIT_MAPPING[key],
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
|
|||||||
Reference in New Issue
Block a user