From 3d735daf922b39cb58ee55a5a0549beb2817bd6f Mon Sep 17 00:00:00 2001 From: Dylan Moreland Date: Tue, 20 Jan 2026 13:42:55 -0500 Subject: [PATCH] Add a sacd(Address!) query --- gqlgen.yml | 2 + graph/generated.go | 135 ++++++++++++++++++ graph/model/models_gen.go | 2 + graph/schema/vehicle.graphqls | 4 + graph/vehicle.resolvers.go | 5 + .../repositories/vehiclesacd/vehiclesacd.go | 18 +++ 6 files changed, 166 insertions(+) diff --git a/gqlgen.yml b/gqlgen.yml index 1203bde3..49eaf8e6 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -154,6 +154,8 @@ models: resolver: true sacds: resolver: true + sacd: + resolver: true syntheticDevice: resolver: true dcn: diff --git a/graph/generated.go b/graph/generated.go index a24f7a1a..93c778b6 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -462,6 +462,7 @@ type ComplexityRoot struct { Name func(childComplexity int) int Owner func(childComplexity int) int Privileges func(childComplexity int, first *int, after *string, last *int, before *string, filterBy *model.PrivilegeFilterBy) int + Sacd func(childComplexity int, grantee common.Address) int Sacds func(childComplexity int, first *int, after *string, last *int, before *string) int Stake func(childComplexity int) int StorageNode func(childComplexity int) int @@ -560,6 +561,7 @@ type VehicleResolver interface { AftermarketDevice(ctx context.Context, obj *model.Vehicle) (*model.AftermarketDevice, error) Privileges(ctx context.Context, obj *model.Vehicle, first *int, after *string, last *int, before *string, filterBy *model.PrivilegeFilterBy) (*model.PrivilegesConnection, error) Sacds(ctx context.Context, obj *model.Vehicle, first *int, after *string, last *int, before *string) (*model.SacdConnection, error) + Sacd(ctx context.Context, obj *model.Vehicle, grantee common.Address) (*model.Sacd, error) SyntheticDevice(ctx context.Context, obj *model.Vehicle) (*model.SyntheticDevice, error) Dcn(ctx context.Context, obj *model.Vehicle) (*model.Dcn, error) @@ -2270,6 +2272,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.Vehicle.Privileges(childComplexity, args["first"].(*int), args["after"].(*string), args["last"].(*int), args["before"].(*string), args["filterBy"].(*model.PrivilegeFilterBy)), true + case "Vehicle.sacd": + if e.complexity.Vehicle.Sacd == nil { + break + } + + args, err := ec.field_Vehicle_sacd_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Vehicle.Sacd(childComplexity, args["grantee"].(common.Address)), true case "Vehicle.sacds": if e.complexity.Vehicle.Sacds == nil { break @@ -3144,6 +3157,17 @@ func (ec *executionContext) field_Vehicle_privileges_args(ctx context.Context, r return args, nil } +func (ec *executionContext) field_Vehicle_sacd_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, "grantee", ec.unmarshalNAddress2githubᚗcomᚋethereumᚋgoᚑethereumᚋcommonᚐAddress) + if err != nil { + return nil, err + } + args["grantee"] = arg0 + return args, nil +} + func (ec *executionContext) field_Vehicle_sacds_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -3712,6 +3736,8 @@ func (ec *executionContext) fieldContext_AftermarketDevice_vehicle(_ context.Con return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -4902,6 +4928,8 @@ func (ec *executionContext) fieldContext_DCN_vehicle(_ context.Context, field gr return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -6651,6 +6679,8 @@ func (ec *executionContext) fieldContext_Earning_vehicle(_ context.Context, fiel return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -8982,6 +9012,8 @@ func (ec *executionContext) fieldContext_Query_vehicle(ctx context.Context, fiel return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -10408,6 +10440,8 @@ func (ec *executionContext) fieldContext_Stake_vehicle(_ context.Context, field return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -11117,6 +11151,8 @@ func (ec *executionContext) fieldContext_SyntheticDevice_vehicle(_ context.Conte return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -12252,6 +12288,61 @@ func (ec *executionContext) fieldContext_Vehicle_sacds(ctx context.Context, fiel return fc, nil } +func (ec *executionContext) _Vehicle_sacd(ctx context.Context, field graphql.CollectedField, obj *model.Vehicle) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_Vehicle_sacd, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.resolvers.Vehicle().Sacd(ctx, obj, fc.Args["grantee"].(common.Address)) + }, + nil, + ec.marshalOSacd2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋidentityᚑapiᚋgraphᚋmodelᚐSacd, + true, + false, + ) +} + +func (ec *executionContext) fieldContext_Vehicle_sacd(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Vehicle", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "grantee": + return ec.fieldContext_Sacd_grantee(ctx, field) + case "permissions": + return ec.fieldContext_Sacd_permissions(ctx, field) + case "source": + return ec.fieldContext_Sacd_source(ctx, field) + case "createdAt": + return ec.fieldContext_Sacd_createdAt(ctx, field) + case "expiresAt": + return ec.fieldContext_Sacd_expiresAt(ctx, field) + case "template": + return ec.fieldContext_Sacd_template(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Sacd", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Vehicle_sacd_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Vehicle_syntheticDevice(ctx context.Context, field graphql.CollectedField, obj *model.Vehicle) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -12742,6 +12833,8 @@ func (ec *executionContext) fieldContext_VehicleConnection_nodes(_ context.Conte return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -12930,6 +13023,8 @@ func (ec *executionContext) fieldContext_VehicleEdge_node(_ context.Context, fie return ec.fieldContext_Vehicle_privileges(ctx, field) case "sacds": return ec.fieldContext_Vehicle_sacds(ctx, field) + case "sacd": + return ec.fieldContext_Vehicle_sacd(ctx, field) case "syntheticDevice": return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) case "definition": @@ -19057,6 +19152,39 @@ func (ec *executionContext) _Vehicle(ctx context.Context, sel ast.SelectionSet, continue } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "sacd": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Vehicle_sacd(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) case "syntheticDevice": field := field @@ -22364,6 +22492,13 @@ func (ec *executionContext) unmarshalOPrivilegeFilterBy2ᚖgithubᚗcomᚋDIMO return &res, graphql.ErrorOnPath(ctx, err) } +func (ec *executionContext) marshalOSacd2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋidentityᚑapiᚋgraphᚋmodelᚐSacd(ctx context.Context, sel ast.SelectionSet, v *model.Sacd) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._Sacd(ctx, sel, v) +} + func (ec *executionContext) marshalOStake2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋidentityᚑapiᚋgraphᚋmodelᚐStake(ctx context.Context, sel ast.SelectionSet, v *model.Stake) graphql.Marshaler { if v == nil { return graphql.Null diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index 30b2eb6a..88d2c19e 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -695,6 +695,8 @@ type Vehicle struct { Privileges *PrivilegesConnection `json:"privileges"` // A Relay-style connection listing any active SACD permission grants on this vehicle. Sacds *SacdConnection `json:"sacds"` + // The active SACD for this vehicle and the specified grantee, if there is one. + Sacd *Sacd `json:"sacd,omitempty"` // The paired synthetic device, if any. SyntheticDevice *SyntheticDevice `json:"syntheticDevice,omitempty"` // The device definition for this vehicle; which includes make, model, and year among diff --git a/graph/schema/vehicle.graphqls b/graph/schema/vehicle.graphqls index 49d9ec35..e2eb7713 100644 --- a/graph/schema/vehicle.graphqls +++ b/graph/schema/vehicle.graphqls @@ -132,6 +132,10 @@ type Vehicle implements Node { """ sacds(first: Int, after: String, last: Int, before: String): SacdConnection! """ + The active SACD for this vehicle and the specified grantee, if there is one. + """ + sacd(grantee: Address!): Sacd + """ The paired synthetic device, if any. """ syntheticDevice: SyntheticDevice diff --git a/graph/vehicle.resolvers.go b/graph/vehicle.resolvers.go index afe5289d..6e86347a 100644 --- a/graph/vehicle.resolvers.go +++ b/graph/vehicle.resolvers.go @@ -57,6 +57,11 @@ func (r *vehicleResolver) Sacds(ctx context.Context, obj *model.Vehicle, first * return r.vehiclesacd.GetSacdsForVehicle(ctx, obj.TokenID, first, after, last, before) } +// Sacd is the resolver for the sacd field. +func (r *vehicleResolver) Sacd(ctx context.Context, obj *model.Vehicle, grantee common.Address) (*model.Sacd, error) { + return r.vehiclesacd.GetSacdForVehicleAndGrantee(ctx, obj.TokenID, grantee) +} + // SyntheticDevice is the resolver for the syntheticDevice field. func (r *vehicleResolver) SyntheticDevice(ctx context.Context, obj *model.Vehicle) (*model.SyntheticDevice, error) { return loader.GetSyntheticDeviceByVehicleID(ctx, obj.TokenID) diff --git a/internal/repositories/vehiclesacd/vehiclesacd.go b/internal/repositories/vehiclesacd/vehiclesacd.go index 69161e4c..b72a4d70 100644 --- a/internal/repositories/vehiclesacd/vehiclesacd.go +++ b/internal/repositories/vehiclesacd/vehiclesacd.go @@ -2,6 +2,8 @@ package vehiclesacd import ( "context" + "database/sql" + "errors" "fmt" "math/big" "slices" @@ -257,3 +259,19 @@ func (p *Repository) GetSacdsForVehicle(ctx context.Context, tokenID int, first return p.createSacdResponse(page, totalCount, hasNext, hasPrevious, pHelp) } + +func (p *Repository) GetSacdForVehicleAndGrantee(ctx context.Context, tokenID int, grantee common.Address) (*gmodel.Sacd, error) { + vs, err := models.VehicleSacds( + models.VehicleSacdWhere.VehicleID.EQ(tokenID), + models.VehicleSacdWhere.Grantee.EQ(grantee.Bytes()), + models.VehicleSacdWhere.ExpiresAt.GT(time.Now()), + ).One(ctx, p.PDB.DBS().Reader) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + return nil, fmt.Errorf("failed to search for SACD: %w", err) + } + + return sacdToAPIResponse(vs) +}