feat(saved-views): add organization and private saved views across API and UI#2160
feat(saved-views): add organization and private saved views across API and UI#2160
Conversation
There was a problem hiding this comment.
Pull request overview
Adds end-to-end Saved Views support (organization-wide + user-private) across the .NET API, Elasticsearch persistence, generated OpenAPI/TS client artifacts, and Svelte UI integrations for Events/Issues/Stream dashboards, including feature-flag gating at the organization level.
Changes:
- Introduces
SavedViewdomain model + repository/index, plus API endpoints for CRUD and listing by org/view with default-view semantics. - Adds mapping (Mapperly) + backend integration tests + OpenAPI snapshot updates for saved views and org features.
- Integrates Saved Views into the Svelte app (picker, auto-load default, sidebar submenu, org “Features” admin page), plus filter (de)serialization helpers and tests.
Reviewed changes
Copilot reviewed 40 out of 42 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/http/saved-views.http | Adds HTTP scratchpad requests for login, feature enablement, and saved-views CRUD flows. |
| tests/Exceptionless.Tests/Mapping/SavedViewMapperTests.cs | Validates Mapperly mappings between saved-view DTOs and domain/view models. |
| tests/Exceptionless.Tests/Controllers/SavedViewControllerTests.cs | Adds integration coverage for scoping, feature gating, validation, default behavior, and access control. |
| tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs | Adds tests for enabling/disabling feature flags and returning features in org DTOs. |
| tests/Exceptionless.Tests/Controllers/Data/openapi.json | Updates OpenAPI snapshot to include saved-views endpoints and schemas. |
| src/Exceptionless.Web/Models/SavedView/ViewSavedView.cs | Adds API response DTO for saved views. |
| src/Exceptionless.Web/Models/SavedView/UpdateSavedView.cs | Adds PATCH/PUT DTO with validation hooks. |
| src/Exceptionless.Web/Models/SavedView/NewSavedView.cs | Adds POST DTO with server-side validation (view + filter defs + columns). |
| src/Exceptionless.Web/Models/Organization/ViewOrganization.cs | Adds Features collection to org view model for UI gating. |
| src/Exceptionless.Web/Mapping/SavedViewMapper.cs | Adds Mapperly mapper for SavedView DTO ↔ domain ↔ view model. |
| src/Exceptionless.Web/Mapping/ApiMapper.cs | Wires SavedView mapping methods into the central ApiMapper. |
| src/Exceptionless.Web/Controllers/SavedViewController.cs | Adds saved-views API endpoints + permissions + feature gating + default clearing logic. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Adds global-admin feature-flag toggle endpoints; removes user private saved views on membership removal. |
| src/Exceptionless.Web/ClientApp/src/routes/routes.svelte.ts | Extends navigation item types to support saved-view children/default metadata. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/stream/+page.svelte | Integrates saved view query param + picker into Stream dashboard. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/routes.svelte.ts | Adds “Features” nav item for global admins. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/features/+page.svelte | Adds UI for toggling known org feature flags. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/issues/+page.svelte | Integrates saved view query param + picker into Issues dashboard. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+page.svelte | Integrates saved view query param + picker into Events dashboard. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte | Adds websocket invalidation and sidebar submenu building from saved views. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/(components)/layouts/sidebar.svelte | Renders dashboard submenu for saved views and active highlighting logic. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/schemas.ts | Adds generated Zod schemas for SavedView DTOs + org features. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/api.ts | Adds generated TS interfaces for SavedView DTOs + org features. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/use-saved-views.svelte.ts | Implements saved-view loading, hydration, default auto-load, and modified detection. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/models.ts | Adds local TS models for saved-view requests/responses. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/index.ts | Exposes saved-view slice API/models for feature consumption. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/components/saved-view-picker.svelte | Adds picker UI for create/select/update/rename/delete/default operations with optimistic caching. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/api.svelte.ts | Adds query/mutation helpers + websocket-driven invalidation for saved views. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds client mutations for org feature enable/disable. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/components/filters/helpers.svelte.ts | Adds filter serialization/deserialization used by saved views. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/components/filters/helpers.svelte.test.ts | Adds Vitest coverage for filter (de)serialization round-trips and defensive parsing. |
| src/Exceptionless.Core/Services/OrganizationService.cs | Adds saved-view cleanup on org deletion and user membership removal. |
| src/Exceptionless.Core/Repositories/SavedViewRepository.cs | Adds ES queries for view/org scoping and private cleanup. |
| src/Exceptionless.Core/Repositories/Interfaces/ISavedViewRepository.cs | Defines saved-view repository contract. |
| src/Exceptionless.Core/Repositories/Configuration/Indexes/SavedViewIndex.cs | Adds ES index mapping/settings for saved views. |
| src/Exceptionless.Core/Repositories/Configuration/Indexes/OrganizationIndex.cs | Indexes org Features as keyword array. |
| src/Exceptionless.Core/Repositories/Configuration/ExceptionlessElasticConfiguration.cs | Registers the SavedViews index with the ES configuration. |
| src/Exceptionless.Core/Models/SavedView.cs | Adds core SavedView domain model + valid views/columns definitions. |
| src/Exceptionless.Core/Models/Organization.cs | Adds Features set + OrganizationFeatures constants. |
| src/Exceptionless.Core/Bootstrapper.cs | Registers ISavedViewRepository in DI. |
| .agents/skills/typescript-conventions/SKILL.md | Captures/relocates frontend code-style rules into owned skills docs. |
| .agents/skills/frontend-architecture/SKILL.md | Captures/relocates frontend architecture guidance into owned skills docs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
f8028c4 to
67884ad
Compare
…I and UI Implements saved views end-to-end with repository/index support, API endpoints, and Svelte integration for events/issues/stream dashboards. Adds coverage for controller behavior and Mapperly mappings, including organization-wide vs private visibility and default-view behavior.
67884ad to
1056033
Compare
There was a problem hiding this comment.
Pull request overview
Adds end-to-end Saved Views support (org-wide and user-private) across the Exceptionless API and Svelte UI, including persistence/indexing, mappings, and tests, plus an admin-only organization feature toggle UI.
Changes:
- Introduces SavedView domain model + Elasticsearch index/repository, API controller endpoints, and Mapperly mappings.
- Adds organization feature-flag storage and admin endpoints/UI to toggle
feature-saved-views. - Integrates Saved Views selection/creation into Events/Issues/Stream dashboards and updates generated OpenAPI/client artifacts.
Reviewed changes
Copilot reviewed 40 out of 42 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/http/saved-views.http | Adds HTTP scratch workflow for feature toggle + CRUD of saved views. |
| tests/Exceptionless.Tests/Mapping/SavedViewMapperTests.cs | Verifies Mapperly mappings for SavedView DTOs. |
| tests/Exceptionless.Tests/Controllers/SavedViewControllerTests.cs | Integration tests for saved-views CRUD, scoping, defaults, validation, and feature gating. |
| tests/Exceptionless.Tests/Controllers/OrganizationControllerTests.cs | Adds tests for org feature enable/disable endpoints and DTO features field. |
| tests/Exceptionless.Tests/Controllers/Data/openapi.json | Updates OpenAPI snapshot with saved-views endpoints/schemas and org features field. |
| src/Exceptionless.Web/Models/SavedView/ViewSavedView.cs | Adds SavedView response DTO. |
| src/Exceptionless.Web/Models/SavedView/UpdateSavedView.cs | Adds update DTO + column-key validation hook. |
| src/Exceptionless.Web/Models/SavedView/NewSavedView.cs | Adds create DTO + view/JSON/column validation. |
| src/Exceptionless.Web/Models/Organization/ViewOrganization.cs | Exposes organization Features set to clients. |
| src/Exceptionless.Web/Mapping/SavedViewMapper.cs | Adds Mapperly mapper for SavedView <-> DTOs. |
| src/Exceptionless.Web/Mapping/ApiMapper.cs | Wires SavedView mappings into API mapper facade. |
| src/Exceptionless.Web/Controllers/SavedViewController.cs | Implements saved-views endpoints with feature gating, scoping, and default handling. |
| src/Exceptionless.Web/Controllers/OrganizationController.cs | Removes private saved views on org user removal; adds feature enable/disable endpoints. |
| src/Exceptionless.Web/ClientApp/src/routes/routes.svelte.ts | Extends navigation types to support dashboard children entries. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/stream/+page.svelte | Adds saved-view query param + picker integration for Stream dashboard. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/routes.svelte.ts | Adds “Features” admin route entry. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/organization/[organizationId]/features/+page.svelte | New UI page for toggling org feature flags. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/issues/+page.svelte | Adds saved-view query param + picker integration for Issues dashboard. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+page.svelte | Adds saved-view query param + picker integration for Events dashboard. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/+layout.svelte | Subscribes to SavedView websocket invalidation + builds sidebar children from saved views. |
| src/Exceptionless.Web/ClientApp/src/routes/(app)/(components)/layouts/sidebar.svelte | Renders collapsible dashboard submenu for saved views. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/schemas.ts | Adds generated schemas/types for saved views and org features. |
| src/Exceptionless.Web/ClientApp/src/lib/generated/api.ts | Adds generated API types for saved views and org features. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/use-saved-views.svelte.ts | Adds saved-view state management (hydrate/reset/modified/default restore). |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/models.ts | Adds saved-views feature-layer types over generated client types. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/index.ts | Re-exports saved-views APIs/models for feature consumers. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/components/saved-view-picker.svelte | Adds UI for selecting/saving/renaming/deleting/defaulting saved views. |
| src/Exceptionless.Web/ClientApp/src/lib/features/saved-views/api.svelte.ts | Adds saved-views queries/mutations + websocket invalidation behavior. |
| src/Exceptionless.Web/ClientApp/src/lib/features/organizations/api.svelte.ts | Adds org feature toggle mutations. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/components/filters/helpers.svelte.ts | Adds filter serialization/deserialization for saved-view persistence. |
| src/Exceptionless.Web/ClientApp/src/lib/features/events/components/filters/helpers.svelte.test.ts | Adds unit tests for filter (de)serialization + round-trips. |
| src/Exceptionless.Core/Services/OrganizationService.cs | Removes saved views on org deletion; removes private views when user leaves org. |
| src/Exceptionless.Core/Repositories/SavedViewRepository.cs | Adds repository queries for org/user/view scoping and cleanup operations. |
| src/Exceptionless.Core/Repositories/Interfaces/ISavedViewRepository.cs | Adds saved-view repository contract. |
| src/Exceptionless.Core/Repositories/Configuration/Indexes/SavedViewIndex.cs | Adds ES index/mapping for saved views. |
| src/Exceptionless.Core/Repositories/Configuration/Indexes/OrganizationIndex.cs | Adds ES mapping for organization features field. |
| src/Exceptionless.Core/Repositories/Configuration/ExceptionlessElasticConfiguration.cs | Registers SavedViews index in elastic configuration. |
| src/Exceptionless.Core/Models/SavedView.cs | Adds SavedView core model + allowed views/columns definitions. |
| src/Exceptionless.Core/Models/Organization.cs | Adds Features set + OrganizationFeatures constants. |
| src/Exceptionless.Core/Bootstrapper.cs | Registers ISavedViewRepository in DI. |
| .agents/skills/typescript-conventions/SKILL.md | Captures project-specific frontend conventions. |
| .agents/skills/frontend-architecture/SKILL.md | Captures project Svelte/architecture guidance (query param binding, $derived/$effect). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Fixes for code quality and correctness issues identified in saved-views PR review: - OrganizationController: Add post-trim validation to prevent empty feature flags from whitespace-only input (closes security gap in minlength route constraint) - DeltaSchemaTransformer: Fix Dictionary schema generation to emit object with additionalProperties instead of array (IDictionary detection must run before IEnumerable check) - helpers.svelte.ts: Replace vague `as []` casts with concrete types (LogLevel[], StackStatus[], PersistentEventKnownTypes[]) for type safety - +layout.svelte: Resolve duplicate currentOrganization declaration by renaming Intercom-specific variable - OrganizationControllerTests: Add whitespace-only feature name test coverage - openapi.json: Regenerate snapshot to reflect Dictionary schema fix
Fixes for code quality and correctness issues identified in saved-views PR review: - OrganizationController: Add post-trim validation to prevent empty feature flags from whitespace-only input (closes security gap in minlength route constraint) - DeltaSchemaTransformer: Fix Dictionary schema generation to emit object with additionalProperties instead of array (IDictionary detection must run before IEnumerable check) - helpers.svelte.ts: Replace vague `as []` casts with concrete types (LogLevel[], StackStatus[], PersistentEventKnownTypes[]) for type safety - +layout.svelte: Resolve duplicate currentOrganization declaration by renaming Intercom-specific variable - OrganizationControllerTests: Add whitespace-only feature name test coverage - openapi.json: Regenerate snapshot to reflect Dictionary schema fix
Sidebar and picker now update immediately when creating or saving views by writing to both queryKeys.view and queryKeys.organization caches optimistically. Delayed websocket invalidation still provides ES-refresh-safe reconciliation.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 45 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Resolve conflict in AGENTS.md — keep both feature/filters frontend notes and main constraints.
| /// <summary>The set of valid dashboard view identifiers.</summary> | ||
| public static readonly string[] ValidViews = ["events", "issues", "stream"]; | ||
|
|
||
| /// <summary>Valid column IDs per view, matching the TanStack Table column definitions.</summary> | ||
| public static readonly IReadOnlyDictionary<string, IReadOnlySet<string>> ValidColumnIds = | ||
| new Dictionary<string, IReadOnlySet<string>> | ||
| { | ||
| ["events"] = new HashSet<string> { "user", "date" }, | ||
| ["issues"] = new HashSet<string> { "status", "users", "events", "first", "last" }, | ||
| ["stream"] = new HashSet<string> { "user", "date" } | ||
| }; | ||
|
|
||
| /// <summary>Union of all valid column IDs across all views.</summary> | ||
| public static readonly IReadOnlySet<string> AllValidColumnIds = | ||
| new HashSet<string>(ValidColumnIds.Values.SelectMany(ids => ids)); |
There was a problem hiding this comment.
This is a question for @ejsmith about the model design. The ValidViews set and ValidColumnIds dictionary on the domain model serve as the single source of truth for valid view identifiers and column keys, referenced by both the API validation and frontend.
…se mutations in components - Replace raw NEST Query<T> operators with Foundatio FilterExpression for OR logic - Rename 'view' parameter to 'dashboardView' for clarity in SavedViewRepository - Remove delayedInvalidate, ES_INDEXING_DELAY_MS, and all delay-based workarounds - Move all API client calls from saved-view-picker component to api.svelte.ts mutations - Use typography Muted component for form helper text - Rename deleteTarget to viewToDelete for clarity - Add guard in organization feature mutations for undefined route.id - Remove redundant XML doc comment from OrganizationService
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 44 out of 46 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export function invalidateSavedViewQueries(queryClient: QueryClient, message: WebSocketMessageValue<'SavedViewChanged'>) { | ||
| const { organization_id } = message; | ||
|
|
||
| if (organization_id) { | ||
| return queryClient.invalidateQueries({ queryKey: queryKeys.organization(organization_id) }); | ||
| } | ||
|
|
||
| return queryClient.invalidateQueries({ queryKey: queryKeys.type }); | ||
| } |
There was a problem hiding this comment.
invalidateSavedViewQueries currently invalidates immediately regardless of change_type, but the new tests (and the documented behavior) require delaying invalidation for Added/Saved events (~1.5s) to avoid stale Elasticsearch reads overwriting optimistic cache updates. Update this helper to conditionally await a delay based on message.change_type before calling queryClient.invalidateQueries(...) (while keeping Removed immediate).
ab72745 to
0c6469e
Compare
…ttern matching, test naming
0c6469e to
e4e6df6
Compare
- Table types: Table<TData> → Table<StockFeatures, TData> - ColumnDef/Cell/Header/Column: add StockFeatures type parameter - getCoreRowModel() → createCoreRowModel(), getSortedRowModel() → createSortedRowModel(sortFns) - VisibilityState → ColumnVisibilityState - table.getState() → table.store.state - FlexRender: content/context props → header/cell props - RowData constraint on generic data-table components - layerchart: BrushEventPayload.xDomain → brush.xDomainMin/xDomainMax - layerchart: getTooltipContext → getChartContext, TooltipPayload type - Fix ViewOrganization storybook features property
Split auth guard and WebSocket into separate effects so page navigation doesn't tear down and recreate the WebSocket connection, which caused unnecessary query invalidation and data refetching.
Summary
Key Decisions (moved from AGENTS into owned skills)
Why
Test Plan
Dogfood
Browser artifacts
API flow verification
Follow-up for attachments
Notes