From d397b476b40e41a5b60f8a40778c57825463d722 Mon Sep 17 00:00:00 2001 From: Steven Eubank Date: Fri, 6 Jun 2025 13:45:09 -0700 Subject: [PATCH 1/2] feat(uptime):add-config-defaults Adding some default suggestions for config of Uptime Monitors, to reduce required clicks and friction for developers to create new monitors --- .../alerts/rules/uptime/uptimeAlertForm.tsx | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx b/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx index fd14b890ae989d..9932fd82d46419 100644 --- a/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx +++ b/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx @@ -28,7 +28,6 @@ import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import getDuration from 'sentry/utils/duration/getDuration'; import {useNavigate} from 'sentry/utils/useNavigate'; -import useOrganization from 'sentry/utils/useOrganization'; import useProjects from 'sentry/utils/useProjects'; import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import type {UptimeRule} from 'sentry/views/alerts/rules/uptime/types'; @@ -72,14 +71,21 @@ function getFormDataFromRule(rule: UptimeRule) { }; } -export function UptimeAlertForm({project, handleDelete, rule}: Props) { +export function UptimeAlertForm({project, handleDelete, rule, organization}: Props) { const navigate = useNavigate(); - const organization = useOrganization(); const {projects} = useProjects(); const initialData = rule ? getFormDataFromRule(rule) - : {projectSlug: project.slug, method: 'GET', headers: []}; + : { + projectSlug: project.slug, + method: 'GET', + headers: [], + owner: + (organization as any).teams?.length === 1 + ? `team:${(organization as any).teams[0].id}` + : undefined, + }; const [formModel] = useState(() => new FormModel()); @@ -87,6 +93,28 @@ export function UptimeAlertForm({project, handleDelete, rule}: Props) { const [newEnvironment, setNewEnvironment] = useState(undefined); const environments = [newEnvironment, ...knownEnvironments].filter(Boolean); + // Suggest rule name from URL + useEffect(() => { + if (rule) { + return () => {}; + } + return autorun(() => { + const url = formModel.getValue('url'); + const name = formModel.getValue('name'); + if (url && typeof url === 'string' && !name) { + try { + const urlObject = new URL(url); + const suggestedName = `Uptime Check for ${urlObject.hostname}${ + urlObject.pathname === '/' ? '' : urlObject.pathname + }`; + formModel.setValue('name', suggestedName.replace(/\/$/, '')); + } catch (e) { + // do nothing + } + } + }); + }, [formModel, rule]); + // XXX(epurkhiser): The forms API endpoint is derived from the selcted // project. We don't have an easy way to interpolate this into the
// components `apiEndpoint` prop, so instead we setup a mobx observer on @@ -95,7 +123,7 @@ export function UptimeAlertForm({project, handleDelete, rule}: Props) { useEffect( () => autorun(() => { - const projectSlug = formModel.getValue('projectSlug'); + const projectSlug = formModel.getValue('projectSlug'); const selectedProject = projects.find(p => p.slug === projectSlug); const apiEndpoint = rule ? `/projects/${organization.slug}/${projectSlug}/uptime/${rule.id}/` @@ -113,6 +141,9 @@ export function UptimeAlertForm({project, handleDelete, rule}: Props) { if (selectedProject) { setEnvironments(selectedProject.environments); + if (selectedProject.environments.length === 1 && !rule) { + formModel.setValue('environment', selectedProject.environments[0]); + } } }), [formModel, navigate, organization, projects, rule] From e1e244685a2773f9bb5d3613bfa50cb3f26c3b8b Mon Sep 17 00:00:00 2001 From: Steven Eubank Date: Fri, 6 Jun 2025 16:25:35 -0700 Subject: [PATCH 2/2] fix test and eslint --- .../app/views/alerts/rules/uptime/uptimeAlertForm.spec.tsx | 6 +----- static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/static/app/views/alerts/rules/uptime/uptimeAlertForm.spec.tsx b/static/app/views/alerts/rules/uptime/uptimeAlertForm.spec.tsx index 3c8b22b78b333c..e85db5a160d7f5 100644 --- a/static/app/views/alerts/rules/uptime/uptimeAlertForm.spec.tsx +++ b/static/app/views/alerts/rules/uptime/uptimeAlertForm.spec.tsx @@ -59,10 +59,6 @@ describe('Uptime Alert Form', function () { await userEvent.click(screen.getByRole('checkbox', {name: 'Allow Sampling'})); - const name = input('Uptime rule name'); - await userEvent.clear(name); - await userEvent.type(name, 'New Uptime Rule'); - await selectEvent.select(input('Owner'), 'Foo Bar'); const updateMock = MockApiClient.addMockResponse({ @@ -77,7 +73,7 @@ describe('Uptime Alert Form', function () { expect.objectContaining({ data: expect.objectContaining({ environment: 'prod', - name: 'New Uptime Rule', + name: 'Uptime Check for example.com', owner: 'user:1', url: 'http://example.com', method: 'POST', diff --git a/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx b/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx index 9932fd82d46419..aabf90219b417e 100644 --- a/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx +++ b/static/app/views/alerts/rules/uptime/uptimeAlertForm.tsx @@ -123,7 +123,7 @@ export function UptimeAlertForm({project, handleDelete, rule, organization}: Pro useEffect( () => autorun(() => { - const projectSlug = formModel.getValue('projectSlug'); + const projectSlug = formModel.getValue('projectSlug'); const selectedProject = projects.find(p => p.slug === projectSlug); const apiEndpoint = rule ? `/projects/${organization.slug}/${projectSlug}/uptime/${rule.id}/`