Skip to content

Add Sentry alert rule creation functionality - #569

Merged
crimsonstrife merged 1 commit into
prodfrom
dev
Jun 2, 2026
Merged

Add Sentry alert rule creation functionality#569
crimsonstrife merged 1 commit into
prodfrom
dev

Conversation

@crimsonstrife

@crimsonstrife crimsonstrife commented Jun 2, 2026

Copy link
Copy Markdown
Owner
  • Implement createIssueAlertRule in SentryClient to create Sentry issue alert rules.
  • Add ensureAlertRuleConfigured for Sentry rule prerequisites validation.
  • Extend ConnectorsAndSyncSettings with a new action to create Sentry alert rules via user inputs.
  • Add feature tests for creating Sentry alert rules and validating API requests.

Summary by Sourcery

Add UI and backend support for creating Sentry issue alert rules wired to the Forge Sentry app action.

New Features:

  • Introduce a Filament action that lets users create Sentry issue alert rules from the connectors and sync settings page using Forge project, issue type, and priority inputs.
  • Add a Sentry client API method to create issue alert rules for a given Sentry project, integrating with the Forge Sentry app action.

Enhancements:

  • Add Sentry configuration validation specific to alert rule creation, ensuring organisation slug and installation UUID are present before calling the Sentry API.

Tests:

  • Extend Sentry outbound sync feature tests to cover issue alert rule creation payloads and responses.

- Implement `createIssueAlertRule` in `SentryClient` to create Sentry issue alert rules.
- Add `ensureAlertRuleConfigured` for Sentry rule prerequisites validation.
- Extend `ConnectorsAndSyncSettings` with a new action to create Sentry alert rules via user inputs.
- Add feature tests for creating Sentry alert rules and validating API requests.
@crimsonstrife crimsonstrife self-assigned this Jun 2, 2026
Copilot AI review requested due to automatic review settings June 2, 2026 17:17
@sourcery-ai

sourcery-ai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Implements end-to-end support for creating Sentry issue alert rules targeting the Forge Sentry app, including a new Filament UI action, SentryClient helper with configuration validation, and feature tests that verify the outbound Sentry API request shape.

Sequence diagram for creating a Sentry issue alert rule from the UI

sequenceDiagram
    actor Admin
    participant ConnectorsAndSyncSettings
    participant SentryClient
    participant SentryAPI

    Admin->>ConnectorsAndSyncSettings: Action createSentryAlertRule(data)
    ConnectorsAndSyncSettings->>ConnectorsAndSyncSettings: createSentryAlertRule(data)
    ConnectorsAndSyncSettings->>SentryClient: createIssueAlertRule(sentryProjectSlug, ruleName, forgeProjectId, issueTypeId, priorityId, frequency)
    SentryClient->>SentryClient: ensureAlertRuleConfigured()
    SentryClient->>SentryAPI: POST /projects/{org_slug}/{project_slug}/rules/
    SentryAPI-->>SentryClient: 200 OK, rule JSON
    SentryClient-->>ConnectorsAndSyncSettings: rule
    ConnectorsAndSyncSettings-->>Admin: Success notification
    alt error
        SentryAPI-->>SentryClient: error response
        SentryClient-->>ConnectorsAndSyncSettings: throws ConnectionException
        ConnectorsAndSyncSettings-->>Admin: Error notification
    end
Loading

File-Level Changes

Change Details Files
Add a Filament header action that lets users create Sentry alert rules via a form and delegates to a new page method.
  • Introduce a createSentryAlertRule header Action gated by the Sentry enabled flag.
  • Add form schema fields for Sentry project slug, rule name, action frequency, and Forge project/issue type/priority selections with defaults from existing Sentry config data.
  • Wire the action to a new private createSentryAlertRule(array $data) method on the page.
app/Filament/Pages/ConnectorsAndSyncSettings.php
Implement SentryClient support for creating issue alert rules and validating required Sentry configuration for rule creation.
  • Add createIssueAlertRule(...) to construct and POST the Sentry issue alert rule payload with fixed condition and a Sentry app notification action referencing the Forge installation UUID.
  • Clamp the requested frequency into Sentry’s allowed range (5–43200 minutes) before sending.
  • Add ensureAlertRuleConfigured() to enforce that base configuration is valid and that org_slug and installation_uuid are present, throwing RuntimeException otherwise.
  • Return the decoded JSON response as an array after asserting a successful HTTP response.
app/Integrations/Sentry/Services/SentryClient.php
Handle user feedback for rule creation in the Filament page using notifications.
  • On successful rule creation, display a success notification including the returned rule ID when available.
  • On failure (any Throwable), display a danger notification with the exception message.
