diff --git a/cmd/identity-api/main.go b/cmd/identity-api/main.go index 6d756a5b..55ad2c5f 100644 --- a/cmd/identity-api/main.go +++ b/cmd/identity-api/main.go @@ -74,7 +74,7 @@ func main() { s := newDefaultServer(graph.NewExecutableSchema(cfg)) - srv := loader.Middleware(dbs, s, settings) + srv := loader.Middleware(dbs, s, settings, &logger) http.Handle("/", playground.Handler("GraphQL playground", "/query")) http.Handle("/query", srv) diff --git a/graph/dcn_test.go b/graph/dcn_test.go index 7e142dff..b03dcd5e 100644 --- a/graph/dcn_test.go +++ b/graph/dcn_test.go @@ -88,7 +88,7 @@ func TestDCNQuery(t *testing.T) { require.NoError(err) cfg := Config{Resolvers: resolver} - c := client.New(loader.Middleware(pdb, NewDefaultServer(NewExecutableSchema(cfg)), settings)) + c := client.New(loader.Middleware(pdb, NewDefaultServer(NewExecutableSchema(cfg)), settings, nil)) type response struct { DCN struct { diff --git a/graph/resolver.go b/graph/resolver.go index c2cfac4a..b2aa7cc5 100644 --- a/graph/resolver.go +++ b/graph/resolver.go @@ -122,6 +122,7 @@ type Resolver struct { // NewResolver creates a new Resolver with allocated repositories. func NewResolver(baseRepo *base.Repository) *Resolver { tablelandApiService := services.NewTablelandApiService(baseRepo.Log, &baseRepo.Settings) + deviceDefRepo := devicedefinition.New(baseRepo, tablelandApiService) return &Resolver{ aftermarket: aftermarket.New(baseRepo), @@ -129,10 +130,10 @@ func NewResolver(baseRepo *base.Repository) *Resolver { manufacturer: manufacturer.New(baseRepo), reward: reward.Repository{Repository: baseRepo}, synthetic: synthetic.New(baseRepo), - vehicle: vehicle.New(baseRepo), + vehicle: vehicle.New(baseRepo, deviceDefRepo), vehicleprivilege: vehicleprivilege.Repository{Repository: baseRepo}, vehiclesacd: vehiclesacd.Repository{Repository: baseRepo}, - deviceDefinition: devicedefinition.New(baseRepo, tablelandApiService), + deviceDefinition: deviceDefRepo, developerLicense: developerlicense.New(baseRepo), stake: stake.New(baseRepo), connection: connection.New(baseRepo), diff --git a/graph/resolver_test.go b/graph/resolver_test.go index 52297f68..49034ce1 100644 --- a/graph/resolver_test.go +++ b/graph/resolver_test.go @@ -114,7 +114,7 @@ func TestResolver(t *testing.T) { logger := zerolog.Nop() repo := base.NewRepository(pdb, settings, &logger) resolver := NewResolver(repo) - c := client.New(loader.Middleware(pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver})), settings)) + c := client.New(loader.Middleware(pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver})), settings, nil)) t.Run("ownedAftermarketDevices, return only one response", func(t *testing.T) { var resp interface{} diff --git a/graph/rewards_test.go b/graph/rewards_test.go index adcebab7..af48b861 100644 --- a/graph/rewards_test.go +++ b/graph/rewards_test.go @@ -213,10 +213,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetEarningsByVehicle_FwdPaginate() { }` c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -389,10 +386,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetEarningsByVehicle_FwdPaginate_Firs }` c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -594,10 +588,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetEarningsByVehicle_BackPaginate_Las }` c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -799,10 +790,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetEarningsByVehicle_BackPaginate_Las }` c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -962,10 +950,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetAftermarketDeviceEarnings_FwdPagin }` c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -1113,10 +1098,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetUserRewards_FwdPaginate() { }`, beneficiary.Hex()) c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -1286,10 +1268,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetUserRewards_BackPaginate_LastBefor }`, beneficiary.Hex()) c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp interface{} @@ -1416,10 +1395,7 @@ func (r *RewardsQueryTestSuite) Test_Query_GetUserRewards_NullEarnings() { }`, beneficiary.Hex()) c := client.New( - loader.Middleware( - r.pdb, - NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, - ), + loader.Middleware(r.pdb, NewDefaultServer(NewExecutableSchema(Config{Resolvers: r.resolver})), r.settings, nil), ) var resp any diff --git a/graph/schema.resolvers_test.go b/graph/schema.resolvers_test.go index e55c257d..33fa52bc 100644 --- a/graph/schema.resolvers_test.go +++ b/graph/schema.resolvers_test.go @@ -9,6 +9,7 @@ import ( "github.com/DIMO-Network/identity-api/internal/repositories/aftermarket" "github.com/DIMO-Network/identity-api/internal/repositories/base" "github.com/DIMO-Network/identity-api/internal/repositories/dcn" + "github.com/DIMO-Network/identity-api/internal/repositories/devicedefinition" "github.com/DIMO-Network/identity-api/internal/repositories/manufacturer" "github.com/DIMO-Network/identity-api/internal/repositories/synthetic" "github.com/DIMO-Network/identity-api/internal/repositories/vehicle" @@ -24,8 +25,10 @@ func TestQueryResolver_Node(t *testing.T) { baseRepo := &base.Repository{} - vehicleRepo := vehicle.New(baseRepo) - testVehicle, err := vehicleRepo.ToAPI(&models.Vehicle{ID: 1}, "", "") + // Create a mock device definition repository for the vehicle repository + mockDeviceDefRepo := &devicedefinition.Repository{} + vehicleRepo := vehicle.New(baseRepo, mockDeviceDefRepo) + testVehicle, err := vehicleRepo.ToAPI(&models.Vehicle{ID: 1}, "", "", nil) require.NoError(t, err) aftermarketRepo := aftermarket.New(baseRepo) diff --git a/graph/vehicle_test.go b/graph/vehicle_test.go index 5e318ed0..a2245c8f 100644 --- a/graph/vehicle_test.go +++ b/graph/vehicle_test.go @@ -27,6 +27,11 @@ type VehicleTestSuite struct { consumer *services.ContractsEventsConsumer } +//// Test Runner - commenting this out since seems this test was WIP +//func TestVehicleTestSuite(t *testing.T) { +// suite.Run(t, new(VehicleTestSuite)) +//} + func (s *VehicleTestSuite) SetupSuite() { ctx := context.TODO() var db db.Store @@ -46,7 +51,7 @@ func (s *VehicleTestSuite) SetupSuite() { resolver := NewResolver(repo) s.consumer = services.NewContractsEventsConsumer(db, &logger, &settings) - s.handler = loader.Middleware(db, NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver})), settings) + s.handler = loader.Middleware(db, NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver})), settings, nil) } func (s *VehicleTestSuite) TearDownSuite() { diff --git a/internal/loader/loader.go b/internal/loader/loader.go index 7deddfad..28d35163 100644 --- a/internal/loader/loader.go +++ b/internal/loader/loader.go @@ -10,13 +10,16 @@ import ( "github.com/DIMO-Network/identity-api/internal/repositories/base" "github.com/DIMO-Network/identity-api/internal/repositories/connection" "github.com/DIMO-Network/identity-api/internal/repositories/dcn" + "github.com/DIMO-Network/identity-api/internal/repositories/devicedefinition" "github.com/DIMO-Network/identity-api/internal/repositories/manufacturer" "github.com/DIMO-Network/identity-api/internal/repositories/stake" "github.com/DIMO-Network/identity-api/internal/repositories/storagenode" "github.com/DIMO-Network/identity-api/internal/repositories/synthetic" "github.com/DIMO-Network/identity-api/internal/repositories/vehicle" + "github.com/DIMO-Network/identity-api/internal/services" "github.com/DIMO-Network/shared/pkg/db" "github.com/graph-gophers/dataloader/v7" + "github.com/rs/zerolog" ) type loadersString string @@ -39,10 +42,15 @@ type Loaders struct { } // NewDataLoader returns the instantiated Loaders struct for use in a request -func NewDataLoader(dbs db.Store, settings config.Settings) *Loaders { +func NewDataLoader(dbs db.Store, settings config.Settings, lg *zerolog.Logger) *Loaders { // instantiate the user dataloader baseRepo := &base.Repository{PDB: dbs, Settings: settings} - vehicle := NewVehicleLoader(vehicle.New(baseRepo)) + + // Create device definition repository first + tablelandAPI := services.NewTablelandApiService(lg, &settings) + deviceDefRepo := devicedefinition.New(baseRepo, tablelandAPI) + + vehicle := NewVehicleLoader(vehicle.New(baseRepo, deviceDefRepo), deviceDefRepo) aftermarketDevice := NewAftermarketDeviceLoader(aftermarket.New(baseRepo)) syntheticDevice := NewSyntheticDeviceLoader(synthetic.New(baseRepo)) dcn := NewDCNLoader(dcn.New(baseRepo)) @@ -98,9 +106,9 @@ func NewDataLoader(dbs db.Store, settings config.Settings) *Loaders { // Middleware injects a DataLoader into the request context so it can be // used later in the schema resolvers -func Middleware(db db.Store, next http.Handler, settings config.Settings) http.Handler { +func Middleware(db db.Store, next http.Handler, settings config.Settings, lg *zerolog.Logger) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - loader := NewDataLoader(db, settings) + loader := NewDataLoader(db, settings, lg) nextCtx := context.WithValue(r.Context(), dataLoadersKey, loader) r = r.WithContext(nextCtx) next.ServeHTTP(w, r) diff --git a/internal/loader/vehicle_loader.go b/internal/loader/vehicle_loader.go index e1ec7183..52cde4e3 100644 --- a/internal/loader/vehicle_loader.go +++ b/internal/loader/vehicle_loader.go @@ -5,21 +5,24 @@ import ( "errors" "fmt" - "github.com/DIMO-Network/identity-api/graph/model" + "github.com/DIMO-Network/identity-api/internal/repositories/devicedefinition" + + gmodel "github.com/DIMO-Network/identity-api/graph/model" "github.com/DIMO-Network/identity-api/internal/repositories/vehicle" "github.com/DIMO-Network/identity-api/models" "github.com/graph-gophers/dataloader/v7" ) type VehicleLoader struct { - repo *vehicle.Repository + repo *vehicle.Repository + definitionsRepo *devicedefinition.Repository } -func NewVehicleLoader(repo *vehicle.Repository) *VehicleLoader { - return &VehicleLoader{repo: repo} +func NewVehicleLoader(repo *vehicle.Repository, definitionsRepo *devicedefinition.Repository) *VehicleLoader { + return &VehicleLoader{repo: repo, definitionsRepo: definitionsRepo} } -func GetVehicleByID(ctx context.Context, vehicleID int) (*model.Vehicle, error) { +func GetVehicleByID(ctx context.Context, vehicleID int) (*gmodel.Vehicle, error) { // read loader from context loaders := ctx.Value(dataLoadersKey).(*Loaders) // invoke and get thunk @@ -29,13 +32,13 @@ func GetVehicleByID(ctx context.Context, vehicleID int) (*model.Vehicle, error) } // BatchGetVehicleByID implements the dataloader for finding vehicles by their ids. -func (v *VehicleLoader) BatchGetVehicleByID(ctx context.Context, vehicleIDs []int) []*dataloader.Result[*model.Vehicle] { - results := make([]*dataloader.Result[*model.Vehicle], len(vehicleIDs)) +func (v *VehicleLoader) BatchGetVehicleByID(ctx context.Context, vehicleIDs []int) []*dataloader.Result[*gmodel.Vehicle] { + results := make([]*dataloader.Result[*gmodel.Vehicle], len(vehicleIDs)) vehicles, err := models.Vehicles(models.VehicleWhere.ID.IN(vehicleIDs)).All(ctx, v.repo.PDB.DBS().Reader) if err != nil { for i := range results { - results[i] = &dataloader.Result[*model.Vehicle]{Error: err} + results[i] = &dataloader.Result[*gmodel.Vehicle]{Error: err} } return results } @@ -46,6 +49,26 @@ func (v *VehicleLoader) BatchGetVehicleByID(ctx context.Context, vehicleIDs []in vehicleByID[v.ID] = v } + // populate a map of device definitions by id for fast lookup later + definitions := make(map[string]*gmodel.DeviceDefinition) + for _, veh := range vehicles { + definitions[veh.DeviceDefinitionID.String] = &gmodel.DeviceDefinition{} + } + ids := make([]string, 0, len(definitions)) + for id := range definitions { + ids = append(ids, id) + } + dds, err := v.definitionsRepo.GetDeviceDefinitionsByIDs(ctx, ids) + if err != nil { + for i := range results { + results[i] = &dataloader.Result[*gmodel.Vehicle]{Error: err} + } + return results + } + for _, dd := range dds { + definitions[dd.DeviceDefinitionID] = dd + } + for i, k := range vehicleIDs { if veh, ok := vehicleByID[k]; ok { var retErr error @@ -66,16 +89,21 @@ func (v *VehicleLoader) BatchGetVehicleByID(ctx context.Context, vehicleIDs []in if err != nil { retErr = errors.Join(retErr, fmt.Errorf("error getting vehicle data uri: %w", err)) } - obj, err := v.repo.ToAPI(veh, imageURI, dataURI) + definition, ok := definitions[veh.DeviceDefinitionID.String] + if !ok { + retErr = errors.Join(retErr, fmt.Errorf("error getting device definition: not found %s", veh.DeviceDefinitionID.String)) + } + + obj, err := v.repo.ToAPI(veh, imageURI, dataURI, definition) if err != nil { retErr = errors.Join(retErr, fmt.Errorf("error converting vehicle to API: %w", err)) } - results[i] = &dataloader.Result[*model.Vehicle]{ + results[i] = &dataloader.Result[*gmodel.Vehicle]{ Data: obj, Error: retErr, } } else { - results[i] = &dataloader.Result[*model.Vehicle]{Error: fmt.Errorf("no vehicle with id %d", k)} + results[i] = &dataloader.Result[*gmodel.Vehicle]{Error: fmt.Errorf("no vehicle with id %d", k)} } } diff --git a/internal/repositories/devicedefinition/devicedefinition.go b/internal/repositories/devicedefinition/devicedefinition.go index 567f0f01..8ed734d9 100644 --- a/internal/repositories/devicedefinition/devicedefinition.go +++ b/internal/repositories/devicedefinition/devicedefinition.go @@ -321,6 +321,76 @@ func (r *Repository) GetDeviceDefinitions(ctx context.Context, tableID, first *i return res, nil } +func (r *Repository) GetDeviceDefinitionsByIDs(ctx context.Context, ids []string) ([]*gmodel.DeviceDefinition, error) { + ids = filterDuplicates(ids) + slices.Sort(ids) // order alphabetically + + // get the unique manufacturer slugs + mfrs := make(map[string]*models.Manufacturer) + ddsByMfr := make(map[string][]string) + + for _, id := range ids { + mfrSlug, _, found := strings.Cut(id, "_") + if found { // this should always be true unless if we get eg. a legacy ksuid for the id + ddsByMfr[mfrSlug] = append(ddsByMfr[mfrSlug], id) + _, ok := mfrs[mfrSlug] + if !ok { + mfr, err := models.Manufacturers(models.ManufacturerWhere.Slug.EQ(mfrSlug)).One(ctx, r.PDB.DBS().Reader) + if err != nil { + return nil, err + } + mfrs[mfrSlug] = mfr + } + } + } + var errList gqlerror.List + var deviceDefinitions []*gmodel.DeviceDefinition + // get each manufacturer by mSlug to get the table id + for mSlug := range ddsByMfr { + // sqllite limit check + if len(ddsByMfr[mSlug]) >= 1000 { + return nil, fmt.Errorf("too many device definitions to query in one request") + } + + // Check if manufacturer exists + mfr, exists := mfrs[mSlug] + if !exists || mfr == nil { + errList = append(errList, gqlerror.Wrap(fmt.Errorf("manufacturer not found for slug: %s", mSlug))) + continue + } + + // do query + table := fmt.Sprintf("_%d_%d", r.Settings.DIMORegistryChainID, mfr.TableID.Int) + sqlBuild := goqu.Dialect("sqlite3").From(table) + sqlBuild = sqlBuild.Where(goqu.Ex{"id": ddsByMfr[mSlug]}) + // do rest of query stuff like above + allSQL, _, err := sqlBuild.ToSQL() + if err != nil { + return nil, fmt.Errorf("error constructing selection SQL: %w", err) + } + + var all []DeviceDefinitionTablelandModel + if err = r.TablelandApiService.Query(ctx, allSQL, &all); err != nil { + return nil, err + } + // copy all to device definitions + for _, dv := range all { + gv, err := r.ToAPI(&dv, mfr) + if err != nil { + errList = append(errList, gqlerror.Wrap(err)) + continue + } + deviceDefinitions = append(deviceDefinitions, gv) + } + } + + if len(errList) > 0 { + return deviceDefinitions, errList + } + + return deviceDefinitions, nil +} + func idToCursor(id string) string { return base64.StdEncoding.EncodeToString([]byte(id)) } @@ -333,3 +403,24 @@ func cursorToID(cursor string) (string, error) { return string(b), nil } + +// filterDuplicates removes duplicate strings from a slice while preserving order +func filterDuplicates(ids []string) []string { + if len(ids) <= 1 { + return ids + } + + seen := make(map[string]struct{}) + + uniqueIDs := make([]string, 0, len(ids)) + + for _, id := range ids { + // If we haven't seen this ID before, add it to our result + if _, exists := seen[id]; !exists { + seen[id] = struct{}{} // Use empty struct{} as value to minimize memory usage + uniqueIDs = append(uniqueIDs, id) + } + } + + return uniqueIDs +} diff --git a/internal/repositories/devicedefinition/devicedefinition_test.go b/internal/repositories/devicedefinition/devicedefinition_test.go index 9c48156d..fc85d80d 100644 --- a/internal/repositories/devicedefinition/devicedefinition_test.go +++ b/internal/repositories/devicedefinition/devicedefinition_test.go @@ -215,3 +215,116 @@ func Test_GetDeviceDefinition_Query(t *testing.T) { assert.Equal(t, "Toyota", res.Manufacturer.Name) assert.Equal(t, 137, res.Manufacturer.TokenID) } + +func Test_GetDeviceDefinitionsByIDs_Query(t *testing.T) { + ctx := context.Background() + + pdb, _ := helpers.StartContainerDatabase(ctx, t, migrationsDir) + + mfr := models.Manufacturer{ + ID: 137, + Name: "Alfa Romeo", + Owner: common.FromHex("0xaba3A41bd932244Dd08186e4c19F1a7E48cbcDf4"), + Slug: "alfa-romeo", + TableID: null.IntFrom(1), + } + err := mfr.Insert(ctx, pdb.DBS().Writer, boil.Infer()) + assert.NoError(t, err) + mfr2 := models.Manufacturer{ + ID: 13, + Name: "BMW", + Owner: common.FromHex("0xaba3A41bd932244Dd08186e4c19F1a7E48cbcDf4"), + Slug: "bmw", + TableID: null.IntFrom(2), + } + err = mfr2.Insert(ctx, pdb.DBS().Writer, boil.Infer()) + assert.NoError(t, err) + + logger := zerolog.Nop() + + const baseURL = "http://local" + + repo := base.NewRepository(pdb, config.Settings{DIMORegistryChainID: 30001, TablelandAPIGateway: baseURL}, &logger) + + tablelandAPI := services.NewTablelandApiService(&logger, &config.Settings{ + TablelandAPIGateway: baseURL, + }) + + adController := New(repo, tablelandAPI) + + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + // Mock the query for alfa-romeo manufacturer (table _30001_1) + queryURLAlfaRomeo := "api/v1/query?statement=SELECT+%2A+FROM+%22_30001_1%22+WHERE+%28%22id%22+IN+%28%27alfa-romeo_147_2007%27%29%29" + respQueryBodyAlfaRomeo := `[ + { + "id": "alfa-romeo_147_2007", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "26G3iFH7Xc9Wvsw7pg6sD7uzoSS", + "metadata": { + "device_attributes": [ + { + "name": "powertrain_type", + "value": "ICE" + } + ] + } + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLAlfaRomeo, httpmock.NewStringResponder(200, respQueryBodyAlfaRomeo)) + + // Mock the query for bmw manufacturer (table _30001_2) + queryURLBMW := "api/v1/query?statement=SELECT+%2A+FROM+%22_30001_2%22+WHERE+%28%22id%22+IN+%28%27bmw_x5_2019%27%29%29" + respQueryBodyBMW := `[ + { + "id": "bmw_x5_2019", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "12G3iFH7Xc9Wvsw7pg6sD7uzoKK", + "metadata": "" + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLBMW, httpmock.NewStringResponder(200, respQueryBodyBMW)) + + res, err := adController.GetDeviceDefinitionsByIDs(ctx, []string{"alfa-romeo_147_2007", "bmw_x5_2019"}) + require.NoError(t, err) + + assert.Len(t, res, 2) + assert.Equal(t, res[0].DeviceDefinitionID, "alfa-romeo_147_2007") + assert.Equal(t, res[1].DeviceDefinitionID, "bmw_x5_2019") + + deviceType := "vehicle" + legacyID1 := "26G3iFH7Xc9Wvsw7pg6sD7uzoSS" + legacyID2 := "12G3iFH7Xc9Wvsw7pg6sD7uzoKK" + + expected := []*model.DeviceDefinition{ + { + DeviceDefinitionID: "alfa-romeo_147_2007", + LegacyID: &legacyID1, + DeviceType: &deviceType, + Manufacturer: &model.Manufacturer{ + Name: "Alfa Romeo", + TokenID: 137, + }, + }, + { + DeviceDefinitionID: "bmw_x5_2019", + LegacyID: &legacyID2, + DeviceType: &deviceType, + Manufacturer: &model.Manufacturer{ + Name: "BMW", + TokenID: 13, + }, + }, + } + + for i, e := range expected { + assert.Equal(t, e.DeviceDefinitionID, res[i].DeviceDefinitionID) + assert.Equal(t, e.LegacyID, res[i].LegacyID) + assert.Equal(t, e.DeviceType, res[i].DeviceType) + assert.Equal(t, e.Manufacturer.Name, res[i].Manufacturer.Name) + assert.Equal(t, e.Manufacturer.TokenID, res[i].Manufacturer.TokenID) + } +} diff --git a/internal/repositories/vehicle/owned_vehicles_test.go b/internal/repositories/vehicle/owned_vehicles_test.go index 3f488846..31711f9a 100644 --- a/internal/repositories/vehicle/owned_vehicles_test.go +++ b/internal/repositories/vehicle/owned_vehicles_test.go @@ -11,6 +11,7 @@ import ( "github.com/DIMO-Network/identity-api/internal/config" "github.com/DIMO-Network/identity-api/internal/helpers" "github.com/DIMO-Network/identity-api/internal/repositories/base" + "github.com/DIMO-Network/identity-api/internal/repositories/devicedefinition" "github.com/DIMO-Network/identity-api/models" "github.com/DIMO-Network/mnemonic" "github.com/DIMO-Network/shared/pkg/db" @@ -46,7 +47,11 @@ func (s *OwnedVehiclesRepoTestSuite) SetupSuite() { BaseVehicleDataURI: "https://dimoData/vehicles/", } logger := zerolog.Nop() - s.repo = New(base.NewRepository(s.pdb, s.settings, &logger)) + baseRepo := base.NewRepository(s.pdb, s.settings, &logger) + + // Create a mock device definition repository for the vehicle repository + mockDeviceDefRepo := &devicedefinition.Repository{} + s.repo = New(baseRepo, mockDeviceDefRepo) } // TearDownTest after each test truncate tables @@ -365,7 +370,11 @@ func Test_GetOwnedVehicles_Filters(t *testing.T) { pdb, _ := helpers.StartContainerDatabase(ctx, t, migrationsDir) logger := zerolog.Nop() - repo := New(base.NewRepository(pdb, config.Settings{}, &logger)) + baseRepo := base.NewRepository(pdb, config.Settings{}, &logger) + + // Create a mock device definition repository for the vehicle repository + mockDeviceDefRepo := &devicedefinition.Repository{} + repo := New(baseRepo, mockDeviceDefRepo) _, walletA, err := helpers.GenerateWallet() assert.NoError(err) _, walletB, err := helpers.GenerateWallet() @@ -501,3 +510,53 @@ func Test_GetOwnedVehicles_Filters(t *testing.T) { } } } + +func (s *OwnedVehiclesRepoTestSuite) Test_Debug_Database_Fields() { + _, wallet, err := helpers.GenerateWallet() + s.NoError(err) + + m := models.Manufacturer{ + ID: 131, + Name: "Toyota", + Owner: common.FromHex("0x46a3A41bd932244Dd08186e4c19F1a7E48cbcDf4"), + MintedAt: time.Now(), + Slug: "toyota", + } + + if err := m.Insert(s.ctx, s.pdb.DBS().Writer, boil.Infer()); err != nil { + s.NoError(err) + } + + currTime := time.Now().UTC().Truncate(time.Second) + vehicle := models.Vehicle{ + ID: 1, + ManufacturerID: 131, + OwnerAddress: wallet.Bytes(), + Make: null.StringFrom("Toyota"), + Model: null.StringFrom("Camry"), + Year: null.IntFrom(2022), + MintedAt: currTime, + } + + if err := vehicle.Insert(s.ctx, s.pdb.DBS().Writer, boil.Infer()); err != nil { + s.NoError(err) + } + + // Query the vehicle back from the database + retrievedVehicle, err := models.FindVehicle(s.ctx, s.pdb.DBS().Reader, 1) + s.NoError(err) + + // Debug: Print the actual values + s.T().Logf("Retrieved vehicle - Make: %v, Model: %v, Year: %v", + retrievedVehicle.Make.String, retrievedVehicle.Model.String, retrievedVehicle.Year.Int) + + // Test the ToAPI method directly + imageURI := "https://mockUrl.com/v1/vehicle/1/image" + dataURI := "https://dimoData/vehicles/1" + + result, err := s.repo.ToAPI(retrievedVehicle, imageURI, dataURI, nil) + s.NoError(err) + + s.T().Logf("ToAPI result - Make: %v, Model: %v, Year: %v", + *result.Definition.Make, *result.Definition.Model, *result.Definition.Year) +} diff --git a/internal/repositories/vehicle/vehicles.go b/internal/repositories/vehicle/vehicles.go index ba0bccde..f9407087 100644 --- a/internal/repositories/vehicle/vehicles.go +++ b/internal/repositories/vehicle/vehicles.go @@ -17,6 +17,7 @@ import ( "github.com/DIMO-Network/identity-api/internal/helpers" "github.com/DIMO-Network/identity-api/internal/repositories" "github.com/DIMO-Network/identity-api/internal/repositories/base" + "github.com/DIMO-Network/identity-api/internal/repositories/devicedefinition" "github.com/DIMO-Network/identity-api/models" "github.com/DIMO-Network/mnemonic" "github.com/aarondl/null/v8" @@ -32,18 +33,20 @@ type Repository struct { *base.Repository chainID uint64 contractAddress common.Address + definitionsRepo *devicedefinition.Repository } // New creates a new vehicle repository. -func New(db *base.Repository) *Repository { +func New(db *base.Repository, definitionsRepo *devicedefinition.Repository) *Repository { return &Repository{ Repository: db, chainID: uint64(db.Settings.DIMORegistryChainID), contractAddress: common.HexToAddress(db.Settings.VehicleNFTAddr), + definitionsRepo: definitionsRepo, } } -func (r *Repository) createVehiclesResponse(totalCount int64, vehicles models.VehicleSlice, hasNext bool, hasPrevious bool) (*gmodel.VehicleConnection, error) { +func (r *Repository) createVehiclesResponse(totalCount int64, vehicles models.VehicleSlice, hasNext bool, hasPrevious bool, definitions map[string]*gmodel.DeviceDefinition) (*gmodel.VehicleConnection, error) { var errList gqlerror.List var endCur, startCur *string if len(vehicles) != 0 { @@ -78,7 +81,7 @@ func (r *Repository) createVehiclesResponse(totalCount int64, vehicles models.Ve errList = append(errList, gqlerror.Wrap(wErr)) continue } - gv, err := r.ToAPI(dv, imageURI, dataURI) + gv, err := r.ToAPI(dv, imageURI, dataURI, definitions[dv.DeviceDefinitionID.String]) if err != nil { wErr := fmt.Errorf("error converting vehicle to API: %w", err) errList = append(errList, gqlerror.Wrap(wErr)) @@ -179,6 +182,26 @@ func (r *Repository) GetVehicles(ctx context.Context, first *int, after *string, return nil, err } + // populate a map of device definitions by id for fast lookup later + definitions := make(map[string]*gmodel.DeviceDefinition) + for _, veh := range all { + // Only add to map if DeviceDefinitionID is not null/empty + if veh.DeviceDefinitionID.Valid && veh.DeviceDefinitionID.String != "" { + definitions[veh.DeviceDefinitionID.String] = nil // Will be populated if found + } + } + defIds := make([]string, 0, len(definitions)) + for id := range definitions { + defIds = append(defIds, id) + } + dds, err := r.definitionsRepo.GetDeviceDefinitionsByIDs(ctx, defIds) + if err != nil { + return nil, err + } + for _, dd := range dds { + definitions[dd.DeviceDefinitionID] = dd + } + // We assume that cursors come from real elements. hasNext := before != nil hasPrevious := after != nil @@ -195,7 +218,7 @@ func (r *Repository) GetVehicles(ctx context.Context, first *int, after *string, slices.Reverse(all) } - return r.createVehiclesResponse(totalCount, all, hasNext, hasPrevious) + return r.createVehiclesResponse(totalCount, all, hasNext, hasPrevious, definitions) } func (r *Repository) GetVehicle(ctx context.Context, tokenID *int, tokenDID *string) (*gmodel.Vehicle, error) { @@ -244,7 +267,7 @@ func (r *Repository) GetVehicle(ctx context.Context, tokenID *int, tokenDID *str return nil, fmt.Errorf("error getting vehicle data uri: %w", err) } - return r.ToAPI(v, imageURI, dataURI) + return r.ToAPI(v, imageURI, dataURI, nil) } // queryModsFromFilters returns a slice of query mods from the given filters. @@ -309,7 +332,7 @@ func (r *Repository) queryModsFromFilters(filter *gmodel.VehiclesFilter) ([]qm.Q } // ToAPI converts a vehicle to a corresponding graphql model. -func (r *Repository) ToAPI(v *models.Vehicle, imageURI string, dataURI string) (*gmodel.Vehicle, error) { +func (r *Repository) ToAPI(v *models.Vehicle, imageURI string, dataURI string, definition *gmodel.DeviceDefinition) (*gmodel.Vehicle, error) { nameList := mnemonic.FromInt32WithObfuscation(int32(v.ID)) name := strings.Join(nameList, " ") @@ -342,6 +365,13 @@ func (r *Repository) ToAPI(v *models.Vehicle, imageURI string, dataURI string) ( Image: imageURI, DataURI: dataURI, } + if definition != nil { + if definition.Manufacturer != nil { + out.Definition.Make = &definition.Manufacturer.Name + } + out.Definition.Model = &definition.Model + out.Definition.Year = &definition.Year + } if v.StorageNodeID.Valid { out.StorageNodeID = v.StorageNodeID.Bytes diff --git a/internal/repositories/vehicle/vehicles_test.go b/internal/repositories/vehicle/vehicles_test.go index eb5e68af..6255aff2 100644 --- a/internal/repositories/vehicle/vehicles_test.go +++ b/internal/repositories/vehicle/vehicles_test.go @@ -9,16 +9,21 @@ import ( "testing" "time" + "net/http" + gmodel "github.com/DIMO-Network/identity-api/graph/model" "github.com/DIMO-Network/identity-api/internal/config" test "github.com/DIMO-Network/identity-api/internal/helpers" "github.com/DIMO-Network/identity-api/internal/repositories/base" + "github.com/DIMO-Network/identity-api/internal/repositories/devicedefinition" + "github.com/DIMO-Network/identity-api/internal/services" "github.com/DIMO-Network/identity-api/models" "github.com/DIMO-Network/mnemonic" "github.com/DIMO-Network/shared/pkg/db" "github.com/aarondl/null/v8" "github.com/aarondl/sqlboiler/v4/boil" "github.com/ethereum/go-ethereum/common" + "github.com/jarcoal/httpmock" "github.com/rs/zerolog" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -32,8 +37,8 @@ var ( highlander = null.StringFrom("Highlander") rav4 = null.StringFrom("RAV4") corolla = null.StringFrom("Corolla") - civic = null.StringFrom("civic") - accord = null.StringFrom("accord") + civic = null.StringFrom("Civic") + accord = null.StringFrom("Accord") year2018 = null.IntFrom(2018) year2020 = null.IntFrom(2020) year2022 = null.IntFrom(2022) @@ -59,9 +64,15 @@ func (o *AccessibleVehiclesRepoTestSuite) SetupSuite() { VehicleNFTAddr: "0x45fbCD3ef7361d156e8b16F5538AE36DEdf61Da8", BaseImageURL: "https://mockUrl.com/v1", BaseVehicleDataURI: "https://dimoData/vehicles/", + TablelandAPIGateway: "http://local/", } logger := zerolog.Nop() - o.repo = New(base.NewRepository(o.pdb, o.settings, &logger)) + baseRepo := base.NewRepository(o.pdb, o.settings, &logger) + + // Create a real device definition repository with HTTP mocks + tablelandAPI := services.NewTablelandApiService(&logger, &o.settings) + deviceDefRepo := devicedefinition.New(baseRepo, tablelandAPI) + o.repo = New(baseRepo, deviceDefRepo) } // TearDownTest after each test truncate tables @@ -1112,6 +1123,7 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { Owner: common.FromHex("0x46a3A41bd932244Dd08186e4c19F1a7E48cbcDf4"), MintedAt: time.Now(), Slug: "toyota", + TableID: null.IntFrom(1), } hondaMfr := models.Manufacturer{ @@ -1120,18 +1132,223 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { Owner: common.FromHex("0x46a3A41bd932244Dd08186e4c19F1a7E48cbcDff"), MintedAt: time.Now(), Slug: "honda", + TableID: null.IntFrom(2), + } + + nissanMfr := models.Manufacturer{ + ID: 49, + Name: "Nissan", + Owner: common.FromHex("0x46a3A41bd932244Dd08186e4c19F1a7E48cbcDf5"), + MintedAt: time.Now(), + Slug: "nissan", + TableID: null.IntFrom(3), + } + + cadillacMfr := models.Manufacturer{ + ID: 50, + Name: "Cadillac", + Owner: common.FromHex("0x46a3A41bd932244Dd08186e4c19F1a7E48cbcDf6"), + MintedAt: time.Now(), + Slug: "cadillac", + TableID: null.IntFrom(4), + } + + mazdaMfr := models.Manufacturer{ + ID: 51, + Name: "Mazda", + Owner: common.FromHex("0x46a3A41bd932244Dd08186e4c19F1a7E48cbcDf7"), + MintedAt: time.Now(), + Slug: "mazda", + TableID: null.IntFrom(5), } currTime := time.Now().UTC().Truncate(time.Second) - mfrs := []models.Manufacturer{toyotaMfr, hondaMfr} + mfrs := []models.Manufacturer{toyotaMfr, hondaMfr, nissanMfr, cadillacMfr, mazdaMfr} for _, v := range mfrs { if err := v.Insert(o.ctx, o.pdb.DBS().Writer, boil.Infer()); err != nil { o.Require().NoError(err) } } - testVehicle1 := models.Vehicle{ + // Set up HTTP mocks for device definition calls + httpmock.Activate() + defer httpmock.DeactivateAndReset() + + const baseURL = "http://local/" + + // mock all the possible tableland sql lite queries for all the test cases. + + queryURLNissan := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_3%22+WHERE+%28%22id%22+IN+%28%27nissan_gt-r_2020%27%29%29" + respQueryBodyNissan := `[ + { + "id": "nissan_gt-r_2020", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "26G3iFH7Xc9Wvsw7pg6sD7uzoSS", + "model": "GT R", + "year": 2020, + "metadata": { + "device_attributes": [ + { + "name": "powertrain_type", + "value": "ICE" + } + ] + } + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLNissan, httpmock.NewStringResponder(200, respQueryBodyNissan)) + + // Mock the query for toyota manufacturer + queryURLToyota := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_1%22+WHERE+%28%22id%22+IN+%28%27toyota_camry_2020%27%2C+%27toyota_rav4_2022%27%29%29" + respQueryBodyToyota := `[ + { + "id": "toyota_rav4_2022", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "26G3iFH7Xc9Wvsw7pg6sD7uzoSS", + "model": "RAV4", + "year": 2022, + "metadata": { + "device_attributes": [ + { + "name": "powertrain_type", + "value": "ICE" + } + ] + } + }, + { + "id": "toyota_camry_2020", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "26G3iFH7Xc9Wvsw7pg6sD7uzoSS", + "model": "Camry", + "year": 2020, + "metadata": { + "device_attributes": [ + { + "name": "powertrain_type", + "value": "ICE" + } + ] + } + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLToyota, httpmock.NewStringResponder(200, respQueryBodyToyota)) + + queryURLToyotaRav4 := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_1%22+WHERE+%28%22id%22+IN+%28%27toyota_rav4_2022%27%29%29" + respQueryBodyToyotaRav4 := `[ + { + "id": "toyota_rav4_2022", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "26G3iFH7Xc9Wvsw7pg6sD7uzoSS", + "model": "RAV4", + "year": 2022, + "metadata": { + "device_attributes": [ + { + "name": "powertrain_type", + "value": "ICE" + } + ] + } + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLToyotaRav4, httpmock.NewStringResponder(200, respQueryBodyToyotaRav4)) + + queryURLToyotaCamry := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_1%22+WHERE+%28%22id%22+IN+%28%27toyota_camry_2020%27%29%29" + respQueryBodyToyotaCamry := `[ + { + "id": "toyota_camry_2020", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "26G3iFH7Xc9Wvsw7pg6sD7uzoSS", + "model": "Camry", + "year": 2020, + "metadata": { + "device_attributes": [ + { + "name": "powertrain_type", + "value": "ICE" + } + ] + } + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLToyotaCamry, httpmock.NewStringResponder(200, respQueryBodyToyotaCamry)) + + // Mock the query for cadillac manufacturer (table _30001_4) + queryURLCadillac := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_4%22+WHERE+%28%22id%22+IN+%28%27cadillac_ats-v-coupe_2019%27%29%29" + respQueryBodyCadillac := `[ + { + "id": "cadillac_ats-v-coupe_2019", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "12G3iFH7Xc9Wvsw7pg6sD7uzoKK", + "metadata": "", + "model": "ATS V Coupe", + "year": 2019 + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLCadillac, httpmock.NewStringResponder(200, respQueryBodyCadillac)) + + // Mock the query for mazda manufacturer (table _30001_5) + queryURLMazda := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_5%22+WHERE+%28%22id%22+IN+%28%27mazda_cx-5_2023%27%29%29" + respQueryBodyMazda := `[ + { + "id": "mazda_cx-5_2023", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "34G3iFH7Xc9Wvsw7pg6sD7uzoLL", + "metadata": "", + "model": "CX 5", + "year": 2023 + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLMazda, httpmock.NewStringResponder(200, respQueryBodyMazda)) + + // Mock the query for honda manufacturer + queryURLHonda := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_2%22+WHERE+%28%22id%22+IN+%28%27honda_accord_2020%27%2C+%27honda_civic_2022%27%29%29" + respQueryBodyHonda := `[ + { + "id": "honda_accord_2020", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "34G3iFH7Xc9Wvsw7pg6sD7uzoLL", + "metadata": "", + "model": "Accord", + "year": 2020 + }, + { + "id": "honda_civic_2022", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "34G3iFH7Xc9Wvsw7pg6sD7uzoLL", + "metadata": "", + "model": "Civic", + "year": 2022 + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLHonda, httpmock.NewStringResponder(200, respQueryBodyHonda)) + + queryURLHondaCivic := "api/v1/query?statement=SELECT+%2A+FROM+%22_80001_2%22+WHERE+%28%22id%22+IN+%28%27honda_civic_2022%27%29%29" + respQueryBodyHondaCivic := `[ + { + "id": "honda_civic_2022", + "deviceType": "vehicle", + "imageURI": "https://image", + "ksuid": "34G3iFH7Xc9Wvsw7pg6sD7uzoLL", + "metadata": "", + "model": "Civic", + "year": 2022 + } + ]` + httpmock.RegisterResponder(http.MethodGet, baseURL+queryURLHondaCivic, httpmock.NewStringResponder(200, respQueryBodyHondaCivic)) + + toyotaCamryTkID1 := models.Vehicle{ ID: 1, ManufacturerID: 131, OwnerAddress: wallet1.Bytes(), @@ -1139,16 +1356,16 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { Model: camry, Year: year2020, MintedAt: currTime, - DeviceDefinitionID: null.StringFrom("nissan_gt-r_2020"), + DeviceDefinitionID: null.StringFrom("toyota_camry_2020"), } - vehicle1ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, testVehicle1.ID) + vehicle1ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, toyotaCamryTkID1.ID) o.Require().NoError(err) - vehicle1DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, testVehicle1.ID) + vehicle1DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, toyotaCamryTkID1.ID) o.Require().NoError(err) - vehicle1AsAPI, err := o.repo.ToAPI(&testVehicle1, vehicle1ImageURL, vehicle1DataURI) + vehicle1AsAPI, err := o.repo.ToAPI(&toyotaCamryTkID1, vehicle1ImageURL, vehicle1DataURI, nil) o.NoError(err) - testVehicle2 := models.Vehicle{ + hondaCivicTkID2 := models.Vehicle{ ID: 2, OwnerAddress: wallet1.Bytes(), Make: honda, @@ -1156,16 +1373,16 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { ManufacturerID: 48, Year: year2022, MintedAt: currTime, - DeviceDefinitionID: null.StringFrom("cadillac_ats-v-coupe_2019"), + DeviceDefinitionID: null.StringFrom("honda_civic_2022"), } - vehicle2ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, testVehicle2.ID) + vehicle2ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, hondaCivicTkID2.ID) o.Require().NoError(err) - vehicle2DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, testVehicle2.ID) + vehicle2DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, hondaCivicTkID2.ID) o.Require().NoError(err) - vehicle2AsAPI, err := o.repo.ToAPI(&testVehicle2, vehicle2ImageURL, vehicle2DataURI) + vehicle2AsAPI, err := o.repo.ToAPI(&hondaCivicTkID2, vehicle2ImageURL, vehicle2DataURI, nil) o.NoError(err) - testVehicle3 := models.Vehicle{ + toyotaRav4TkID3 := models.Vehicle{ ID: 3, OwnerAddress: wallet2.Bytes(), Make: toyota, @@ -1173,16 +1390,16 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { Model: rav4, Year: year2022, MintedAt: currTime, - DeviceDefinitionID: testVehicle1.DeviceDefinitionID, + DeviceDefinitionID: null.StringFrom("toyota_rav4_2022"), } - vehicle3ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, testVehicle3.ID) + vehicle3ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, toyotaRav4TkID3.ID) o.Require().NoError(err) - vehicle3DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, testVehicle3.ID) + vehicle3DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, toyotaRav4TkID3.ID) o.Require().NoError(err) - vehicle3AsAPI, err := o.repo.ToAPI(&testVehicle3, vehicle3ImageURL, vehicle3DataURI) + vehicle3AsAPI, err := o.repo.ToAPI(&toyotaRav4TkID3, vehicle3ImageURL, vehicle3DataURI, nil) o.NoError(err) - testVehicle4 := models.Vehicle{ + hondaAccordTkID4 := models.Vehicle{ ID: 4, OwnerAddress: wallet2.Bytes(), Make: honda, @@ -1190,16 +1407,16 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { ManufacturerID: 48, Year: year2020, MintedAt: currTime, - DeviceDefinitionID: null.StringFrom("mazda_cx-5_2023"), + DeviceDefinitionID: null.StringFrom("honda_accord_2020"), } - vehicle4ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, testVehicle4.ID) + vehicle4ImageURL, err := DefaultImageURI(o.settings.BaseImageURL, hondaAccordTkID4.ID) o.Require().NoError(err) - vehicle4DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, testVehicle4.ID) + vehicle4DataURI, err := GetVehicleDataURI(o.settings.BaseVehicleDataURI, hondaAccordTkID4.ID) o.Require().NoError(err) - vehicle4AsAPI, err := o.repo.ToAPI(&testVehicle4, vehicle4ImageURL, vehicle4DataURI) + vehicle4AsAPI, err := o.repo.ToAPI(&hondaAccordTkID4, vehicle4ImageURL, vehicle4DataURI, nil) o.Require().NoError(err) - vehicles := []models.Vehicle{testVehicle1, testVehicle2, testVehicle3, testVehicle4} + vehicles := []models.Vehicle{toyotaCamryTkID1, hondaCivicTkID2, toyotaRav4TkID3, hondaAccordTkID4} first := len(vehicles) for _, v := range vehicles { if err := v.Insert(o.ctx, o.pdb.DBS().Writer, boil.Infer()); err != nil { @@ -1209,7 +1426,7 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { privileges := []models.Privilege{ { - TokenID: testVehicle3.ID, + TokenID: toyotaRav4TkID3.ID, PrivilegeID: 1, UserAddress: wallet1.Bytes(), SetAt: currTime, @@ -1383,11 +1600,10 @@ func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehiclesFilters() { { name: "Filter by Device Definition ID", filter: &gmodel.VehiclesFilter{ - DeviceDefinitionID: &testVehicle1.DeviceDefinitionID.String, + DeviceDefinitionID: &toyotaCamryTkID1.DeviceDefinitionID.String, }, results: []*gmodel.VehicleEdge{ {Node: vehicle1AsAPI}, - {Node: vehicle3AsAPI}, }, }, }