Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ public CopySimpleDataToIdxAction(AppOptions options, ILoggerFactory loggerFactor
public override Task ProcessAsync(EventContext ctx)
{
if (!ctx.Organization.HasPremiumFeatures)
{
if (ctx.Event.GetEventReference("parent") is not null)
ctx.Event.CopyDataToIndex(["@ref:parent"]);
Comment thread
niemyjski marked this conversation as resolved.
Outdated

return Task.CompletedTask;
}

// TODO: Do we need a pipeline action to trim keys and remove null values that may be sent by other native clients.
ctx.Event.CopyDataToIndex([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public sealed class PersistentEventQueryValidator : AppQueryValidator
"date",
"type",
EventIndex.Alias.ReferenceId,
"idx.parent-r",
Comment thread
niemyjski marked this conversation as resolved.
Outdated
"reference_id",
EventIndex.Alias.OrganizationId,
"organization_id",
Expand Down
4 changes: 2 additions & 2 deletions src/Exceptionless.Web/Api/Handlers/EventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public async Task<Result<PagedResult<object>>> Handle(GetEventsByReferenceId mes

var ti = TimeRangeParser.GetTimeInfo(null, message.Offset, timeProvider, _allowedDateFields, DefaultDateField, organizations.GetRetentionUtcCutoff(appOptions.MaximumRetentionDays, timeProvider));
var sf = new AppFilter(organizations) { IsUserOrganizationsFilter = true };
return await GetInternalAsync(sf, ti, httpContext, String.Concat("reference:", message.ReferenceId), null, message.Mode, message.Page, message.Limit, message.Before, message.After, includeTotal: ShouldIncludeTotal(message.Include));
return await GetInternalAsync(sf, ti, httpContext, $"(reference:{message.ReferenceId} OR ref.parent:{message.ReferenceId})", null, message.Mode, message.Page, message.Limit, message.Before, message.After, includeTotal: ShouldIncludeTotal(message.Include));
Comment thread
niemyjski marked this conversation as resolved.
Outdated
Comment thread
niemyjski marked this conversation as resolved.
Outdated
}

public async Task<Result<PagedResult<object>>> Handle(GetEventsByReferenceIdAndProject message)
Expand All @@ -249,7 +249,7 @@ public async Task<Result<PagedResult<object>>> Handle(GetEventsByReferenceIdAndP

var ti = TimeRangeParser.GetTimeInfo(null, message.Offset, timeProvider, _allowedDateFields, DefaultDateField, organization.GetRetentionUtcCutoff(project, appOptions.MaximumRetentionDays, timeProvider));
var sf = new AppFilter(project, organization);
return await GetInternalAsync(sf, ti, httpContext, String.Concat("reference:", message.ReferenceId), null, message.Mode, message.Page, message.Limit, message.Before, message.After, includeTotal: ShouldIncludeTotal(message.Include));
return await GetInternalAsync(sf, ti, httpContext, $"(reference:{message.ReferenceId} OR ref.parent:{message.ReferenceId})", null, message.Mode, message.Page, message.Limit, message.Before, message.After, includeTotal: ShouldIncludeTotal(message.Include));
}

public async Task<Result<PagedResult<object>>> Handle(GetEventsBySessionId message)
Expand Down
27 changes: 27 additions & 0 deletions tests/Exceptionless.Tests/Api/Endpoints/EventEndpointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,33 @@ public async Task GetByReferenceIdAsync_WithExistingReference_ReturnsMatchingEve
Assert.Equal(referenceId, ev.ReferenceId);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task GetByReferenceIdAsync_WithOnlyParentReference_ReturnsMatchingEvents(bool projectScoped)
{
// Arrange
string referenceId = Guid.NewGuid().ToString("N");
await CreateDataAsync(d => d.Event().FreeProject().Reference("parent", referenceId).Message("parent reference route"));
await RefreshDataAsync();

string[] paths = projectScoped
? ["projects", SampleDataService.FREE_PROJECT_ID, "events", "by-ref", referenceId]
: ["events", "by-ref", referenceId];

// Act
var events = await SendRequestAsAsync<IReadOnlyCollection<PersistentEvent>>(r => r
.AsFreeOrganizationUser()
.AppendPaths(paths)
.StatusCodeShouldBeOk()
);

// Assert
Assert.NotNull(events);
var ev = Assert.Single(events);
Assert.Equal(referenceId, ev.GetEventReference("parent"));
}

[Fact]
public async Task GetCountByOrganizationAsync_WithExistingEvents_ReturnsOrganizationCount()
{
Expand Down
26 changes: 26 additions & 0 deletions tests/Exceptionless.Tests/Pipeline/EventPipelineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,32 @@ public void CanIndexExtendedData()
Assert.Equal(11, ev.Idx.Count);
}

[Fact]
public async Task ProcessAsync_FreePlanParentReference_IndexesOnlyParentReference()
{
var organization = await _organizationRepository.GetByIdAsync(TestConstants.OrganizationId3, o => o.Cache());
Assert.NotNull(organization);
Assert.False(organization.HasPremiumFeatures);

var project = await _projectRepository.AddAsync(_projectData.GenerateProject(organizationId: organization.Id), o => o.ImmediateConsistency().Cache());
var ev = _eventData.GenerateEvent(organizationId: organization.Id, projectId: project.Id, generateData: false, occurrenceDate: TimeProvider.GetUtcNow());
ev.SetEventReference("parent", "parent-reference");
ev.Data!["custom"] = "not indexed";

var context = await _pipeline.RunAsync(ev, organization, project);
Assert.False(context.HasError, context.ErrorMessage);
Assert.NotNull(context.Event.Idx);
Assert.Equal("parent-reference", context.Event.Idx["parent-r"]);
Assert.False(context.Event.Idx.ContainsKey("custom-s"));

await RefreshDataAsync();
var parentMatches = await _eventRepository.FindAsync(q => q.FieldEquals("idx.parent-r", "parent-reference"));
Assert.Equal(ev.Id, Assert.Single(parentMatches.Documents).Id);

var customMatches = await _eventRepository.FindAsync(q => q.FieldEquals("idx.custom-s", "not indexed"));
Assert.Empty(customMatches.Documents);
}

[Fact]
public async Task SyncStackTagsAsync()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public PersistentEventQueryValidatorTests(ITestOutputHelper output) : base(outpu
[InlineData("organization:404", "organization:404", true, false)]
[InlineData("project:404", "project:404", true, false)]
[InlineData("stack:404", "stack:404", true, false)]
[InlineData("ref.parent:12345678", "idx.parent-r:12345678", true, false)]
[InlineData("ref.session:12345678", "idx.session-r:12345678", true, true)]
[InlineData("status:open", "status:open", true, false)]
public async Task CanProcessQueryAsync(string query, string expected, bool isValid, bool usesPremiumFeatures)
Expand Down
8 changes: 8 additions & 0 deletions tests/http/events.http
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ Content-Type: application/json
"reference_id": "{{referenceId}}"
}

### By ReferenceId
GET {{apiUrl}}/events/by-ref/{{referenceId}}
Authorization: Bearer {{token}}

### By ReferenceId And Project
GET {{apiUrl}}/projects/{{projectId}}/events/by-ref/{{referenceId}}
Authorization: Bearer {{token}}

### Post User Description
POST {{apiUrl}}/events/by-ref/{{referenceId}}/user-description?access_token={{clientToken}}
Content-Type: application/json
Expand Down
Loading