diff --git a/cmd/devices-api/api.go b/cmd/devices-api/api.go index cddf65458..3e08e3724 100644 --- a/cmd/devices-api/api.go +++ b/cmd/devices-api/api.go @@ -11,6 +11,8 @@ import ( "syscall" "time" + "github.com/DIMO-Network/devices-api/internal/middleware" + "github.com/DIMO-Network/devices-api/internal/rpc" "github.com/DIMO-Network/devices-api/internal/middleware/metrics" @@ -18,6 +20,8 @@ import ( grpc_ctxtags "github.com/grpc-ecosystem/go-grpc-middleware/tags" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" + "github.com/DIMO-Network/shared/redis" "github.com/DIMO-Network/shared/db" @@ -106,6 +110,7 @@ func startWebAPI(logger zerolog.Logger, settings *config.Settings, pdb db.Store, hardwareTemplateService := autopi.NewHardwareTemplateService(autoPiSvc, pdb.DBS, &logger) autoPi := autopi.NewIntegration(pdb.DBS, ddSvc, autoPiSvc, autoPiTaskService, autoPiIngest, eventService, deviceDefinitionRegistrar, hardwareTemplateService, &logger) macaron := macaron.NewIntegration(pdb.DBS, ddSvc, autoPiIngest, eventService, deviceDefinitionRegistrar, &logger) + userDeviceSvc := services.NewUserDeviceService(ddSvc, logger, pdb.DBS, eventService) openAI := services.NewOpenAI(&logger, *settings) dcnSvc := registry.NewDcnService(settings) @@ -131,7 +136,10 @@ func startWebAPI(logger zerolog.Logger, settings *config.Settings, pdb db.Store, } // controllers - userDeviceController := controllers.NewUserDevicesController(settings, pdb.DBS, &logger, ddSvc, ddIntSvc, eventService, smartcarClient, scTaskSvc, teslaSvc, teslaTaskService, cipher, autoPiSvc, services.NewNHTSAService(), autoPiIngest, deviceDefinitionRegistrar, autoPiTaskService, producer, s3NFTServiceClient, autoPi, redisCache, openAI, usersClient, ddaSvc, natsSvc, wallet, valuationsSvc) + userDeviceController := controllers.NewUserDevicesController(settings, pdb.DBS, &logger, ddSvc, ddIntSvc, eventService, + smartcarClient, scTaskSvc, teslaSvc, teslaTaskService, cipher, autoPiSvc, services.NewNHTSAService(), autoPiIngest, + deviceDefinitionRegistrar, autoPiTaskService, producer, s3NFTServiceClient, autoPi, redisCache, openAI, usersClient, + ddaSvc, natsSvc, wallet, userDeviceSvc, valuationsSvc) geofenceController := controllers.NewGeofencesController(settings, pdb.DBS, &logger, producer, ddSvc) webhooksController := controllers.NewWebhooksController(settings, pdb.DBS, &logger, autoPiSvc, ddIntSvc) documentsController := controllers.NewDocumentsController(settings, &logger, s3ServiceClient, pdb.DBS) @@ -359,7 +367,7 @@ func startWebAPI(logger zerolog.Logger, settings *config.Settings, pdb db.Store, logger.Fatal().Err(err).Msg("Failed to create transaction listener") } - go startGRPCServer(settings, pdb.DBS, hardwareTemplateService, &logger, ddSvc, eventService, iss) + go startGRPCServer(settings, pdb.DBS, hardwareTemplateService, &logger, ddSvc, eventService, iss, userDeviceSvc) // start task consumer for autopi autoPiTaskService.StartConsumer(ctx) @@ -397,6 +405,7 @@ func startGRPCServer( deviceDefSvc services.DeviceDefinitionService, eventService services.EventService, vcIss *issuer.Issuer, + userDeviceSvc services.UserDeviceService, ) { lis, err := net.Listen("tcp", ":"+settings.GRPCPort) if err != nil { @@ -404,16 +413,19 @@ func startGRPCServer( } logger.Info().Msgf("Starting gRPC server on port %s", settings.GRPCPort) + gp := middleware.GRPCPanicker{Logger: logger} server := grpc.NewServer( grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( metrics.GRPCMetricsMiddleware(), grpc_ctxtags.UnaryServerInterceptor(), grpc_prometheus.UnaryServerInterceptor, + recovery.UnaryServerInterceptor(recovery.WithRecoveryHandler(gp.GRPCPanicRecoveryHandler)), )), grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor), ) - pb.RegisterUserDeviceServiceServer(server, rpc.NewUserDeviceService(dbs, settings, hardwareTemplateService, logger, deviceDefSvc, eventService, vcIss)) + pb.RegisterUserDeviceServiceServer(server, rpc.NewUserDeviceRPCService(dbs, settings, hardwareTemplateService, logger, + deviceDefSvc, eventService, vcIss, userDeviceSvc)) pb.RegisterAftermarketDeviceServiceServer(server, rpc.NewAftermarketDeviceService(dbs, logger)) if err := server.Serve(lis); err != nil { diff --git a/cmd/devices-api/generate_events.go b/cmd/devices-api/generate_events.go index 629a3bb66..c164e2066 100644 --- a/cmd/devices-api/generate_events.go +++ b/cmd/devices-api/generate_events.go @@ -4,6 +4,8 @@ import ( "context" "flag" + "github.com/DIMO-Network/shared" + "github.com/DIMO-Network/devices-api/internal/constants" ddgrpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" @@ -90,7 +92,7 @@ func generateEvents(logger zerolog.Logger, pdb db.Store, eventService services.E } err = eventService.Emit( - &services.Event{ + &shared.CloudEvent[any]{ Type: constants.UserDeviceCreationEventType, Subject: device.UserID, Source: "devices-api", @@ -149,7 +151,7 @@ func generateEvents(logger zerolog.Logger, pdb db.Store, eventService services.E } err = eventService.Emit( - &services.Event{ + &shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.create", Subject: scInteg.UserDeviceID, Source: "devices-api", diff --git a/cmd/devices-api/populate_powertrain.go b/cmd/devices-api/populate_powertrain.go index 8fdd4a99f..4a1dc7924 100644 --- a/cmd/devices-api/populate_powertrain.go +++ b/cmd/devices-api/populate_powertrain.go @@ -97,8 +97,8 @@ func populateUSAPowertrain(ctx context.Context, logger *zerolog.Logger, pdb db.S if len(resp.DeviceAttributes) > 0 { // Find device attribute (powertrain_type) for _, item := range resp.DeviceAttributes { - if item.Name == constants.PowerTrainType { - powertrainType := deviceDefSvc.ConvertPowerTrainStringToPowertrain(item.Value) + if item.Name == constants.PowerTrainTypeKey { + powertrainType := services.ConvertPowerTrainStringToPowertrain(item.Value) md.PowertrainType = &powertrainType if err := device.Metadata.Marshal(md); err != nil { return err diff --git a/go.mod b/go.mod index 8675c103b..b7ad08be0 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/DIMO-Network/device-data-api v0.4.23-0.20230718210255-60a2c7be0aa7 github.com/DIMO-Network/device-definitions-api v1.0.16 github.com/DIMO-Network/go-mnemonic v0.0.0-20230406181942-6ddfe6f8c21c - github.com/DIMO-Network/shared v0.10.2 + github.com/DIMO-Network/shared v0.10.4 github.com/DIMO-Network/synthetic-wallet-instance v0.0.0-20230601233541-6a4c8afb27d3 github.com/DIMO-Network/valuations-api v0.2.0 github.com/DIMO-Network/zflogger v1.0.0-beta @@ -25,9 +25,9 @@ require ( github.com/go-ozzo/ozzo-validation/v4 v4.3.0 github.com/gofiber/fiber/v2 v2.48.0 github.com/golang-jwt/jwt/v5 v5.0.0 - github.com/golang/mock v1.6.0 github.com/google/subcommands v1.2.0 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/jarcoal/httpmock v1.1.0 github.com/lib/pq v1.10.8 @@ -48,6 +48,7 @@ require ( github.com/volatiletech/sqlboiler/v4 v4.14.2 github.com/volatiletech/strmangle v0.0.4 go.uber.org/automaxprocs v1.5.1 + go.uber.org/mock v0.2.0 golang.org/x/mod v0.12.0 ) @@ -65,6 +66,7 @@ require ( github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/holiman/uint256 v1.2.2 // indirect github.com/lovoo/goka v1.1.7 // indirect @@ -164,7 +166,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect golang.org/x/crypto v0.12.0 // indirect google.golang.org/grpc v1.57.0 - google.golang.org/protobuf v1.30.0 + google.golang.org/protobuf v1.31.0 ) require ( @@ -194,7 +196,7 @@ require ( github.com/valyala/tcplisten v1.0.0 // indirect github.com/volatiletech/inflect v0.0.1 // indirect github.com/volatiletech/randomize v0.0.1 // indirect - golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/net v0.14.0 // indirect golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect diff --git a/go.sum b/go.sum index 7bdf9187c..92ca963e9 100644 --- a/go.sum +++ b/go.sum @@ -69,14 +69,14 @@ github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1M github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/DIMO-Network/device-data-api v0.4.23-0.20230718210255-60a2c7be0aa7 h1:E+/o2SQ7An3ZPxbr2lMgv0MjKrk+q//PME2SUaB2hlM= github.com/DIMO-Network/device-data-api v0.4.23-0.20230718210255-60a2c7be0aa7/go.mod h1:kVJwYVg1ThTY3DA2AFLz2hIMK9yOpxV2BgZ4Wd7qMxg= -github.com/DIMO-Network/device-definitions-api v1.0.7 h1:20IdqrAdPJ9YtBksgGR/edDe4K6eXBtXrEnSRs/hocQ= -github.com/DIMO-Network/device-definitions-api v1.0.7/go.mod h1:akk+RglaLatrddxwgbTXG0Ly8nd/yfKOE++AgcqlDBw= github.com/DIMO-Network/device-definitions-api v1.0.16 h1:dgltlUbd7tEiDNhHsEJHcb2q4LuZoSZx4KiIOWI5tJg= github.com/DIMO-Network/device-definitions-api v1.0.16/go.mod h1:pyxLiwySelZWMEtO0I8+1LkbSCZR2AiLO/7ttip4l28= github.com/DIMO-Network/go-mnemonic v0.0.0-20230406181942-6ddfe6f8c21c h1:sKuB4i3m/5NpLjO6tKyc2gSqQHmRe02sMP+pQqJNTuQ= github.com/DIMO-Network/go-mnemonic v0.0.0-20230406181942-6ddfe6f8c21c/go.mod h1:K5a/Iqh9iZYdMSh3UE3J+tc4aV1PxHe4Fz0ZEhZMPbg= github.com/DIMO-Network/shared v0.10.2 h1:PtBQaVWYwN0FlYrK63dq2AXz/7Zn6P7727/WfQIJ2CQ= github.com/DIMO-Network/shared v0.10.2/go.mod h1:GnzCY18sFTdOnvXl/1a2/lo71Fj/iusiqeC0hWDWqeA= +github.com/DIMO-Network/shared v0.10.4 h1:jBWTEWHqyXbqjzAabIo5L5/qsMOhXaN9mDCZL2y+QSw= +github.com/DIMO-Network/shared v0.10.4/go.mod h1:IwAA6IKMoZv+8OackfTzJZwjdtk7KpIqyxHZRUNiSkg= github.com/DIMO-Network/synthetic-wallet-instance v0.0.0-20230601233541-6a4c8afb27d3 h1:XMU6rRN57hoh7IYyuVytsIKzDymrbAnnKECFeVf88Hg= github.com/DIMO-Network/synthetic-wallet-instance v0.0.0-20230601233541-6a4c8afb27d3/go.mod h1:+8E6RVHd04ewjrSOohmLf82Grt0kfSaVTQlCnHCTLZU= github.com/DIMO-Network/taskq/v3 v3.2.9-0.20220518233332-179b5552605f h1:pHPu50/uAiENAiUTQkdThxr7GxzVFwO9STl/jzSaKXg= @@ -97,6 +97,7 @@ github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4Oe github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/hcsshim v0.10.0-rc.8 h1:YSZVvlIIDD1UxQpJp0h+dnpLUw+TrY0cx8obKsp3bek= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= @@ -244,9 +245,12 @@ github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5b github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= github.com/cockroachdb/pebble v0.0.0-20230209160836-829675f94811 h1:ytcWPaNPhNoGMWEhDvS3zToKcDpRsLuRolQJBVGdozk= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd v1.7.3 h1:cKwYKkP1eTj54bP3wCdXXBymmKRQMrWjkLSWZZJDa8o= github.com/containerd/containerd v1.7.3/go.mod h1:32FOM4/O0RkNg7AjQj3hDzN9cUGtu+HMvaKUNiqCZB8= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= @@ -282,6 +286,8 @@ github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3sm github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -345,6 +351,8 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -462,6 +470,7 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -482,6 +491,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -504,6 +514,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0 h1:2cz5kSrxzMYHiWOBbKj8itQm+nRykkB8aMv4ThcHYHA= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -668,6 +680,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -682,11 +695,16 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -822,6 +840,7 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= @@ -854,6 +873,7 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -909,6 +929,7 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= @@ -929,6 +950,8 @@ github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23n github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -937,10 +960,12 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -953,6 +978,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/mock v0.2.0 h1:TaP3xedm7JaAgScZO7tlvlKrqT0p7I6OsdGB5YNSMDU= +go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= @@ -989,8 +1016,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= -golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1502,8 +1529,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1524,9 +1551,11 @@ gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mN gopkg.in/jcmturner/gokrb5.v7 v7.2.3/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/gokrb5.v7 v7.4.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM= gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU= gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c= gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal/appmetrics/counters.go b/internal/appmetrics/counters.go index 490de3e32..619081962 100644 --- a/internal/appmetrics/counters.go +++ b/internal/appmetrics/counters.go @@ -38,6 +38,13 @@ var ( []string{"method", "status"}, ) + GRPCPanicCount = promauto.NewCounter( + prometheus.CounterOpts{ + Name: "devices_api_grpc_panic_count", + Help: "The total number of panics served by the GRPC Server", + }, + ) + GRPCResponseTime = promauto.NewHistogramVec( prometheus.HistogramOpts{ Name: "devices_api_grpc_response_time", diff --git a/internal/constants/consts.go b/internal/constants/consts.go index a0db1f53f..436272dd7 100644 --- a/internal/constants/consts.go +++ b/internal/constants/consts.go @@ -3,7 +3,7 @@ package constants const UserDeviceCreationEventType = "com.dimo.zone.device.create" const ( - PowerTrainType = "powertrain_type" + PowerTrainTypeKey = "powertrain_type" ) const ( @@ -44,7 +44,3 @@ const ( func (r AutoPiSubStatusEnum) String() string { return string(r) } - -const ( - ValuationTopic = "table.device.valuation" -) diff --git a/internal/controllers/device_data_controller_test.go b/internal/controllers/device_data_controller_test.go index 243027312..133ace6d1 100644 --- a/internal/controllers/device_data_controller_test.go +++ b/internal/controllers/device_data_controller_test.go @@ -20,7 +20,6 @@ import ( "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/devices-api/models" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" "github.com/rs/zerolog" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" @@ -28,6 +27,7 @@ import ( "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" "github.com/volatiletech/sqlboiler/v4/queries/qm" + "go.uber.org/mock/gomock" ) const migrationsDirRelPath = "../../migrations" @@ -62,7 +62,7 @@ func TestUserDevicesController_calculateRange(t *testing.T) { DeviceAttributes: attrs, }, nil) - _ = NewUserDevicesController(&config.Settings{Port: "3000"}, nil, &logger, deviceDefSvc, nil, &fakeEventService{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) + _ = NewUserDevicesController(&config.Settings{Port: "3000"}, nil, &logger, deviceDefSvc, nil, &fakeEventService{}, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) rge, err := calculateRange(ctx, deviceDefSvc, ddID, styleID, .7) require.NoError(t, err) require.NotNil(t, rge) @@ -147,7 +147,7 @@ func TestUserDevicesController_GetUserDeviceStatus(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, mockDeps.deviceDataSvc, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, mockDeps.deviceDataSvc, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Get("/user/devices/:userDeviceID/status", test.AuthInjectorTestHandler(testUserID), c.GetUserDeviceStatus) @@ -200,7 +200,7 @@ func TestUserDevicesController_QueryDeviceErrorCodes(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes", test.AuthInjectorTestHandler(testUserID), c.QueryDeviceErrorCodes) @@ -274,7 +274,7 @@ func TestUserDevicesController_ShouldErrorOnTooManyErrorCodes(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes", test.AuthInjectorTestHandler(testUserID), c.QueryDeviceErrorCodes) @@ -347,7 +347,7 @@ func TestUserDevicesController_ShouldErrorInvalidErrorCodes(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes", test.AuthInjectorTestHandler(testUserID), c.QueryDeviceErrorCodes) @@ -416,7 +416,7 @@ func TestUserDevicesController_ShouldErrorOnEmptyErrorCodes(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes", test.AuthInjectorTestHandler(testUserID), c.QueryDeviceErrorCodes) @@ -485,7 +485,7 @@ func TestUserDevicesController_ShouldStoreErrorCodeResponse(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes", test.AuthInjectorTestHandler(testUserID), c.QueryDeviceErrorCodes) @@ -570,7 +570,7 @@ func TestUserDevicesController_GetUserDevicesErrorCodeQueries(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Get("/user/devices/:userDeviceID/error-codes", test.AuthInjectorTestHandler(testUserID), c.GetUserDeviceErrorCodeQueries) @@ -646,7 +646,7 @@ func TestUserDevicesController_ClearUserDeviceErrorCodeQuery(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes/clear", test.AuthInjectorTestHandler(testUserID), c.ClearUserDeviceErrorCodeQuery) @@ -733,7 +733,7 @@ func TestUserDevicesController_ErrorOnAllErrorCodesCleared(t *testing.T) { }() testUserID := "123123" - c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, mockDeps.valuationsSvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, pdb.DBS, &mockDeps.logger, mockDeps.deviceDefSvc, mockDeps.deviceDefIntSvc, &fakeEventService{}, mockDeps.scClient, mockDeps.scTaskSvc, mockDeps.teslaSvc, mockDeps.teslaTaskService, nil, nil, mockDeps.nhtsaService, mockDeps.autoPiIngest, mockDeps.deviceDefinitionIngest, mockDeps.autoPiTaskSvc, nil, nil, nil, nil, mockDeps.openAISvc, nil, nil, nil, nil, nil, mockDeps.valuationsSvc) app := fiber.New() app.Post("/user/devices/:userDeviceID/error-codes/clear", test.AuthInjectorTestHandler(testUserID), c.ClearUserDeviceErrorCodeQuery) diff --git a/internal/controllers/devices_controller_test.go b/internal/controllers/devices_controller_test.go index 9f80ca81a..719389aad 100644 --- a/internal/controllers/devices_controller_test.go +++ b/internal/controllers/devices_controller_test.go @@ -11,11 +11,11 @@ import ( mock_services "github.com/DIMO-Network/devices-api/internal/services/mocks" "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/shared/db" - "github.com/golang/mock/gomock" _ "github.com/lib/pq" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/testcontainers/testcontainers-go" + "go.uber.org/mock/gomock" ) type DevicesControllerTestSuite struct { diff --git a/internal/controllers/geofences_controller_test.go b/internal/controllers/geofences_controller_test.go index 822c537f2..b676330a3 100644 --- a/internal/controllers/geofences_controller_test.go +++ b/internal/controllers/geofences_controller_test.go @@ -18,7 +18,6 @@ import ( "github.com/Shopify/sarama" saramamocks "github.com/Shopify/sarama/mocks" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" "github.com/rs/zerolog" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" @@ -26,6 +25,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/testcontainers/testcontainers-go" "github.com/tidwall/gjson" + "go.uber.org/mock/gomock" ) type partialFenceCloudEvent struct { diff --git a/internal/controllers/nft_controller_test.go b/internal/controllers/nft_controller_test.go index 1f92f18ac..2fb6d94eb 100644 --- a/internal/controllers/nft_controller_test.go +++ b/internal/controllers/nft_controller_test.go @@ -14,13 +14,13 @@ import ( "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/devices-api/models" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" + "go.uber.org/mock/gomock" ) func TestNFTController_GetDcnNFTMetadata(t *testing.T) { diff --git a/internal/controllers/synthetic_devices_controller_test.go b/internal/controllers/synthetic_devices_controller_test.go index 23f500aa9..3bf20aa81 100644 --- a/internal/controllers/synthetic_devices_controller_test.go +++ b/internal/controllers/synthetic_devices_controller_test.go @@ -22,13 +22,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" "github.com/pkg/errors" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/testcontainers/testcontainers-go" "github.com/volatiletech/sqlboiler/v4/types" + "go.uber.org/mock/gomock" ) var signature = "0xa4438e5cb667dc63ebd694167ae3ad83585f2834c9b04895dd890f805c4c459a024ed9df1b03872536b4ac0c7720d02cb787884a093cfcde5c3bd7f94657e30c1b" diff --git a/internal/controllers/user_devices_controller.go b/internal/controllers/user_devices_controller.go index cc58b19b2..bf97c5c1f 100644 --- a/internal/controllers/user_devices_controller.go +++ b/internal/controllers/user_devices_controller.go @@ -82,6 +82,7 @@ type UserDevicesController struct { deviceDataSvc services.DeviceDataService NATSSvc *services.NATSService wallet services.SyntheticWalletInstanceService + userDeviceSvc services.UserDeviceService valuationsAPISrv services.ValuationsAPIService } @@ -138,6 +139,7 @@ func NewUserDevicesController(settings *config.Settings, deviceDataSvc services.DeviceDataService, natsSvc *services.NATSService, wallet services.SyntheticWalletInstanceService, + userDeviceSvc services.UserDeviceService, valuationsAPISrv services.ValuationsAPIService, ) UserDevicesController { return UserDevicesController{ @@ -167,6 +169,7 @@ func NewUserDevicesController(settings *config.Settings, NATSSvc: natsSvc, wallet: wallet, valuationsAPISrv: valuationsAPISrv, + userDeviceSvc: userDeviceSvc, } } @@ -517,9 +520,9 @@ func (udc *UserDevicesController) RegisterDeviceForUser(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, err.Error()) } - udFull, err := udc.createUserDevice(c.Context(), *reg.DeviceDefinitionID, reg.CountryCode, userID, nil, nil) + udFull, err := udc.createUserDevice(c.Context(), *reg.DeviceDefinitionID, "", reg.CountryCode, userID, nil, nil) if err != nil { - return err + return helpers.GrpcErrorToFiber(err, "") } return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "userDevice": udFull, @@ -598,31 +601,8 @@ func (udc *UserDevicesController) RegisterDeviceForUserFromVIN(c *fiber.Ctx) err return fiber.NewError(fiber.StatusFailedDependency, "unable to decode vin") } deviceDefinitionID = decodeVIN.DeviceDefinitionId - // attach device def to user - var udMd *services.UserDeviceMetadata - if reg.CANProtocol != "" { - udMd = &services.UserDeviceMetadata{CANProtocol: ®.CANProtocol} - } - // Validate if it has device style id - if len(decodeVIN.DeviceStyleId) > 0 { - ds, err := udc.DeviceDefSvc.GetDeviceStyleByID(c.Context(), decodeVIN.DeviceStyleId) - if err != nil { - return errors.Wrapf(err, "failed to get device style %s", decodeVIN.DeviceStyleId) - } - - if len(ds.DeviceAttributes) > 0 { - // Find device attribute (powertrain_type) - for _, item := range ds.DeviceAttributes { - if item.Name == constants.PowerTrainType { - powertrainType := udc.DeviceDefSvc.ConvertPowerTrainStringToPowertrain(item.Value) - udMd.PowertrainType = &powertrainType - break - } - } - } - } - udFull, err = udc.createUserDevice(c.Context(), deviceDefinitionID, reg.CountryCode, userID, &vin, udMd) + udFull, err = udc.createUserDevice(c.Context(), deviceDefinitionID, decodeVIN.DeviceStyleId, reg.CountryCode, userID, &vin, ®.CANProtocol) if err != nil { return err } @@ -803,28 +783,8 @@ func (udc *UserDevicesController) RegisterDeviceForUserFromSmartcar(c *fiber.Ctx return errors.Wrap(err, "unable to attach smartcar integration to device definition id") } - // Validate if it has device style id - var udMd *services.UserDeviceMetadata - if len(decodeVIN.DeviceStyleId) > 0 { - ds, err := udc.DeviceDefSvc.GetDeviceStyleByID(c.Context(), decodeVIN.DeviceStyleId) - if err != nil { - return errors.Wrapf(err, "failed to get device style %s", decodeVIN.DeviceStyleId) - } - - if len(ds.DeviceAttributes) > 0 { - // Find device attribute (powertrain_type) - for _, item := range ds.DeviceAttributes { - if item.Name == constants.PowerTrainType { - powertrainType := udc.DeviceDefSvc.ConvertPowerTrainStringToPowertrain(item.Value) - udMd.PowertrainType = &powertrainType - break - } - } - } - } - // attach device def to user - udFull, err := udc.createUserDevice(c.Context(), decodeVIN.DeviceDefinitionId, reg.CountryCode, userID, &vin, udMd) + udFull, err := udc.createUserDevice(c.Context(), decodeVIN.DeviceDefinitionId, decodeVIN.DeviceStyleId, reg.CountryCode, userID, &vin, nil) if err != nil { return err } @@ -861,65 +821,14 @@ func buildSmartcarTokenKey(vin, userID string) string { return fmt.Sprintf("sc-temp-tok-%s-%s", vin, userID) } -func (udc *UserDevicesController) createUserDevice(ctx context.Context, deviceDefID, countryCode, userID string, vin *string, metadata *services.UserDeviceMetadata) (*UserDeviceFull, error) { - // attach device def to user - dd, err2 := udc.DeviceDefSvc.GetDeviceDefinitionByID(ctx, deviceDefID) - if err2 != nil { - return nil, helpers.GrpcErrorToFiber(err2, fmt.Sprintf("error querying for device definition id: %s ", deviceDefID)) - } - - tx, err := udc.DBS().Writer.DB.BeginTx(ctx, nil) - defer tx.Rollback() //nolint +// todo move this to be used directly +func (udc *UserDevicesController) createUserDevice(ctx context.Context, deviceDefID, styleID, countryCode, userID string, vin, canProtocol *string) (*UserDeviceFull, error) { + ud, dd, err := udc.userDeviceSvc.CreateUserDevice(ctx, deviceDefID, styleID, countryCode, userID, vin, canProtocol) if err != nil { return nil, err } - userDeviceID := ksuid.New().String() - // register device for the user - ud := models.UserDevice{ - ID: userDeviceID, - UserID: userID, - DeviceDefinitionID: dd.DeviceDefinitionId, - CountryCode: null.StringFrom(countryCode), - VinIdentifier: null.StringFromPtr(vin), - } - if metadata != nil { - err = ud.Metadata.Marshal(metadata) - if err != nil { - udc.log.Warn().Str("func", "createUserDevice").Msg("failed to marshal user device metadata on create") - } - } - err = ud.Insert(ctx, tx, boil.Infer()) - if err != nil { - return nil, fiber.NewError(fiber.StatusInternalServerError, "could not create user device for def_id: "+dd.DeviceDefinitionId) - } - - err = tx.Commit() // commmit the transaction - if err != nil { - return nil, errors.Wrapf(err, "error commiting transaction to create geofence") - } - - // todo call devide definitions to check and pull image for this device in case don't have one - err = udc.eventService.Emit(&services.Event{ - Type: constants.UserDeviceCreationEventType, - Subject: userID, - Source: "devices-api", - Data: services.UserDeviceEvent{ - Timestamp: time.Now(), - UserID: userID, - Device: services.UserDeviceEventDevice{ - ID: userDeviceID, - Make: dd.Make.Name, - Model: dd.Type.Model, - Year: int(dd.Type.Year), // Odd. - }, - }, - }) - if err != nil { - udc.log.Err(err).Msg("Failed emitting device creation event") - } - - return builUserDeviceFull(&ud, dd, countryCode) + return builUserDeviceFull(ud, dd, countryCode) } func builUserDeviceFull(ud *models.UserDevice, dd *ddgrpc.GetDeviceDefinitionItemResponse, countryCode string) (*UserDeviceFull, error) { @@ -932,6 +841,9 @@ func builUserDeviceFull(ud *models.UserDevice, dd *ddgrpc.GetDeviceDefinitionIte ddNice.CompatibleIntegrations[i].Country = countryCode } + udMd := &services.UserDeviceMetadata{} + _ = ud.Metadata.Unmarshal(udMd) + return &UserDeviceFull{ ID: ud.ID, VIN: ud.VinIdentifier.Ptr(), @@ -940,6 +852,7 @@ func builUserDeviceFull(ud *models.UserDevice, dd *ddgrpc.GetDeviceDefinitionIte CustomImageURL: ud.CustomImageURL.Ptr(), DeviceDefinition: ddNice, CountryCode: ud.CountryCode.Ptr(), + Metadata: *udMd, Integrations: nil, // userDevice just created, there would never be any integrations setup }, nil } @@ -1100,9 +1013,9 @@ func (udc *UserDevicesController) UpdateVIN(c *fiber.Ctx) error { return err } - // TODO: Genericize this for more countries. + // this is kinda funky, ideally we set it based on device styleId we have for the VIN. But not sure if nhtsa is more precise for Hybrid variants? if userDevice.CountryCode.Valid && userDevice.CountryCode.String == "USA" { - if err := udc.updateUSAPowertrain(c.Context(), userDevice, false); err != nil { + if err := udc.updateUSAPowertrain(c.Context(), userDevice, true); err != nil { logger.Err(err).Msg("Failed to update American powertrain type.") } } @@ -1110,12 +1023,12 @@ func (udc *UserDevicesController) UpdateVIN(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusNoContent) } -func (udc *UserDevicesController) updateUSAPowertrain(ctx context.Context, userDevice *models.UserDevice, useNHTSA bool) error { - const ( - PowerTrainType = "powertrain_type" - ) +const ( + PowerTrainTypeKey = "powertrain_type" +) - // todo grpc pull vin decoder via grpc from device definitions +// todo revisit this depending on what observe with below log message +func (udc *UserDevicesController) updateUSAPowertrain(ctx context.Context, userDevice *models.UserDevice, useNHTSA bool) error { md := new(services.UserDeviceMetadata) if err := userDevice.Metadata.Unmarshal(md); err != nil { return err @@ -1131,7 +1044,10 @@ func (udc *UserDevicesController) updateUSAPowertrain(ctx context.Context, userD if err != nil { return err } - + if md.PowertrainType != nil && !strings.EqualFold(md.PowertrainType.String(), dt.String()) { + udc.log.Info().Str("user_device_id", userDevice.ID). + Msgf("NHTSA decoder returned different powertrain_type. original: %s, new: %s", md.PowertrainType.String(), dt.String()) + } md.PowertrainType = &dt } @@ -1144,8 +1060,8 @@ func (udc *UserDevicesController) updateUSAPowertrain(ctx context.Context, userD if len(resp.DeviceAttributes) > 0 { // Find device attribute (powertrain_type) for _, item := range resp.DeviceAttributes { - if item.Name == PowerTrainType { - powertrainType := udc.DeviceDefSvc.ConvertPowerTrainStringToPowertrain(item.Value) + if item.Name == PowerTrainTypeKey { + powertrainType := services.ConvertPowerTrainStringToPowertrain(item.Value) md.PowertrainType = &powertrainType break } @@ -1498,7 +1414,7 @@ func (udc *UserDevicesController) DeleteUserDevice(c *fiber.Ctx) error { return err } - err = udc.eventService.Emit(&services.Event{ + err = udc.eventService.Emit(&shared.CloudEvent[any]{ Type: "com.dimo.zone.device.delete", Subject: userID, Source: "devices-api", diff --git a/internal/controllers/user_devices_controller_test.go b/internal/controllers/user_devices_controller_test.go index 3c77a789f..0d4a7a492 100644 --- a/internal/controllers/user_devices_controller_test.go +++ b/internal/controllers/user_devices_controller_test.go @@ -33,7 +33,6 @@ import ( "github.com/ethereum/go-ethereum/common/math" signer "github.com/ethereum/go-ethereum/signer/core/apitypes" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" _ "github.com/lib/pq" "github.com/segmentio/ksuid" smartcar "github.com/smartcar/go-sdk" @@ -44,13 +43,14 @@ import ( "github.com/tidwall/gjson" "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" + "go.uber.org/mock/gomock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) type fakeEventService struct{} -func (f *fakeEventService) Emit(event *services.Event) error { +func (f *fakeEventService) Emit(event *shared.CloudEvent[any]) error { fmt.Printf("Emitting %v\n", event) return nil } @@ -74,6 +74,7 @@ type UserDevicesControllerTestSuite struct { usersClient *mock_services.MockUserServiceClient natsService *services.NATSService natsServer *server.Server + userDeviceSvc *mock_services.MockUserDeviceService valuationsSrvc *mock_services.MockValuationsAPIService } @@ -102,41 +103,16 @@ func (s *UserDevicesControllerTestSuite) SetupSuite() { s.autoPiSvc = mock_services.NewMockAutoPiAPIService(mockCtrl) s.usersClient = mock_services.NewMockUserServiceClient(mockCtrl) s.natsService, s.natsServer, err = mock_services.NewMockNATSService(natsStreamName) + s.userDeviceSvc = mock_services.NewMockUserDeviceService(mockCtrl) + s.valuationsSrvc = mock_services.NewMockValuationsAPIService(mockCtrl) if err != nil { s.T().Fatal(err) } - s.valuationsSrvc = mock_services.NewMockValuationsAPIService(mockCtrl) - s.testUserID = "123123" testUserID2 := "3232451" - c := NewUserDevicesController( - &config.Settings{Port: "3000", Environment: "prod"}, - s.pdb.DBS, - logger, - s.deviceDefSvc, - s.deviceDefIntSvc, - &fakeEventService{}, - s.scClient, - s.scTaskSvc, - teslaSvc, - teslaTaskService, - new(shared.ROT13Cipher), - s.autoPiSvc, - s.nhtsaService, - autoPiIngest, - deviceDefinitionIngest, - autoPiTaskSvc, - nil, - nil, - nil, - s.redisClient, - nil, - s.usersClient, - nil, - s.natsService, - nil, - s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: "prod"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, teslaSvc, teslaTaskService, new(shared.ROT13Cipher), s.autoPiSvc, + s.nhtsaService, autoPiIngest, deviceDefinitionIngest, autoPiTaskSvc, nil, nil, nil, s.redisClient, nil, s.usersClient, nil, s.natsService, nil, s.userDeviceSvc, s.valuationsSrvc) app := test.SetupAppFiber(*logger) app.Post("/user/devices", test.AuthInjectorTestHandler(s.testUserID), c.RegisterDeviceForUser) app.Post("/user/devices/fromvin", test.AuthInjectorTestHandler(s.testUserID), c.RegisterDeviceForUserFromVIN) @@ -184,7 +160,7 @@ func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromSmartcar() { integration := test.BuildIntegrationGRPC(constants.AutoPiVendor, 10, 0) dd := test.BuildDeviceDefinitionGRPC(ksuid.New().String(), "Ford", "F150", 2020, integration) // act request - const vinny = "4T3R6RFVXMU023395" + vinny := "4T3R6RFVXMU023395" reg := RegisterUserDeviceSmartcar{Code: "XX", RedirectURI: "https://mobile-app", CountryCode: "USA"} j, _ := json.Marshal(reg) @@ -208,7 +184,18 @@ func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromSmartcar() { s.deviceDefIntSvc.EXPECT().CreateDeviceDefinitionIntegration(gomock.Any(), "22N2xaPOq2WW2gAHBHd0Ikn4Zob", dd[0].DeviceDefinitionId, "Americas").Times(1). Return(nil, nil) s.redisClient.EXPECT().Set(gomock.Any(), buildSmartcarTokenKey(vinny, testUserID), gomock.Any(), time.Hour*2).Return(nil) - s.deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), dd[0].DeviceDefinitionId).Times(1).Return(dd[0], nil) + //s.deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), dd[0].DeviceDefinitionId).Times(1).Return(dd[0], nil) + s.userDeviceSvc.EXPECT().CreateUserDevice(gomock.Any(), dd[0].DeviceDefinitionId, "", "USA", testUserID, &vinny, nil). + Return(&models.UserDevice{ + ID: ksuid.New().String(), + UserID: testUserID, + DeviceDefinitionID: dd[0].DeviceDefinitionId, + VinIdentifier: null.StringFrom(vinny), + CountryCode: null.StringFrom("USA"), + VinConfirmed: true, + Metadata: null.JSONFrom([]byte(`{ "powertrainType": "ICE" }`)), + }, dd[0], nil) + request := test.BuildRequest("POST", "/user/devices/fromsmartcar", string(j)) response, responseError := s.app.Test(request, 10000) fmt.Println(responseError) @@ -229,12 +216,7 @@ func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromSmartcar() { assert.Equal(s.T(), integration.Vendor, regUserResp.DeviceDefinition.CompatibleIntegrations[0].Vendor) assert.Equal(s.T(), integration.Type, regUserResp.DeviceDefinition.CompatibleIntegrations[0].Type) assert.Equal(s.T(), integration.Id, regUserResp.DeviceDefinition.CompatibleIntegrations[0].ID) - - userDevice, err := models.UserDevices().One(s.ctx, s.pdb.DBS().Reader) - require.NoError(s.T(), err) - assert.NotNilf(s.T(), userDevice, "expected a user device in the database to exist") - assert.Equal(s.T(), s.testUserID, userDevice.UserID) - assert.Equal(s.T(), vinny, userDevice.VinIdentifier.String) + assert.Equal(s.T(), &vinny, regUserResp.VIN) } func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromSmartcar_Fail_Decode() { @@ -337,9 +319,10 @@ func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromVIN() { integration := test.BuildIntegrationGRPC(constants.AutoPiVendor, 10, 0) dd := test.BuildDeviceDefinitionGRPC(ksuid.New().String(), "Ford", "F150", 2020, integration) // act request - const vinny = "4T3R6RFVXMU023395" const deviceStyleID = "24GE7Mlc4c9o4j5P4mcD1Fzinx1" - reg := RegisterUserDeviceVIN{VIN: vinny, CountryCode: "USA", CANProtocol: "06"} + vinny := "4T3R6RFVXMU023395" + canProtocol := "06" + reg := RegisterUserDeviceVIN{VIN: vinny, CountryCode: "USA", CANProtocol: canProtocol} j, _ := json.Marshal(reg) s.deviceDefSvc.EXPECT().DecodeVIN(gomock.Any(), vinny, "", 0, reg.CountryCode).Times(1).Return(&grpc.DecodeVinResponse{ @@ -348,15 +331,21 @@ func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromVIN() { DeviceStyleId: deviceStyleID, Year: dd[0].Type.Year, }, nil) - s.deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), dd[0].DeviceDefinitionId).Times(1).Return(dd[0], nil) - s.deviceDefSvc.EXPECT().GetDeviceStyleByID(gomock.Any(), deviceStyleID).Times(1).Return(&grpc.DeviceStyle{ - Id: deviceStyleID, - DeviceAttributes: dd[0].DeviceAttributes, - }, nil) - s.deviceDefSvc.EXPECT().ConvertPowerTrainStringToPowertrain(gomock.Any()).Times(1).Return(services.BEV) + apInteg := test.BuildIntegrationGRPC(constants.AutoPiVendor, 10, 10) s.deviceDefIntSvc.EXPECT().GetAutoPiIntegration(gomock.Any()).Times(1).Return(apInteg, nil) s.deviceDefIntSvc.EXPECT().CreateDeviceDefinitionIntegration(gomock.Any(), apInteg.Id, dd[0].DeviceDefinitionId, "Americas") + s.userDeviceSvc.EXPECT().CreateUserDevice(gomock.Any(), dd[0].DeviceDefinitionId, deviceStyleID, "USA", s.testUserID, &vinny, &canProtocol).Times(1). + Return(&models.UserDevice{ + ID: ksuid.New().String(), + UserID: s.testUserID, + DeviceDefinitionID: dd[0].DeviceDefinitionId, + VinIdentifier: null.StringFrom(vinny), + CountryCode: null.StringFrom("USA"), + VinConfirmed: true, + Metadata: null.JSONFrom([]byte(`{ "powertrainType": "ICE", "canProtocol": "6" }`)), + DeviceStyleID: null.StringFrom(deviceStyleID), + }, dd[0], nil) request := test.BuildRequest("POST", "/user/devices/fromvin", string(j)) response, responseError := s.app.Test(request, 10000) @@ -378,18 +367,17 @@ func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromVIN() { assert.Equal(s.T(), integration.Vendor, regUserResp.DeviceDefinition.CompatibleIntegrations[0].Vendor) assert.Equal(s.T(), integration.Type, regUserResp.DeviceDefinition.CompatibleIntegrations[0].Type) assert.Equal(s.T(), integration.Id, regUserResp.DeviceDefinition.CompatibleIntegrations[0].ID) + assert.Equal(s.T(), "USA", *regUserResp.CountryCode) + assert.Equal(s.T(), vinny, *regUserResp.VIN) + assert.Equal(s.T(), true, regUserResp.VINConfirmed) + require.NotNil(s.T(), regUserResp.Metadata.CANProtocol) + assert.Equal(s.T(), "6", *regUserResp.Metadata.CANProtocol) + assert.EqualValues(s.T(), "ICE", *regUserResp.Metadata.PowertrainType) msg, responseError := s.natsService.JetStream.GetLastMsg(natsStreamName, s.natsService.JetStreamSubject) assert.NoError(s.T(), responseError, "expected no error from nats") vinResult := gjson.GetBytes(msg.Data, "vin") assert.Equal(s.T(), vinny, vinResult.Str) - - userDevice, err := models.UserDevices().One(s.ctx, s.pdb.DBS().Reader) - require.NoError(s.T(), err) - assert.NotNilf(s.T(), userDevice, "expected a user device in the database to exist") - assert.Equal(s.T(), s.testUserID, userDevice.UserID) - assert.Equal(s.T(), vinny, userDevice.VinIdentifier.String) - assert.Equal(s.T(), "06", gjson.GetBytes(userDevice.Metadata.JSON, "canProtocol").Str) } func (s *UserDevicesControllerTestSuite) TestPostUserDeviceFromVIN_SameUser_DuplicateVIN() { @@ -457,7 +445,16 @@ func (s *UserDevicesControllerTestSuite) TestPostWithExistingDefinitionID() { } j, _ := json.Marshal(reg) - s.deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), dd[0].DeviceDefinitionId).Times(1).Return(dd[0], nil) + s.userDeviceSvc.EXPECT().CreateUserDevice(gomock.Any(), dd[0].DeviceDefinitionId, "", "USA", s.testUserID, nil, nil).Times(1). + Return(&models.UserDevice{ + ID: ksuid.New().String(), + UserID: testUserID, + DeviceDefinitionID: dd[0].DeviceDefinitionId, + CountryCode: null.StringFrom("USA"), + VinConfirmed: true, + Metadata: null.JSONFrom([]byte(`{ "powertrainType": "ICE" }`)), + }, dd[0], nil) + request := test.BuildRequest("POST", "/user/devices", string(j)) response, responseError := s.app.Test(request) fmt.Println(responseError) @@ -479,12 +476,6 @@ func (s *UserDevicesControllerTestSuite) TestPostWithExistingDefinitionID() { assert.Equal(s.T(), integration.Vendor, regUserResp.DeviceDefinition.CompatibleIntegrations[0].Vendor) assert.Equal(s.T(), integration.Type, regUserResp.DeviceDefinition.CompatibleIntegrations[0].Type) assert.Equal(s.T(), integration.Id, regUserResp.DeviceDefinition.CompatibleIntegrations[0].ID) - - userDevice, err := models.UserDevices().One(s.ctx, s.pdb.DBS().Reader) - require.NoError(s.T(), err) - assert.NotNilf(s.T(), userDevice, "expected a user device in the database to exist") - assert.Equal(s.T(), s.testUserID, userDevice.UserID) - assert.Nil(s.T(), userDevice.VinIdentifier.Ptr()) } func (s *UserDevicesControllerTestSuite) TestPostWithoutDefinitionID_BadRequest() { @@ -519,8 +510,9 @@ func (s *UserDevicesControllerTestSuite) TestPostBadPayload() { func (s *UserDevicesControllerTestSuite) TestPostInvalidDefinitionID() { invalidDD := "caca" - grpcErr := status.Error(codes.NotFound, "dd not found") - s.deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), invalidDD).Times(1).Return(nil, grpcErr) + grpcErr := status.Error(codes.NotFound, "dd not found: "+invalidDD) + s.userDeviceSvc.EXPECT().CreateUserDevice(gomock.Any(), invalidDD, "", "USA", s.testUserID, nil, nil). + Return(nil, nil, grpcErr) reg := RegisterUserDevice{ DeviceDefinitionID: &invalidDD, CountryCode: "USA", @@ -629,31 +621,30 @@ func (s *UserDevicesControllerTestSuite) TestPatchVIN() { integration := test.BuildIntegrationGRPC(constants.AutoPiVendor, 10, 4) dd := test.BuildDeviceDefinitionGRPC(ksuid.New().String(), "Ford", "Escape", 2020, integration) - const powertrainType = "powertrain_type" - powertrainValue := "" - for _, item := range dd[0].DeviceAttributes { - if item.Name == powertrainType { - powertrainValue = item.Value - break - } - } + //const powertrainType = "powertrain_type" + //powertrainValue := "BEV" + //for _, item := range dd[0].DeviceAttributes { + // if item.Name == powertrainType { + // powertrainValue = item.Value + // break + // } + //} - ud := test.SetupCreateUserDevice(s.T(), s.testUserID, dd[0].DeviceDefinitionId, nil, "", s.pdb) + ud := test.SetupCreateUserDevice(s.T(), testUserID, dd[0].DeviceDefinitionId, nil, "", s.pdb) s.deviceDefSvc.EXPECT().GetIntegrations(gomock.Any()).Return([]*grpc.Integration{integration}, nil) s.usersClient.EXPECT().GetUser(gomock.Any(), &pb.GetUserRequest{Id: s.testUserID}).Return(&pb.User{Id: s.testUserID, EthereumAddress: nil}, nil) - //evID := "4" - //s.nhtsaService.EXPECT().DecodeVIN("5YJYGDEE5MF085533").Return(&services.NHTSADecodeVINResponse{ - // Results: []services.NHTSAResult{ - // { - // VariableID: 126, - // ValueID: &evID, - // }, - // }, - //}, nil) + // validates that if country=USA we update the powertrain based on what the NHTSA vin decoder says + evID := "4" + s.nhtsaService.EXPECT().DecodeVIN("5YJYGDEE5MF085533").Return(&services.NHTSADecodeVINResponse{ + Results: []services.NHTSAResult{ + { + VariableID: 126, + ValueID: &evID, + }, + }, + }, nil) - s.deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), dd[0].DeviceDefinitionId).Times(1).Return(dd[0], nil) - s.deviceDefSvc.EXPECT().ConvertPowerTrainStringToPowertrain(powertrainValue).Times(1).Return(services.BEV) payload := `{ "vin": "5YJYGDEE5MF085533" }` request := test.BuildRequest("PATCH", "/user/devices/"+ud.ID+"/vin", payload) response, responseError := s.app.Test(request) @@ -662,11 +653,11 @@ func (s *UserDevicesControllerTestSuite) TestPatchVIN() { body, _ := io.ReadAll(response.Body) fmt.Println("message: " + string(body)) } - - s.deviceDefSvc.EXPECT().GetDeviceDefinitionsByIDs(gomock.Any(), []string{dd[0].DeviceDefinitionId}).Times(1).Return(dd, nil) - + // seperate request to validate info persisted to user_device table + s.deviceDefSvc.EXPECT().GetDeviceDefinitionsByIDs(gomock.Any(), []string{dd[0].DeviceDefinitionId}).Times(1). + Return(dd, nil) request = test.BuildRequest("GET", "/user/devices/me", "") - response, responseError = s.app.Test(request) + response, responseError = s.app.Test(request, 120*1000) require.NoError(s.T(), responseError) body, _ := io.ReadAll(response.Body) diff --git a/internal/controllers/user_integrations_controller.go b/internal/controllers/user_integrations_controller.go index 7ca286a71..095ccc302 100644 --- a/internal/controllers/user_integrations_controller.go +++ b/internal/controllers/user_integrations_controller.go @@ -124,7 +124,7 @@ func (udc *UserDevicesController) deleteDeviceIntegration(ctx context.Context, u return err } - err = udc.eventService.Emit(&services.Event{ + err = udc.eventService.Emit(&shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.delete", Source: "devices-api", Subject: userDeviceID, @@ -1656,7 +1656,7 @@ func (udc *UserDevicesController) runPostRegistration(ctx context.Context, logge } err = udc.eventService.Emit( - &services.Event{ + &shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.create", Source: "devices-api", Subject: userDeviceID, diff --git a/internal/controllers/user_integrations_controller_test.go b/internal/controllers/user_integrations_controller_test.go index 957b898e6..981e0e257 100644 --- a/internal/controllers/user_integrations_controller_test.go +++ b/internal/controllers/user_integrations_controller_test.go @@ -37,7 +37,6 @@ import ( "github.com/DIMO-Network/shared" smock "github.com/Shopify/sarama/mocks" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" "github.com/pkg/errors" "github.com/segmentio/ksuid" smartcar "github.com/smartcar/go-sdk" @@ -49,6 +48,7 @@ import ( "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" "github.com/volatiletech/sqlboiler/v4/types" + "go.uber.org/mock/gomock" ) type UserIntegrationsControllerTestSuite struct { @@ -74,6 +74,7 @@ type UserIntegrationsControllerTestSuite struct { valuationsSrvc *mock_services.MockValuationsAPIService natsSvc *services.NATSService natsServer *server.Server + userDeviceSvc *mock_services.MockUserDeviceService } const testUserID = "123123" @@ -101,6 +102,7 @@ func (s *UserIntegrationsControllerTestSuite) SetupSuite() { s.redisClient = mocks.NewMockCacheService(s.mockCtrl) s.userClient = mock_services.NewMockUserServiceClient(s.mockCtrl) s.natsSvc, s.natsServer, err = mock_services.NewMockNATSService(natsStreamName) + s.userDeviceSvc = mock_services.NewMockUserDeviceService(s.mockCtrl) s.valuationsSrvc = mock_services.NewMockValuationsAPIService(s.mockCtrl) if err != nil { @@ -109,7 +111,7 @@ func (s *UserIntegrationsControllerTestSuite) SetupSuite() { logger := test.Logger() c := NewUserDevicesController(&config.Settings{Port: "3000"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, s.eventSvc, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), s.autopiAPISvc, nil, - s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, s.redisClient, nil, nil, nil, s.natsSvc, nil, s.valuationsSrvc) + s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, s.redisClient, nil, nil, nil, s.natsSvc, nil, s.userDeviceSvc, s.valuationsSrvc) app := test.SetupAppFiber(*logger) @@ -204,7 +206,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPostSmartCar_SuccessNewToken() }, nil) s.eventSvc.EXPECT().Emit(gomock.Any()).Return(nil).Do( - func(event *services.Event) error { + func(event *shared.CloudEvent[any]) error { assert.Equal(s.T(), ud.ID, event.Subject) assert.Equal(s.T(), "com.dimo.zone.device.integration.create", event.Type) @@ -293,7 +295,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPostSmartCar_FailureTestVIN() logger := test.Logger() c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: "prod"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, s.eventSvc, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), s.autopiAPISvc, nil, - s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, s.redisClient, nil, nil, nil, s.natsSvc, nil, s.valuationsSrvc) + s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, s.redisClient, nil, nil, nil, s.natsSvc, nil, s.userDeviceSvc, s.valuationsSrvc) app := test.SetupAppFiber(*logger) @@ -341,7 +343,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPostSmartCar_SuccessCachedToke s.redisClient.EXPECT().Get(gomock.Any(), buildSmartcarTokenKey(vin, testUserID)).Return(redis.NewStringResult(encrypted, nil)) s.redisClient.EXPECT().Del(gomock.Any(), buildSmartcarTokenKey(vin, testUserID)).Return(redis.NewIntResult(1, nil)) s.eventSvc.EXPECT().Emit(gomock.Any()).Return(nil).Do( - func(event *services.Event) error { + func(event *shared.CloudEvent[any]) error { assert.Equal(s.T(), ud.ID, event.Subject) assert.Equal(s.T(), "com.dimo.zone.device.integration.create", event.Type) @@ -418,7 +420,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPostTesla() { oUdai := &models.UserDeviceAPIIntegration{} s.eventSvc.EXPECT().Emit(gomock.Any()).Return(nil).Do( - func(event *services.Event) error { + func(event *shared.CloudEvent[any]) error { assert.Equal(s.T(), ud.ID, event.Subject) assert.Equal(s.T(), "com.dimo.zone.device.integration.create", event.Type) @@ -518,7 +520,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPostAutoPiBlockedForDuplicateD // specific dependency and controller autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) logger := test.Logger() - c := NewUserDevicesController(&config.Settings{Port: "3000"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) app := test.SetupAppFiber(*logger) app.Post("/user/devices/:userDeviceID/integrations/:integrationID", test.AuthInjectorTestHandler(testUserID), c.RegisterDeviceIntegration) // arrange @@ -554,7 +556,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPostAutoPiBlockedForDuplicateD // specific dependency and controller autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) logger := test.Logger() - c := NewUserDevicesController(&config.Settings{Port: "3000"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000"}, s.pdb.DBS, logger, s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, s.autoPiTaskService, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) app := test.SetupAppFiber(*logger) app.Post("/user/devices/:userDeviceID/integrations/:integrationID", test.AuthInjectorTestHandler(testUser2), c.RegisterDeviceIntegration) // arrange @@ -590,7 +592,7 @@ func (s *UserIntegrationsControllerTestSuite) TestGetAutoPiInfoNoUDAI_ShouldUpda const environment = "prod" // shouldUpdate only applies in prod // specific dependency and controller autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) - c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) app := fiber.New() logger := zerolog.Nop() app.Get("/aftermarket/device/by-serial/:serial", test.AuthInjectorTestHandler(testUserID), owner.AftermarketDevice(s.pdb, s.userClient, &logger), c.GetAutoPiUnitInfo) @@ -628,7 +630,7 @@ func (s *UserIntegrationsControllerTestSuite) TestGetAutoPiInfoNoUDAI_UpToDate() const environment = "prod" // shouldUpdate only applies in prod // specific dependency and controller autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) - c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) app := fiber.New() logger := zerolog.Nop() app.Get("/aftermarket/device/by-serial/:serial", test.AuthInjectorTestHandler(testUserID), owner.AftermarketDevice(s.pdb, s.userClient, &logger), c.GetAutoPiUnitInfo) @@ -663,7 +665,7 @@ func (s *UserIntegrationsControllerTestSuite) TestGetAutoPiInfoNoUDAI_FutureUpda const environment = "prod" // shouldUpdate only applies in prod // specific dependency and controller autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) - c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) app := fiber.New() logger := zerolog.Nop() app.Get("/aftermarket/device/by-serial/:serial", test.AuthInjectorTestHandler(testUserID), owner.AftermarketDevice(s.pdb, s.userClient, &logger), c.GetAutoPiUnitInfo) @@ -700,7 +702,7 @@ func (s *UserIntegrationsControllerTestSuite) TestGetAutoPiInfoNoUDAI_ShouldUpda const environment = "prod" // shouldUpdate only applies in prod // specific dependency and controller autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) - c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000", Environment: environment}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, s.valuationsSrvc) app := fiber.New() logger := zerolog.Nop() app.Get("/aftermarket/device/by-serial/:serial", test.AuthInjectorTestHandler(testUserID), owner.AftermarketDevice(s.pdb, s.userClient, &logger), c.GetAutoPiUnitInfo) @@ -748,7 +750,7 @@ func (s *UserIntegrationsControllerTestSuite) TestPairAftermarketNoLegacy() { userAddr := crypto.PubkeyToAddress(privateKey.PublicKey) autopiAPISvc := mock_services.NewMockAutoPiAPIService(s.mockCtrl) - c := NewUserDevicesController(&config.Settings{Port: "3000", DIMORegistryChainID: 1337, DIMORegistryAddr: common.BigToAddress(big.NewInt(7)).Hex()}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, kprod, nil, nil, nil, nil, s.userClient, nil, nil, nil, s.valuationsSrvc) + c := NewUserDevicesController(&config.Settings{Port: "3000", DIMORegistryChainID: 1337, DIMORegistryAddr: common.BigToAddress(big.NewInt(7)).Hex()}, s.pdb.DBS, test.Logger(), s.deviceDefSvc, s.deviceDefIntSvc, &fakeEventService{}, s.scClient, s.scTaskSvc, s.teslaSvc, s.teslaTaskService, new(shared.ROT13Cipher), autopiAPISvc, nil, s.autoPiIngest, s.deviceDefinitionRegistrar, nil, kprod, nil, nil, nil, nil, s.userClient, nil, nil, nil, nil, s.valuationsSrvc) s.deviceDefIntSvc.EXPECT().GetAutoPiIntegration(gomock.Any()).Return(&ddgrpc.Integration{Id: ksuid.New().String()}, nil).AnyTimes() userID := "louxUser" diff --git a/internal/controllers/webhooks_controller_test.go b/internal/controllers/webhooks_controller_test.go index b39795027..ca18afc03 100644 --- a/internal/controllers/webhooks_controller_test.go +++ b/internal/controllers/webhooks_controller_test.go @@ -17,12 +17,12 @@ import ( "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/devices-api/models" "github.com/gofiber/fiber/v2" - "github.com/golang/mock/gomock" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" + "go.uber.org/mock/gomock" ) type WebHooksControllerTestSuite struct { diff --git a/internal/middleware/grpc_panicker.go b/internal/middleware/grpc_panicker.go new file mode 100644 index 000000000..900cbb4fe --- /dev/null +++ b/internal/middleware/grpc_panicker.go @@ -0,0 +1,22 @@ +package middleware + +import ( + "fmt" + "runtime/debug" + + "github.com/DIMO-Network/devices-api/internal/appmetrics" + "github.com/rs/zerolog" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type GRPCPanicker struct { + Logger *zerolog.Logger +} + +func (pr *GRPCPanicker) GRPCPanicRecoveryHandler(p any) (err error) { + appmetrics.GRPCPanicCount.Inc() + + pr.Logger.Err(fmt.Errorf("%s", p)).Str("stack", string(debug.Stack())).Msg("grpc recovered from panic") + return status.Errorf(codes.Internal, "%s", p) +} diff --git a/internal/rpc/user_devices.go b/internal/rpc/user_devices.go index 4b339b82a..86fc8e515 100644 --- a/internal/rpc/user_devices.go +++ b/internal/rpc/user_devices.go @@ -6,15 +6,12 @@ import ( "errors" "math/big" - "fmt" "strings" - "time" "github.com/DIMO-Network/devices-api/internal/config" "github.com/DIMO-Network/devices-api/internal/constants" "github.com/DIMO-Network/devices-api/internal/services" "github.com/ethereum/go-ethereum/common" - "github.com/segmentio/ksuid" "github.com/volatiletech/sqlboiler/v4/boil" "github.com/volatiletech/sqlboiler/v4/queries" "google.golang.org/protobuf/types/known/emptypb" @@ -39,7 +36,7 @@ const ( euroToUsd float64 = 1.10 ) -func NewUserDeviceService( +func NewUserDeviceRPCService( dbs func() *db.ReaderWriter, settings *config.Settings, hardwareTemplateService autopi.HardwareTemplateService, @@ -47,18 +44,21 @@ func NewUserDeviceService( deviceDefSvc services.DeviceDefinitionService, eventService services.EventService, vcIss *issuer.Issuer, + userDeviceService services.UserDeviceService, ) pb.UserDeviceServiceServer { - return &userDeviceService{dbs: dbs, + return &userDeviceRPCServer{dbs: dbs, logger: logger, settings: settings, hardwareTemplateService: hardwareTemplateService, deviceDefSvc: deviceDefSvc, eventService: eventService, vcIss: vcIss, + userDeviceSvc: userDeviceService, } } -type userDeviceService struct { +// userDeviceRPCServer is the grpc server implementation for the proto services +type userDeviceRPCServer struct { pb.UnimplementedUserDeviceServiceServer dbs func() *db.ReaderWriter hardwareTemplateService autopi.HardwareTemplateService @@ -67,9 +67,10 @@ type userDeviceService struct { deviceDefSvc services.DeviceDefinitionService eventService services.EventService vcIss *issuer.Issuer + userDeviceSvc services.UserDeviceService } -func (s *userDeviceService) GetUserDevice(ctx context.Context, req *pb.GetUserDeviceRequest) (*pb.UserDevice, error) { +func (s *userDeviceRPCServer) GetUserDevice(ctx context.Context, req *pb.GetUserDeviceRequest) (*pb.UserDevice, error) { dbDevice, err := models.UserDevices( models.UserDeviceWhere.ID.EQ(req.Id), qm.Load( @@ -95,7 +96,7 @@ func (s *userDeviceService) GetUserDevice(ctx context.Context, req *pb.GetUserDe return s.deviceModelToAPI(dbDevice), nil } -func (s *userDeviceService) GetUserDeviceByTokenId(ctx context.Context, req *pb.GetUserDeviceByTokenIdRequest) (*pb.UserDevice, error) { //nolint +func (s *userDeviceRPCServer) GetUserDeviceByTokenId(ctx context.Context, req *pb.GetUserDeviceByTokenIdRequest) (*pb.UserDevice, error) { //nolint tknID := types.NewNullDecimal(decimal.New(req.TokenId, 0)) @@ -119,7 +120,7 @@ func (s *userDeviceService) GetUserDeviceByTokenId(ctx context.Context, req *pb. return out, nil } -func (s *userDeviceService) ListUserDevicesForUser(ctx context.Context, req *pb.ListUserDevicesForUserRequest) (*pb.ListUserDevicesForUserResponse, error) { +func (s *userDeviceRPCServer) ListUserDevicesForUser(ctx context.Context, req *pb.ListUserDevicesForUserRequest) (*pb.ListUserDevicesForUserResponse, error) { var query []qm.QueryMod if req.UserId == "" { @@ -159,7 +160,7 @@ func (s *userDeviceService) ListUserDevicesForUser(ctx context.Context, req *pb. return &pb.ListUserDevicesForUserResponse{UserDevices: out}, nil } -func (s *userDeviceService) ApplyHardwareTemplate(ctx context.Context, req *pb.ApplyHardwareTemplateRequest) (*pb.ApplyHardwareTemplateResponse, error) { +func (s *userDeviceRPCServer) ApplyHardwareTemplate(ctx context.Context, req *pb.ApplyHardwareTemplateRequest) (*pb.ApplyHardwareTemplateResponse, error) { resp, err := s.hardwareTemplateService.ApplyHardwareTemplate(ctx, req) if err != nil { s.logger.Err(err).Str("autopi_unit_id", req.AutoApiUnitId).Str("user_device_id", req.UserDeviceId).Msgf("failed to apply hardware template id %s", req.HardwareTemplateId) @@ -169,7 +170,7 @@ func (s *userDeviceService) ApplyHardwareTemplate(ctx context.Context, req *pb.A return resp, err } -func (s *userDeviceService) CreateTemplate(_ context.Context, req *pb.CreateTemplateRequest) (*pb.CreateTemplateResponse, error) { +func (s *userDeviceRPCServer) CreateTemplate(_ context.Context, req *pb.CreateTemplateRequest) (*pb.CreateTemplateResponse, error) { resp, err := s.hardwareTemplateService.CreateTemplate(req) if err != nil { s.logger.Err(err).Str("template name", req.Name).Msgf("failed to create template %s", req.Name) @@ -179,7 +180,7 @@ func (s *userDeviceService) CreateTemplate(_ context.Context, req *pb.CreateTemp return resp, err } -func (s *userDeviceService) RegisterUserDeviceFromVIN(ctx context.Context, req *pb.RegisterUserDeviceFromVINRequest) (*pb.RegisterUserDeviceFromVINResponse, error) { +func (s *userDeviceRPCServer) RegisterUserDeviceFromVIN(ctx context.Context, req *pb.RegisterUserDeviceFromVINRequest) (*pb.RegisterUserDeviceFromVINResponse, error) { country := constants.FindCountry(req.CountryCode) if country == nil { return nil, status.Errorf(codes.InvalidArgument, "invalid countryCode field or country not supported: %s", req.CountryCode) @@ -231,55 +232,17 @@ func (s *userDeviceService) RegisterUserDeviceFromVIN(ctx context.Context, req * return nil, err } + // todo this needs to use udc.createUserDevice refactor method. // future refactor: udc controller has a createUserDevice - userDeviceID := ksuid.New().String() - // register device for the user - ud := models.UserDevice{ - ID: userDeviceID, - UserID: req.UserDeviceId, - DeviceDefinitionID: dd.DeviceDefinitionId, - VinIdentifier: null.StringFrom(vin), - CountryCode: null.StringFrom(req.CountryCode), - VinConfirmed: true, - Metadata: null.JSON{}, // todo set powertrain - } - if len(resp.DeviceStyleId) > 0 { - ud.DeviceStyleID = null.StringFrom(resp.DeviceStyleId) - } - err = ud.Insert(ctx, tx, boil.Infer()) - if err != nil { - return nil, status.Error(codes.Internal, fmt.Errorf("could not create user device for def_id: %s", dd.DeviceDefinitionId).Error()) - } - - err = tx.Commit() // commmit the transaction + _, _, err = s.userDeviceSvc.CreateUserDevice(ctx, dd.DeviceDefinitionId, resp.DeviceStyleId, req.CountryCode, req.UserDeviceId, &vin, nil) if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - err = s.eventService.Emit(&services.Event{ - Type: constants.UserDeviceCreationEventType, - Subject: req.UserDeviceId, - Source: "devices-api", - Data: services.UserDeviceEvent{ - Timestamp: time.Now(), - UserID: req.UserDeviceId, - Device: services.UserDeviceEventDevice{ - ID: userDeviceID, - Make: dd.Make.Name, - Model: dd.Type.Model, - Year: int(dd.Type.Year), // Odd. - }, - }, - }) - - if err != nil { - s.logger.Err(err).Msg("Failed emitting device creation event") - } - return &pb.RegisterUserDeviceFromVINResponse{Created: true}, err } -func (s *userDeviceService) UpdateDeviceIntegrationStatus(ctx context.Context, req *pb.UpdateDeviceIntegrationStatusRequest) (*pb.UserDevice, error) { +func (s *userDeviceRPCServer) UpdateDeviceIntegrationStatus(ctx context.Context, req *pb.UpdateDeviceIntegrationStatusRequest) (*pb.UserDevice, error) { logger := s.logger.With().Str("integrationId", req.IntegrationId).Str("userDeviceId", req.UserDeviceId).Logger() apiIntegration, err := models.UserDeviceAPIIntegrations( @@ -307,7 +270,7 @@ func (s *userDeviceService) UpdateDeviceIntegrationStatus(ctx context.Context, r } //nolint:all -func (s *userDeviceService) GetUserDeviceByAutoPIUnitId(ctx context.Context, req *pb.GetUserDeviceByAutoPIUnitIdRequest) (*pb.UserDeviceAutoPIUnitResponse, error) { +func (s *userDeviceRPCServer) GetUserDeviceByAutoPIUnitId(ctx context.Context, req *pb.GetUserDeviceByAutoPIUnitIdRequest) (*pb.UserDeviceAutoPIUnitResponse, error) { dbDevice, err := models.UserDeviceAPIIntegrations( models.UserDeviceAPIIntegrationWhere.Serial.EQ(null.StringFrom(req.Id)), qm.Load(models.UserDeviceAPIIntegrationRels.UserDevice), @@ -333,7 +296,7 @@ func (s *userDeviceService) GetUserDeviceByAutoPIUnitId(ctx context.Context, req return result, nil } -func (s *userDeviceService) GetAllUserDeviceValuation(ctx context.Context, _ *emptypb.Empty) (*pb.ValuationResponse, error) { +func (s *userDeviceRPCServer) GetAllUserDeviceValuation(ctx context.Context, _ *emptypb.Empty) (*pb.ValuationResponse, error) { query := `select sum(evd.retail_price) as total_retail, sum(evd.vincario_price) as total_vincario @@ -405,7 +368,7 @@ func (s *userDeviceService) GetAllUserDeviceValuation(ctx context.Context, _ *em }, nil } -func (s *userDeviceService) GetAllUserDevice(req *pb.GetAllUserDeviceRequest, stream pb.UserDeviceService_GetAllUserDeviceServer) error { +func (s *userDeviceRPCServer) GetAllUserDevice(req *pb.GetAllUserDeviceRequest, stream pb.UserDeviceService_GetAllUserDeviceServer) error { ctx := context.Background() all, err := models.UserDevices( models.UserDeviceWhere.VinConfirmed.EQ(true)). @@ -436,7 +399,7 @@ func (s *userDeviceService) GetAllUserDevice(req *pb.GetAllUserDeviceRequest, st return nil } -func (s *userDeviceService) deviceModelToAPI(ud *models.UserDevice) *pb.UserDevice { +func (s *userDeviceRPCServer) deviceModelToAPI(ud *models.UserDevice) *pb.UserDevice { out := &pb.UserDevice{ Id: ud.ID, UserId: ud.UserID, @@ -511,7 +474,7 @@ func (s *userDeviceService) deviceModelToAPI(ud *models.UserDevice) *pb.UserDevi return out } -func (s *userDeviceService) GetClaimedVehiclesGrowth(ctx context.Context, _ *emptypb.Empty) (*pb.ClaimedVehiclesGrowth, error) { +func (s *userDeviceRPCServer) GetClaimedVehiclesGrowth(ctx context.Context, _ *emptypb.Empty) (*pb.ClaimedVehiclesGrowth, error) { // Checking both that the nft exists and is linked to a device. query := `select count(1) @@ -553,7 +516,7 @@ func (s *userDeviceService) GetClaimedVehiclesGrowth(ctx context.Context, _ *emp // toUint64 takes a nullable decimal and returns nil if there is no value, or // a reference to the uint64 value of the decimal otherwise. If the value does not // fit then we return nil and log. -func (s *userDeviceService) toUint64(dec types.NullDecimal) *uint64 { +func (s *userDeviceRPCServer) toUint64(dec types.NullDecimal) *uint64 { if dec.IsZero() { return nil } @@ -575,7 +538,7 @@ func nullTimeToPB(t null.Time) *timestamppb.Timestamp { return timestamppb.New(t.Time) } -func (s *userDeviceService) IssueVinCredential(ctx context.Context, req *pb.IssueVinCredentialRequest) (*pb.IssueVinCredentialResponse, error) { +func (s *userDeviceRPCServer) IssueVinCredential(ctx context.Context, req *pb.IssueVinCredentialRequest) (*pb.IssueVinCredentialResponse, error) { v, err := models.VehicleNFTS(models.VehicleNFTWhere.TokenID.EQ(types.NewNullDecimal(decimal.New(int64(req.TokenId), 0)))).One(ctx, s.dbs().Reader) if err != nil { if err == sql.ErrNoRows { @@ -598,7 +561,7 @@ func (s *userDeviceService) IssueVinCredential(ctx context.Context, req *pb.Issu }, nil } -func (s *userDeviceService) UpdateUserDeviceMetadata(ctx context.Context, req *pb.UpdateUserDeviceMetadataRequest) (*emptypb.Empty, error) { +func (s *userDeviceRPCServer) UpdateUserDeviceMetadata(ctx context.Context, req *pb.UpdateUserDeviceMetadataRequest) (*emptypb.Empty, error) { ud, err := models.FindUserDevice(ctx, s.dbs().Reader, req.UserDeviceId) if err != nil { return nil, err diff --git a/internal/services/autopi/hardware_template_service_test.go b/internal/services/autopi/hardware_template_service_test.go index 5b7753fda..39ec26986 100644 --- a/internal/services/autopi/hardware_template_service_test.go +++ b/internal/services/autopi/hardware_template_service_test.go @@ -16,11 +16,11 @@ import ( "github.com/DIMO-Network/devices-api/internal/constants" "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/devices-api/models" - "github.com/golang/mock/gomock" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" "github.com/volatiletech/null/v8" + "go.uber.org/mock/gomock" ) type HardwareTemplateServiceTestSuite struct { diff --git a/internal/services/autopi/integration.go b/internal/services/autopi/integration.go index f1562f484..355c35ca4 100644 --- a/internal/services/autopi/integration.go +++ b/internal/services/autopi/integration.go @@ -7,6 +7,8 @@ import ( "strconv" "time" + "github.com/DIMO-Network/shared" + ddgrpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" "github.com/DIMO-Network/devices-api/internal/constants" "github.com/DIMO-Network/devices-api/internal/services" @@ -271,7 +273,7 @@ func (i *Integration) Pair(ctx context.Context, autoPiTokenID, vehicleTokenID *b } _ = i.eventer.Emit( - &services.Event{ + &shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.create", Source: "devices-api", Subject: ud.ID, @@ -377,7 +379,7 @@ func (i *Integration) Unpair(ctx context.Context, autoPiTokenID, vehicleTokenID return err } - _ = i.eventer.Emit(&services.Event{ + _ = i.eventer.Emit(&shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.delete", Source: "devices-api", Subject: ud.ID, diff --git a/internal/services/autopi/integration_test.go b/internal/services/autopi/integration_test.go index 341a92788..834016b90 100644 --- a/internal/services/autopi/integration_test.go +++ b/internal/services/autopi/integration_test.go @@ -22,8 +22,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/testcontainers/testcontainers-go" - "github.com/golang/mock/gomock" "github.com/stretchr/testify/suite" + "go.uber.org/mock/gomock" ) type IntegrationTestSuite struct { diff --git a/internal/services/autopi_api_service_test.go b/internal/services/autopi_api_service_test.go index dd41ec1ad..cde6bc7f8 100644 --- a/internal/services/autopi_api_service_test.go +++ b/internal/services/autopi_api_service_test.go @@ -10,7 +10,6 @@ import ( "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/devices-api/models" "github.com/DIMO-Network/shared/db" - "github.com/golang/mock/gomock" "github.com/jarcoal/httpmock" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" @@ -19,6 +18,7 @@ import ( "github.com/testcontainers/testcontainers-go" "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" + "go.uber.org/mock/gomock" ) const migrationsDirRelPath = "../../migrations" diff --git a/internal/services/device_definitions_service.go b/internal/services/device_definitions_service.go index 2cc87c51f..9809ab19c 100644 --- a/internal/services/device_definitions_service.go +++ b/internal/services/device_definitions_service.go @@ -2,25 +2,15 @@ package services import ( "context" - "database/sql" - "encoding/json" "fmt" "math/big" - "strings" - "time" ddgrpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" - "github.com/DIMO-Network/devices-api/internal/appmetrics" "github.com/DIMO-Network/devices-api/internal/config" - "github.com/DIMO-Network/devices-api/models" "github.com/DIMO-Network/shared/db" "github.com/pkg/errors" "github.com/rs/zerolog" - "github.com/segmentio/ksuid" - "github.com/tidwall/gjson" - "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" - "github.com/volatiletech/sqlboiler/v4/queries/qm" "golang.org/x/exp/slices" "google.golang.org/grpc" "google.golang.org/grpc/codes" @@ -34,8 +24,6 @@ import ( type DeviceDefinitionService interface { FindDeviceDefinitionByMMY(ctx context.Context, mk, model string, year int) (*ddgrpc.GetDeviceDefinitionItemResponse, error) UpdateDeviceDefinitionFromNHTSA(ctx context.Context, deviceDefinitionID string, vin string) error - PullDrivlyData(ctx context.Context, userDeviceID, deviceDefinitionID, vin string) (DataPullStatusEnum, error) - PullVincarioValuation(ctx context.Context, userDeiceID, deviceDefinitionID, vin string) (DataPullStatusEnum, error) GetOrCreateMake(ctx context.Context, tx boil.ContextExecutor, makeName string) (*ddgrpc.DeviceMake, error) GetMakeByTokenID(ctx context.Context, tokenID *big.Int) (*ddgrpc.DeviceMake, error) GetDeviceDefinitionsByIDs(ctx context.Context, ids []string) ([]*ddgrpc.GetDeviceDefinitionItemResponse, error) @@ -47,7 +35,6 @@ type DeviceDefinitionService interface { CreateIntegration(ctx context.Context, integrationType string, vendor string, style string) (*ddgrpc.Integration, error) DecodeVIN(ctx context.Context, vin string, model string, year int, countryCode string) (*ddgrpc.DecodeVinResponse, error) GetIntegrationByTokenID(ctx context.Context, tokenID uint64) (*ddgrpc.Integration, error) - ConvertPowerTrainStringToPowertrain(value string) PowertrainType GetDeviceStyleByID(ctx context.Context, id string) (*ddgrpc.DeviceStyle, error) } @@ -346,58 +333,6 @@ func (d *deviceDefinitionService) UpdateDeviceDefinitionFromNHTSA(ctx context.Co return nil } -func (d *deviceDefinitionService) PullVincarioValuation(ctx context.Context, userDeviceID, deviceDefinitionID, vin string) (DataPullStatusEnum, error) { - const repullWindow = time.Hour * 24 * 14 - if len(vin) != 17 { - return ErrorDataPullStatus, errors.Errorf("invalid VIN %s", vin) - } - - // make sure userdevice exists - ud, err := models.FindUserDevice(ctx, d.dbs().Reader, userDeviceID) - if err != nil { - return ErrorDataPullStatus, err - } - // do not pull for USA - if strings.EqualFold(ud.CountryCode.String, "USA") { - return SkippedDataPullStatus, nil - } - - // check repull window - existingPricingData, _ := models.ExternalVinData( - models.ExternalVinDatumWhere.Vin.EQ(vin), - models.ExternalVinDatumWhere.VincarioMetadata.IsNotNull(), - qm.OrderBy("updated_at desc"), qm.Limit(1)). - One(context.Background(), d.dbs().Writer) - - // just return if already pulled recently for this VIN, but still need to insert never pulled vin - should be uncommon scenario - if existingPricingData != nil && existingPricingData.UpdatedAt.Add(repullWindow).After(time.Now()) { - return SkippedDataPullStatus, nil - } - - externalVinData := &models.ExternalVinDatum{ - ID: ksuid.New().String(), - DeviceDefinitionID: null.StringFrom(deviceDefinitionID), - Vin: vin, - UserDeviceID: null.StringFrom(userDeviceID), - } - - valuation, err := d.vincarioSvc.GetMarketValuation(vin) - - if err != nil { - return ErrorDataPullStatus, errors.Wrap(err, "error pulling market data from vincario") - } - err = externalVinData.VincarioMetadata.Marshal(valuation) - if err != nil { - return ErrorDataPullStatus, errors.Wrap(err, "error marshalling vincario responset") - } - err = externalVinData.Insert(ctx, d.dbs().Writer, boil.Infer()) - if err != nil { - return ErrorDataPullStatus, errors.Wrap(err, "error inserting external_vin_data for vincario") - } - - return PulledValuationVincarioStatus, nil -} - const MilesToKmFactor = 1.609344 // there is 1.609 kilometers in a mile. const should probably be KmToMilesFactor const EstMilesPerYear = 12000.0 @@ -408,159 +343,6 @@ type ValuationRequestData struct { type DataPullStatusEnum string -const ( - // PulledInfoAndValuationStatus means we pulled vin, edmunds, build and valuations - PulledInfoAndValuationStatus DataPullStatusEnum = "PulledAll" - // PulledValuationDrivlyStatus means we only pulled offers and pricing - PulledValuationDrivlyStatus DataPullStatusEnum = "PulledValuations" - PulledValuationVincarioStatus DataPullStatusEnum = "PulledValuationVincario" - SkippedDataPullStatus DataPullStatusEnum = "Skipped" - ErrorDataPullStatus DataPullStatusEnum = "Error" -) - -// PullDrivlyData pulls vin info from drivly, and inserts a record with the data. -// Will only pull if haven't in last 2 weeks. Does not re-pull VIN info, updates DD metadata, sets the device_style_id using the edmunds data pulled. -func (d *deviceDefinitionService) PullDrivlyData(ctx context.Context, userDeviceID, deviceDefinitionID, vin string) (DataPullStatusEnum, error) { - const repullWindow = time.Hour * 24 * 14 - if len(vin) != 17 { - return ErrorDataPullStatus, errors.Errorf("invalid VIN %s", vin) - } - - deviceDef, err := d.GetDeviceDefinitionByID(ctx, deviceDefinitionID) - if err != nil { - return ErrorDataPullStatus, err - } - localLog := d.log.With().Str("vin", vin).Str("deviceDefinitionID", deviceDefinitionID).Logger() - - existingVINData, err := models.ExternalVinData( - models.ExternalVinDatumWhere.Vin.EQ(vin), - models.ExternalVinDatumWhere.VinMetadata.IsNotNull(), - qm.OrderBy("updated_at desc"), qm.Limit(1)). - One(context.Background(), d.dbs().Writer) - - if err != nil { - return ErrorDataPullStatus, err - } - - // make sure userdevice exists - ud, err := models.FindUserDevice(ctx, d.dbs().Reader, userDeviceID) - if err != nil { - return ErrorDataPullStatus, err - } - - // by this point we know we might need to insert drivly raw json data - externalVinData := &models.ExternalVinDatum{ - ID: ksuid.New().String(), - DeviceDefinitionID: null.StringFrom(deviceDef.DeviceDefinitionId), - Vin: vin, - UserDeviceID: null.StringFrom(userDeviceID), - } - - // should probably move this up to top as our check for never pulled, then seperate call to get latest pull date for repullWindow check - if existingVINData != nil && existingVINData.VinMetadata.Valid { - var vinInfo map[string]interface{} - err = existingVINData.VinMetadata.Unmarshal(&vinInfo) - if err != nil { - return ErrorDataPullStatus, errors.Wrap(err, "unable to unmarshal vin metadata") - } - // update the device attributes via gRPC - err2 := d.updateDeviceDefAttrs(ctx, deviceDef, vinInfo) - if err2 != nil { - return ErrorDataPullStatus, err2 - } - } - - // determine if want to pull pricing data - existingPricingData, _ := models.ExternalVinData( - models.ExternalVinDatumWhere.Vin.EQ(vin), - models.ExternalVinDatumWhere.PricingMetadata.IsNotNull(), - qm.OrderBy("updated_at desc"), qm.Limit(1)). - One(context.Background(), d.dbs().Writer) - // just return if already pulled recently for this VIN, but still need to insert never pulled vin - should be uncommon scenario - if existingPricingData != nil && existingPricingData.UpdatedAt.Add(repullWindow).After(time.Now()) { - localLog.Info().Msgf("already pulled pricing data for vin %s, skipping", vin) - return SkippedDataPullStatus, nil - } - - // get mileage for the drivly request - deviceMileage, err := d.getDeviceMileage(userDeviceID, int(deviceDef.Type.Year)) - if err != nil { - return ErrorDataPullStatus, err - } - - reqData := ValuationRequestData{ - Mileage: deviceMileage, - } - - udMD := new(UserDeviceMetadata) - _ = ud.Metadata.Unmarshal(udMD) - - if udMD.PostalCode == nil { - lat, long := d.getDeviceLatLong(userDeviceID) - localLog.Info().Msgf("lat long found: %f, %f", lat, long) - if lat != 0 && long != 0 { - gl, err := GeoDecodeLatLong(lat, long, d.googleMapsAPIKey) - if err != nil { - localLog.Err(err).Msgf("failed to GeoDecode lat long %f, %f", lat, long) - } - if gl != nil { - // update UD, ignore if fails doesn't matter - udMD.PostalCode = &gl.PostalCode - udMD.GeoDecodedCountry = &gl.Country - udMD.GeoDecodedStateProv = &gl.AdminAreaLevel1 - _ = ud.Metadata.Marshal(udMD) - _, err = ud.Update(ctx, d.dbs().Writer, boil.Whitelist(models.UserDeviceColumns.Metadata, models.UserDeviceColumns.UpdatedAt)) - if err != nil { - localLog.Err(err).Msg("failed to update user_device.metadata with geodecode info") - } - localLog.Info().Msgf("GeoDecoded a lat long: %+v", gl) - } - } - } - - if udMD.PostalCode != nil { - reqData.ZipCode = udMD.PostalCode - } - _ = externalVinData.RequestMetadata.Marshal(reqData) - - // only pull offers and pricing on every pull. - offer, err := d.drivlySvc.GetOffersByVIN(vin, &reqData) - if err == nil { - _ = externalVinData.OfferMetadata.Marshal(offer) - } - pricing, err := d.drivlySvc.GetVINPricing(vin, &reqData) - if err == nil { - _ = externalVinData.PricingMetadata.Marshal(pricing) - } - - // check on edmunds data so we can get the style id - edmundsExists, _ := models.ExternalVinData(models.ExternalVinDatumWhere.UserDeviceID.EQ(null.StringFrom(ud.ID)), - models.ExternalVinDatumWhere.EdmundsMetadata.IsNotNull()).Exists(ctx, d.dbs().Reader) - if !edmundsExists { - // extra optional data that only needs to be pulled once. - edmunds, err := d.drivlySvc.GetEdmundsByVIN(vin) // this is source data that will only be available after pulling vin + pricing - if err == nil { - _ = externalVinData.EdmundsMetadata.Marshal(edmunds) - } - // fill in edmunds style_id in our user_device if it exists and not already set. None of these seen as bad errors so just logs - if edmunds != nil && ud.DeviceStyleID.IsZero() { - d.setUserDeviceStyleFromEdmunds(ctx, edmunds, ud) - localLog.Info().Msgf("set device_style_id for ud id %s", ud.ID) - } else { - localLog.Warn().Msgf("could not set edmunds style id. edmunds data exists: %v. ud style_id already set: %v", edmunds != nil, !ud.DeviceStyleID.IsZero()) - } - } - - err = externalVinData.Insert(ctx, d.dbs().Writer, boil.Infer()) - if err != nil { - return ErrorDataPullStatus, err - } - - defer appmetrics.DrivlyIngestTotalOps.Inc() - - return PulledValuationDrivlyStatus, nil -} - func (d *deviceDefinitionService) GetDeviceStyleByID(ctx context.Context, id string) (*ddgrpc.DeviceStyle, error) { definitionsClient, conn, err := d.getDeviceDefsGrpcClient() if err != nil { @@ -579,25 +361,6 @@ func (d *deviceDefinitionService) GetDeviceStyleByID(ctx context.Context, id str return ds, nil } -func (d *deviceDefinitionService) updateDeviceDefAttrs(ctx context.Context, deviceDef *ddgrpc.GetDeviceDefinitionItemResponse, vinInfo map[string]any) error { - deviceAttributes := buildDeviceAttributes(deviceDef.DeviceAttributes, vinInfo) - - definitionsClient, conn, err := d.getDeviceDefsGrpcClient() - if err != nil { - return err - } - defer conn.Close() - - _, err = definitionsClient.UpdateDeviceDefinition(ctx, &ddgrpc.UpdateDeviceDefinitionRequest{ - DeviceDefinitionId: deviceDef.DeviceDefinitionId, - DeviceAttributes: deviceAttributes, - }) - if err != nil { - return err - } - return nil -} - // buildDeviceAttributes returns list of set attributes based on what already exists and vinInfo pulled from drivly. based on a predetermined list func buildDeviceAttributes(existingDeviceAttrs []*ddgrpc.DeviceTypeAttribute, vinInfo map[string]any) []*ddgrpc.DeviceTypeAttributeRequest { // TODO: replace seekAttributes with a better solution based on device_types.attributes @@ -658,41 +421,6 @@ func buildDeviceAttributes(existingDeviceAttrs []*ddgrpc.DeviceTypeAttribute, vi return deviceAttributes } -// setUserDeviceStyleFromEdmunds given edmunds json, sets the device style_id in the user_device per what edmunds says. -// If errors just logs and continues, since non critical -func (d *deviceDefinitionService) setUserDeviceStyleFromEdmunds(ctx context.Context, edmunds map[string]interface{}, ud *models.UserDevice) { - edmundsJSON, err := json.Marshal(edmunds) - if err != nil { - d.log.Err(err).Msg("could not marshal edmunds response to json") - return - } - styleIDResult := gjson.GetBytes(edmundsJSON, "edmundsStyle.data.style.id") - styleID := styleIDResult.String() - if styleIDResult.Exists() && len(styleID) > 0 { - - definitionsClient, conn, err := d.getDeviceDefsGrpcClient() - if err != nil { - return - } - defer conn.Close() - - deviceStyle, err := definitionsClient.GetDeviceStyleByExternalID(ctx, &ddgrpc.GetDeviceStyleByIDRequest{ - Id: styleID, - }) - - if err != nil { - d.log.Err(err).Msgf("unable to find device_style for edmunds style_id %s", styleID) - return - } - ud.DeviceStyleID = null.StringFrom(deviceStyle.Id) // set foreign key - _, err = ud.Update(ctx, d.dbs().Writer, boil.Whitelist("updated_at", "device_style_id")) - if err != nil { - d.log.Err(err).Msgf("unable to update user_device_id %s with styleID %s", ud.ID, deviceStyle.Id) - return - } - } -} - // getDeviceDefsGrpcClient instanties new connection with client to dd service. You must defer conn.close from returned connection func (d *deviceDefinitionService) getDeviceDefsGrpcClient() (ddgrpc.DeviceDefinitionServiceClient, *grpc.ClientConn, error) { conn, err := grpc.Dial(d.definitionsGRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) @@ -713,70 +441,7 @@ func (d *deviceDefinitionService) getVINDecodeGrpcClient() (ddgrpc.VinDecoderSer return client, conn, nil } -func (d *deviceDefinitionService) getDeviceMileage(udID string, modelYear int) (mileage *float64, err error) { - var deviceMileage *float64 - - // Get user device odometer - deviceData, err := models.UserDeviceData( - models.UserDeviceDatumWhere.UserDeviceID.EQ(udID), - models.UserDeviceDatumWhere.Signals.IsNotNull(), - qm.OrderBy("updated_at desc"), - qm.Limit(1)).One(context.Background(), d.dbs().Writer) - if err != nil { - if !errors.Is(err, sql.ErrNoRows) { - return nil, err - } - } else { - deviceOdometer := gjson.GetBytes(deviceData.Signals.JSON, "odometer.value") - if deviceOdometer.Exists() { - deviceMileage = new(float64) - *deviceMileage = deviceOdometer.Float() / MilesToKmFactor - } - } - - // Estimate mileage based on model year - if deviceMileage == nil { - deviceMileage = new(float64) - yearDiff := time.Now().Year() - modelYear - switch { - case yearDiff > 0: - // Past model year - *deviceMileage = float64(yearDiff) * EstMilesPerYear - case yearDiff == 0: - // Current model year - *deviceMileage = EstMilesPerYear / 2 - default: - // Next model year - *deviceMileage = 0 - } - } - - return deviceMileage, nil -} - -func (d *deviceDefinitionService) getDeviceLatLong(userDeviceID string) (lat, long float64) { - deviceData, err := models.UserDeviceData( - models.UserDeviceDatumWhere.UserDeviceID.EQ(userDeviceID), - models.UserDeviceDatumWhere.Signals.IsNotNull(), - qm.OrderBy("updated_at desc"), - qm.Limit(1)).One(context.Background(), d.dbs().Writer) - if err != nil { - if !errors.Is(err, sql.ErrNoRows) { - return - } - } else { - latitude := gjson.GetBytes(deviceData.Signals.JSON, "latitude.value") - longitude := gjson.GetBytes(deviceData.Signals.JSON, "longitude.value") - if latitude.Exists() && longitude.Exists() { - lat = latitude.Float() - long = longitude.Float() - return - } - } - return -} - -func (d *deviceDefinitionService) ConvertPowerTrainStringToPowertrain(value string) PowertrainType { +func ConvertPowerTrainStringToPowertrain(value string) PowertrainType { switch value { case "HEV": return HEV diff --git a/internal/services/event_service.go b/internal/services/event_service.go index b80e6f8ed..58d1bcbab 100644 --- a/internal/services/event_service.go +++ b/internal/services/event_service.go @@ -14,17 +14,17 @@ import ( "github.com/segmentio/ksuid" ) -//go:generate mockgen -source event_service.go -destination mocks/event_service_mock.go +//go:generate mockgen -source event_service.go -destination mocks/event_service_mock.go -package mock_services -type Event struct { - Type string - Subject string - Source string - Data any -} +//type Event struct { +// Type string +// Subject string +// Source string +// Data any +//} type EventService interface { - Emit(event *Event) error + Emit(event *shared.CloudEvent[any]) error } type eventService struct { @@ -41,7 +41,7 @@ func NewEventService(logger *zerolog.Logger, settings *config.Settings, producer } } -func (e *eventService) Emit(event *Event) error { +func (e *eventService) Emit(event *shared.CloudEvent[any]) error { msgBytes, err := json.Marshal(shared.CloudEvent[any]{ ID: ksuid.New().String(), Source: event.Source, diff --git a/internal/services/fingerprint/consumer_test.go b/internal/services/fingerprint/consumer_test.go index 1882ff1aa..541b77619 100644 --- a/internal/services/fingerprint/consumer_test.go +++ b/internal/services/fingerprint/consumer_test.go @@ -18,7 +18,6 @@ import ( "github.com/ericlagergren/decimal" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/golang/mock/gomock" "github.com/rs/zerolog" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" @@ -28,6 +27,7 @@ import ( "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" "github.com/volatiletech/sqlboiler/v4/types" + "go.uber.org/mock/gomock" ) type ConsumerTestSuite struct { diff --git a/internal/services/issuer/verifiable_credential_test.go b/internal/services/issuer/verifiable_credential_test.go index e27c40fef..5a1c9fa70 100644 --- a/internal/services/issuer/verifiable_credential_test.go +++ b/internal/services/issuer/verifiable_credential_test.go @@ -14,7 +14,6 @@ import ( "github.com/DIMO-Network/shared/db" "github.com/ericlagergren/decimal" "github.com/ethereum/go-ethereum/common" - "github.com/golang/mock/gomock" "github.com/rs/zerolog" "github.com/segmentio/ksuid" "github.com/stretchr/testify/assert" @@ -24,6 +23,7 @@ import ( "github.com/volatiletech/null/v8" "github.com/volatiletech/sqlboiler/v4/boil" "github.com/volatiletech/sqlboiler/v4/types" + "go.uber.org/mock/gomock" ) type CredentialTestSuite struct { diff --git a/internal/services/macaron/integration.go b/internal/services/macaron/integration.go index 365462db4..c3f6cb59d 100644 --- a/internal/services/macaron/integration.go +++ b/internal/services/macaron/integration.go @@ -6,6 +6,8 @@ import ( "math/big" "time" + "github.com/DIMO-Network/shared" + "github.com/DIMO-Network/devices-api/internal/constants" "github.com/DIMO-Network/devices-api/internal/services" "github.com/DIMO-Network/devices-api/models" @@ -133,7 +135,7 @@ func (i *Integration) Pair(ctx context.Context, amTokenID, vehicleTokenID *big.I } _ = i.eventer.Emit( - &services.Event{ + &shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.create", Source: "devices-api", Subject: ud.ID, @@ -234,7 +236,7 @@ func (i *Integration) Unpair(ctx context.Context, autoPiTokenID, vehicleTokenID return err } - _ = i.eventer.Emit(&services.Event{ + _ = i.eventer.Emit(&shared.CloudEvent[any]{ Type: "com.dimo.zone.device.integration.delete", Source: "devices-api", Subject: ud.ID, diff --git a/internal/services/mocks/autopi_api_service_mock.go b/internal/services/mocks/autopi_api_service_mock.go index c83dc5cd3..26dff070f 100644 --- a/internal/services/mocks/autopi_api_service_mock.go +++ b/internal/services/mocks/autopi_api_service_mock.go @@ -10,7 +10,7 @@ import ( services "github.com/DIMO-Network/devices-api/internal/services" models "github.com/DIMO-Network/devices-api/models" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockAutoPiAPIService is a mock of AutoPiAPIService interface. diff --git a/internal/services/mocks/autopi_task_service_mock.go b/internal/services/mocks/autopi_task_service_mock.go index 6f079698a..31a07eb4d 100644 --- a/internal/services/mocks/autopi_task_service_mock.go +++ b/internal/services/mocks/autopi_task_service_mock.go @@ -11,7 +11,7 @@ import ( services "github.com/DIMO-Network/devices-api/internal/services" redis "github.com/go-redis/redis/v8" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockAutoPiTaskService is a mock of AutoPiTaskService interface. diff --git a/internal/services/mocks/device_data_service_mock.go b/internal/services/mocks/device_data_service_mock.go index 1658c6bce..a0e9dc8ff 100644 --- a/internal/services/mocks/device_data_service_mock.go +++ b/internal/services/mocks/device_data_service_mock.go @@ -9,7 +9,7 @@ import ( reflect "reflect" grpc "github.com/DIMO-Network/device-data-api/pkg/grpc" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDeviceDataService is a mock of DeviceDataService interface. diff --git a/internal/services/mocks/device_definition_integration_service_mock.go b/internal/services/mocks/device_definition_integration_service_mock.go index 97dde2294..a5d915582 100644 --- a/internal/services/mocks/device_definition_integration_service_mock.go +++ b/internal/services/mocks/device_definition_integration_service_mock.go @@ -11,7 +11,7 @@ import ( grpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" services "github.com/DIMO-Network/devices-api/internal/services" models "github.com/DIMO-Network/devices-api/models" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" boil "github.com/volatiletech/sqlboiler/v4/boil" ) diff --git a/internal/services/mocks/device_definition_registrar_mock.go b/internal/services/mocks/device_definition_registrar_mock.go index 94219c560..dd0d0da69 100644 --- a/internal/services/mocks/device_definition_registrar_mock.go +++ b/internal/services/mocks/device_definition_registrar_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDeviceDefinitionRegistrar is a mock of DeviceDefinitionRegistrar interface. diff --git a/internal/services/mocks/device_definitions_service_mock.go b/internal/services/mocks/device_definitions_service_mock.go index 96f984c16..764bd2f4a 100644 --- a/internal/services/mocks/device_definitions_service_mock.go +++ b/internal/services/mocks/device_definitions_service_mock.go @@ -10,8 +10,7 @@ import ( reflect "reflect" grpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" - services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" boil "github.com/volatiletech/sqlboiler/v4/boil" ) @@ -38,20 +37,6 @@ func (m *MockDeviceDefinitionService) EXPECT() *MockDeviceDefinitionServiceMockR return m.recorder } -// ConvertPowerTrainStringToPowertrain mocks base method. -func (m *MockDeviceDefinitionService) ConvertPowerTrainStringToPowertrain(value string) services.PowertrainType { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ConvertPowerTrainStringToPowertrain", value) - ret0, _ := ret[0].(services.PowertrainType) - return ret0 -} - -// ConvertPowerTrainStringToPowertrain indicates an expected call of ConvertPowerTrainStringToPowertrain. -func (mr *MockDeviceDefinitionServiceMockRecorder) ConvertPowerTrainStringToPowertrain(value interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConvertPowerTrainStringToPowertrain", reflect.TypeOf((*MockDeviceDefinitionService)(nil).ConvertPowerTrainStringToPowertrain), value) -} - // CreateIntegration mocks base method. func (m *MockDeviceDefinitionService) CreateIntegration(ctx context.Context, integrationType, vendor, style string) (*grpc.Integration, error) { m.ctrl.T.Helper() @@ -247,36 +232,6 @@ func (mr *MockDeviceDefinitionServiceMockRecorder) GetOrCreateMake(ctx, tx, make return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrCreateMake", reflect.TypeOf((*MockDeviceDefinitionService)(nil).GetOrCreateMake), ctx, tx, makeName) } -// PullDrivlyData mocks base method. -func (m *MockDeviceDefinitionService) PullDrivlyData(ctx context.Context, userDeviceID, deviceDefinitionID, vin string) (services.DataPullStatusEnum, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PullDrivlyData", ctx, userDeviceID, deviceDefinitionID, vin) - ret0, _ := ret[0].(services.DataPullStatusEnum) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PullDrivlyData indicates an expected call of PullDrivlyData. -func (mr *MockDeviceDefinitionServiceMockRecorder) PullDrivlyData(ctx, userDeviceID, deviceDefinitionID, vin interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PullDrivlyData", reflect.TypeOf((*MockDeviceDefinitionService)(nil).PullDrivlyData), ctx, userDeviceID, deviceDefinitionID, vin) -} - -// PullVincarioValuation mocks base method. -func (m *MockDeviceDefinitionService) PullVincarioValuation(ctx context.Context, userDeiceID, deviceDefinitionID, vin string) (services.DataPullStatusEnum, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "PullVincarioValuation", ctx, userDeiceID, deviceDefinitionID, vin) - ret0, _ := ret[0].(services.DataPullStatusEnum) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// PullVincarioValuation indicates an expected call of PullVincarioValuation. -func (mr *MockDeviceDefinitionServiceMockRecorder) PullVincarioValuation(ctx, userDeiceID, deviceDefinitionID, vin interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PullVincarioValuation", reflect.TypeOf((*MockDeviceDefinitionService)(nil).PullVincarioValuation), ctx, userDeiceID, deviceDefinitionID, vin) -} - // UpdateDeviceDefinitionFromNHTSA mocks base method. func (m *MockDeviceDefinitionService) UpdateDeviceDefinitionFromNHTSA(ctx context.Context, deviceDefinitionID, vin string) error { m.ctrl.T.Helper() diff --git a/internal/services/mocks/drivly_api_service_mock.go b/internal/services/mocks/drivly_api_service_mock.go index 4372d9d7d..df8cb5035 100644 --- a/internal/services/mocks/drivly_api_service_mock.go +++ b/internal/services/mocks/drivly_api_service_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockDrivlyAPIService is a mock of DrivlyAPIService interface. diff --git a/internal/services/mocks/event_service_mock.go b/internal/services/mocks/event_service_mock.go index 36b222680..0fe5973c6 100644 --- a/internal/services/mocks/event_service_mock.go +++ b/internal/services/mocks/event_service_mock.go @@ -7,8 +7,8 @@ package mock_services import ( reflect "reflect" - services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + shared "github.com/DIMO-Network/shared" + gomock "go.uber.org/mock/gomock" ) // MockEventService is a mock of EventService interface. @@ -35,7 +35,7 @@ func (m *MockEventService) EXPECT() *MockEventServiceMockRecorder { } // Emit mocks base method. -func (m *MockEventService) Emit(event *services.Event) error { +func (m *MockEventService) Emit(event *shared.CloudEvent[any]) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Emit", event) ret0, _ := ret[0].(error) diff --git a/internal/services/mocks/ingest_registrar_mock.go b/internal/services/mocks/ingest_registrar_mock.go index 83bb8f1fa..e03aeb3ad 100644 --- a/internal/services/mocks/ingest_registrar_mock.go +++ b/internal/services/mocks/ingest_registrar_mock.go @@ -9,7 +9,7 @@ import ( services "github.com/DIMO-Network/devices-api/internal/services" common "github.com/ethereum/go-ethereum/common" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockIngestRegistrar is a mock of IngestRegistrar interface. diff --git a/internal/services/mocks/nhtsa_api_service_mock.go b/internal/services/mocks/nhtsa_api_service_mock.go index 1b4294139..10f07bbe3 100644 --- a/internal/services/mocks/nhtsa_api_service_mock.go +++ b/internal/services/mocks/nhtsa_api_service_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockINHTSAService is a mock of INHTSAService interface. diff --git a/internal/services/mocks/openai_service_mock.go b/internal/services/mocks/openai_service_mock.go index c5683d2e8..ca0248859 100644 --- a/internal/services/mocks/openai_service_mock.go +++ b/internal/services/mocks/openai_service_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockOpenAI is a mock of OpenAI interface. diff --git a/internal/services/mocks/smartcar_client_mock.go b/internal/services/mocks/smartcar_client_mock.go index 069411a47..796d8f3ae 100644 --- a/internal/services/mocks/smartcar_client_mock.go +++ b/internal/services/mocks/smartcar_client_mock.go @@ -8,7 +8,7 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" smartcar "github.com/smartcar/go-sdk" ) diff --git a/internal/services/mocks/smartcar_task_service_mock.go b/internal/services/mocks/smartcar_task_service_mock.go index 7901da437..98a09493b 100644 --- a/internal/services/mocks/smartcar_task_service_mock.go +++ b/internal/services/mocks/smartcar_task_service_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" models "github.com/DIMO-Network/devices-api/models" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockSmartcarTaskService is a mock of SmartcarTaskService interface. diff --git a/internal/services/mocks/synthetic_device_instance_service_mock.go b/internal/services/mocks/synthetic_device_instance_service_mock.go index 6559d511e..cc8844c6e 100644 --- a/internal/services/mocks/synthetic_device_instance_service_mock.go +++ b/internal/services/mocks/synthetic_device_instance_service_mock.go @@ -8,7 +8,7 @@ import ( context "context" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockSyntheticWalletInstanceService is a mock of SyntheticWalletInstanceService interface. diff --git a/internal/services/mocks/tesla_service_mock.go b/internal/services/mocks/tesla_service_mock.go index 2b9a4b2aa..483d8cab0 100644 --- a/internal/services/mocks/tesla_service_mock.go +++ b/internal/services/mocks/tesla_service_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockTeslaService is a mock of TeslaService interface. diff --git a/internal/services/mocks/tesla_task_service_mock.go b/internal/services/mocks/tesla_task_service_mock.go index 1a958e1b3..9f1ff0fa7 100644 --- a/internal/services/mocks/tesla_task_service_mock.go +++ b/internal/services/mocks/tesla_task_service_mock.go @@ -9,7 +9,7 @@ import ( services "github.com/DIMO-Network/devices-api/internal/services" models "github.com/DIMO-Network/devices-api/models" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockTeslaTaskService is a mock of TeslaTaskService interface. diff --git a/internal/services/mocks/user_device_service_mock.go b/internal/services/mocks/user_device_service_mock.go new file mode 100644 index 000000000..222e65203 --- /dev/null +++ b/internal/services/mocks/user_device_service_mock.go @@ -0,0 +1,53 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: user_device_service.go + +// Package mock_services is a generated GoMock package. +package mock_services + +import ( + context "context" + reflect "reflect" + + grpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" + models "github.com/DIMO-Network/devices-api/models" + gomock "go.uber.org/mock/gomock" +) + +// MockUserDeviceService is a mock of UserDeviceService interface. +type MockUserDeviceService struct { + ctrl *gomock.Controller + recorder *MockUserDeviceServiceMockRecorder +} + +// MockUserDeviceServiceMockRecorder is the mock recorder for MockUserDeviceService. +type MockUserDeviceServiceMockRecorder struct { + mock *MockUserDeviceService +} + +// NewMockUserDeviceService creates a new mock instance. +func NewMockUserDeviceService(ctrl *gomock.Controller) *MockUserDeviceService { + mock := &MockUserDeviceService{ctrl: ctrl} + mock.recorder = &MockUserDeviceServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockUserDeviceService) EXPECT() *MockUserDeviceServiceMockRecorder { + return m.recorder +} + +// CreateUserDevice mocks base method. +func (m *MockUserDeviceService) CreateUserDevice(ctx context.Context, deviceDefID, styleID, countryCode, userID string, vin, canProtocol *string) (*models.UserDevice, *grpc.GetDeviceDefinitionItemResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateUserDevice", ctx, deviceDefID, styleID, countryCode, userID, vin, canProtocol) + ret0, _ := ret[0].(*models.UserDevice) + ret1, _ := ret[1].(*grpc.GetDeviceDefinitionItemResponse) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// CreateUserDevice indicates an expected call of CreateUserDevice. +func (mr *MockUserDeviceServiceMockRecorder) CreateUserDevice(ctx, deviceDefID, styleID, countryCode, userID, vin, canProtocol interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUserDevice", reflect.TypeOf((*MockUserDeviceService)(nil).CreateUserDevice), ctx, deviceDefID, styleID, countryCode, userID, vin, canProtocol) +} diff --git a/internal/services/mocks/user_service_client.go b/internal/services/mocks/user_service_client.go index 05cf4d128..88a2920fa 100644 --- a/internal/services/mocks/user_service_client.go +++ b/internal/services/mocks/user_service_client.go @@ -9,7 +9,7 @@ import ( reflect "reflect" users "github.com/DIMO-Network/shared/api/users" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" grpc "google.golang.org/grpc" ) diff --git a/internal/services/mocks/valuations_api_service_mock.go b/internal/services/mocks/valuations_api_service_mock.go index dfbb2a948..41398c579 100644 --- a/internal/services/mocks/valuations_api_service_mock.go +++ b/internal/services/mocks/valuations_api_service_mock.go @@ -9,7 +9,7 @@ import ( reflect "reflect" grpc "github.com/DIMO-Network/valuations-api/pkg/grpc" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockValuationsAPIService is a mock of ValuationsAPIService interface. diff --git a/internal/services/mocks/verifible_credential_service_mock.go b/internal/services/mocks/verifible_credential_service_mock.go index 55ef3eb44..5f2dabfd2 100644 --- a/internal/services/mocks/verifible_credential_service_mock.go +++ b/internal/services/mocks/verifible_credential_service_mock.go @@ -8,7 +8,7 @@ import ( big "math/big" reflect "reflect" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockVCService is a mock of VCService interface. diff --git a/internal/services/mocks/vincario_api_service_mock.go b/internal/services/mocks/vincario_api_service_mock.go index 15654a569..af3aea664 100644 --- a/internal/services/mocks/vincario_api_service_mock.go +++ b/internal/services/mocks/vincario_api_service_mock.go @@ -8,7 +8,7 @@ import ( reflect "reflect" services "github.com/DIMO-Network/devices-api/internal/services" - gomock "github.com/golang/mock/gomock" + gomock "go.uber.org/mock/gomock" ) // MockVincarioAPIService is a mock of VincarioAPIService interface. diff --git a/internal/services/registry/storage.go b/internal/services/registry/storage.go index c481a4ab9..c89137121 100644 --- a/internal/services/registry/storage.go +++ b/internal/services/registry/storage.go @@ -5,6 +5,8 @@ import ( "encoding/json" "time" + "github.com/DIMO-Network/shared" + "github.com/DIMO-Network/devices-api/internal/config" "github.com/DIMO-Network/devices-api/internal/contracts" "github.com/DIMO-Network/devices-api/internal/services" @@ -97,7 +99,7 @@ func (p *proc) Handle(ctx context.Context, data *ceData) error { } if ud := vnft.R.UserDevice; ud != nil { - p.Eventer.Emit(&services.Event{ //nolint + p.Eventer.Emit(&shared.CloudEvent[any]{ //nolint Type: "com.dimo.zone.device.mint", Subject: ud.ID, Source: "devices-api", @@ -148,7 +150,7 @@ func (p *proc) Handle(ctx context.Context, data *ceData) error { } if ud := vnft.R.UserDevice; ud != nil { - p.Eventer.Emit(&services.Event{ // nolint + p.Eventer.Emit(&shared.CloudEvent[any]{ // nolint Type: "com.dimo.zone.device.mint", Subject: ud.ID, Source: "devices-api", diff --git a/internal/services/registry/storage_test.go b/internal/services/registry/storage_test.go index 3b5e9dc7a..99547e6e3 100644 --- a/internal/services/registry/storage_test.go +++ b/internal/services/registry/storage_test.go @@ -11,7 +11,6 @@ import ( "github.com/DIMO-Network/devices-api/internal/config" "github.com/DIMO-Network/devices-api/internal/contracts" - "github.com/DIMO-Network/devices-api/internal/services" mock_services "github.com/DIMO-Network/devices-api/internal/services/mocks" "github.com/DIMO-Network/devices-api/internal/test" "github.com/DIMO-Network/devices-api/models" @@ -19,7 +18,6 @@ import ( "github.com/DIMO-Network/shared/db" "github.com/ericlagergren/decimal" "github.com/ethereum/go-ethereum/common" - "github.com/golang/mock/gomock" "github.com/segmentio/ksuid" "github.com/stretchr/testify/suite" "github.com/testcontainers/testcontainers-go" @@ -27,6 +25,7 @@ import ( "github.com/volatiletech/sqlboiler/v4/boil" "github.com/volatiletech/sqlboiler/v4/queries/qm" "github.com/volatiletech/sqlboiler/v4/types" + "go.uber.org/mock/gomock" ) type StorageTestSuite struct { @@ -195,8 +194,8 @@ func (s *StorageTestSuite) TestMintVehicle() { } s.MustInsert(&mtr) - var emEv *services.Event - s.eventSvc.EXPECT().Emit(gomock.Any()).Do(func(event *services.Event) { + var emEv *shared.CloudEvent[any] + s.eventSvc.EXPECT().Emit(gomock.Any()).Do(func(event *shared.CloudEvent[any]) { emEv = event }) diff --git a/internal/services/task_status_listener_test.go b/internal/services/task_status_listener_test.go index 9d2cd59d1..f8370745d 100644 --- a/internal/services/task_status_listener_test.go +++ b/internal/services/task_status_listener_test.go @@ -5,7 +5,7 @@ package services // "context" // "github.com/DIMO-Network/devices-api/internal/constants" // mock_services "github.com/DIMO-Network/devices-api/internal/services/mocks" -// "github.com/golang/mock/gomock" +// "go.uber.org/mock/gomock" // "os" // "testing" // @@ -49,7 +49,7 @@ package services // }() // // cio := new(fakeCIO) -// ingest := NewTaskStatusListener(pdb.DBS, &logger, cio, deviceDefSvc) +// ingest := NewTaskStatusListener(pdb.dbs, &logger, cio, deviceDefSvc) // // integration := test.BuildIntegrationGRPC(constants.SmartCarVendor, 10, 0) // dd := test.BuildDeviceDefinitionGRPC(ksuid.New().String(), "Tesla", "Model Y", 2021, integration) @@ -60,7 +60,7 @@ package services // IntegrationID: integration.ID, // Status: models.UserDeviceAPIIntegrationStatusActive, // } -// err := udai.Insert(ctx, pdb.DBS().Writer, boil.Infer()) +// err := udai.Insert(ctx, pdb.dbs().Writer, boil.Infer()) // assert.NoError(t, err) // // input := &shared.CloudEvent[TaskStatusData]{ @@ -80,7 +80,7 @@ package services // t.Fatalf("Got an unexpected error processing status update: %v", err) // } // -// if err := udai.Reload(ctx, pdb.DBS().Writer); err != nil { +// if err := udai.Reload(ctx, pdb.dbs().Writer); err != nil { // t.Fatalf("Couldn't reload UDAI: %v", err) // } // diff --git a/internal/services/user_device_service.go b/internal/services/user_device_service.go new file mode 100644 index 000000000..03e395704 --- /dev/null +++ b/internal/services/user_device_service.go @@ -0,0 +1,135 @@ +package services + +import ( + "context" + "fmt" + "time" + + "github.com/DIMO-Network/shared" + + ddgrpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" + "github.com/DIMO-Network/devices-api/internal/constants" + "github.com/DIMO-Network/devices-api/models" + "github.com/DIMO-Network/shared/db" + "github.com/gofiber/fiber/v2" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "github.com/segmentio/ksuid" + "github.com/volatiletech/null/v8" + "github.com/volatiletech/sqlboiler/v4/boil" +) + +//go:generate mockgen -source user_device_service.go -destination mocks/user_device_service_mock.go -package mock_services + +type UserDeviceService interface { + CreateUserDevice(ctx context.Context, deviceDefID, styleID, countryCode, userID string, vin, canProtocol *string) (*models.UserDevice, *ddgrpc.GetDeviceDefinitionItemResponse, error) +} + +type userDeviceService struct { + deviceDefSvc DeviceDefinitionService + log zerolog.Logger + dbs func() *db.ReaderWriter + eventService EventService +} + +func NewUserDeviceService(deviceDefSvc DeviceDefinitionService, log zerolog.Logger, dbs func() *db.ReaderWriter, eventService EventService) UserDeviceService { + return &userDeviceService{ + deviceDefSvc: deviceDefSvc, + log: log, + dbs: dbs, + eventService: eventService, + } +} + +// CreateUserDevice creates the user_device record with all the logic we manage, including setting the countryCode, setting the powertrain based on the def or style, and setting the protocol +func (uds *userDeviceService) CreateUserDevice(ctx context.Context, deviceDefID, styleID, countryCode, userID string, vin, canProtocol *string) (*models.UserDevice, *ddgrpc.GetDeviceDefinitionItemResponse, error) { + // attach device def to user + dd, err2 := uds.deviceDefSvc.GetDeviceDefinitionByID(ctx, deviceDefID) + if err2 != nil { + return nil, nil, errors.Wrap(err2, fmt.Sprintf("error querying for device definition id: %s ", deviceDefID)) + } + powertrainType := ICE // default + for _, attr := range dd.DeviceAttributes { + if attr.Name == constants.PowerTrainTypeKey { + powertrainType = PowertrainType(attr.Value) // todo does this work? validat with test + break + } + } + // check if style exists to get powertrain + if len(styleID) > 0 { + ds, err := uds.deviceDefSvc.GetDeviceStyleByID(ctx, styleID) + if err != nil { + // just log warn + uds.log.Warn().Err(err).Msgf("failed to get device style %s - continuing", styleID) + } + + if ds != nil && len(ds.DeviceAttributes) > 0 { + // Find device attribute (powertrain_type) + for _, item := range ds.DeviceAttributes { + if item.Name == constants.PowerTrainTypeKey { + powertrainType = ConvertPowerTrainStringToPowertrain(item.Value) + break + } + } + } + } + + tx, err := uds.dbs().Writer.DB.BeginTx(ctx, nil) + defer tx.Rollback() //nolint + if err != nil { + return nil, nil, err + } + + userDeviceID := ksuid.New().String() + // register device for the user + ud := models.UserDevice{ + ID: userDeviceID, + UserID: userID, + DeviceDefinitionID: dd.DeviceDefinitionId, + CountryCode: null.StringFrom(countryCode), + VinIdentifier: null.StringFromPtr(vin), + } + // always instantiate metadata with powerTrain and CANProtocol + udMD := &UserDeviceMetadata{ + PowertrainType: &powertrainType, + } + if canProtocol != nil && len(*canProtocol) > 0 { + udMD.CANProtocol = canProtocol + } + err = ud.Metadata.Marshal(udMD) + if err != nil { + uds.log.Warn().Str("func", "createUserDevice").Msg("failed to marshal user device metadata on create") + } + + err = ud.Insert(ctx, tx, boil.Infer()) + if err != nil { + return nil, nil, fiber.NewError(fiber.StatusInternalServerError, "could not create user device for def_id: "+dd.DeviceDefinitionId) + } + + err = tx.Commit() // commmit the transaction + if err != nil { + return nil, nil, errors.Wrapf(err, "error commiting transaction to create geofence") + } + + // todo call devide definitions to check and pull image for this device in case don't have one + + err = uds.eventService.Emit(&shared.CloudEvent[any]{ + Type: constants.UserDeviceCreationEventType, + Subject: userID, + Source: "devices-api", + Data: UserDeviceEvent{ + Timestamp: time.Now(), + UserID: userID, + Device: UserDeviceEventDevice{ + ID: userDeviceID, + Make: dd.Make.Name, + Model: dd.Type.Model, + Year: int(dd.Type.Year), // Odd. + }, + }, + }) + if err != nil { + uds.log.Err(err).Msg("Failed emitting device creation event") + } + return &ud, dd, nil +} diff --git a/internal/services/user_device_service_test.go b/internal/services/user_device_service_test.go new file mode 100644 index 000000000..3233b9cd3 --- /dev/null +++ b/internal/services/user_device_service_test.go @@ -0,0 +1,71 @@ +package services + +//import ( +// "context" +// ddgrpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" +// mock_services "github.com/DIMO-Network/devices-api/internal/services/mocks" +// "github.com/DIMO-Network/devices-api/internal/test" +// "go.uber.org/mock/gomock" +// +// "github.com/rs/zerolog" +// "github.com/segmentio/ksuid" +// "github.com/stretchr/testify/assert" +// "github.com/stretchr/testify/require" +// "github.com/tidwall/gjson" +// "os" +// "testing" +//) +// +//func Test_userDeviceService_CreateUserDevice(t *testing.T) { +// mockCtrl := gomock.NewController(t) +// defer mockCtrl.Finish() +// +// ctx := context.Background() +// deviceDefSvc := mock_services.NewMockDeviceDefinitionService(mockCtrl) +// eventSvc := mock_services.NewMockEventService(mockCtrl) +// +// pdb, container := test.StartContainerDatabase(ctx, t, migrationsDirRelPath) +// defer func() { +// if err := container.Terminate(ctx); err != nil { +// t.Fatal(err) +// } +// }() +// +// logger := zerolog.New(os.Stdout).With(). +// Timestamp(). +// Str("app", "devices-api"). +// Logger() +// +// ddID := ksuid.New().String() +// styleID := ksuid.New().String() +// userID := ksuid.New().String() +// vin := "VINNNY1231231" +// can := "7" +// apInt := test.BuildIntegrationGRPC("autopi", 10, 12) +// dd := test.BuildDeviceDefinitionGRPC(ddID, "Ford", "Escaped", 2023, apInt) +// deviceDefSvc.EXPECT().GetDeviceDefinitionByID(gomock.Any(), ddID).Times(1).Return(dd[0], nil) +// //style will have hybrid name in it and powertrain attr HEV +// deviceDefSvc.EXPECT().GetDeviceStyleByID(gomock.Any(), styleID).Times(1).Return(&ddgrpc.DeviceStyle{ +// Id: ksuid.New().String(), +// Name: "Super Hybrid", +// DeviceDefinitionId: ddID, +// DeviceAttributes: []*ddgrpc.DeviceTypeAttribute{ +// { +// Name: "powertrain_type", +// Value: "HEV", +// }, +// }, +// }, nil) +// +// userDeviceSvc := NewUserDeviceService(deviceDefSvc, logger, pdb.DBS, eventSvc) +// +// userDevice, _, err := userDeviceSvc.CreateUserDevice(ctx, ddID, styleID, "USA", userID, &vin, &can) +// require.NoError(t, err) +// assert.Equal(t, vin, userDevice.VinIdentifier.String) +// assert.Equal(t, userID, userDevice.UserID) +// assert.Equal(t, ddID, userDevice.DeviceDefinitionID) +// assert.Equal(t, styleID, userDevice.DeviceStyleID.String) +// assert.Equal(t, "USA", userDevice.CountryCode.String) +// assert.Equal(t, can, gjson.GetBytes(userDevice.Metadata.JSON, "canProtocol")) +// assert.Equal(t, "HEV", gjson.GetBytes(userDevice.Metadata.JSON, "powertrainType")) +//} diff --git a/internal/test/helpers.go b/internal/test/helpers.go index bbeb0a282..939fbf2e5 100644 --- a/internal/test/helpers.go +++ b/internal/test/helpers.go @@ -8,6 +8,7 @@ import ( "math/big" "net/http" "os" + "strconv" "strings" "testing" "time" @@ -15,9 +16,7 @@ import ( ddgrpc "github.com/DIMO-Network/device-definitions-api/pkg/grpc" "github.com/DIMO-Network/devices-api/internal/config" "github.com/DIMO-Network/devices-api/internal/constants" - "github.com/DIMO-Network/devices-api/internal/controllers/helpers" "github.com/DIMO-Network/devices-api/models" - "github.com/DIMO-Network/shared/api/users" pb "github.com/DIMO-Network/shared/api/users" "github.com/DIMO-Network/shared/db" "github.com/docker/go-connections/nat" @@ -150,7 +149,23 @@ func getTestDbSettings() config.Settings { func SetupAppFiber(logger zerolog.Logger) *fiber.App { app := fiber.New(fiber.Config{ ErrorHandler: func(c *fiber.Ctx, err error) error { - return helpers.ErrorHandler(c, err, &logger, false) + // copied from controllers.helpers.ErrorHandler - but temporarily in here to see if resolved circular deps issue + code := fiber.StatusInternalServerError // Default 500 statuscode + + e, fiberTypeErr := err.(*fiber.Error) + if fiberTypeErr { + // Override status code if fiber.Error type + code = e.Code + } + logger.Err(err).Str("httpStatusCode", strconv.Itoa(code)). + Str("httpMethod", c.Method()). + Str("httpPath", c.Path()). + Msg("caught an error from http request") + + return c.Status(code).JSON(fiber.Map{ + "code": code, + "message": err.Error(), + }) }, }) return app @@ -432,7 +447,7 @@ func SetupCreateExternalVINData(t *testing.T, ddID string, ud *models.UserDevice return &evd } -// BuildIntegrationGRPC depending on integration vendor, defines an integration object with typical settings. Smartcar refresh limit default is 100 seconds. +// BuildIntegrationDefaultGRPC depending on integration vendor, defines an integration object with typical settings. Smartcar refresh limit default is 100 seconds. func BuildIntegrationDefaultGRPC(integrationVendor string, autoPiDefaultTemplateID int, bevTemplateID int, includeAutoPiPowertrainTemplate bool) *ddgrpc.Integration { var integration *ddgrpc.Integration switch integrationVendor { @@ -472,7 +487,7 @@ func BuildIntegrationDefaultGRPC(integrationVendor string, autoPiDefaultTemplate return integration } -// BuildIntegrationWithOutAutoPiPowertrainTemplateGRPC depending on integration vendor, defines an integration object with typical settings. Smartcar refresh limit default is 100 seconds. +// BuildIntegrationGRPC depending on integration vendor, defines an integration object with typical settings. Smartcar refresh limit default is 100 seconds. func BuildIntegrationGRPC(integrationVendor string, autoPiDefaultTemplateID int, bevTemplateID int) *ddgrpc.Integration { return BuildIntegrationDefaultGRPC(integrationVendor, autoPiDefaultTemplateID, bevTemplateID, false) } @@ -532,7 +547,7 @@ func BuildDeviceDefinitionGRPC(deviceDefinitionID string, mk string, model strin return []*ddgrpc.GetDeviceDefinitionItemResponse{rp} } -func BuildGetUserGRPC(id string, email *string, ethereumAddress *string, referredBy *users.UserReferrer) *pb.User { +func BuildGetUserGRPC(id string, email *string, ethereumAddress *string, referredBy *pb.UserReferrer) *pb.User { return &pb.User{ Id: id, EthereumAddress: ethereumAddress,