Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ jobs:
run: |
export PATH="$HOME/.dotnet/tools:$PATH"
if dotnet tool list --global | grep -Eiq '^aspire\.cli[[:space:]]'; then
dotnet tool update --global Aspire.Cli --version 13.2.4
dotnet tool update --global Aspire.Cli --version 13.3.3
else
dotnet tool install --global Aspire.Cli --version 13.2.4
dotnet tool install --global Aspire.Cli --version 13.3.3
fi
echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
aspire --version
Expand Down
10 changes: 5 additions & 5 deletions src/Exceptionless.AppHost/Exceptionless.AppHost.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Aspire.AppHost.Sdk/13.3.0">
<Project Sdk="Aspire.AppHost.Sdk/13.3.3">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
Expand All @@ -8,10 +8,10 @@
<NoWarn>$(NoWarn);ASPIRECERTIFICATES001</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Azure.Storage" Version="13.3.0" />
<PackageReference Include="Aspire.Hosting.Browsers" Version="13.3.0-preview.1.26256.5" />
<PackageReference Include="Aspire.Hosting.JavaScript" Version="13.3.0" />
<PackageReference Include="Aspire.Hosting.Redis" Version="13.3.0" />
<PackageReference Include="Aspire.Hosting.Azure.Storage" Version="13.3.3" />
<PackageReference Include="Aspire.Hosting.Browsers" Version="13.3.3-preview.1.26264.13" />
<PackageReference Include="Aspire.Hosting.JavaScript" Version="13.3.3" />
<PackageReference Include="Aspire.Hosting.Redis" Version="13.3.3" />
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="9.0.0" />
</ItemGroup>

Expand Down
4 changes: 4 additions & 0 deletions src/Exceptionless.Core/Models/SavedView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public record SavedView : IOwnedByOrganizationWithIdentity, IHaveDates
[MaxLength(100)]
public string? Time { get; set; }

/// <summary>Sort expression for the dashboard table, e.g. "-date".</summary>
[MaxLength(100)]
public string? Sort { get; set; }

/// <summary>Schema version for future filter definition migrations.</summary>
public int Version { get; set; } = 1;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import type { BooleanFilter } from './models.svelte';

let { filter, filterChanged, filterRemoved, ...props }: FacetedFilterProps<BooleanFilter> = $props();

function toggleHidden() {
filter.hidden = !filter.hidden;
filterChanged(filter);
}
</script>

<FacetedFilter.Boolean
Expand All @@ -17,6 +22,8 @@
filter.value = undefined;
filterRemoved(filter);
}}
hidden={filter.hidden}
{toggleHidden}
value={filter.value}
{...props}
></FacetedFilter.Boolean>
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import { DateFilter } from './models.svelte';

let { filter, filterChanged, open = $bindable(false), title = 'Date Range' }: FacetedFilterProps<DateFilter> = $props();
let { filter, filterChanged, filterRemoved, open = $bindable(false), title = 'Date Range' }: FacetedFilterProps<DateFilter> = $props();

let dateRangePickerRef: DateRangePickerType | undefined = $state();
let shouldApply = $state(true);
Expand All @@ -36,6 +36,25 @@
shouldApply = false;
}
}

function onClearFilter() {
shouldApply = false;
filter.value = undefined;
filterChanged(filter);
open = false;
}

function onRemoveFilter() {
shouldApply = false;
filterRemoved(filter);
open = false;
Comment thread
ejsmith marked this conversation as resolved.
}

function toggleHidden() {
shouldApply = false;
filter.hidden = !filter.hidden;
filterChanged(filter);
}
</script>

<Popover.Root bind:open {onOpenChange}>
Expand All @@ -55,5 +74,6 @@
<div class="flex flex-col">
<DateRangePicker bind:this={dateRangePickerRef} {quickRanges} value={filter.value} onselect={handleSelect} />
</div>
<FacetedFilter.Actions clear={onClearFilter} hidden={filter.hidden} remove={onRemoveFilter} showClear={filter.value !== undefined} {toggleHidden} />
</Popover.Content>
</Popover.Root>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';

