diff --git a/graph/generated.go b/graph/generated.go index 6653a039..001442a1 100644 --- a/graph/generated.go +++ b/graph/generated.go @@ -80,8 +80,8 @@ type ComplexityRoot struct { } Query struct { + AccessibleVehicles func(childComplexity int, address common.Address, first *int, after *string) int OwnedAftermarketDevices func(childComplexity int, address common.Address, first *int, after *string) int - OwnedVehicles func(childComplexity int, address common.Address, first *int, after *string) int } Vehicle struct { @@ -107,7 +107,7 @@ type ComplexityRoot struct { } type QueryResolver interface { - OwnedVehicles(ctx context.Context, address common.Address, first *int, after *string) (*model.VehicleConnection, error) + AccessibleVehicles(ctx context.Context, address common.Address, first *int, after *string) (*model.VehicleConnection, error) OwnedAftermarketDevices(ctx context.Context, address common.Address, first *int, after *string) (*model.AftermarketDeviceConnection, error) } @@ -245,29 +245,29 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Privilege.ID(childComplexity), true - case "Query.ownedAftermarketDevices": - if e.complexity.Query.OwnedAftermarketDevices == nil { + case "Query.accessibleVehicles": + if e.complexity.Query.AccessibleVehicles == nil { break } - args, err := ec.field_Query_ownedAftermarketDevices_args(context.TODO(), rawArgs) + args, err := ec.field_Query_accessibleVehicles_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Query.OwnedAftermarketDevices(childComplexity, args["address"].(common.Address), args["first"].(*int), args["after"].(*string)), true + return e.complexity.Query.AccessibleVehicles(childComplexity, args["address"].(common.Address), args["first"].(*int), args["after"].(*string)), true - case "Query.ownedVehicles": - if e.complexity.Query.OwnedVehicles == nil { + case "Query.ownedAftermarketDevices": + if e.complexity.Query.OwnedAftermarketDevices == nil { break } - args, err := ec.field_Query_ownedVehicles_args(context.TODO(), rawArgs) + args, err := ec.field_Query_ownedAftermarketDevices_args(context.TODO(), rawArgs) if err != nil { return 0, false } - return e.complexity.Query.OwnedVehicles(childComplexity, args["address"].(common.Address), args["first"].(*int), args["after"].(*string)), true + return e.complexity.Query.OwnedAftermarketDevices(childComplexity, args["address"].(common.Address), args["first"].(*int), args["after"].(*string)), true case "Vehicle.id": if e.complexity.Vehicle.ID == nil { @@ -476,7 +476,7 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs return args, nil } -func (ec *executionContext) field_Query_ownedAftermarketDevices_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_accessibleVehicles_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 common.Address @@ -509,7 +509,7 @@ func (ec *executionContext) field_Query_ownedAftermarketDevices_args(ctx context return args, nil } -func (ec *executionContext) field_Query_ownedVehicles_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { +func (ec *executionContext) field_Query_ownedAftermarketDevices_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} var arg0 common.Address @@ -1336,8 +1336,8 @@ func (ec *executionContext) fieldContext_Privilege_expiresAt(ctx context.Context return fc, nil } -func (ec *executionContext) _Query_ownedVehicles(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_Query_ownedVehicles(ctx, field) +func (ec *executionContext) _Query_accessibleVehicles(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_accessibleVehicles(ctx, field) if err != nil { return graphql.Null } @@ -1350,7 +1350,7 @@ func (ec *executionContext) _Query_ownedVehicles(ctx context.Context, field grap }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().OwnedVehicles(rctx, fc.Args["address"].(common.Address), fc.Args["first"].(*int), fc.Args["after"].(*string)) + return ec.resolvers.Query().AccessibleVehicles(rctx, fc.Args["address"].(common.Address), fc.Args["first"].(*int), fc.Args["after"].(*string)) }) if err != nil { ec.Error(ctx, err) @@ -1367,7 +1367,7 @@ func (ec *executionContext) _Query_ownedVehicles(ctx context.Context, field grap return ec.marshalNVehicleConnection2ᚖgithubᚗcomᚋDIMOᚑNetworkᚋidentityᚑapiᚋgraphᚋmodelᚐVehicleConnection(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_Query_ownedVehicles(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Query_accessibleVehicles(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Query", Field: field, @@ -1392,7 +1392,7 @@ func (ec *executionContext) fieldContext_Query_ownedVehicles(ctx context.Context } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Query_ownedVehicles_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Query_accessibleVehicles_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -4182,7 +4182,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr switch field.Name { case "__typename": out.Values[i] = graphql.MarshalString("Query") - case "ownedVehicles": + case "accessibleVehicles": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -4191,7 +4191,7 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Query_ownedVehicles(ctx, field) + res = ec._Query_accessibleVehicles(ctx, field) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } diff --git a/graph/schema.graphqls b/graph/schema.graphqls index 71a02993..4f12f22f 100644 --- a/graph/schema.graphqls +++ b/graph/schema.graphqls @@ -59,7 +59,7 @@ type Privilege { } type Query { - ownedVehicles( + accessibleVehicles( address: Address! first: Int after: String diff --git a/graph/schema.resolvers.go b/graph/schema.resolvers.go index ab3c2b00..def44006 100644 --- a/graph/schema.resolvers.go +++ b/graph/schema.resolvers.go @@ -11,9 +11,9 @@ import ( "github.com/ethereum/go-ethereum/common" ) -// OwnedVehicles is the resolver for the ownedVehicles field. -func (r *queryResolver) OwnedVehicles(ctx context.Context, address common.Address, first *int, after *string) (*model.VehicleConnection, error) { - return r.Repo.GetOwnedVehicles(ctx, address, first, after) +// AccessibleVehicles is the resolver for the accessibleVehicles field. +func (r *queryResolver) AccessibleVehicles(ctx context.Context, address common.Address, first *int, after *string) (*model.VehicleConnection, error) { + return r.Repo.GetAccessibleVehicles(ctx, address, first, after) } // OwnedAftermarketDevices is the resolver for the ownedAftermarketDevices field. diff --git a/internal/repositories/owned_vehicles.go b/internal/repositories/accessible_vehicles.go similarity index 78% rename from internal/repositories/owned_vehicles.go rename to internal/repositories/accessible_vehicles.go index 2ddab252..4f257634 100644 --- a/internal/repositories/owned_vehicles.go +++ b/internal/repositories/accessible_vehicles.go @@ -77,21 +77,7 @@ func (v *VehiclesRepo) createVehiclesResponse(totalCount int64, vehicles models. return res } -func (v *VehiclesRepo) GetOwnedVehicles(ctx context.Context, addr common.Address, first *int, after *string) (*gmodel.VehicleConnection, error) { - totalCount, err := models.Vehicles( - models.VehicleWhere.OwnerAddress.EQ(addr.Bytes()), - ).Count(ctx, v.pdb.DBS().Reader) - if err != nil { - return nil, err - } - - if totalCount == 0 { - return &gmodel.VehicleConnection{ - TotalCount: 0, - Edges: []*gmodel.VehicleEdge{}, - }, nil - } - +func (v *VehiclesRepo) GetAccessibleVehicles(ctx context.Context, addr common.Address, first *int, after *string) (*gmodel.VehicleConnection, error) { limit := defaultPageSize if first != nil { limit = *first @@ -100,12 +86,16 @@ func (v *VehiclesRepo) GetOwnedVehicles(ctx context.Context, addr common.Address } } + vAlias := "identity_api." + models.TableNames.Vehicles + pAlias := "identity_api." + models.TableNames.Privileges queryMods := []qm.QueryMod{ - models.VehicleWhere.OwnerAddress.EQ(addr.Bytes()), + qm.Select(vAlias + ".*"), + qm.LeftOuterJoin( + "identity_api." + models.TableNames.Privileges + " ON " + vAlias + ".id = " + pAlias + "." + models.PrivilegeColumns.TokenID, + ), + qm.Or2(models.VehicleWhere.OwnerAddress.EQ(addr.Bytes())), + qm.Or2(models.PrivilegeWhere.GrantedToAddress.EQ(addr.Bytes())), // Use limit + 1 here to check if there's a next page. - qm.Limit(limit + 1), - qm.OrderBy(models.VehicleColumns.ID + " DESC"), - qm.Load(models.VehicleRels.TokenPrivileges), } if after != nil { @@ -121,6 +111,23 @@ func (v *VehiclesRepo) GetOwnedVehicles(ctx context.Context, addr common.Address queryMods = append(queryMods, models.VehicleWhere.ID.LT(lastCursorVal)) } + totalCount, err := models.Vehicles(queryMods...).Count(ctx, v.pdb.DBS().Reader) + if err != nil { + return nil, err + } + + if totalCount == 0 { + return &gmodel.VehicleConnection{ + TotalCount: 0, + Edges: []*gmodel.VehicleEdge{}, + }, nil + } + + queryMods = append(queryMods, + qm.Limit(limit+1), + qm.OrderBy(models.VehicleColumns.ID+" DESC"), + qm.Load(models.VehicleRels.TokenPrivileges)) + all, err := models.Vehicles(queryMods...).All(ctx, v.pdb.DBS().Reader) if err != nil { return nil, err diff --git a/internal/repositories/owned_vehicles_test.go b/internal/repositories/accessible_vehicles_test.go similarity index 65% rename from internal/repositories/owned_vehicles_test.go rename to internal/repositories/accessible_vehicles_test.go index cb13c625..d67edb3b 100644 --- a/internal/repositories/owned_vehicles_test.go +++ b/internal/repositories/accessible_vehicles_test.go @@ -19,7 +19,7 @@ import ( "github.com/volatiletech/sqlboiler/v4/boil" ) -type OwnedVehiclesRepoTestSuite struct { +type AccessibleVehiclesRepoTestSuite struct { suite.Suite ctx context.Context pdb db.Store @@ -28,7 +28,7 @@ type OwnedVehiclesRepoTestSuite struct { settings config.Settings } -func (o *OwnedVehiclesRepoTestSuite) SetupSuite() { +func (o *AccessibleVehiclesRepoTestSuite) SetupSuite() { o.ctx = context.Background() o.pdb, o.container = test.StartContainerDatabase(o.ctx, o.T(), test.MigrationsDirRelPath) @@ -37,19 +37,15 @@ func (o *OwnedVehiclesRepoTestSuite) SetupSuite() { DIMORegistryChainID: 80001, } o.repo = NewVehiclesRepo(o.pdb) - /* c := client.New(handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{Resolvers: &graph.Resolver{ - Repo: ov, - }}))) */ - } // TearDownTest after each test truncate tables -func (s *OwnedVehiclesRepoTestSuite) TearDownTest() { +func (s *AccessibleVehiclesRepoTestSuite) TearDownTest() { test.TruncateTables(s.pdb.DBS().Writer.DB, s.T()) } // TearDownSuite cleanup at end by terminating container -func (o *OwnedVehiclesRepoTestSuite) TearDownSuite() { +func (o *AccessibleVehiclesRepoTestSuite) TearDownSuite() { fmt.Printf("shutting down postgres at with session: %s \n", o.container.SessionID()) if err := o.container.Terminate(o.ctx); err != nil { @@ -58,12 +54,12 @@ func (o *OwnedVehiclesRepoTestSuite) TearDownSuite() { } // Test Runner -func TestOwnedVehiclesRepoTestSuite(t *testing.T) { - suite.Run(t, new(OwnedVehiclesRepoTestSuite)) +func TestAccessibleVehiclesRepoTestSuite(t *testing.T) { + suite.Run(t, new(AccessibleVehiclesRepoTestSuite)) } /* Actual Tests */ -func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Success() { +func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehicles_Success() { _, wallet, err := test.GenerateWallet() o.NoError(err) @@ -114,7 +110,7 @@ func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Success() { } first := 3 - res, err := o.repo.GetOwnedVehicles(o.ctx, *wallet, &first, nil) + res, err := o.repo.GetAccessibleVehicles(o.ctx, *wallet, &first, nil) o.NoError(err) o.Equal(2, res.TotalCount) @@ -157,7 +153,7 @@ func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Success() { o.Exactly(expected, res.Edges) } -func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Pagination() { +func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehicles_Pagination() { _, wallet, err := test.GenerateWallet() o.NoError(err) @@ -188,7 +184,7 @@ func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Pagination() { } first := 1 - res, err := o.repo.GetOwnedVehicles(o.ctx, *wallet, &first, nil) + res, err := o.repo.GetAccessibleVehicles(o.ctx, *wallet, &first, nil) o.NoError(err) o.Equal(len(vehicles), res.TotalCount) @@ -212,7 +208,7 @@ func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Pagination() { o.Exactly(expected, res.Edges) } -func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Pagination_NextPage() { +func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehicles_Pagination_NextPage() { _, wallet, err := test.GenerateWallet() o.NoError(err) @@ -244,7 +240,7 @@ func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Pagination_NextPage() first := 1 after := "Mg==" - res, err := o.repo.GetOwnedVehicles(o.ctx, *wallet, &first, &after) + res, err := o.repo.GetAccessibleVehicles(o.ctx, *wallet, &first, &after) o.NoError(err) o.Equal(len(vehicles), res.TotalCount) @@ -267,3 +263,97 @@ func (o *OwnedVehiclesRepoTestSuite) Test_GetOwnedVehicles_Pagination_NextPage() o.Exactly(expected, res.Edges) } + +func (o *AccessibleVehiclesRepoTestSuite) Test_GetAccessibleVehicles_OwnedByUser_And_ForPrivilegesGrandted() { + _, wallet, err := test.GenerateWallet() + o.NoError(err) + + _, wallet2, err := test.GenerateWallet() + o.NoError(err) + + currTime := time.Now().UTC().Truncate(time.Microsecond) + vehicles := []models.Vehicle{ + { + ID: 1, + OwnerAddress: wallet.Bytes(), + Make: null.StringFrom("Toyota"), + Model: null.StringFrom("Camry"), + Year: null.IntFrom(2020), + MintedAt: currTime, + }, + { + ID: 2, + OwnerAddress: wallet2.Bytes(), + Make: null.StringFrom("Toyota"), + Model: null.StringFrom("Camry"), + Year: null.IntFrom(2022), + MintedAt: currTime, + }, + } + + for _, v := range vehicles { + if err := v.Insert(o.ctx, o.pdb.DBS().Writer, boil.Infer()); err != nil { + o.NoError(err) + } + } + + privileges := []models.Privilege{ + { + ID: ksuid.New().String(), + TokenID: 2, + PrivilegeID: 1, + GrantedToAddress: wallet.Bytes(), + GrantedAt: currTime, + ExpiresAt: currTime, + }, + } + + for _, p := range privileges { + if err := p.Insert(o.ctx, o.pdb.DBS().Writer, boil.Infer()); err != nil { + o.NoError(err) + } + } + + first := 3 + res, err := o.repo.GetAccessibleVehicles(o.ctx, *wallet, &first, nil) + o.NoError(err) + + o.Equal(2, res.TotalCount) + o.Equal(res.PageInfo.HasNextPage, false) + + expected := []*gmodel.VehicleEdge{ + { + Node: &gmodel.Vehicle{ + ID: "2", + Owner: common.BytesToAddress(wallet2.Bytes()), + Make: &vehicles[1].Make.String, + Model: &vehicles[1].Model.String, + Year: &vehicles[1].Year.Int, + MintedAt: vehicles[1].MintedAt, + Privileges: []*gmodel.Privilege{ + { + ID: privileges[0].PrivilegeID, + GrantedToAddress: common.BytesToAddress(privileges[0].GrantedToAddress), + GrantedAt: privileges[0].GrantedAt, + ExpiresAt: privileges[0].ExpiresAt, + }, + }, + }, + Cursor: "Mg==", + }, + { + Node: &gmodel.Vehicle{ + ID: "1", + Owner: common.BytesToAddress(wallet.Bytes()), + Make: &vehicles[0].Make.String, + Model: &vehicles[0].Model.String, + Year: &vehicles[0].Year.Int, + MintedAt: vehicles[0].MintedAt, + Privileges: []*gmodel.Privilege{}, + }, + Cursor: "MQ==", + }, + } + + o.Exactly(expected, res.Edges) +}