diff --git a/internal/auth/auth.go b/internal/auth/auth.go index 71f9514..c63fbb8 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -12,6 +12,7 @@ import ( const ( tokenIdArg = "tokenId" + subjectArg = "subject" ) type UnauthorizedError struct { @@ -42,19 +43,45 @@ func newError(msg string, args ...any) error { func NewVehicleTokenCheck(requiredAddr common.Address) func(context.Context, any, graphql.Resolver) (any, error) { return func(ctx context.Context, _ any, next graphql.Resolver) (any, error) { - vehicleTokenID, err := getArg[int](ctx, tokenIdArg) - if err != nil { - return nil, UnauthorizedError{err: err} + tokenID, err := getArg[*int](ctx, tokenIdArg) + if err != nil && !errors.Is(err, errArgNotFound) { + return nil, UnauthorizedError{err: fmt.Errorf("failed to get %s arg: %w", tokenIdArg, err)} + } + subject, err := getArg[*string](ctx, subjectArg) + if err != nil && !errors.Is(err, errArgNotFound) { + return nil, UnauthorizedError{err: fmt.Errorf("failed to get %s arg: %w", subjectArg, err)} } - if err := validateHeader(ctx, requiredAddr, vehicleTokenID); err != nil { - return nil, UnauthorizedError{err: err} + switch { + case tokenID != nil && subject != nil: + return nil, UnauthorizedError{message: "provide either tokenId or subject, not both"} + case tokenID != nil: + if err := validateHeader(ctx, requiredAddr, *tokenID); err != nil { + return nil, UnauthorizedError{err: err} + } + case subject != nil: + if err := validateSubject(ctx, *subject); err != nil { + return nil, UnauthorizedError{err: err} + } + default: + return nil, UnauthorizedError{message: "tokenId or subject is required"} } return next(ctx) } } +func validateSubject(ctx context.Context, subject string) error { + claim, err := getTelemetryClaim(ctx) + if err != nil { + return err + } + if subject != claim.Asset { + return newError("subject does not match token claim") + } + return nil +} + func validateHeader(ctx context.Context, requiredAddr common.Address, tokenID int) error { claim, err := getTelemetryClaim(ctx) if err != nil { @@ -104,6 +131,8 @@ func OneOfPrivilegeCheck(ctx context.Context, _ any, next graphql.Resolver, requ return nil, newError("requires at least one of the following privileges %v", requiredPrivs) } +var errArgNotFound = errors.New("arg not found") + func getArg[T any](ctx context.Context, name string) (T, error) { var resp T fCtx := graphql.GetFieldContext(ctx) @@ -113,7 +142,7 @@ func getArg[T any](ctx context.Context, name string) (T, error) { val, ok := fCtx.Args[name] if !ok { - return resp, fmt.Errorf("no argument named %s", name) + return resp, errArgNotFound } resp, ok = val.(T) diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index 272b1cc..3021e87 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -25,6 +25,11 @@ func TestRequiresVehicleTokenCheck(t *testing.T) { vehicleNFTAddr := common.HexToAddress("0x1") + tokenID123 := 123 + tokenID456 := 456 + validSubject := "did:erc721:1:0x0000000000000000000000000000000000000001:123" + wrongSubject := "did:erc721:1:0x0000000000000000000000000000000000000001:456" + testCases := []struct { name string args map[string]any @@ -34,7 +39,7 @@ func TestRequiresVehicleTokenCheck(t *testing.T) { { name: "valid_token", args: map[string]any{ - "tokenId": 123, + "tokenId": &tokenID123, }, telemetryClaim: &TelemetryClaim{ AssetDID: cloudevent.ERC721DID{ @@ -47,7 +52,7 @@ func TestRequiresVehicleTokenCheck(t *testing.T) { { name: "invalid_token", args: map[string]any{ - "tokenId": 456, + "tokenId": &tokenID456, }, telemetryClaim: &TelemetryClaim{ AssetDID: cloudevent.ERC721DID{ @@ -59,7 +64,7 @@ func TestRequiresVehicleTokenCheck(t *testing.T) { expectedError: true, }, { - name: "missing_tokenId", + name: "missing_both", args: map[string]any{}, expectedError: true, telemetryClaim: &TelemetryClaim{ @@ -71,8 +76,10 @@ func TestRequiresVehicleTokenCheck(t *testing.T) { }, }, { - name: "wrong_contract", - args: map[string]any{}, + name: "wrong_contract", + args: map[string]any{ + "tokenId": &tokenID123, + }, expectedError: true, telemetryClaim: &TelemetryClaim{ AssetDID: cloudevent.ERC721DID{ @@ -85,7 +92,66 @@ func TestRequiresVehicleTokenCheck(t *testing.T) { { name: "missing claim", args: map[string]any{ - "tokenId": 123, + "tokenId": &tokenID123, + }, + expectedError: true, + telemetryClaim: nil, + }, + { + name: "valid_subject", + args: map[string]any{ + "subject": &validSubject, + }, + telemetryClaim: &TelemetryClaim{ + CustomClaims: tokenclaims.CustomClaims{ + Asset: validSubject, + }, + AssetDID: cloudevent.ERC721DID{ + ChainID: 1, + ContractAddress: vehicleNFTAddr, + TokenID: big.NewInt(123), + }, + }, + }, + { + name: "wrong_subject", + args: map[string]any{ + "subject": &wrongSubject, + }, + telemetryClaim: &TelemetryClaim{ + CustomClaims: tokenclaims.CustomClaims{ + Asset: validSubject, + }, + AssetDID: cloudevent.ERC721DID{ + ChainID: 1, + ContractAddress: vehicleNFTAddr, + TokenID: big.NewInt(123), + }, + }, + expectedError: true, + }, + { + name: "both_tokenId_and_subject", + args: map[string]any{ + "tokenId": &tokenID123, + "subject": &validSubject, + }, + telemetryClaim: &TelemetryClaim{ + CustomClaims: tokenclaims.CustomClaims{ + Asset: validSubject, + }, + AssetDID: cloudevent.ERC721DID{ + ChainID: 1, + ContractAddress: vehicleNFTAddr, + TokenID: big.NewInt(123), + }, + }, + expectedError: true, + }, + { + name: "subject_missing_claim", + args: map[string]any{ + "subject": &validSubject, }, expectedError: true, telemetryClaim: nil, diff --git a/internal/graph/arguments.go b/internal/graph/arguments.go index d2569de..656352b 100644 --- a/internal/graph/arguments.go +++ b/internal/graph/arguments.go @@ -2,24 +2,40 @@ package graph import ( "context" + "errors" "fmt" "time" "github.com/99designs/gqlgen/graphql" "github.com/DIMO-Network/model-garage/pkg/vss" + "github.com/DIMO-Network/server-garage/pkg/gql/errorhandler" "github.com/DIMO-Network/telemetry-api/internal/graph/model" ) +// resolveSubject resolves the subject from the provided tokenID or subject arguments. +// Exactly one of tokenID or subject must be provided. +func (r *queryResolver) resolveSubject(ctx context.Context, tokenID *int, subject *string) (string, error) { + if subject != nil && tokenID != nil { + return "", errorhandler.NewBadRequestError(ctx, errors.New("provide either tokenId or subject, not both")) + } + if subject != nil { + return *subject, nil + } + if tokenID != nil { + return r.BaseRepo.ToSubject(uint32(*tokenID)), nil + } + return "", errorhandler.NewBadRequestError(ctx, errors.New("tokenId or subject is required")) +} + // aggregationArgsFromContext creates an aggregated signals arguments from the context and the provided arguments. -func aggregationArgsFromContext(ctx context.Context, tokenID int, interval string, from time.Time, to time.Time, filter *model.SignalFilter) (*model.AggregatedSignalArgs, error) { - // 1h 1s +func aggregationArgsFromContext(ctx context.Context, subject string, interval string, from time.Time, to time.Time, filter *model.SignalFilter) (*model.AggregatedSignalArgs, error) { intervalInt, err := getIntervalMicroseconds(interval) if err != nil { return nil, err } aggArgs := model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: uint32(tokenID), + Subject: subject, Filter: filter, }, FromTS: from, @@ -86,11 +102,11 @@ func addSignalAggregation(aggArgs *model.AggregatedSignalArgs, child *graphql.Fi } // latestArgsFromContext creates a latest signals arguments from the context and the provided arguments. -func latestArgsFromContext(ctx context.Context, tokenID int, filter *model.SignalFilter) (*model.LatestSignalsArgs, error) { +func latestArgsFromContext(ctx context.Context, subject string, filter *model.SignalFilter) (*model.LatestSignalsArgs, error) { fields := graphql.CollectFieldsCtx(ctx, nil) latestArgs := model.LatestSignalsArgs{ SignalArgs: model.SignalArgs{ - TokenID: uint32(tokenID), + Subject: subject, Filter: filter, }, SignalNames: make(map[string]struct{}), diff --git a/internal/graph/base.resolvers.go b/internal/graph/base.resolvers.go index 2bf62cf..8707385 100644 --- a/internal/graph/base.resolvers.go +++ b/internal/graph/base.resolvers.go @@ -14,8 +14,12 @@ import ( ) // Signals is the resolver for the Signals field. -func (r *queryResolver) Signals(ctx context.Context, tokenID int, interval string, from time.Time, to time.Time, filter *model.SignalFilter) ([]*model.SignalAggregations, error) { - aggArgs, err := aggregationArgsFromContext(ctx, tokenID, interval, from, to, filter) +func (r *queryResolver) Signals(ctx context.Context, tokenID *int, subject *string, interval string, from time.Time, to time.Time, filter *model.SignalFilter) ([]*model.SignalAggregations, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + aggArgs, err := aggregationArgsFromContext(ctx, sub, interval, from, to, filter) if err != nil { return nil, err } @@ -23,8 +27,12 @@ func (r *queryResolver) Signals(ctx context.Context, tokenID int, interval strin } // SignalsLatest is the resolver for the SignalsLatest field. -func (r *queryResolver) SignalsLatest(ctx context.Context, tokenID int, filter *model.SignalFilter) (*model.SignalCollection, error) { - latestArgs, err := latestArgsFromContext(ctx, tokenID, filter) +func (r *queryResolver) SignalsLatest(ctx context.Context, tokenID *int, subject *string, filter *model.SignalFilter) (*model.SignalCollection, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + latestArgs, err := latestArgsFromContext(ctx, sub, filter) if err != nil { return nil, err } @@ -32,13 +40,21 @@ func (r *queryResolver) SignalsLatest(ctx context.Context, tokenID int, filter * } // AvailableSignals is the resolver for the AvailableSignals field. -func (r *queryResolver) AvailableSignals(ctx context.Context, tokenID int, filter *model.SignalFilter) ([]string, error) { - return r.BaseRepo.GetAvailableSignals(ctx, uint32(tokenID), filter) +func (r *queryResolver) AvailableSignals(ctx context.Context, tokenID *int, subject *string, filter *model.SignalFilter) ([]string, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + return r.BaseRepo.GetAvailableSignals(ctx, sub, filter) } // DataSummary is the resolver for the dataSummary field. -func (r *queryResolver) DataSummary(ctx context.Context, tokenID int, filter *model.SignalFilter) (*model.DataSummary, error) { - return r.BaseRepo.GetDataSummary(ctx, uint32(tokenID), filter) +func (r *queryResolver) DataSummary(ctx context.Context, tokenID *int, subject *string, filter *model.SignalFilter) (*model.DataSummary, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + return r.BaseRepo.GetDataSummary(ctx, sub, filter) } // CurrentLocationApproximateCoordinates is the resolver for the currentLocationApproximateCoordinates field on SignalAggregations. diff --git a/internal/graph/events.resolvers.go b/internal/graph/events.resolvers.go index db194a4..8fb3fb8 100644 --- a/internal/graph/events.resolvers.go +++ b/internal/graph/events.resolvers.go @@ -12,6 +12,10 @@ import ( ) // Events is the resolver for the events field. -func (r *queryResolver) Events(ctx context.Context, tokenID int, from time.Time, to time.Time, filter *model.EventFilter) ([]*model.Event, error) { - return r.BaseRepo.GetEvents(ctx, tokenID, from, to, filter) +func (r *queryResolver) Events(ctx context.Context, tokenID *int, subject *string, from time.Time, to time.Time, filter *model.EventFilter) ([]*model.Event, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + return r.BaseRepo.GetEvents(ctx, sub, from, to, filter) } diff --git a/internal/graph/generated.go b/internal/graph/generated.go index ec2e049..29b3069 100644 --- a/internal/graph/generated.go +++ b/internal/graph/generated.go @@ -106,14 +106,14 @@ type ComplexityRoot struct { Query struct { Attestations func(childComplexity int, tokenID *int, subject *string, filter *model.AttestationFilter) int - AvailableSignals func(childComplexity int, tokenID int, filter *model.SignalFilter) int - DailyActivity func(childComplexity int, tokenID int, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) int - DataSummary func(childComplexity int, tokenID int, filter *model.SignalFilter) int - Events func(childComplexity int, tokenID int, from time.Time, to time.Time, filter *model.EventFilter) int - Segments func(childComplexity int, tokenID int, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) int - Signals func(childComplexity int, tokenID int, interval string, from time.Time, to time.Time, filter *model.SignalFilter) int - SignalsLatest func(childComplexity int, tokenID int, filter *model.SignalFilter) int - VinVCLatest func(childComplexity int, tokenID int) int + AvailableSignals func(childComplexity int, tokenID *int, subject *string, filter *model.SignalFilter) int + DailyActivity func(childComplexity int, tokenID *int, subject *string, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) int + DataSummary func(childComplexity int, tokenID *int, subject *string, filter *model.SignalFilter) int + Events func(childComplexity int, tokenID *int, subject *string, from time.Time, to time.Time, filter *model.EventFilter) int + Segments func(childComplexity int, tokenID *int, subject *string, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) int + Signals func(childComplexity int, tokenID *int, subject *string, interval string, from time.Time, to time.Time, filter *model.SignalFilter) int + SignalsLatest func(childComplexity int, tokenID *int, subject *string, filter *model.SignalFilter) int + VinVCLatest func(childComplexity int, tokenID *int, subject *string) int } Segment struct { @@ -403,15 +403,15 @@ type ComplexityRoot struct { } type QueryResolver interface { - Signals(ctx context.Context, tokenID int, interval string, from time.Time, to time.Time, filter *model.SignalFilter) ([]*model.SignalAggregations, error) - SignalsLatest(ctx context.Context, tokenID int, filter *model.SignalFilter) (*model.SignalCollection, error) - AvailableSignals(ctx context.Context, tokenID int, filter *model.SignalFilter) ([]string, error) - DataSummary(ctx context.Context, tokenID int, filter *model.SignalFilter) (*model.DataSummary, error) + Signals(ctx context.Context, tokenID *int, subject *string, interval string, from time.Time, to time.Time, filter *model.SignalFilter) ([]*model.SignalAggregations, error) + SignalsLatest(ctx context.Context, tokenID *int, subject *string, filter *model.SignalFilter) (*model.SignalCollection, error) + AvailableSignals(ctx context.Context, tokenID *int, subject *string, filter *model.SignalFilter) ([]string, error) + DataSummary(ctx context.Context, tokenID *int, subject *string, filter *model.SignalFilter) (*model.DataSummary, error) Attestations(ctx context.Context, tokenID *int, subject *string, filter *model.AttestationFilter) ([]*model.Attestation, error) - Events(ctx context.Context, tokenID int, from time.Time, to time.Time, filter *model.EventFilter) ([]*model.Event, error) - Segments(ctx context.Context, tokenID int, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) ([]*model.Segment, error) - DailyActivity(ctx context.Context, tokenID int, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) ([]*model.DailyActivity, error) - VinVCLatest(ctx context.Context, tokenID int) (*model.Vinvc, error) + Events(ctx context.Context, tokenID *int, subject *string, from time.Time, to time.Time, filter *model.EventFilter) ([]*model.Event, error) + Segments(ctx context.Context, tokenID *int, subject *string, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) ([]*model.Segment, error) + DailyActivity(ctx context.Context, tokenID *int, subject *string, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) ([]*model.DailyActivity, error) + VinVCLatest(ctx context.Context, tokenID *int, subject *string) (*model.Vinvc, error) } type SignalAggregationsResolver interface { CurrentLocationApproximateCoordinates(ctx context.Context, obj *model.SignalAggregations, agg model.LocationAggregation) (*model.Location, error) @@ -764,7 +764,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.AvailableSignals(childComplexity, args["tokenId"].(int), args["filter"].(*model.SignalFilter)), true + return e.complexity.Query.AvailableSignals(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["filter"].(*model.SignalFilter)), true case "Query.dailyActivity": if e.complexity.Query.DailyActivity == nil { break @@ -775,7 +775,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.DailyActivity(childComplexity, args["tokenId"].(int), args["from"].(time.Time), args["to"].(time.Time), args["mechanism"].(model.DetectionMechanism), args["config"].(*model.SegmentConfig), args["signalRequests"].([]*model.SegmentSignalRequest), args["eventRequests"].([]*model.SegmentEventRequest), args["timezone"].(*string)), true + return e.complexity.Query.DailyActivity(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["from"].(time.Time), args["to"].(time.Time), args["mechanism"].(model.DetectionMechanism), args["config"].(*model.SegmentConfig), args["signalRequests"].([]*model.SegmentSignalRequest), args["eventRequests"].([]*model.SegmentEventRequest), args["timezone"].(*string)), true case "Query.dataSummary": if e.complexity.Query.DataSummary == nil { break @@ -786,7 +786,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.DataSummary(childComplexity, args["tokenId"].(int), args["filter"].(*model.SignalFilter)), true + return e.complexity.Query.DataSummary(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["filter"].(*model.SignalFilter)), true case "Query.events": if e.complexity.Query.Events == nil { break @@ -797,7 +797,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.Events(childComplexity, args["tokenId"].(int), args["from"].(time.Time), args["to"].(time.Time), args["filter"].(*model.EventFilter)), true + return e.complexity.Query.Events(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["from"].(time.Time), args["to"].(time.Time), args["filter"].(*model.EventFilter)), true case "Query.segments": if e.complexity.Query.Segments == nil { break @@ -808,7 +808,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.Segments(childComplexity, args["tokenId"].(int), args["from"].(time.Time), args["to"].(time.Time), args["mechanism"].(model.DetectionMechanism), args["config"].(*model.SegmentConfig), args["signalRequests"].([]*model.SegmentSignalRequest), args["eventRequests"].([]*model.SegmentEventRequest), args["limit"].(*int), args["after"].(*time.Time)), true + return e.complexity.Query.Segments(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["from"].(time.Time), args["to"].(time.Time), args["mechanism"].(model.DetectionMechanism), args["config"].(*model.SegmentConfig), args["signalRequests"].([]*model.SegmentSignalRequest), args["eventRequests"].([]*model.SegmentEventRequest), args["limit"].(*int), args["after"].(*time.Time)), true case "Query.signals": if e.complexity.Query.Signals == nil { break @@ -819,7 +819,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.Signals(childComplexity, args["tokenId"].(int), args["interval"].(string), args["from"].(time.Time), args["to"].(time.Time), args["filter"].(*model.SignalFilter)), true + return e.complexity.Query.Signals(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["interval"].(string), args["from"].(time.Time), args["to"].(time.Time), args["filter"].(*model.SignalFilter)), true case "Query.signalsLatest": if e.complexity.Query.SignalsLatest == nil { break @@ -830,7 +830,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.SignalsLatest(childComplexity, args["tokenId"].(int), args["filter"].(*model.SignalFilter)), true + return e.complexity.Query.SignalsLatest(childComplexity, args["tokenId"].(*int), args["subject"].(*string), args["filter"].(*model.SignalFilter)), true case "Query.vinVCLatest": if e.complexity.Query.VinVCLatest == nil { break @@ -841,7 +841,7 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return 0, false } - return e.complexity.Query.VinVCLatest(childComplexity, args["tokenId"].(int)), true + return e.complexity.Query.VinVCLatest(childComplexity, args["tokenId"].(*int), args["subject"].(*string)), true case "Segment.duration": if e.complexity.Segment.Duration == nil { @@ -3209,10 +3209,18 @@ The root query type for the GraphQL schema. """ type Query { """ - signals returns a collection of signals for a given token in a given time range. + Compute aggregations of signals for a given subject. """ signals( - tokenId: Int! + """ + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String """ interval is a time span that used for aggregatting the data with. A duration string is a sequence of decimal numbers, each with optional fraction and a unit suffix, @@ -3224,20 +3232,53 @@ type Query { filter: SignalFilter ): [SignalAggregations!] @requiresVehicleToken """ - SignalsLatest returns the latest signals for a given token. + Query the latest values of signals for a given subject. """ - signalsLatest(tokenId: Int!, filter: SignalFilter): SignalCollection + signalsLatest( + """ + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String + filter: SignalFilter + ): SignalCollection @requiresVehicleToken """ - availableSignals returns a list of queryable signal names that have stored data for a given tokenId. + List queryable signal names that have stored data for a given subject. """ - availableSignals(tokenId: Int!, filter: SignalFilter): [String!] + availableSignals( + """ + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String + filter: SignalFilter + ): [String!] @requiresVehicleToken """ - data summary of all signals for a given tokenId + Data summary of all signals for a given subject. """ - dataSummary(tokenId: Int!, filter: SignalFilter): DataSummary + dataSummary( + """ + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String + filter: SignalFilter + ): DataSummary @requiresVehicleToken } type SignalAggregations { @@ -3563,13 +3604,18 @@ input StringArrayFilter { `, BuiltIn: false}, {Name: "../../schema/events.graphqls", Input: `extend type Query { """ - events returns a list of events for a given token in a given time range. + Returns a list of events for a given subject in a given time range. """ events( """ - tokenId is the id of the token to get events for. + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. """ - tokenId: Int! + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested events. Typically a W3C DID. + """ + subject: String """ from is the start time of the event. """ @@ -3682,7 +3728,15 @@ extend type Query { When signalRequests is provided, those requests are added on top of the default set; duplicates (same name and agg) are omitted. """ segments( - tokenId: Int! + """ + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested data. Typically a W3C DID. + """ + subject: String from: Time! to: Time! mechanism: DetectionMechanism! @@ -3706,7 +3760,15 @@ extend type Query { Maximum date range: 31 days. """ dailyActivity( - tokenId: Int! + """ + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested data. Typically a W3C DID. + """ + subject: String from: Time! to: Time! mechanism: DetectionMechanism! @@ -5596,15 +5658,20 @@ extend input EventFilter { `, BuiltIn: false}, {Name: "../../schema/vc.graphqls", Input: `extend type Query { """ - vinVCLatest returns the latest VINVC data for a given token. + vinVCLatest returns the latest VIN VC data for a given subject. Required Privileges: [VEHICLE_VIN_CREDENTIAL] """ vinVCLatest( """ - The token ID of the vehicle. + A vehicle token id. This is translated into a ERC-721 DID ` + "`" + `subject` + "`" + ` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use ` + "`" + `subject` + "`" + ` instead.") + """ + The subject of the requested data. Typically a W3C DID. """ - tokenId: Int! + subject: String ): VINVC @requiresVehicleToken @requiresAllOfPrivileges(privileges: [VEHICLE_VIN_CREDENTIAL]) @@ -5723,213 +5790,253 @@ func (ec *executionContext) field_Query_attestations_args(ctx context.Context, r func (ec *executionContext) field_Query_availableSignals_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } - args["filter"] = arg1 + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + if err != nil { + return nil, err + } + args["filter"] = arg2 return args, nil } func (ec *executionContext) field_Query_dailyActivity_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } - args["from"] = arg1 - arg2, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) + if err != nil { + return nil, err + } + args["from"] = arg2 + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["to"] = arg2 - arg3, err := graphql.ProcessArgField(ctx, rawArgs, "mechanism", ec.unmarshalNDetectionMechanism2githubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐDetectionMechanism) + args["to"] = arg3 + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "mechanism", ec.unmarshalNDetectionMechanism2githubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐDetectionMechanism) if err != nil { return nil, err } - args["mechanism"] = arg3 - arg4, err := graphql.ProcessArgField(ctx, rawArgs, "config", ec.unmarshalOSegmentConfig2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentConfig) + args["mechanism"] = arg4 + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "config", ec.unmarshalOSegmentConfig2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentConfig) if err != nil { return nil, err } - args["config"] = arg4 - arg5, err := graphql.ProcessArgField(ctx, rawArgs, "signalRequests", ec.unmarshalOSegmentSignalRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentSignalRequestᚄ) + args["config"] = arg5 + arg6, err := graphql.ProcessArgField(ctx, rawArgs, "signalRequests", ec.unmarshalOSegmentSignalRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentSignalRequestᚄ) if err != nil { return nil, err } - args["signalRequests"] = arg5 - arg6, err := graphql.ProcessArgField(ctx, rawArgs, "eventRequests", ec.unmarshalOSegmentEventRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentEventRequestᚄ) + args["signalRequests"] = arg6 + arg7, err := graphql.ProcessArgField(ctx, rawArgs, "eventRequests", ec.unmarshalOSegmentEventRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentEventRequestᚄ) if err != nil { return nil, err } - args["eventRequests"] = arg6 - arg7, err := graphql.ProcessArgField(ctx, rawArgs, "timezone", ec.unmarshalOString2ᚖstring) + args["eventRequests"] = arg7 + arg8, err := graphql.ProcessArgField(ctx, rawArgs, "timezone", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } - args["timezone"] = arg7 + args["timezone"] = arg8 return args, nil } func (ec *executionContext) field_Query_dataSummary_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } - args["filter"] = arg1 + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + if err != nil { + return nil, err + } + args["filter"] = arg2 return args, nil } func (ec *executionContext) field_Query_events_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) + if err != nil { + return nil, err + } + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["from"] = arg1 - arg2, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) + args["from"] = arg2 + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["to"] = arg2 - arg3, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOEventFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐEventFilter) + args["to"] = arg3 + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOEventFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐEventFilter) if err != nil { return nil, err } - args["filter"] = arg3 + args["filter"] = arg4 return args, nil } func (ec *executionContext) field_Query_segments_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) + if err != nil { + return nil, err + } + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["from"] = arg1 - arg2, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) + args["from"] = arg2 + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["to"] = arg2 - arg3, err := graphql.ProcessArgField(ctx, rawArgs, "mechanism", ec.unmarshalNDetectionMechanism2githubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐDetectionMechanism) + args["to"] = arg3 + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "mechanism", ec.unmarshalNDetectionMechanism2githubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐDetectionMechanism) if err != nil { return nil, err } - args["mechanism"] = arg3 - arg4, err := graphql.ProcessArgField(ctx, rawArgs, "config", ec.unmarshalOSegmentConfig2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentConfig) + args["mechanism"] = arg4 + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "config", ec.unmarshalOSegmentConfig2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentConfig) if err != nil { return nil, err } - args["config"] = arg4 - arg5, err := graphql.ProcessArgField(ctx, rawArgs, "signalRequests", ec.unmarshalOSegmentSignalRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentSignalRequestᚄ) + args["config"] = arg5 + arg6, err := graphql.ProcessArgField(ctx, rawArgs, "signalRequests", ec.unmarshalOSegmentSignalRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentSignalRequestᚄ) if err != nil { return nil, err } - args["signalRequests"] = arg5 - arg6, err := graphql.ProcessArgField(ctx, rawArgs, "eventRequests", ec.unmarshalOSegmentEventRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentEventRequestᚄ) + args["signalRequests"] = arg6 + arg7, err := graphql.ProcessArgField(ctx, rawArgs, "eventRequests", ec.unmarshalOSegmentEventRequest2ᚕᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSegmentEventRequestᚄ) if err != nil { return nil, err } - args["eventRequests"] = arg6 - arg7, err := graphql.ProcessArgField(ctx, rawArgs, "limit", ec.unmarshalOInt2ᚖint) + args["eventRequests"] = arg7 + arg8, err := graphql.ProcessArgField(ctx, rawArgs, "limit", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } - args["limit"] = arg7 - arg8, err := graphql.ProcessArgField(ctx, rawArgs, "after", ec.unmarshalOTime2ᚖtimeᚐTime) + args["limit"] = arg8 + arg9, err := graphql.ProcessArgField(ctx, rawArgs, "after", ec.unmarshalOTime2ᚖtimeᚐTime) if err != nil { return nil, err } - args["after"] = arg8 + args["after"] = arg9 return args, nil } func (ec *executionContext) field_Query_signalsLatest_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } - args["filter"] = arg1 + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + if err != nil { + return nil, err + } + args["filter"] = arg2 return args, nil } func (ec *executionContext) field_Query_signals_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 - arg1, err := graphql.ProcessArgField(ctx, rawArgs, "interval", ec.unmarshalNString2string) + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) if err != nil { return nil, err } - args["interval"] = arg1 - arg2, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) + args["subject"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "interval", ec.unmarshalNString2string) if err != nil { return nil, err } - args["from"] = arg2 - arg3, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) + args["interval"] = arg2 + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "from", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["to"] = arg3 - arg4, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + args["from"] = arg3 + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "to", ec.unmarshalNTime2timeᚐTime) if err != nil { return nil, err } - args["filter"] = arg4 + args["to"] = arg4 + arg5, err := graphql.ProcessArgField(ctx, rawArgs, "filter", ec.unmarshalOSignalFilter2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋtelemetryᚑapiᚋinternalᚋgraphᚋmodelᚐSignalFilter) + if err != nil { + return nil, err + } + args["filter"] = arg5 return args, nil } func (ec *executionContext) field_Query_vinVCLatest_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} - arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalNInt2int) + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "tokenId", ec.unmarshalOInt2ᚖint) if err != nil { return nil, err } args["tokenId"] = arg0 + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "subject", ec.unmarshalOString2ᚖstring) + if err != nil { + return nil, err + } + args["subject"] = arg1 return args, nil } @@ -8692,7 +8799,7 @@ func (ec *executionContext) _Query_signals(ctx context.Context, field graphql.Co ec.fieldContext_Query_signals, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().Signals(ctx, fc.Args["tokenId"].(int), fc.Args["interval"].(string), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["filter"].(*model.SignalFilter)) + return ec.resolvers.Query().Signals(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["interval"].(string), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["filter"].(*model.SignalFilter)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -8970,7 +9077,7 @@ func (ec *executionContext) _Query_signalsLatest(ctx context.Context, field grap ec.fieldContext_Query_signalsLatest, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().SignalsLatest(ctx, fc.Args["tokenId"].(int), fc.Args["filter"].(*model.SignalFilter)) + return ec.resolvers.Query().SignalsLatest(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["filter"].(*model.SignalFilter)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -9248,7 +9355,7 @@ func (ec *executionContext) _Query_availableSignals(ctx context.Context, field g ec.fieldContext_Query_availableSignals, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().AvailableSignals(ctx, fc.Args["tokenId"].(int), fc.Args["filter"].(*model.SignalFilter)) + return ec.resolvers.Query().AvailableSignals(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["filter"].(*model.SignalFilter)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -9302,7 +9409,7 @@ func (ec *executionContext) _Query_dataSummary(ctx context.Context, field graphq ec.fieldContext_Query_dataSummary, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().DataSummary(ctx, fc.Args["tokenId"].(int), fc.Args["filter"].(*model.SignalFilter)) + return ec.resolvers.Query().DataSummary(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["filter"].(*model.SignalFilter)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -9433,7 +9540,7 @@ func (ec *executionContext) _Query_events(ctx context.Context, field graphql.Col ec.fieldContext_Query_events, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().Events(ctx, fc.Args["tokenId"].(int), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["filter"].(*model.EventFilter)) + return ec.resolvers.Query().Events(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["filter"].(*model.EventFilter)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -9511,7 +9618,7 @@ func (ec *executionContext) _Query_segments(ctx context.Context, field graphql.C ec.fieldContext_Query_segments, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().Segments(ctx, fc.Args["tokenId"].(int), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["mechanism"].(model.DetectionMechanism), fc.Args["config"].(*model.SegmentConfig), fc.Args["signalRequests"].([]*model.SegmentSignalRequest), fc.Args["eventRequests"].([]*model.SegmentEventRequest), fc.Args["limit"].(*int), fc.Args["after"].(*time.Time)) + return ec.resolvers.Query().Segments(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["mechanism"].(model.DetectionMechanism), fc.Args["config"].(*model.SegmentConfig), fc.Args["signalRequests"].([]*model.SegmentSignalRequest), fc.Args["eventRequests"].([]*model.SegmentEventRequest), fc.Args["limit"].(*int), fc.Args["after"].(*time.Time)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -9593,7 +9700,7 @@ func (ec *executionContext) _Query_dailyActivity(ctx context.Context, field grap ec.fieldContext_Query_dailyActivity, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().DailyActivity(ctx, fc.Args["tokenId"].(int), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["mechanism"].(model.DetectionMechanism), fc.Args["config"].(*model.SegmentConfig), fc.Args["signalRequests"].([]*model.SegmentSignalRequest), fc.Args["eventRequests"].([]*model.SegmentEventRequest), fc.Args["timezone"].(*string)) + return ec.resolvers.Query().DailyActivity(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string), fc.Args["from"].(time.Time), fc.Args["to"].(time.Time), fc.Args["mechanism"].(model.DetectionMechanism), fc.Args["config"].(*model.SegmentConfig), fc.Args["signalRequests"].([]*model.SegmentSignalRequest), fc.Args["eventRequests"].([]*model.SegmentEventRequest), fc.Args["timezone"].(*string)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next @@ -9673,7 +9780,7 @@ func (ec *executionContext) _Query_vinVCLatest(ctx context.Context, field graphq ec.fieldContext_Query_vinVCLatest, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.resolvers.Query().VinVCLatest(ctx, fc.Args["tokenId"].(int)) + return ec.resolvers.Query().VinVCLatest(ctx, fc.Args["tokenId"].(*int), fc.Args["subject"].(*string)) }, func(ctx context.Context, next graphql.Resolver) graphql.Resolver { directive0 := next diff --git a/internal/graph/model/signalArgs.go b/internal/graph/model/signalArgs.go index d3f383b..6675877 100644 --- a/internal/graph/model/signalArgs.go +++ b/internal/graph/model/signalArgs.go @@ -17,8 +17,8 @@ const ( type SignalArgs struct { // Filter is an optional filter for the signals. Filter *SignalFilter - // TokenID is the vehicle's NFT token ID. - TokenID uint32 + // Subject is the DID subject string for the vehicle. + Subject string } // LatestSignalsArgs is the arguments for querying the latest signals. diff --git a/internal/graph/segments.resolvers.go b/internal/graph/segments.resolvers.go index 42b327f..b39b163 100644 --- a/internal/graph/segments.resolvers.go +++ b/internal/graph/segments.resolvers.go @@ -12,11 +12,19 @@ import ( ) // Segments is the resolver for the segments field. -func (r *queryResolver) Segments(ctx context.Context, tokenID int, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) ([]*model.Segment, error) { - return r.BaseRepo.GetSegments(ctx, tokenID, from, to, mechanism, config, signalRequests, eventRequests, limit, after) +func (r *queryResolver) Segments(ctx context.Context, tokenID *int, subject *string, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) ([]*model.Segment, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + return r.BaseRepo.GetSegments(ctx, sub, from, to, mechanism, config, signalRequests, eventRequests, limit, after) } // DailyActivity is the resolver for the dailyActivity field. -func (r *queryResolver) DailyActivity(ctx context.Context, tokenID int, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) ([]*model.DailyActivity, error) { - return r.BaseRepo.GetDailyActivity(ctx, tokenID, from, to, mechanism, config, signalRequests, eventRequests, timezone) +func (r *queryResolver) DailyActivity(ctx context.Context, tokenID *int, subject *string, from time.Time, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) ([]*model.DailyActivity, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + return r.BaseRepo.GetDailyActivity(ctx, sub, from, to, mechanism, config, signalRequests, eventRequests, timezone) } diff --git a/internal/graph/vc.resolvers.go b/internal/graph/vc.resolvers.go index d0937dd..3dfd763 100644 --- a/internal/graph/vc.resolvers.go +++ b/internal/graph/vc.resolvers.go @@ -11,6 +11,10 @@ import ( ) // VinVCLatest is the resolver for the vinVCLatest field. -func (r *queryResolver) VinVCLatest(ctx context.Context, tokenID int) (*model.Vinvc, error) { - return r.VCRepo.GetLatestVINVC(ctx, uint32(tokenID)) +func (r *queryResolver) VinVCLatest(ctx context.Context, tokenID *int, subject *string) (*model.Vinvc, error) { + sub, err := r.resolveSubject(ctx, tokenID, subject) + if err != nil { + return nil, err + } + return r.VCRepo.GetLatestVINVC(ctx, sub) } diff --git a/internal/repositories/repositories.go b/internal/repositories/repositories.go index 6b19b09..9543098 100644 --- a/internal/repositories/repositories.go +++ b/internal/repositories/repositories.go @@ -67,8 +67,8 @@ func NewRepository(chService CHService, settings config.Settings) (*Repository, } -// toSubject converts a vehicle token id to a DID subject string. -func (r *Repository) toSubject(tokenID uint32) string { +// ToSubject converts a vehicle token id to a DID subject string. +func (r *Repository) ToSubject(tokenID uint32) string { return cloudevent.ERC721DID{ ChainID: r.chainID, ContractAddress: r.vehicleAddress, @@ -82,8 +82,7 @@ func (r *Repository) GetSignal(ctx context.Context, aggArgs *model.AggregatedSig return nil, errorhandler.NewBadRequestError(ctx, err) } - subject := r.toSubject(aggArgs.TokenID) - signals, err := r.chService.GetAggregatedSignals(ctx, subject, aggArgs) + signals, err := r.chService.GetAggregatedSignals(ctx, aggArgs.Subject, aggArgs) if err != nil { return nil, handleDBError(ctx, err) } @@ -135,8 +134,7 @@ func (r *Repository) GetSignalLatest(ctx context.Context, latestArgs *model.Late if err := validateLatestSigArgs(latestArgs); err != nil { return nil, errorhandler.NewBadRequestError(ctx, err) } - subject := r.toSubject(latestArgs.TokenID) - signals, err := r.chService.GetLatestSignals(ctx, subject, latestArgs) + signals, err := r.chService.GetLatestSignals(ctx, latestArgs.Subject, latestArgs) if err != nil { return nil, handleDBError(ctx, err) } @@ -155,8 +153,7 @@ func (r *Repository) GetSignalLatest(ctx context.Context, latestArgs *model.Late // GetAvailableSignals returns the available signals for the given tokenID and filter. // If no signals are found, a nil slice is returned. -func (r *Repository) GetAvailableSignals(ctx context.Context, tokenID uint32, filter *model.SignalFilter) ([]string, error) { - subject := r.toSubject(tokenID) +func (r *Repository) GetAvailableSignals(ctx context.Context, subject string, filter *model.SignalFilter) ([]string, error) { allSignals, err := r.chService.GetAvailableSignals(ctx, subject, filter) if err != nil { return nil, handleDBError(ctx, err) @@ -171,8 +168,7 @@ func (r *Repository) GetAvailableSignals(ctx context.Context, tokenID uint32, fi } // GetDataSummary returns the signal and event metadata for the given tokenID and filter. -func (r *Repository) GetDataSummary(ctx context.Context, tokenID uint32, filter *model.SignalFilter) (*model.DataSummary, error) { - subject := r.toSubject(tokenID) +func (r *Repository) GetDataSummary(ctx context.Context, subject string, filter *model.SignalFilter) (*model.DataSummary, error) { signalDataSummary, err := r.chService.GetSignalSummaries(ctx, subject, filter) if err != nil { return nil, handleDBError(ctx, err) @@ -222,16 +218,11 @@ func (r *Repository) GetDataSummary(ctx context.Context, tokenID uint32, filter }, nil } -// GetEvents returns the events for the given tokenID, from, to and filter. -func (r *Repository) GetEvents(ctx context.Context, tokenID int, from, to time.Time, filter *model.EventFilter) ([]*model.Event, error) { - if err := validateEventArgs(tokenID, from, to, filter); err != nil { +// GetEvents returns the events for the given subject, from, to and filter. +func (r *Repository) GetEvents(ctx context.Context, subject string, from, to time.Time, filter *model.EventFilter) ([]*model.Event, error) { + if err := validateEventArgs(from, to, filter); err != nil { return nil, errorhandler.NewBadRequestError(ctx, err) } - subject := cloudevent.ERC721DID{ - ChainID: r.chainID, - ContractAddress: r.vehicleAddress, - TokenID: big.NewInt(int64(tokenID)), - }.String() allEvents, err := r.chService.GetEvents(ctx, subject, from, to, filter) if err != nil { return nil, handleDBError(ctx, err) diff --git a/internal/repositories/repositories_test.go b/internal/repositories/repositories_test.go index 10108bb..8dc4d23 100644 --- a/internal/repositories/repositories_test.go +++ b/internal/repositories/repositories_test.go @@ -44,7 +44,7 @@ func TestGetSignal(t *testing.T) { }.String() defaultArgs := &model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject, }, FromTS: time.Now(), ToTS: time.Now().Add(time.Hour), @@ -217,7 +217,7 @@ func TestGetSignalLatest(t *testing.T) { }.String() defaultArgs := &model.LatestSignalsArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject, }, IncludeLastSeen: true, } @@ -426,7 +426,7 @@ func TestGetAvailableSignals(t *testing.T) { repo, err := repositories.NewRepository(mocks.CHService, baseSettings) require.NoError(t, err) - result, err := repo.GetAvailableSignals(context.Background(), 1, nil) + result, err := repo.GetAvailableSignals(context.Background(), testSubject, nil) if tt.expectError { require.Error(t, err) require.Nil(t, result) @@ -478,7 +478,7 @@ func TestGetEvents(t *testing.T) { mocks.CHService.EXPECT(). GetEvents(gomock.Any(), subject, from, to, filter). Return(vssEvents, nil) - result, err := repo.GetEvents(context.Background(), tokenID, from, to, filter) + result, err := repo.GetEvents(context.Background(), subject, from, to, filter) require.NoError(t, err) require.Len(t, result, 2) require.Equal(t, vssEvents[0].Name, result[0].Name) @@ -497,7 +497,7 @@ func TestGetEvents(t *testing.T) { mocks.CHService.EXPECT(). GetEvents(gomock.Any(), subject, from, to, filter). Return(nil, errors.New("service error")) - result, err := repo.GetEvents(context.Background(), tokenID, from, to, filter) + result, err := repo.GetEvents(context.Background(), subject, from, to, filter) require.Error(t, err) require.Nil(t, result) }) diff --git a/internal/repositories/segments.go b/internal/repositories/segments.go index 3c8c88f..6014430 100644 --- a/internal/repositories/segments.go +++ b/internal/repositories/segments.go @@ -3,11 +3,9 @@ package repositories import ( "context" "fmt" - "math/big" "sort" "time" - "github.com/DIMO-Network/cloudevent" "github.com/DIMO-Network/model-garage/pkg/vss" "github.com/DIMO-Network/server-garage/pkg/gql/errorhandler" "github.com/DIMO-Network/telemetry-api/internal/graph/model" @@ -32,11 +30,7 @@ func validateSegmentDateRange(from, to time.Time) error { } // validateSegmentArgs validates the arguments for segment queries. -func validateSegmentArgs(tokenID int, from, to time.Time) error { - if tokenID <= 0 { - return fmt.Errorf("invalid tokenID: %d", tokenID) - } - +func validateSegmentArgs(from, to time.Time) error { if from.After(to) { return fmt.Errorf("from time must be before to time") } @@ -199,11 +193,11 @@ func sortSegmentSignals(signals []*model.SignalAggregationValue) { // Pagination: pass after (exclusive cursor = startTime of last segment from previous page) and limit (default 100, max 200). // Segments are ordered by startTime ascending. When after is set, only segments with startTime > after are requested from CH. // If to is in the future (e.g. client sent end-of-day in user TZ), it is capped to now so the query succeeds. -func (r *Repository) GetSegments(ctx context.Context, tokenID int, from, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) ([]*model.Segment, error) { +func (r *Repository) GetSegments(ctx context.Context, subject string, from, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, limit *int, after *time.Time) ([]*model.Segment, error) { if now := time.Now(); to.After(now) { to = now } - if err := validateSegmentArgs(tokenID, from, to); err != nil { + if err := validateSegmentArgs(from, to); err != nil { return nil, errorhandler.NewBadRequestError(ctx, err) } if err := validateSegmentConfig(config, mechanism); err != nil { @@ -220,7 +214,6 @@ func (r *Repository) GetSegments(ctx context.Context, tokenID int, from, to time } } - subject := r.toSubject(uint32(tokenID)) chSegments, err := r.chService.GetSegments(ctx, subject, from, to, mechanism, config) if err != nil { return nil, handleDBError(ctx, err) @@ -330,7 +323,7 @@ func (r *Repository) GetSegments(ctx context.Context, tokenID int, from, to time preFetchedAggs = []*ch.AggSignal{} } } - summary, err := r.segmentSummary(ctx, uint32(tokenID), subject, seg, to, signalReqs, eventNames, eventCounts, preFetchedAggs) + summary, err := r.segmentSummary(ctx, subject, seg, to, signalReqs, eventNames, eventCounts, preFetchedAggs) if err != nil { return nil, err } @@ -445,7 +438,7 @@ func eventCountsToMap(counts []*ch.EventCount) map[string]int { return m } -func (r *Repository) segmentSummary(ctx context.Context, tokenID uint32, subject string, seg *model.Segment, queryTo time.Time, signalReqs []*model.SegmentSignalRequest, eventNames []string, preFetchedEventCounts []*ch.EventCount, preFetchedAggs []*ch.AggSignal) (*segmentSummaryResult, error) { +func (r *Repository) segmentSummary(ctx context.Context, subject string, seg *model.Segment, queryTo time.Time, signalReqs []*model.SegmentSignalRequest, eventNames []string, preFetchedEventCounts []*ch.EventCount, preFetchedAggs []*ch.AggSignal) (*segmentSummaryResult, error) { segFrom := seg.Start.Timestamp segTo := queryTo if seg.End != nil { @@ -462,7 +455,7 @@ func (r *Repository) segmentSummary(ctx context.Context, tokenID uint32, subject aggs = preFetchedAggs } else { aggArgs := &model.AggregatedSignalArgs{ - SignalArgs: model.SignalArgs{TokenID: tokenID}, + SignalArgs: model.SignalArgs{Subject: subject}, FromTS: segFrom, ToTS: segTo, Interval: intervalMicro, @@ -499,7 +492,7 @@ func (r *Repository) segmentSummary(ctx context.Context, tokenID uint32, subject // GetDailyActivity returns one record per calendar day in the requested date range, including days with zero segments. // mechanism must be ignitionDetection, frequencyAnalysis, or changePointDetection; idling, refuel, recharge return 400. -func (r *Repository) GetDailyActivity(ctx context.Context, tokenID int, from, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) ([]*model.DailyActivity, error) { +func (r *Repository) GetDailyActivity(ctx context.Context, subject string, from, to time.Time, mechanism model.DetectionMechanism, config *model.SegmentConfig, signalRequests []*model.SegmentSignalRequest, eventRequests []*model.SegmentEventRequest, timezone *string) ([]*model.DailyActivity, error) { if mechanism == model.DetectionMechanismIdling || mechanism == model.DetectionMechanismRefuel || mechanism == model.DetectionMechanismRecharge { return nil, errorhandler.NewBadRequestError(ctx, fmt.Errorf("dailyActivity does not accept mechanism %s; use ignitionDetection, frequencyAnalysis, or changePointDetection", mechanism)) } @@ -537,15 +530,10 @@ func (r *Repository) GetDailyActivity(ctx context.Context, tokenID int, from, to } } - segments, err := r.GetSegments(ctx, tokenID, rangeStart, rangeEnd, mechanism, config, signalReqs, eventRequests, nil, nil) + segments, err := r.GetSegments(ctx, subject, rangeStart, rangeEnd, mechanism, config, signalReqs, eventRequests, nil, nil) if err != nil { return nil, err } - subject := cloudevent.ERC721DID{ - ChainID: r.chainID, - ContractAddress: r.vehicleAddress, - TokenID: big.NewInt(int64(tokenID)), - }.String() var out []*model.DailyActivity for d := fromDate; !d.After(toDate); d = d.Add(24 * time.Hour) { @@ -581,7 +569,7 @@ func (r *Repository) GetDailyActivity(ctx context.Context, tokenID int, from, to lastSeg = seg } - signalSummary, startLoc, endLoc, eventSummary, err := r.daySummary(ctx, uint32(tokenID), subject, dayStartUTC, dayEndUTC, signalReqs, eventNames) + signalSummary, startLoc, endLoc, eventSummary, err := r.daySummary(ctx, subject, dayStartUTC, dayEndUTC, signalReqs, eventNames) if err != nil { return nil, err } @@ -615,14 +603,14 @@ func (r *Repository) GetDailyActivity(ctx context.Context, tokenID int, from, to return out, nil } -func (r *Repository) daySummary(ctx context.Context, tokenID uint32, subject string, dayStart, dayEnd time.Time, signalReqs []*model.SegmentSignalRequest, eventNames []string) ([]*model.SignalAggregationValue, *model.Location, *model.Location, []*model.EventCount, error) { +func (r *Repository) daySummary(ctx context.Context, subject string, dayStart, dayEnd time.Time, signalReqs []*model.SegmentSignalRequest, eventNames []string) ([]*model.SignalAggregationValue, *model.Location, *model.Location, []*model.EventCount, error) { intervalMicro := dayEnd.Sub(dayStart).Microseconds() if intervalMicro <= 0 { intervalMicro = 1 } floatArgs, locationArgs := buildAggArgs(signalReqs) aggArgs := &model.AggregatedSignalArgs{ - SignalArgs: model.SignalArgs{TokenID: tokenID}, + SignalArgs: model.SignalArgs{Subject: subject}, FromTS: dayStart, ToTS: dayEnd, Interval: intervalMicro, diff --git a/internal/repositories/validate.go b/internal/repositories/validate.go index 4414088..d683116 100644 --- a/internal/repositories/validate.go +++ b/internal/repositories/validate.go @@ -97,8 +97,8 @@ func validateSignalArgs(args *model.SignalArgs) error { return ValidationError("signal args not provided") } - if args.TokenID < 1 { - return ValidationError("tokenID is not a positive integer") + if args.Subject == "" { + return ValidationError("subject is empty") } return validateFilter(args.Filter) @@ -116,10 +116,7 @@ func validateFilter(filter *model.SignalFilter) error { return nil } -func validateEventArgs(tokenID int, from, to time.Time, filter *model.EventFilter) error { - if tokenID < 1 { - return ValidationError("tokenID is not a positive integer") - } +func validateEventArgs(from, to time.Time, filter *model.EventFilter) error { if from.IsZero() { return ValidationError("from timestamp is zero") } diff --git a/internal/repositories/validate_test.go b/internal/repositories/validate_test.go index 6600264..1b7a69e 100644 --- a/internal/repositories/validate_test.go +++ b/internal/repositories/validate_test.go @@ -15,41 +15,36 @@ func TestValidateEventArgs(t *testing.T) { validFilter := &model.EventFilter{} t.Run("valid args", func(t *testing.T) { - err := validateEventArgs(1, validFrom, validTo, validFilter) + err := validateEventArgs(validFrom, validTo, validFilter) require.NoError(t, err) }) - t.Run("tokenID < 1", func(t *testing.T) { - err := validateEventArgs(0, validFrom, validTo, validFilter) - require.Error(t, err) - }) - t.Run("from is zero", func(t *testing.T) { - err := validateEventArgs(1, time.Time{}, validTo, validFilter) + err := validateEventArgs(time.Time{}, validTo, validFilter) require.Error(t, err) }) t.Run("to is zero", func(t *testing.T) { - err := validateEventArgs(1, validFrom, time.Time{}, validFilter) + err := validateEventArgs(validFrom, time.Time{}, validFilter) require.Error(t, err) }) t.Run("from after to", func(t *testing.T) { from := validTo.Add(time.Second) - err := validateEventArgs(1, from, validTo, validFilter) + err := validateEventArgs(from, validTo, validFilter) require.Error(t, err) }) t.Run("valid tags", func(t *testing.T) { - err := validateEventArgs(1, validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAny: []string{vss.TagBehaviorHarshAcceleration, vss.TagSafetyCollision}}}) + err := validateEventArgs(validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAny: []string{vss.TagBehaviorHarshAcceleration, vss.TagSafetyCollision}}}) require.NoError(t, err) - err = validateEventArgs(1, validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAll: []string{vss.TagBehaviorHarshAcceleration}}}) + err = validateEventArgs(validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAll: []string{vss.TagBehaviorHarshAcceleration}}}) require.NoError(t, err) }) t.Run("invalid tags", func(t *testing.T) { - err := validateEventArgs(1, validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAny: []string{"invalid"}}}) + err := validateEventArgs(validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAny: []string{"invalid"}}}) require.Error(t, err) - err = validateEventArgs(1, validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAll: []string{vss.TagBehaviorHarshAcceleration, "invalid"}}}) + err = validateEventArgs(validFrom, validTo, &model.EventFilter{Tags: &model.StringArrayFilter{ContainsAll: []string{vss.TagBehaviorHarshAcceleration, "invalid"}}}) require.Error(t, err) }) @@ -60,34 +55,29 @@ func TestValidateSegmentArgs(t *testing.T) { validTo := time.Now() t.Run("valid args", func(t *testing.T) { - err := validateSegmentArgs(1, validFrom, validTo) + err := validateSegmentArgs(validFrom, validTo) require.NoError(t, err) }) t.Run("exactly 31 days passes", func(t *testing.T) { from := validTo.Add(-31 * 24 * time.Hour) - err := validateSegmentArgs(1, from, validTo) + err := validateSegmentArgs(from, validTo) require.NoError(t, err) }) - t.Run("tokenID <= 0", func(t *testing.T) { - err := validateSegmentArgs(0, validFrom, validTo) - require.Error(t, err) - }) - t.Run("from after to", func(t *testing.T) { - err := validateSegmentArgs(1, validTo.Add(time.Minute), validTo) + err := validateSegmentArgs(validTo.Add(time.Minute), validTo) require.Error(t, err) }) t.Run("from equal to", func(t *testing.T) { - err := validateSegmentArgs(1, validFrom, validFrom) + err := validateSegmentArgs(validFrom, validFrom) require.Error(t, err) }) t.Run("date range exceeded", func(t *testing.T) { from := validTo.Add(-33 * 24 * time.Hour) // max is 32 days - err := validateSegmentArgs(1, from, validTo) + err := validateSegmentArgs(from, validTo) require.Error(t, err) }) } diff --git a/internal/repositories/vc/vc.go b/internal/repositories/vc/vc.go index 54b38f4..f4bb0b5 100644 --- a/internal/repositories/vc/vc.go +++ b/internal/repositories/vc/vc.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "math/big" "time" "github.com/DIMO-Network/attestation-api/pkg/types" @@ -44,13 +43,8 @@ func New(indexService indexRepoService, settings config.Settings) *Repository { } // GetLatestVINVC fetches the latest VIN VC for the given vehicle. -func (r *Repository) GetLatestVINVC(ctx context.Context, vehicleTokenID uint32) (*model.Vinvc, error) { - vehicleDID := cloudevent.ERC721DID{ - ChainID: r.chainID, - ContractAddress: r.vehicleAddress, - TokenID: new(big.Int).SetUint64(uint64(vehicleTokenID)), - }.String() - dataObj, err := r.getVINVC(ctx, vehicleDID) +func (r *Repository) GetLatestVINVC(ctx context.Context, subject string) (*model.Vinvc, error) { + dataObj, err := r.getVINVC(ctx, subject) if err != nil { if status.Code(err) == codes.NotFound { return nil, nil //nolint // we nil is a valid response diff --git a/internal/repositories/vc/vc_test.go b/internal/repositories/vc/vc_test.go index d5ffc0a..413a205 100644 --- a/internal/repositories/vc/vc_test.go +++ b/internal/repositories/vc/vc_test.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "math/big" "slices" "testing" "time" @@ -52,6 +53,11 @@ func TestGetLatestVC(t *testing.T) { // Initialize variables ctx := context.Background() vehicleTokenID := uint32(123) + vehicleSubject := cloudevent.ERC721DID{ + ChainID: 3, + ContractAddress: common.HexToAddress("0xfEDCBA0987654321FeDcbA0987654321fedCBA09"), + TokenID: new(big.Int).SetUint64(uint64(vehicleTokenID)), + }.String() // Create mock controller ctrl := gomock.NewController(t) @@ -148,7 +154,7 @@ func TestGetLatestVC(t *testing.T) { tt.mockSetup() // Call the method - vc, err := svc.GetLatestVINVC(ctx, vehicleTokenID) + vc, err := svc.GetLatestVINVC(ctx, vehicleSubject) // Assert the results if tt.expectedErr { diff --git a/internal/service/ch/ch_test.go b/internal/service/ch/ch_test.go index 5538df5..e339c1f 100644 --- a/internal/service/ch/ch_test.go +++ b/internal/service/ch/ch_test.go @@ -78,7 +78,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "no aggs", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -90,7 +90,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "average", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -116,7 +116,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "max and min", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -153,7 +153,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "max smartcar", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, Filter: &model.SignalFilter{ Source: ref("did:ethr:137:0xcd445F4c6bDAD32b68a2939b912150Fe3C88803E"), }, @@ -182,7 +182,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "unique", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -208,7 +208,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "Top autopi", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, Filter: &model.SignalFilter{ Source: ref("did:ethr:137:0x5e31bBc786D7bEd95216383787deA1ab0f1c1897"), }, @@ -237,7 +237,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "first float", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -263,7 +263,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "last float", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -289,7 +289,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "lt filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -318,7 +318,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "gt filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -347,7 +347,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "gte filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -376,7 +376,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "lte filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -405,7 +405,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "filter for numeric values in set", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -434,7 +434,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "float neq filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -463,7 +463,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "float neq filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -492,7 +492,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "float not in filter", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -521,7 +521,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "float filters and-ed", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -551,7 +551,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "float filters or-ed", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -583,7 +583,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "first string", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -609,7 +609,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "last string", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -635,7 +635,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "multiple", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -695,7 +695,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "first location", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -721,7 +721,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "last location", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -747,7 +747,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "average location", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -773,7 +773,7 @@ func (c *CHServiceTestSuite) TestGetAggSignal() { name: "first location in fence", aggArgs: model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, FromTS: c.dataStartTime, ToTS: endTs, @@ -833,7 +833,7 @@ func (c *CHServiceTestSuite) TestGetLatestSignal() { name: "latest", latestArgs: model.LatestSignalsArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, SignalNames: map[string]struct{}{ vss.FieldSpeed: {}, @@ -851,7 +851,7 @@ func (c *CHServiceTestSuite) TestGetLatestSignal() { name: "latest smartcar", latestArgs: model.LatestSignalsArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, Filter: &model.SignalFilter{ Source: ref("did:ethr:137:0xcd445F4c6bDAD32b68a2939b912150Fe3C88803E"), }, @@ -872,7 +872,7 @@ func (c *CHServiceTestSuite) TestGetLatestSignal() { name: "lastSeen", latestArgs: model.LatestSignalsArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, IncludeLastSeen: true, SignalNames: map[string]struct{}{}, @@ -888,7 +888,7 @@ func (c *CHServiceTestSuite) TestGetLatestSignal() { name: "latest location", latestArgs: model.LatestSignalsArgs{ SignalArgs: model.SignalArgs{ - TokenID: 1, + Subject: testSubject1, }, SignalNames: map[string]struct{}{}, LocationSignalNames: map[string]struct{}{ @@ -975,7 +975,7 @@ func (c *CHServiceTestSuite) TestOriginGrouping() { // Create aggregation query args aggArgs := &model.AggregatedSignalArgs{ SignalArgs: model.SignalArgs{ - TokenID: 100, + Subject: testSubject100, }, FromTS: startTime, ToTS: endTime, diff --git a/schema/base.graphqls b/schema/base.graphqls index 8380a6b..d4353e2 100644 --- a/schema/base.graphqls +++ b/schema/base.graphqls @@ -26,10 +26,18 @@ The root query type for the GraphQL schema. """ type Query { """ - signals returns a collection of signals for a given token in a given time range. + Compute aggregations of signals for a given subject. """ signals( - tokenId: Int! + """ + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String """ interval is a time span that used for aggregatting the data with. A duration string is a sequence of decimal numbers, each with optional fraction and a unit suffix, @@ -41,20 +49,53 @@ type Query { filter: SignalFilter ): [SignalAggregations!] @requiresVehicleToken """ - SignalsLatest returns the latest signals for a given token. + Query the latest values of signals for a given subject. """ - signalsLatest(tokenId: Int!, filter: SignalFilter): SignalCollection + signalsLatest( + """ + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String + filter: SignalFilter + ): SignalCollection @requiresVehicleToken """ - availableSignals returns a list of queryable signal names that have stored data for a given tokenId. + List queryable signal names that have stored data for a given subject. """ - availableSignals(tokenId: Int!, filter: SignalFilter): [String!] + availableSignals( + """ + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String + filter: SignalFilter + ): [String!] @requiresVehicleToken """ - data summary of all signals for a given tokenId + Data summary of all signals for a given subject. """ - dataSummary(tokenId: Int!, filter: SignalFilter): DataSummary + dataSummary( + """ + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested signals. Typically a W3C DID. + """ + subject: String + filter: SignalFilter + ): DataSummary @requiresVehicleToken } type SignalAggregations { diff --git a/schema/events.graphqls b/schema/events.graphqls index f9b79fe..44737fe 100644 --- a/schema/events.graphqls +++ b/schema/events.graphqls @@ -1,12 +1,17 @@ extend type Query { """ - events returns a list of events for a given token in a given time range. + Returns a list of events for a given subject in a given time range. """ events( """ - tokenId is the id of the token to get events for. + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. """ - tokenId: Int! + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested events. Typically a W3C DID. + """ + subject: String """ from is the start time of the event. """ diff --git a/schema/segments.graphqls b/schema/segments.graphqls index 46d9534..d1bd93c 100644 --- a/schema/segments.graphqls +++ b/schema/segments.graphqls @@ -57,7 +57,15 @@ extend type Query { When signalRequests is provided, those requests are added on top of the default set; duplicates (same name and agg) are omitted. """ segments( - tokenId: Int! + """ + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested data. Typically a W3C DID. + """ + subject: String from: Time! to: Time! mechanism: DetectionMechanism! @@ -81,7 +89,15 @@ extend type Query { Maximum date range: 31 days. """ dailyActivity( - tokenId: Int! + """ + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. + """ + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested data. Typically a W3C DID. + """ + subject: String from: Time! to: Time! mechanism: DetectionMechanism! diff --git a/schema/vc.graphqls b/schema/vc.graphqls index f980bc4..d32ddc7 100644 --- a/schema/vc.graphqls +++ b/schema/vc.graphqls @@ -1,14 +1,19 @@ extend type Query { """ - vinVCLatest returns the latest VINVC data for a given token. + vinVCLatest returns the latest VIN VC data for a given subject. Required Privileges: [VEHICLE_VIN_CREDENTIAL] """ vinVCLatest( """ - The token ID of the vehicle. + A vehicle token id. This is translated into a ERC-721 DID `subject` string, and you + should use that argument instead. """ - tokenId: Int! + tokenId: Int @deprecated(reason: "Use `subject` instead.") + """ + The subject of the requested data. Typically a W3C DID. + """ + subject: String ): VINVC @requiresVehicleToken @requiresAllOfPrivileges(privileges: [VEHICLE_VIN_CREDENTIAL])