import { deserializeFilters, quoteIfSpecialCharacters, serializeFilters } from './helpers.svelte';
import { deserializeFilters, quoteIfSpecialCharacters, serializeFilters, toFilter } from './helpers.svelte';
import {
BooleanFilter,
DateFilter,
Expand Down Expand Up @@ -111,6 +111,14 @@ describe('serializeFilters', () => {
expect(result[0]).toEqual({ type: 'project', value: ['proj1', 'proj2'] });
});

it('serializes hidden filters when hidden is true', () => {
const filter = new ProjectFilter(['proj1']);
filter.hidden = true;
const result = JSON.parse(serializeFilters([filter]));

expect(result[0]).toEqual({ hidden: true, type: 'project', value: ['proj1'] });
});

it('serializes a ReferenceFilter with value', () => {
const filters = [new ReferenceFilter('ref-123')];
const result = JSON.parse(serializeFilters(filters));
Expand Down Expand Up @@ -234,6 +242,14 @@ describe('deserializeFilters', () => {
expect((filters[0] as ProjectFilter).value).toEqual(['p1', 'p2']);
});

it('deserializes hidden filters', () => {
const filters = deserializeFilters('[{"type":"project","value":["p1"],"hidden":true}]');

expect(filters).toHaveLength(1);
expect(filters[0]).toBeInstanceOf(ProjectFilter);
expect(filters[0]?.hidden).toBe(true);
});

it('deserializes a ReferenceFilter', () => {
const filters = deserializeFilters('[{"type":"reference","value":"ref-123"}]');

Expand Down Expand Up @@ -301,6 +317,19 @@ describe('deserializeFilters', () => {
});

describe('round-trip serialization', () => {
it('round-trips hidden state without changing filter output', () => {
const projectFilter = new ProjectFilter(['proj1']);
projectFilter.hidden = true;
const originalFilterString = toFilter([projectFilter]);

const result = deserializeFilters(serializeFilters([projectFilter]));

expect(result).toHaveLength(1);
expect(result[0]).toBeInstanceOf(ProjectFilter);
expect(result[0]?.hidden).toBe(true);
expect(toFilter(result)).toBe(originalFilterString);
});

it('round-trips a BooleanFilter', () => {
const original = [new BooleanFilter('is_fixed', true)];
const result = deserializeFilters(serializeFilters(original));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export function filterCacheVersionNumber() {
const filterCache = new SvelteMap<null | string, IFilter[]>();

interface SerializedFilter {
hidden?: boolean;
term?: string;
type: string;
value?: unknown;
Expand Down Expand Up @@ -155,6 +156,10 @@ export function serializeFilters(filters: IFilter[]): string {
entry.value = (filter as { value?: unknown }).value;
}

if (filter.hidden) {
entry.hidden = true;
}

return entry;
});

Expand Down Expand Up @@ -238,6 +243,8 @@ function processFilterRules(filters: IFilter[]): IFilter[] {
} else if (filter.value !== undefined) {
existingFilter.value = filter.value;
}

existingFilter.hidden = filter.hidden;
} else {
throw new Error('Unable to merge filters');
}
Expand All @@ -253,34 +260,54 @@ function processFilterRules(filters: IFilter[]): IFilter[] {
}

function reconstructFilter(data: SerializedFilter): IFilter | null {
let filter: IFilter | null;
switch (data.type) {
case 'boolean':
return new BooleanFilter(data.term, data.value as boolean | undefined);
filter = new BooleanFilter(data.term, data.value as boolean | undefined);
break;
case 'date':
return new DateFilter(data.term, data.value as Date | string | undefined);
filter = new DateFilter(data.term, data.value as Date | string | undefined);
break;
case 'keyword':
return new KeywordFilter(data.value as string | undefined);
filter = new KeywordFilter(data.value as string | undefined);
break;
case 'level':
return new LevelFilter(data.value as LogLevel[] | undefined);
filter = new LevelFilter(data.value as LogLevel[] | undefined);
break;
case 'number':
return new NumberFilter(data.term, data.value as number | undefined);
filter = new NumberFilter(data.term, data.value as number | undefined);
break;
case 'project':
return new ProjectFilter(data.value as string[] | undefined);
filter = new ProjectFilter(data.value as string[] | undefined);
break;
case 'reference':
return new ReferenceFilter(data.value as string | undefined);
filter = new ReferenceFilter(data.value as string | undefined);
break;
case 'session':
return new SessionFilter(data.value as string | undefined);
filter = new SessionFilter(data.value as string | undefined);
break;
case 'status':
return new StatusFilter(data.value as StackStatus[] | undefined);
filter = new StatusFilter(data.value as StackStatus[] | undefined);
break;
case 'string':
return new StringFilter(data.term, data.value as string | undefined);
filter = new StringFilter(data.term, data.value as string | undefined);
break;
case 'tag':
return new TagFilter(data.value as PersistentEventKnownTypes[] | undefined);
filter = new TagFilter(data.value as PersistentEventKnownTypes[] | undefined);
break;
case 'type':
return new TypeFilter(data.value as PersistentEventKnownTypes[] | undefined);
filter = new TypeFilter(data.value as PersistentEventKnownTypes[] | undefined);
break;
case 'version':
return new VersionFilter(data.term, data.value as string | undefined);
filter = new VersionFilter(data.term, data.value as string | undefined);
break;
default:
return null;
filter = null;
}

if (filter) {
filter.hidden = data.hidden === true;
}

return filter;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
import { KeywordFilter } from './models.svelte';

let { filter, filterChanged, filterRemoved, title = 'Keyword', ...props }: FacetedFilterProps<KeywordFilter> = $props();

function toggleHidden() {
filter.hidden = !filter.hidden;
filterChanged(filter);
}
</script>

<FacetedFilter.Keyword
Expand All @@ -17,7 +22,9 @@
filter.value = undefined;
filterRemoved(filter);
}}
hidden={filter.hidden}
{title}
{toggleHidden}
value={filter.value}
{...props}
></FacetedFilter.Keyword>
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
import { LevelFilter } from './models.svelte';

let { filter, filterChanged, filterRemoved, title = 'Log Level', ...props }: FacetedFilterProps<LevelFilter> = $props();

function toggleHidden() {
filter.hidden = !filter.hidden;
filterChanged(filter);
}
</script>

<FacetedFilter.MultiSelect
Expand All @@ -19,7 +24,9 @@
filter.value = [];
filterRemoved(filter);
}}
hidden={filter.hidden}
{title}
{toggleHidden}
values={filter.value}
{...props}
></FacetedFilter.MultiSelect>
Loading