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
41 changes: 28 additions & 13 deletions backend/modules/observability/application/openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/bytedance/sonic"
"github.com/coze-dev/coze-loop/backend/kitex_gen/base"
"github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/collector"
"github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/time_range"
"github.com/coze-dev/coze-loop/backend/modules/observability/lib/otel"
"github.com/coze-dev/coze-loop/backend/pkg/lang/ptr"
coltracepb "go.opentelemetry.io/proto/otlp/collector/trace/v1"
Expand Down Expand Up @@ -66,6 +67,7 @@ func NewOpenAPIApplication(
traceConfig config.ITraceConfig,
metrics metrics.ITraceMetrics,
collector collector.ICollectorProvider,
timeRange time_range.ITimeRangeProvider,
) (IObservabilityOpenAPIApplication, error) {
return &OpenAPIApplication{
traceService: traceService,
Expand All @@ -77,6 +79,7 @@ func NewOpenAPIApplication(
traceConfig: traceConfig,
metrics: metrics,
collector: collector,
timeRange: timeRange,
}, nil
}

Expand All @@ -90,6 +93,7 @@ type OpenAPIApplication struct {
traceConfig config.ITraceConfig
metrics metrics.ITraceMetrics
collector collector.ICollectorProvider
timeRange time_range.ITimeRangeProvider
}

func (o *OpenAPIApplication) IngestTraces(ctx context.Context, req *openapi.IngestTracesRequest) (*openapi.IngestTracesResponse, error) {
Expand Down Expand Up @@ -558,17 +562,7 @@ func (o *OpenAPIApplication) validateSearchTraceOApiReq(ctx context.Context, req
} else if req.Limit > MaxListSpansLimit || req.Limit < 0 {
return errorx.NewByCode(obErrorx.CommercialCommonInvalidParamCodeCode, errorx.WithExtraMsg("invalid limit"))
}
v := utils.DateValidator{
Start: req.GetStartTime(),
End: req.GetEndTime(),
EarliestDays: 365,
}
newStartTime, newEndTime, err := v.CorrectDate()
if err != nil {
return err
}
req.SetStartTime(newStartTime)
req.SetEndTime(newEndTime)

return nil
}

Expand All @@ -578,14 +572,35 @@ func (o *OpenAPIApplication) buildSearchTraceOApiReq(ctx context.Context, req *o
platformType = loop_span.PlatformCozeLoop
}

startTime := req.GetStartTime()
endTime := req.GetEndTime()

if startTime == 0 && endTime == 0 {
Comment thread
taoyifan89 marked this conversation as resolved.
st, et := o.timeRange.GetTimeRange(ctx, strconv.FormatInt(req.WorkspaceID, 10), req.GetLogid(), req.GetTraceID(), 1000*60*60*24)
if st != nil && et != nil {
startTime = *st
endTime = *et
}
}

v := utils.DateValidator{
Comment thread
taoyifan89 marked this conversation as resolved.
Start: startTime,
End: endTime,
EarliestDays: 365,
}
newStartTime, newEndTime, err := v.CorrectDate()
if err != nil {
return nil, err
}

ret := &service.SearchTraceOApiReq{
WorkspaceID: req.WorkspaceID,
ThirdPartyWorkspaceID: o.workspace.GetThirdPartyQueryWorkSpaceID(ctx, req.WorkspaceID),
Tenants: o.tenant.GetOAPIQueryTenants(ctx, platformType),
TraceID: req.GetTraceID(),
LogID: req.GetLogid(),
StartTime: req.GetStartTime(),
EndTime: req.GetEndTime(),
StartTime: newStartTime,
EndTime: newEndTime,
Limit: req.GetLimit(),
PlatformType: platformType,
WithDetail: true,
Expand Down
69 changes: 58 additions & 11 deletions backend/modules/observability/application/openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
rpcmocks "github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/rpc/mocks"
"github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/tenant"
tenantmocks "github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/tenant/mocks"
time_rangemocks "github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/time_range/mocks"
"github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/workspace"
workspacemocks "github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/workspace/mocks"
"github.com/coze-dev/coze-loop/backend/modules/observability/domain/trace/entity"
Expand Down Expand Up @@ -964,6 +965,7 @@ func TestNewOpenAPIApplication(t *testing.T) {
traceConfigMock := configmocks.NewMockITraceConfig(ctrl)
metricsMock := metricsmocks.NewMockITraceMetrics(ctrl)
collectorMock := collectormocks.NewMockICollectorProvider(ctrl)
timeRangeMock := time_rangemocks.NewMockITimeRangeProvider(ctrl)

rateLimiterFactoryMock.EXPECT().NewRateLimiter().Return(rateLimiterMock)

Expand All @@ -977,6 +979,7 @@ func TestNewOpenAPIApplication(t *testing.T) {
traceConfigMock,
metricsMock,
collectorMock,
timeRangeMock,
)

assert.NoError(t, err)
Expand All @@ -994,6 +997,7 @@ func TestNewOpenAPIApplication(t *testing.T) {
assert.NotNil(t, openAPIApp.traceConfig)
assert.NotNil(t, openAPIApp.metrics)
assert.NotNil(t, openAPIApp.collector)
assert.NotNil(t, openAPIApp.timeRange)
}

// 补充IngestTraces的边界测试场景
Expand Down Expand Up @@ -2016,19 +2020,8 @@ func TestOpenAPIApplication_validateSearchTraceOApiReq(t *testing.T) {
negativeLimit.Limit = -1
assert.Error(t, app.validateSearchTraceOApiReq(ctx, &negativeLimit))

// invalid time range (zero values)
invalidTime := *validReq
invalidTime.StartTime = 0
invalidTime.EndTime = 0
assert.Error(t, app.validateSearchTraceOApiReq(ctx, &invalidTime))

// valid request should pass
assert.NoError(t, app.validateSearchTraceOApiReq(ctx, validReq))

// start time later than end time
invalidRange := *validReq
invalidRange.StartTime = now + 1000
assert.Error(t, app.validateSearchTraceOApiReq(ctx, &invalidRange))
}

func TestOpenAPIApplication_buildSearchTraceOApiReq(t *testing.T) {
Expand Down Expand Up @@ -2808,3 +2801,57 @@ func TestUngzip(t *testing.T) {
assert.Nil(t, result)
})
}

func TestOpenAPIApplication_buildSearchTraceOApiReq_TimeRangeFallback(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

tenantMock := tenantmocks.NewMockITenantProvider(ctrl)
workspaceMock := workspacemocks.NewMockIWorkSpaceProvider(ctrl)
timeRangeMock := time_rangemocks.NewMockITimeRangeProvider(ctrl)

app := &OpenAPIApplication{
tenant: tenantMock,
workspace: workspaceMock,
timeRange: timeRangeMock,
}

ctx := context.Background()

// Case: StartTime=0, EndTime=0 -> use TimeRangeProvider
workspaceMock.EXPECT().GetThirdPartyQueryWorkSpaceID(gomock.Any(), int64(1)).Return("third-1")
tenantMock.EXPECT().GetOAPIQueryTenants(gomock.Any(), loop_span.PlatformCozeLoop).Return([]string{"tenant-a"})

now := time.Now().UnixMilli()
start := now - 10000
end := now
timeRangeMock.EXPECT().GetTimeRange(gomock.Any(), "1", "log-id", "trace-id", gomock.Any()).Return(&start, &end)

req := &openapi.SearchTraceOApiRequest{
WorkspaceID: 1,
TraceID: ptr.Of("trace-id"),
Logid: ptr.Of("log-id"),
StartTime: 0,
EndTime: 0,
Limit: 50,
}

res, err := app.buildSearchTraceOApiReq(ctx, req)
assert.NoError(t, err)
if assert.NotNil(t, res) {
assert.Equal(t, start, res.StartTime)
assert.Equal(t, end, res.EndTime)
}

// Case: StartTime=0, EndTime=0 -> TimeRangeProvider returns nil -> should return error because DateValidator requires non-zero time
timeRangeMock.EXPECT().GetTimeRange(gomock.Any(), "2", "", "", gomock.Any()).Return(nil, nil)

req2 := &openapi.SearchTraceOApiRequest{
WorkspaceID: 2,
StartTime: 0,
EndTime: 0,
}
res2, err := app.buildSearchTraceOApiReq(ctx, req2)
assert.Error(t, err)
assert.Nil(t, res2)
}
2 changes: 2 additions & 0 deletions backend/modules/observability/application/wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import (
"github.com/coze-dev/coze-loop/backend/modules/observability/infra/rpc/user"
obstorage "github.com/coze-dev/coze-loop/backend/modules/observability/infra/storage"
"github.com/coze-dev/coze-loop/backend/modules/observability/infra/tenant"
"github.com/coze-dev/coze-loop/backend/modules/observability/infra/time_range"
"github.com/coze-dev/coze-loop/backend/modules/observability/infra/workspace"
"github.com/coze-dev/coze-loop/backend/pkg/conf"
"github.com/google/wire"
Expand Down Expand Up @@ -137,6 +138,7 @@ var (
NewOpenAPIApplication,
auth.NewAuthProvider,
traceDomainSet,
time_range.NewTimeRangeProvider,
)
taskSet = wire.NewSet(
tracehub.NewTraceHubImpl,
Expand Down
6 changes: 4 additions & 2 deletions backend/modules/observability/application/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) 2026 coze-dev Authors
// SPDX-License-Identifier: Apache-2.0
package time_range

import (
"context"
)

//go:generate mockgen -destination=mocks/time_range_mock.go -package=mocks . ITimeRangeProvider
type ITimeRangeProvider interface {
GetTimeRange(ctx context.Context, workSpaceID, logID, traceID string, delayTime int64) (*int64, *int64)
}
2 changes: 1 addition & 1 deletion backend/modules/observability/infra/rpc/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (a *AuthProviderImpl) CheckWorkspacePermission(ctx context.Context, action,
}
resp, err := a.cli.MCheckPermission(ctx, req)
if err != nil {
return errorx.WrapByCode(err, obErrorx.CommercialCommonRPCErrorCodeCode)
return err
} else if resp == nil {
logs.CtxWarn(ctx, "MCheckPermission returned nil response")
return errorx.NewByCode(obErrorx.CommercialCommonRPCErrorCodeCode)
Expand Down
2 changes: 1 addition & 1 deletion backend/modules/observability/infra/rpc/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func TestAuthProviderImpl_CheckWorkspacePermission(t *testing.T) {
mockClient.EXPECT().MCheckPermission(gomock.Any(), gomock.Any()).Return(nil, errors.New("RPC error"))
},
wantErr: true,
expectedErr: obErrorx.CommercialCommonRPCErrorCodeCode,
expectedErr: 0,
},
{
name: "nil response",
Expand Down
19 changes: 19 additions & 0 deletions backend/modules/observability/infra/time_range/time_range.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) 2026 coze-dev Authors
// SPDX-License-Identifier: Apache-2.0
package time_range

import (
"context"

"github.com/coze-dev/coze-loop/backend/modules/observability/domain/component/time_range"
)

type TimeRangeProvider struct{}

func NewTimeRangeProvider() time_range.ITimeRangeProvider {
return &TimeRangeProvider{}
}

func (p *TimeRangeProvider) GetTimeRange(ctx context.Context, workSpaceID, logID, traceID string, delayTime int64) (*int64, *int64) {
return nil, nil
}
27 changes: 27 additions & 0 deletions backend/modules/observability/infra/time_range/time_range_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) 2026 coze-dev Authors
// SPDX-License-Identifier: Apache-2.0
package time_range

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewTimeRangeProvider(t *testing.T) {
provider := NewTimeRangeProvider()
assert.NotNil(t, provider)
assert.IsType(t, &TimeRangeProvider{}, provider)
}

func TestTimeRangeProvider_GetTimeRange(t *testing.T) {
provider := NewTimeRangeProvider()
ctx := context.Background()

t.Run("returns nil for any input", func(t *testing.T) {
start, end := provider.GetTimeRange(ctx, "workspace1", "log1", "trace1", 0)
assert.Nil(t, start)
assert.Nil(t, end)
})
}
Loading