diff --git a/gqlgen.yml b/gqlgen.yml index 680084b9..09c819d8 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -121,6 +121,13 @@ models: type: "*int" ManufacturerID: type: "int" + SyntheticDevice: + fields: + vehicle: + resolver: true + extraFields: + VehicleID: + type: "int" Vehicle: fields: manufacturer: diff --git a/graph/generated.go b/graph/generated.go index b59d0fed..7571d9a7 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -50,6 +50,7 @@ type ResolverRoot interface { Earning() EarningResolver Manufacturer() ManufacturerResolver Query() QueryResolver + SyntheticDevice() SyntheticDeviceResolver UserRewards() UserRewardsResolver Vehicle() VehicleResolver VehicleEarnings() VehicleEarningsResolver @@ -236,6 +237,7 @@ type ComplexityRoot struct { MintedAt func(childComplexity int) int Name func(childComplexity int) int TokenID func(childComplexity int) int + Vehicle func(childComplexity int) int } SyntheticDeviceConnection struct { @@ -329,6 +331,9 @@ type QueryResolver interface { Vehicle(ctx context.Context, tokenID int) (*model.Vehicle, error) Vehicles(ctx context.Context, first *int, after *string, last *int, before *string, filterBy *model.VehiclesFilter) (*model.VehicleConnection, error) } +type SyntheticDeviceResolver interface { + Vehicle(ctx context.Context, obj *model.SyntheticDevice) (*model.Vehicle, error) +} type UserRewardsResolver interface { History(ctx context.Context, obj *model.UserRewards, first *int, after *string, last *int, before *string) (*model.EarningsConnection, error) } @@ -1225,6 +1230,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.SyntheticDevice.TokenID(childComplexity), true + case "SyntheticDevice.vehicle": + if e.complexity.SyntheticDevice.Vehicle == nil { + break + } + + return e.complexity.SyntheticDevice.Vehicle(childComplexity), true + case "SyntheticDeviceConnection.edges": if e.complexity.SyntheticDeviceConnection.Edges == nil { break @@ -5290,6 +5302,8 @@ func (ec *executionContext) fieldContext_Earning_syntheticDevice(_ context.Conte return ec.fieldContext_SyntheticDevice_address(ctx, field) case "mintedAt": return ec.fieldContext_SyntheticDevice_mintedAt(ctx, field) + case "vehicle": + return ec.fieldContext_SyntheticDevice_vehicle(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type SyntheticDevice", field.Name) }, @@ -7414,6 +7428,8 @@ func (ec *executionContext) fieldContext_Query_syntheticDevice(ctx context.Conte return ec.fieldContext_SyntheticDevice_address(ctx, field) case "mintedAt": return ec.fieldContext_SyntheticDevice_mintedAt(ctx, field) + case "vehicle": + return ec.fieldContext_SyntheticDevice_vehicle(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type SyntheticDevice", field.Name) }, @@ -8042,6 +8058,82 @@ func (ec *executionContext) fieldContext_SyntheticDevice_mintedAt(_ context.Cont return fc, nil } +func (ec *executionContext) _SyntheticDevice_vehicle(ctx context.Context, field graphql.CollectedField, obj *model.SyntheticDevice) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_SyntheticDevice_vehicle(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.SyntheticDevice().Vehicle(rctx, obj) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*model.Vehicle) + fc.Result = res + return ec.marshalNVehicle2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋidentityᚑapiᚋgraphᚋmodelᚐVehicle(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_SyntheticDevice_vehicle(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "SyntheticDevice", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_Vehicle_id(ctx, field) + case "tokenId": + return ec.fieldContext_Vehicle_tokenId(ctx, field) + case "manufacturer": + return ec.fieldContext_Vehicle_manufacturer(ctx, field) + case "owner": + return ec.fieldContext_Vehicle_owner(ctx, field) + case "mintedAt": + return ec.fieldContext_Vehicle_mintedAt(ctx, field) + case "aftermarketDevice": + return ec.fieldContext_Vehicle_aftermarketDevice(ctx, field) + case "privileges": + return ec.fieldContext_Vehicle_privileges(ctx, field) + case "syntheticDevice": + return ec.fieldContext_Vehicle_syntheticDevice(ctx, field) + case "definition": + return ec.fieldContext_Vehicle_definition(ctx, field) + case "dcn": + return ec.fieldContext_Vehicle_dcn(ctx, field) + case "name": + return ec.fieldContext_Vehicle_name(ctx, field) + case "imageURI": + return ec.fieldContext_Vehicle_imageURI(ctx, field) + case "image": + return ec.fieldContext_Vehicle_image(ctx, field) + case "earnings": + return ec.fieldContext_Vehicle_earnings(ctx, field) + case "dataURI": + return ec.fieldContext_Vehicle_dataURI(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type Vehicle", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _SyntheticDeviceConnection_totalCount(ctx context.Context, field graphql.CollectedField, obj *model.SyntheticDeviceConnection) (ret graphql.Marshaler) { fc, err := ec.fieldContext_SyntheticDeviceConnection_totalCount(ctx, field) if err != nil { @@ -8187,6 +8279,8 @@ func (ec *executionContext) fieldContext_SyntheticDeviceConnection_nodes(_ conte return ec.fieldContext_SyntheticDevice_address(ctx, field) case "mintedAt": return ec.fieldContext_SyntheticDevice_mintedAt(ctx, field) + case "vehicle": + return ec.fieldContext_SyntheticDevice_vehicle(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type SyntheticDevice", field.Name) }, @@ -8343,6 +8437,8 @@ func (ec *executionContext) fieldContext_SyntheticDeviceEdge_node(_ context.Cont return ec.fieldContext_SyntheticDevice_address(ctx, field) case "mintedAt": return ec.fieldContext_SyntheticDevice_mintedAt(ctx, field) + case "vehicle": + return ec.fieldContext_SyntheticDevice_vehicle(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type SyntheticDevice", field.Name) }, @@ -8883,6 +8979,8 @@ func (ec *executionContext) fieldContext_Vehicle_syntheticDevice(_ context.Conte return ec.fieldContext_SyntheticDevice_address(ctx, field) case "mintedAt": return ec.fieldContext_SyntheticDevice_mintedAt(ctx, field) + case "vehicle": + return ec.fieldContext_SyntheticDevice_vehicle(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type SyntheticDevice", field.Name) }, @@ -11716,27 +11814,13 @@ func (ec *executionContext) unmarshalInputDeviceDefinitionFilter(ctx context.Con asMap[k] = v } - fieldsInOrder := [...]string{"manufacturer", "id", "model", "year"} + fieldsInOrder := [...]string{"model", "year"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { - case "manufacturer": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("manufacturer")) - data, err := ec.unmarshalNString2string(ctx, v) - if err != nil { - return it, err - } - it.Manufacturer = data - case "id": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - data, err := ec.unmarshalOString2ᚖstring(ctx, v) - if err != nil { - return it, err - } - it.ID = data case "model": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("model")) data, err := ec.unmarshalOString2ᚖstring(ctx, v) @@ -12005,8 +12089,6 @@ func (ec *executionContext) unmarshalInputVehiclesFilter(ctx context.Context, ob } it.Year = data case "manufacturerTokenId": - var err error - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("manufacturerTokenId")) data, err := ec.unmarshalOInt2ᚖint(ctx, v) if err != nil { @@ -13784,33 +13866,69 @@ func (ec *executionContext) _SyntheticDevice(ctx context.Context, sel ast.Select case "id": out.Values[i] = ec._SyntheticDevice_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "name": out.Values[i] = ec._SyntheticDevice_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "tokenId": out.Values[i] = ec._SyntheticDevice_tokenId(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "integrationId": out.Values[i] = ec._SyntheticDevice_integrationId(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "address": out.Values[i] = ec._SyntheticDevice_address(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } case "mintedAt": out.Values[i] = ec._SyntheticDevice_mintedAt(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } + case "vehicle": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._SyntheticDevice_vehicle(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + 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) }) default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/graph/model/models_gen.go b/graph/model/models_gen.go index 2eafe735..718b2c76 100644 --- a/graph/model/models_gen.go +++ b/graph/model/models_gen.go @@ -207,10 +207,6 @@ type DeviceDefinitionEdge struct { // Filter for Device Definition. type DeviceDefinitionFilter struct { - // The manufacturer for the device definition. - Manufacturer string `json:"manufacturer"` - // ID filters for the device definition that are of the given model. - ID *string `json:"id,omitempty"` // Model filters for device definition that are of the given model. Model *string `json:"model,omitempty"` // Year filters for device definition that are of the given year. @@ -336,6 +332,9 @@ type SyntheticDevice struct { Address common.Address `json:"address"` // The block timestamp at which this device was minted. MintedAt time.Time `json:"mintedAt"` + // The vehicle with which the synthetic device is paired. + Vehicle *Vehicle `json:"vehicle"` + VehicleID int `json:"-"` } func (SyntheticDevice) IsNode() {} diff --git a/graph/schema/synthetic.graphqls b/graph/schema/synthetic.graphqls index f82c29aa..0ed59ba7 100644 --- a/graph/schema/synthetic.graphqls +++ b/graph/schema/synthetic.graphqls @@ -64,6 +64,10 @@ type SyntheticDevice implements Node { The block timestamp at which this device was minted. """ mintedAt: Time! + """ + The vehicle with which the synthetic device is paired. + """ + vehicle: Vehicle! } """ diff --git a/graph/synthetic.resolvers.go b/graph/synthetic.resolvers.go index 225f15e3..cbb7cc54 100644 --- a/graph/synthetic.resolvers.go +++ b/graph/synthetic.resolvers.go @@ -8,6 +8,7 @@ import ( "context" "github.com/DIMO-Network/identity-api/graph/model" + "github.com/DIMO-Network/identity-api/internal/loader" ) // SyntheticDevice is the resolver for the syntheticDevice field. @@ -19,3 +20,13 @@ func (r *queryResolver) SyntheticDevice(ctx context.Context, by model.SyntheticD func (r *queryResolver) SyntheticDevices(ctx context.Context, first *int, last *int, after *string, before *string, filterBy *model.SyntheticDevicesFilter) (*model.SyntheticDeviceConnection, error) { return r.synthetic.GetSyntheticDevices(ctx, first, last, after, before, filterBy) } + +// Vehicle is the resolver for the vehicle field. +func (r *syntheticDeviceResolver) Vehicle(ctx context.Context, obj *model.SyntheticDevice) (*model.Vehicle, error) { + return loader.GetVehicleByID(ctx, obj.VehicleID) +} + +// SyntheticDevice returns SyntheticDeviceResolver implementation. +func (r *Resolver) SyntheticDevice() SyntheticDeviceResolver { return &syntheticDeviceResolver{r} } + +type syntheticDeviceResolver struct{ *Resolver } diff --git a/internal/repositories/synthetic/synthetic_devices.go b/internal/repositories/synthetic/synthetic_devices.go index 7b556d5c..96398a9c 100644 --- a/internal/repositories/synthetic/synthetic_devices.go +++ b/internal/repositories/synthetic/synthetic_devices.go @@ -43,6 +43,7 @@ func ToAPI(sd *models.SyntheticDevice) (*gmodel.SyntheticDevice, error) { IntegrationID: sd.IntegrationID, Address: common.BytesToAddress(sd.DeviceAddress), MintedAt: sd.MintedAt, + VehicleID: sd.VehicleID, }, nil } @@ -101,15 +102,15 @@ func (r *Repository) GetSyntheticDevices(ctx context.Context, first *int, last * queryMods = append(queryMods, models.SyntheticDeviceWhere.ID.GT(beforeID)) } - orderBy := "DESC" + orderBy := " DESC" if last != nil { - orderBy = "ASC" + orderBy = " ASC" } queryMods = append(queryMods, // Use limit + 1 here to check if there's another page. qm.Limit(limit+1), - qm.OrderBy(models.SyntheticDeviceColumns.ID+" "+orderBy), + qm.OrderBy(models.SyntheticDeviceColumns.ID+orderBy), ) all, err := models.SyntheticDevices(queryMods...).All(ctx, r.PDB.DBS().Reader) diff --git a/internal/repositories/synthetic/synthetic_devices_test.go b/internal/repositories/synthetic/synthetic_devices_test.go index 255a3721..45d898ae 100644 --- a/internal/repositories/synthetic/synthetic_devices_test.go +++ b/internal/repositories/synthetic/synthetic_devices_test.go @@ -59,6 +59,7 @@ func Test_SyntheticDeviceToAPI(t *testing.T) { IntegrationID: 2, Address: *wallet, MintedAt: currTime, + VehicleID: sd.VehicleID, }, res) }