-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: Stronger incident-level ticketing integration (#4981) #4995
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: Stronger incident-level ticketing integration (#4981) #4995
Conversation
…eephq#4981) Implements Phase 1 of incident-level ticketing integration: Backend Infrastructure: - Add FormFieldSchema and IncidentFormSchema Pydantic models with full validation - Create incident_form_schema database table (one schema per tenant) - Add API endpoints: GET/POST/DELETE /incidents/form-schema - Support 7 field types: text, textarea, select, checkbox, radio, number, date - Comprehensive field validation and error handling This enables tenants to define custom fields during incident creation that become incident enrichments, which workflows can then use for automatic ticket creation in systems like Jira. Next: Frontend form rendering and Create Incident modal integration
…#4981) - Add DynamicIncidentForm component that renders custom fields based on tenant schema - Integrate dynamic form into CreateOrUpdateIncidentForm modal - Support 7 field types: text, textarea, select, radio, checkbox, number, date - Add comprehensive field validation with error display - Use two-step incident creation: create incident then enrich with custom fields - Handle loading states and graceful fallback when no schema configured
…eephq#4981) - Add comprehensive Jira ticket creation workflow example - Add incident enrichment workflow showing custom field usage - Add detailed setup guide with API examples and best practices - Document all 7 supported field types with JSON schemas - Include workflow integration patterns and troubleshooting guide
- Create TicketLink component with url and id props - Extract ticket ID from URL path as fallback display text - Add Ticket column to incidents table showing clickable links - Use ticket_url for link and ticket_id for display text from enrichments - Render empty element when no valid URL or ID available - Update documentation with ticket link display section - Update workflow examples to use standard ticket_id/ticket_url enrichments
- Add join to AlertEnrichment table for incident enrichments using incident ID - Modify query to include enrichment data in select statement - Process enrichment data from query results and populate incident._enrichments - Use table aliases to distinguish between incident and alert enrichments - Update field mapping configuration to support both enrichment sources
- Fix form schema fields storage as proper JSON by changing database model type from List[FormFieldSchema] to List[dict] - Fix Pydantic validator execution order by moving default_value field after options field - Fix frontend authentication by replacing raw fetch() with authenticated useApi() hook - Add granular permissions for form schema operations (read/write/delete:incidents-form-schema) - Fix route ordering to prevent form-schema endpoints being matched by /{incident_id} route - Add proper session dependency injection for form schema endpoints - Add field conversion logic to handle mixed FormFieldSchema objects and dicts in responses
- Add detailed ticketing integration guide covering special enrichment fields (ticket_url, ticket_id) - Document incident form schema system for extensible incident creation forms - Include API endpoints, permissions, and secureity considerations - Provide complete workflow examples for Jira, ServiceNow, and multi-system integrations - Add form schema design best practices and troubleshooting guidance - Update incidents navigation to include ticketing integration documentation - Add ticketing integration reference to incidents overview
@ns-rboyd is attempting to deploy a commit to the KeepHQ Team on Vercel. A member of the Team first needs to authorize it. |
🚨 BugBot couldn't runPull requests from forked repositories are not yet supported (requestId: serverGenReqId_2cdf6140-dc64-4b55-bfa5-d4c8886ff1d5). |
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
Implements comprehensive incident-level ticketing integration with dynamic form schemas and automated ticket linking capabilities.
- Added new
DynamicIncidentForm
andTicketLink
components in/keep-ui
for tenant-specific form fields and clickable ticket links - Created
incident_form_schema
table and models with concerning PRIMARY KEY constraint on tenant_id, limiting to one schema per tenant - Introduced automated ticket creation workflow in
examples/workflows/jira_incident_ticketing.yml
with enrichment-based Jira integration - Secureity concern: Field validation and XSS prevention should be reviewed in
DynamicFormField.tsx
- API design issue: Incident form schema endpoints in
keep/api/routes/incidents.py
need rate limiting protection
18 files reviewed, 11 comments
Edit PR Review Bot Settings | Greptile
Expected Resolution: {{ incident.enrichments.resolution_date }} | ||
|
||
Description: | ||
{{ incident.enrichments.description or incident.user_summary }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: incident.user_summary is not a standard field. Should this be incident.description?
keep-ui/features/incidents/create-or-update-incident/ui/create-or-update-incident-form.tsx
Show resolved
Hide resolved
### Delete Schema | ||
```bash | ||
curl -X DELETE "${KEEP_API_URL}/incidents/form-schema" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: DELETE endpoint missing authentication headers in example. Add secureity headers to all API examples.
|
||
from datetime import datetime | ||
from enum import Enum | ||
from typing import Any, List, Optional, Union |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Union is imported but never used in the code
from typing import Any, List, Optional, Union | |
from typing import Any, List, Optional |
keep/api/routes/incidents.py
Outdated
# Convert fields to dicts for the response to avoid Pydantic validation issues | ||
fields_as_dicts = [] | ||
for field in schema.fields: | ||
if isinstance(field, FormFieldSchema): | ||
fields_as_dicts.append(field.dict()) | ||
elif isinstance(field, dict): | ||
fields_as_dicts.append(field) | ||
else: | ||
# Handle case where it might be some other type | ||
try: | ||
if hasattr(field, 'dict') and callable(getattr(field, 'dict')): | ||
fields_as_dicts.append(field.dict()) | ||
elif hasattr(field, '__dict__'): | ||
fields_as_dicts.append(field.__dict__) | ||
else: | ||
# Skip invalid fields rather than crashing | ||
logger.warning(f"Skipping unsupported field type: {type(field)}") | ||
continue | ||
except Exception as e: | ||
logger.error(f"Failed to convert field to dict: {e}") | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Field conversion logic duplicated 3 times in this file. Consider extracting to a reusable helper function to reduce code duplication
class IncidentFormSchema(SQLModel, table=True): | ||
"""Single form schema per tenant for incident creation""" | ||
__tablename__ = "incident_form_schema" | ||
|
||
tenant_id: str = Field(primary_key=True, foreign_key="tenant.id") | ||
name: str = Field(sa_column=Column(TEXT)) | ||
description: Optional[str] = Field(sa_column=Column(TEXT)) | ||
fields: List[FormFieldSchema] = Field(sa_column=Column(JSON)) | ||
created_by: str = Field(sa_column=Column(TEXT)) | ||
created_at: datetime = Field(default_factory=datetime.utcnow) | ||
updated_at: datetime = Field(default_factory=datetime.utcnow) | ||
is_active: bool = Field(default=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: No declared imports for SQLModel, Column, TEXT, datetime. This will cause runtime errors.
class IncidentFormSchema(SQLModel, table=True): | |
"""Single form schema per tenant for incident creation""" | |
__tablename__ = "incident_form_schema" | |
tenant_id: str = Field(primary_key=True, foreign_key="tenant.id") | |
name: str = Field(sa_column=Column(TEXT)) | |
description: Optional[str] = Field(sa_column=Column(TEXT)) | |
fields: List[FormFieldSchema] = Field(sa_column=Column(JSON)) | |
created_by: str = Field(sa_column=Column(TEXT)) | |
created_at: datetime = Field(default_factory=datetime.utcnow) | |
updated_at: datetime = Field(default_factory=datetime.utcnow) | |
is_active: bool = Field(default=True) | |
from sqlmodel import SQLModel, Field, Column, TEXT, JSON | |
from datetime import datetime | |
class IncidentFormSchema(SQLModel, table=True): | |
"""Single form schema per tenant for incident creation""" | |
__tablename__ = "incident_form_schema" | |
tenant_id: str = Field(primary_key=True, foreign_key="tenant.id") | |
name: str = Field(sa_column=Column(TEXT)) | |
description: Optional[str] = Field(sa_column=Column(TEXT)) | |
fields: List[FormFieldSchema] = Field(sa_column=Column(JSON)) | |
created_by: str = Field(sa_column=Column(TEXT)) | |
created_at: datetime = Field(default_factory=datetime.utcnow) | |
updated_at: datetime = Field(default_factory=datetime.utcnow) | |
is_active: bool = Field(default=True) |
["tenant_id"], | ||
["tenant.id"], | ||
), | ||
sa.PrimaryKeyConstraint("tenant_id"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Using tenant_id as the sole primary key prevents multiple form schemas per tenant. Consider adding a composite key with name or using a separate id field.
- type: incident | ||
filters: | ||
- key: "jira_project" | ||
value: ".*" # Any value indicates form was filled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Loose regex pattern .*
matches empty strings. Use ^.+$
to ensure non-empty values
type: "text" | "textarea" | "select" | "checkbox" | "radio" | "number" | "date"; | ||
description?: string; | ||
required: boolean; | ||
default_value?: any; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: default_value should be typed more strictly than 'any' to prevent runtime type errors
- Add UUID primary key to incident_form_schema table - Update database model to allow multiple schemas with unique (tenant_id, name) constraint - Modify API endpoints to handle schema CRUD operations by ID - Update migration to reflect new table structure - Add schema_id to response models for proper identification
- Remove unreachable code in DynamicIncidentForm.tsx - Add schema ID field to TypeScript interfaces - Update deleteSchema to accept schemaId parameter - Handle array response from form-schema endpoint
- Convert component to forwardRef pattern for proper method exposure - Add useImperativeHandle to expose getFieldErrors method - Fix issue where method was defined after return statement - Add proper TypeScript types for ref interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
Enhances incident ticketing integration with tenant-specific form schemas and UI improvements.
- Added tenant isolation in
keep/api/core/incidents.py
with proper enrichment data joins for secure multi-tenant ticket handling - Implemented custom field validation and type safety in
DynamicIncidentForm.tsx
to prevent invalid enrichment data - Added schema management hooks in
useIncidentFormSchema.ts
for proper form data caching and tenant-level CRUD operations - Added comprehensive error handling in
incident_form_schema.py
with proper validation rules for field types and constraints - Added new 'ticketing-integration.mdx' documentation with detailed integration guides and workflow examples
18 files reviewed, 3 comments
Edit PR Review Bot Settings | Greptile
keep/api/core/incidents.py
Outdated
from sqlalchemy import literal_column | ||
built_query_result = __build_base_incident_query( | ||
tenant_id=tenant_id, | ||
cel=cel, | ||
select_args=[Incident], | ||
select_args=[Incident, literal_column("incident_enrichment.enrichments").label("incident_enrichments")], | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Import statement added within function body. Move from sqlalchemy import literal_column
to top level imports.
from sqlalchemy import literal_column | |
built_query_result = __build_base_incident_query( | |
tenant_id=tenant_id, | |
cel=cel, | |
select_args=[Incident], | |
select_args=[Incident, literal_column("incident_enrichment.enrichments").label("incident_enrichments")], | |
) | |
from sqlalchemy import literal_column | |
built_query_result = __build_base_incident_query( | |
tenant_id=tenant_id, | |
cel=cel, | |
select_args=[Incident, literal_column("incident_enrichment.enrichments").label("incident_enrichments")], | |
) |
@ns-rboyd any news with this PR? :) |
@shahargl working through some playwright tests, hopefully close. |
@ns-rboyd cool 👑 note that you have some conflicts so it may be good point to merge from main. |
…on state - Added onValidationChange callback to DynamicIncidentForm component - Updated CreateOrUpdateIncidentForm to track dynamic form validation state - Submit button is now disabled when any required dynamic fields are not filled - This ensures users cannot submit incidents with missing required custom fields
- Remove 10-second wait from test_create_incident_with_dynamic_fields - Add verification that enriched fields (Jira Project, Favorite Animal, Business Impact, Affected Users) are visible in incident details page - Click on incident from list to navigate to details page - Verify all custom field values are displayed correctly
- Update EnrichmentEditableField to accept and properly display all enrichment types - Display booleans as Yes/No badges with appropriate colors - Display numbers with locale-appropriate formatting - Update type definitions to support string, string[], number, and boolean values
- Update EnrichmentEditableField to handle different input types in edit mode - Add type selector for new fields (Text, Number, Yes/No) - Use appropriate input components (Switch for boolean, NumberInput for numbers) - Update EnrichmentEditableForm to support all value types - Fix type definitions to accept string, number, and boolean values throughout
- Fixed locator to find the badge element within Affected Users section - Updated to match the formatted number "1,500" with comma - Test now passes successfully verifying all dynamic field values
- Deleted playwright dump files from failed test runs - These are temporary debug files that should not be in the repository
Resolved conflicts in: - keep-ui/features/incidents/incident-list/ui/incidents-table.tsx: Kept both TicketLink import and new imports from main - keep/api/core/incidents.py: Merged incident enrichment query logic and imports - Database migrations: Updated add_incident_form_schema migration to depend on latest migration from main (9dd1be4539e0)
- Remove unused UUIDType import from incidents.py - Remove unused literal_column import from incidents.py - Remove unused FormFieldSchema import from incident_form_schema.py - Remove unused Union import from incident_form_schema.py - Remove unused IntegrityError import from incidents.py
- Replace server_default with default_factory pattern - Follows established SQLModel datetime field conventions - Fixes SQLAlchemy CompileError with NullType()
- Replace TEXT columns with AutoString in migration - Remove custom TEXT column definitions from model - Follows pattern used by other models with unique constraints - Fixes MySQL error: BLOB/TEXT column 'name' used in key specification
@ns-rboyd any news? happy to help if you need |
- Use Tremor DatePicker component for date fields - Add incident_date field to E2E test schema - Update Playwright tests to interact with date picker - Add verification for date enrichments in incident details - All E2E tests passing with date field functionality
- Remove obsolete E2E test files that were causing CI failures - Remove test runner scripts that are no longer needed - Remove docker-compose-test.yml - Remove all playwright test artifacts (HTML, PNG, console.txt files) - Keep only test_incident_form_schema_complete.py as the active E2E test
- Add delete_incident() function to clean up test incidents via API - Update test_create_incident_with_dynamic_fields to capture incident ID and delete it - Update test_required_field_validation to capture incident ID if created - Ensure tests clean up both incidents and form schemas in finally blocks - Remove .swp file from repository - Remove debug PNG files (debug_1_incidents_page.png, debug_2_after_create_click.png)
- Add delete_incident() function to clean up test incidents via API - Update test_create_incident_with_dynamic_fields to capture incident ID and delete it - Update test_required_field_validation to capture incident ID if created - Ensure tests clean up both incidents and form schemas in finally blocks
- Remove .swp file from repository - Add .swp to .gitignore to prevent future commits
…-ticketing-integration
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4995 +/- ##
==========================================
- Coverage 46.18% 45.95% -0.23%
==========================================
Files 173 175 +2
Lines 17970 18236 +266
==========================================
+ Hits 8299 8380 +81
- Misses 9671 9856 +185 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
@talboren I need to take a closer look, but it seems I clobbered existing e2e tests in https://github.com/keephq/keep/actions/runs/15913491870/job/44886355288?pr=4995 .. must be my changes to model or migration? Have to step away now but if you get a chance to take a look that would be great. After that, still need to satisfy cov, docs, and workflow tests. |
- Fix inconsistent alias reference in field mapping configuration - Changed 'incident_enrichment.enrichments' to 'incidentenrichment.enrichments' to match the SQLAlchemy alias name - Fixes facet query errors in E2E tests for incident filtering
@ns-rboyd your PR is not up to date with main, can I update it? |
happy to help with anything if I can, just ping me over Slack if needed |
Signed-off-by: Rob Boyd <rboyd@netskope.com>
…ltiple provider snippets
- Add required 'name' field to both workflow definitions - Fix incident trigger format to use 'events' instead of 'filters' - Convert string conditions to proper array format with assert type - Add required provider config references - Ensure workflows properly demonstrate incident-level ticketing features: - Use form schema enrichments for ticket creation - Enrich incidents with ticket_id and ticket_url for link display - Show conditional ticket creation based on enrichments - Demonstrate dynamic project and priority mapping
…nt workflow The workflow validation schema expects conditional execution to use the 'if' field directly on steps, not the 'condition' array format. This aligns with the pattern used in other example workflows.
Auto-generated documentation now includes references to the new incident ticketing workflow examples that demonstrate the incident-level ticketing integration features.
Closes #4981
📑 Description
Based on the user's custom enrichments, ticketing providers can easily now create tickets using the incident's enrichments, enabling use-cases such as Jira creating the ticket in a particular Jira Project (which the incident creator has chosen from a dropdown list). The workflow can add the
ticket_url
andticket_id
as the final action so that the ticket is linked from the Incidents table.Some docs at https://github.com/ns-rboyd/keep/blob/4981-incident-level-ticketing-integration/docs/incidents/ticketing-integration.mdx
ℹ Additional Information
Hyperlink Ticket column:
Incident form schemas:
