Files
zeekay 40d120dbf7 refactor(api): say /v1/ in the Python callers
Every first-party Python caller and test now names the mount the API actually
answers on. Mechanical: URL literals only, and only where the /api/ was ours --
a preceding word character means it was a module path like insights/api/ or a
third-party host like slack.com/api/, and those are untouched.

Left saying /api/, each for a reason rather than for safety:
- products/warehouse_sources/.../sources/** is a catalogue of vendor endpoints
- services/llm-gateway deploys separately and is under a code freeze, so moving
  its two calls would break it during the skew
- the mcp_store and Slack/Teams redirect_uris are registered with third parties
  and must byte-match what was registered
- the Wizard and Raycast client-metadata paths are OAuth client_ids
- /api/surveys and /api/feature_flag/local_evaluation are SDK aliases the
  ingress routes to the Rust fleets, held by released SDK versions

Test URLconfs moved with the requests that call them, and the Datastore
user-selection fixtures now carry the leading slash that request.path has.
2026-08-08 18:38:44 -07:00

231 lines
6.6 KiB
TypeScript

import { Meta, StoryObj } from '@storybook/react'
import { FEATURE_FLAGS } from 'lib/constants'
import { App } from 'scenes/App'
import { urls } from 'scenes/urls'
import { mswDecorator } from '~/mocks/browser'
import { EMPTY_PAGINATED_RESPONSE, toPaginatedResponse } from '~/mocks/handlers'
import {
AlertCalculationInterval,
AlertConditionType,
AlertState,
InsightThresholdType,
} from '~/queries/schema/schema-general'
import type { LogsAlertConfigurationApi } from 'products/logs/frontend/generated/api.schemas'
import { AlertType } from './types'
const createdBy = {
id: 1,
uuid: '018f59f3-4f2f-7c89-b389-73b99b91f442',
distinct_id: 'storybook-user',
first_name: 'Hedge Script',
email: '[email protected]',
mascot_config: null,
}
const makeAlert = (overrides: Partial<AlertType>): AlertType =>
({
id: 'alert-1',
name: 'Weekly active users above 10,000',
enabled: true,
state: AlertState.NOT_FIRING,
calculation_interval: AlertCalculationInterval.HOURLY,
last_checked_at: '2026-07-16T16:45:00Z',
last_notified_at: '2026-07-14T13:30:00Z',
created_at: '2026-06-01T09:00:00Z',
created_by: createdBy,
subscribed_users: [createdBy],
checks: [],
config: {
type: 'TrendsAlertConfig',
series_index: 0,
check_ongoing_interval: false,
},
threshold: {
configuration: {
type: InsightThresholdType.ABSOLUTE,
bounds: { upper: 10000 },
},
},
condition: { type: AlertConditionType.ABSOLUTE_VALUE },
insight: {
id: 101,
short_id: 'weekly-active-users',
name: 'Weekly active users',
derived_name: 'Weekly active users',
},
...overrides,
}) as AlertType
const alerts: AlertType[] = [
makeAlert({
id: 'alert-firing',
name: 'Checkout conversion below 40%',
state: AlertState.FIRING,
last_notified_at: '2026-07-16T16:45:00Z',
insight: {
id: 102,
short_id: 'checkout-conversion',
name: 'Checkout conversion',
derived_name: 'Checkout conversion',
} as AlertType['insight'],
}),
makeAlert({ id: 'alert-healthy' }),
makeAlert({
id: 'alert-disabled',
name: 'Daily signups above 1,000',
enabled: false,
calculation_interval: AlertCalculationInterval.DAILY,
last_notified_at: '',
insight: {
id: 103,
short_id: 'daily-signups',
name: 'Daily signups',
derived_name: 'Daily signups',
} as AlertType['insight'],
}),
]
const logAlerts = [
{
id: '019abcde-1234-7000-8000-000000000101',
name: 'Checkout errors',
enabled: true,
filters: { severityLevels: ['error'], serviceNames: ['checkout-api'] },
threshold_count: 25,
threshold_operator: 'above',
window_minutes: 5,
check_interval_minutes: 5,
state: 'firing',
evaluation_periods: 1,
datapoints_to_alarm: 1,
cooldown_minutes: 30,
snooze_until: null,
next_check_at: '2026-07-16T17:00:00Z',
last_notified_at: '2026-07-16T16:55:00Z',
last_checked_at: '2026-07-16T16:55:00Z',
consecutive_failures: 0,
last_error_message: null,
state_timeline: [
{
start: '2026-07-15T17:00:00Z',
end: '2026-07-16T16:30:00Z',
state: 'not_firing',
enabled: true,
},
{
start: '2026-07-16T16:30:00Z',
end: '2026-07-16T17:00:00Z',
state: 'firing',
enabled: true,
},
],
destination_types: ['slack'],
first_enabled_at: '2026-07-10T15:00:00Z',
created_at: '2026-07-10T14:45:00Z',
created_by: createdBy,
updated_at: '2026-07-16T16:55:00Z',
},
{
id: '019abcde-1234-7000-8000-000000000102',
name: 'No payment logs',
enabled: true,
filters: { serviceNames: ['payments-worker'] },
threshold_count: 1,
threshold_operator: 'below',
window_minutes: 15,
check_interval_minutes: 5,
state: 'not_firing',
evaluation_periods: 3,
datapoints_to_alarm: 2,
cooldown_minutes: 60,
snooze_until: null,
next_check_at: '2026-07-16T17:00:00Z',
last_notified_at: null,
last_checked_at: '2026-07-16T16:55:00Z',
consecutive_failures: 0,
last_error_message: null,
state_timeline: [
{
start: '2026-07-15T17:00:00Z',
end: '2026-07-16T17:00:00Z',
state: 'not_firing',
enabled: true,
},
],
destination_types: [],
first_enabled_at: '2026-07-12T10:00:00Z',
created_at: '2026-07-12T09:45:00Z',
created_by: createdBy,
updated_at: '2026-07-16T16:55:00Z',
},
] satisfies LogsAlertConfigurationApi[]
const meta: Meta = {
component: App,
title: 'Products/Alerts/Alerts scene',
parameters: {
layout: 'fullscreen',
viewMode: 'story',
mockDate: '2026-07-16',
pageUrl: `${urls.alerts()}?alert_type=insights`,
featureFlags: [FEATURE_FLAGS.LOGS_ALERTING],
testOptions: { viewport: { width: 1300, height: 900 } },
},
decorators: [
mswDecorator({
get: {
'/v1/environments/:team_id/alerts/': toPaginatedResponse(alerts),
},
}),
],
}
export default meta
type Story = StoryObj<{}>
export const InsightAlerts: Story = {}
export const EmptyState: Story = {
decorators: [
mswDecorator({
get: {
'/v1/environments/:team_id/alerts/': EMPTY_PAGINATED_RESPONSE,
},
}),
],
}
export const LogAlerts: Story = {
parameters: {
pageUrl: `${urls.alerts()}?alert_type=logs`,
},
decorators: [
mswDecorator({
get: {
'/v1/projects/:team_id/logs/alerts/': [
200,
{ count: logAlerts.length, next: null, previous: null, results: logAlerts },
],
},
}),
],
}
export const EmptyLogAlerts: Story = {
parameters: {
pageUrl: `${urls.alerts()}?alert_type=logs`,
},
decorators: [
mswDecorator({
get: {
'/v1/projects/:team_id/logs/alerts/': EMPTY_PAGINATED_RESPONSE,
},
}),
],
}