app/Filament/Pages/ConnectorsAndSyncSettings.php
Add feature test coverage for Sentry issue alert rule creation, including the outbound HTTP request payload.
  • Create a test that seeds a project, issue type, and priority, then sets the Sentry installation UUID on SentrySettings.
  • Fake the Sentry rules endpoint to return a rule ID and assert the client returns it from createIssueAlertRule.
  • Assert that the posted request has the expected URL, method, condition id, action id, installation UUID, hasSchemaFormConfig flag, and Forge-specific settings payload.
tests/Feature/Integrations/Sentry/OutboundSyncTest.php

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@crimsonstrife

Copy link
Copy Markdown
Owner Author

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@crimsonstrife
crimsonstrife merged commit d9e306a into prod Jun 2, 2026
10 of 12 checks passed

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In ConnectorsAndSyncSettings::createSentryAlertRule, you fall back to default values and cast for all inputs before calling SentryClient; consider validating and failing early when required fields like project/issue type/priority are missing instead of silently sending empty IDs to Sentry.
  • The error notification in createSentryAlertRule uses $e->getMessage() directly in the UI, which may expose internal details from Sentry or the integration layer; consider logging the exception and showing a more user-friendly, generic error message instead.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ConnectorsAndSyncSettings::createSentryAlertRule`, you fall back to default values and cast for all inputs before calling `SentryClient`; consider validating and failing early when required fields like project/issue type/priority are missing instead of silently sending empty IDs to Sentry.
- The error notification in `createSentryAlertRule` uses `$e->getMessage()` directly in the UI, which may expose internal details from Sentry or the integration layer; consider logging the exception and showing a more user-friendly, generic error message instead.

## Individual Comments

### Comment 1
<location path="app/Filament/Pages/ConnectorsAndSyncSettings.php" line_range="337-340" />
<code_context>
+                ->body($ruleId !== '' ? 'Rule ID: '.$ruleId : 'Sentry accepted the rule.')
+                ->success()
+                ->send();
+        } catch (\Throwable $e) {
+            Notification::make()
+                ->title('Could not create Sentry rule')
+                ->body($e->getMessage())
+                ->danger()
+                ->send();
</code_context>
<issue_to_address>
**🚨 issue (security):** Catching Throwable and surfacing the raw exception message to the user can leak internal details.

Using `$e->getMessage()` directly in a user notification may expose sensitive implementation or configuration details. Instead, log the full exception (with stack trace) to your logging/Sentry pipeline, and present a generic, user-friendly error message—optionally including a correlation ID for support/debugging.
</issue_to_address>

### Comment 2
<location path="app/Filament/Pages/ConnectorsAndSyncSettings.php" line_range="199-203" />
<code_context>
+                        ->label('Rule name')
+                        ->default('Send new issues to Forge')
+                        ->required(),
+                    TextInput::make('frequency')
+                        ->label('Action frequency minutes')
+                        ->numeric()
+                        ->minValue(5)
+                        ->maxValue(43200)
+                        ->default(5)
+                        ->required(),
</code_context>
<issue_to_address>
**suggestion:** Frequency bounds are duplicated here and in SentryClient; consider centralizing to avoid drift.

The `frequency` limits (5, 43200) are enforced here on the form and again in `SentryClient::createIssueAlertRule()` when clamping. Please consider extracting these into shared constants (or deriving one from the other) so that both the UI and client always use the same range.

Suggested implementation:

```
namespace App\Filament\Pages;

use App\Services\SentryClient;

```

```
                    TextInput::make('frequency')
                        ->label('Action frequency minutes')
                        ->numeric()
                        ->minValue(SentryClient::MIN_FREQUENCY_MINUTES)
                        ->maxValue(SentryClient::MAX_FREQUENCY_MINUTES)
                        ->default(SentryClient::DEFAULT_FREQUENCY_MINUTES)

```

In `App\Services\SentryClient` (or the actual namespace where `SentryClient` lives), define and use shared constants, for example:

```php
class SentryClient
{
    public const MIN_FREQUENCY_MINUTES = 5;
    public const MAX_FREQUENCY_MINUTES = 43200;
    public const DEFAULT_FREQUENCY_MINUTES = self::MIN_FREQUENCY_MINUTES;

    public function createIssueAlertRule(array $payload): void
    {
        $frequency = (int) ($payload['frequency'] ?? self::DEFAULT_FREQUENCY_MINUTES);
        $frequency = max(self::MIN_FREQUENCY_MINUTES, min(self::MAX_FREQUENCY_MINUTES, $frequency));
        // ...
    }
}
```

You may need to adjust:
1. The `use App\Services\SentryClient;` import to match your actual `SentryClient` namespace.
2. The constant names if you prefer a different naming convention, but they should be used both here and in `createIssueAlertRule()` where clamping is currently implemented.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +337 to +340
} catch (\Throwable $e) {
Notification::make()
->title('Could not create Sentry rule')
->body($e->getMessage())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): Catching Throwable and surfacing the raw exception message to the user can leak internal details.

Using $e->getMessage() directly in a user notification may expose sensitive implementation or configuration details. Instead, log the full exception (with stack trace) to your logging/Sentry pipeline, and present a generic, user-friendly error message—optionally including a correlation ID for support/debugging.

Comment on lines +199 to +203
TextInput::make('frequency')
->label('Action frequency minutes')
->numeric()
->minValue(5)
->maxValue(43200)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Frequency bounds are duplicated here and in SentryClient; consider centralizing to avoid drift.

The frequency limits (5, 43200) are enforced here on the form and again in SentryClient::createIssueAlertRule() when clamping. Please consider extracting these into shared constants (or deriving one from the other) so that both the UI and client always use the same range.

Suggested implementation:

namespace App\Filament\Pages;

use App\Services\SentryClient;

                    TextInput::make('frequency')
                        ->label('Action frequency minutes')
                        ->numeric()
                        ->minValue(SentryClient::MIN_FREQUENCY_MINUTES)
                        ->maxValue(SentryClient::MAX_FREQUENCY_MINUTES)
                        ->default(SentryClient::DEFAULT_FREQUENCY_MINUTES)

In App\Services\SentryClient (or the actual namespace where SentryClient lives), define and use shared constants, for example:

class SentryClient
{
    public const MIN_FREQUENCY_MINUTES = 5;
    public const MAX_FREQUENCY_MINUTES = 43200;
    public const DEFAULT_FREQUENCY_MINUTES = self::MIN_FREQUENCY_MINUTES;

    public function createIssueAlertRule(array $payload): void
    {
        $frequency = (int) ($payload['frequency'] ?? self::DEFAULT_FREQUENCY_MINUTES);
        $frequency = max(self::MIN_FREQUENCY_MINUTES, min(self::MAX_FREQUENCY_MINUTES, $frequency));
        // ...
    }
}

You may need to adjust:

  1. The use App\Services\SentryClient; import to match your actual SentryClient namespace.
  2. The constant names if you prefer a different naming convention, but they should be used both here and in createIssueAlertRule() where clamping is currently implemented.

@deepsource-io

deepsource-io Bot commented Jun 2, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 16e5338...488468f on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
PHP Jun 2, 2026 5:17p.m. Review ↗
JavaScript Jun 2, 2026 5:17p.m. Review ↗
Python Jun 2, 2026 5:17p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-to-end support for creating Sentry issue alert rules that trigger the Forge Sentry app action, including a Filament admin UI entry point, the Sentry API client call, and a feature test to verify the outbound request payload.

Changes:

  • Implement SentryClient::createIssueAlertRule() plus configuration prerequisites (ensureAlertRuleConfigured()).
  • Add a Filament header action on Connectors & Sync Settings to collect inputs and create the rule.
  • Extend Sentry outbound sync feature tests to validate the rule creation request.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
app/Integrations/Sentry/Services/SentryClient.php Adds a client method to POST a Sentry issue alert rule payload wired to the Sentry App action, with prerequisite configuration checks.
app/Filament/Pages/ConnectorsAndSyncSettings.php Adds a Filament action + modal form to create the Sentry rule from the admin settings page.
tests/Feature/Integrations/Sentry/OutboundSyncTest.php Adds a feature test validating the outbound request shape for creating an issue alert rule.

Comment on lines +322 to +329
$rule = app(SentryClient::class)->createIssueAlertRule(
sentryProjectSlug: trim((string) ($data['sentry_project_slug'] ?? '')),
ruleName: trim((string) ($data['rule_name'] ?? 'Send new issues to Forge')),
forgeProjectId: (string) ($data['forge_project_id'] ?? ''),
issueTypeId: (int) ($data['forge_issue_type_id'] ?? 0),
priorityId: (int) ($data['forge_priority_id'] ?? 0),
frequency: (int) ($data['frequency'] ?? 5),
);
Comment on lines +337 to +343
} catch (\Throwable $e) {
Notification::make()
->title('Could not create Sentry rule')
->body($e->getMessage())
->danger()
->send();
}
Comment on lines +192 to +202
return $request->method() === 'POST'
&& str_ends_with($request->url(), '/projects/acme/crash-game/rules/')
&& $request['conditions'][0]['id'] === 'sentry.rules.conditions.first_seen_event.FirstSeenEventCondition'
&& $action['id'] === 'sentry.rules.actions.notify_event_sentry_app.NotifyEventSentryAppAction'
&& $action['sentryAppInstallationUuid'] === '9a89a822-6e0b-4b62-9b99-905b9d742dd1'
&& $action['hasSchemaFormConfig'] === true
&& $action['settings'] === [
['name' => 'forge_project_id', 'value' => (string) $project->id],
['name' => 'forge_issue_type_id', 'value' => (string) $type->id],
['name' => 'forge_priority_id', 'value' => (string) $priority->id],
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants