Repository navigation
fix(store): return every org service user and honour the requested sort #1929
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
f29e774
fix(store): list org service users that have no project policy
rohilsurana 39dbefb
test(store): cover service user rows with an empty project list
rohilsurana bc737e8
fix(store): honour the requested sort when searching org service users
rohilsurana ad298ad
fix(rql): use name and type tags so org service user fields resolve
rohilsurana 33fe1ef
refactor(store): use shared rql helpers for org service user search
rohilsurana 499456a
fix(store): list a project once when a service user has two roles on it
rohilsurana 3ee8db1
feat(store): let org service users be filtered and searched by project
rohilsurana 8c8fb0d
fix(api): validate the rql query before searching org service users
rohilsurana c314b6b
fix(store): reject group_by instead of building a query postgres cann…
rohilsurana 57ebd28
refactor(store): drop the updated_at field the search cannot serve
rohilsurana 20116c5
test(store): cover org service user search against a real postgres
rohilsurana 0bffb47
ci: raise the per package test timeout so the postgres suites fit
rohilsurana 8b6b863
test: cover the search handler error paths and pagination metadata
rohilsurana 5639efb
fix(api): resolve the organization before searching its service users
rohilsurana 0e47579
fix(store): skip soft-deleted rows in the org service user search
rohilsurana e29238e
refactor: drop code comments from the org service user search
rohilsurana 3e5b6f3
refactor(store): drop the subquery alias constant and keep one naming…
rohilsurana 7a8c5f4
docs(store): name which title the default sort uses
rohilsurana 3723ba0
refactor(store): select only the columns the search reads back
rohilsurana bee0719
docs(store): trim the default sort comment
rohilsurana File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
153 changes: 153 additions & 0 deletions
153
internal/api/v1beta1connect/organization_serviceuser_test.go
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| package v1beta1connect | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "testing" | ||
|
|
||
| "connectrpc.com/connect" | ||
| "github.com/raystack/frontier/core/aggregates/orgserviceuser" | ||
| "github.com/raystack/frontier/core/organization" | ||
| "github.com/raystack/frontier/internal/api/v1beta1connect/mocks" | ||
| "github.com/raystack/frontier/internal/store/postgres" | ||
| frontierv1beta1 "github.com/raystack/frontier/proto/v1beta1" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/mock" | ||
| ) | ||
|
|
||
| func TestSearchOrganizationServiceUsers(t *testing.T) { | ||
| const orgID = "1a02f4ae-9d76-446d-bc9c-a42c46e4f852" | ||
|
|
||
| stringFilter := func(name, op, value string) *frontierv1beta1.RQLFilter { | ||
| return &frontierv1beta1.RQLFilter{ | ||
| Name: name, | ||
| Operator: op, | ||
| Value: &frontierv1beta1.RQLFilter_StringValue{StringValue: value}, | ||
| } | ||
| } | ||
|
|
||
| tests := []struct { | ||
| name string | ||
| setup func(*mocks.OrgServiceUserService) | ||
| orgSetup func(*mocks.OrganizationService) | ||
| id string | ||
| query *frontierv1beta1.RQLRequest | ||
| wantCode connect.Code | ||
| wantMsg string | ||
| wantLen int | ||
| }{ | ||
| { | ||
| name: "returns the rows the service gives back", | ||
| setup: func(s *mocks.OrgServiceUserService) { | ||
| s.EXPECT().Search(mock.Anything, orgID, mock.Anything).Return(orgserviceuser.OrganizationServiceUsers{ | ||
| ServiceUsers: []orgserviceuser.AggregatedServiceUser{ | ||
| {ID: "su-1", Title: "first", OrgID: orgID}, | ||
| {ID: "su-2", Title: "second", OrgID: orgID, Projects: []orgserviceuser.Project{{ID: "p1", Name: "proj", Title: "Proj"}}}, | ||
| }, | ||
| Pagination: orgserviceuser.Page{Limit: 50, Offset: 0}, | ||
| }, nil) | ||
| }, | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50}, | ||
| wantLen: 2, | ||
| }, | ||
| { | ||
| name: "unparsable created_at is rejected before reaching the service", | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50, Filters: []*frontierv1beta1.RQLFilter{stringFilter("created_at", "gt", "yesterday-ish")}}, | ||
| wantCode: connect.CodeInvalidArgument, | ||
| wantMsg: "failed to validate rql query", | ||
| }, | ||
| { | ||
| name: "an operator the field does not allow is rejected", | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50, Filters: []*frontierv1beta1.RQLFilter{stringFilter("created_at", "in", "2026-01-01T00:00:00Z,2026-03-01T00:00:00Z")}}, | ||
| wantCode: connect.CodeInvalidArgument, | ||
| wantMsg: "failed to validate rql query", | ||
| }, | ||
| { | ||
| name: "a field that is not on the aggregate is rejected", | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50, Filters: []*frontierv1beta1.RQLFilter{stringFilter("nonsense", "eq", "x")}}, | ||
| wantCode: connect.CodeInvalidArgument, | ||
| wantMsg: "failed to read rql query", | ||
| }, | ||
| { | ||
| name: "an unsortable field is rejected", | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50, Sort: []*frontierv1beta1.RQLSort{{Name: "nonsense", Order: "asc"}}}, | ||
| wantCode: connect.CodeInvalidArgument, | ||
| wantMsg: "failed to validate rql query", | ||
| }, | ||
| { | ||
| name: "bad input from the store maps to invalid argument and keeps the reason", | ||
| setup: func(s *mocks.OrgServiceUserService) { | ||
| s.EXPECT().Search(mock.Anything, orgID, mock.Anything).Return( | ||
| orgserviceuser.OrganizationServiceUsers{}, | ||
| fmt.Errorf("%w: group_by is not supported", postgres.ErrBadInput)) | ||
| }, | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50}, | ||
| wantCode: connect.CodeInvalidArgument, | ||
| wantMsg: "group_by is not supported", | ||
| }, | ||
| { | ||
| name: "org addressed by name is resolved to its id", | ||
| id: "compare-org", | ||
| orgSetup: func(o *mocks.OrganizationService) { | ||
| o.EXPECT().GetRaw(mock.Anything, "compare-org").Return(organization.Organization{ID: orgID}, nil) | ||
| }, | ||
| setup: func(s *mocks.OrgServiceUserService) { | ||
| s.EXPECT().Search(mock.Anything, orgID, mock.Anything).Return(orgserviceuser.OrganizationServiceUsers{}, nil) | ||
| }, | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50}, | ||
| wantLen: 0, | ||
| }, | ||
| { | ||
| name: "org that does not exist is not found", | ||
| orgSetup: func(o *mocks.OrganizationService) { | ||
| o.EXPECT().GetRaw(mock.Anything, mock.Anything).Return(organization.Organization{}, organization.ErrNotExist) | ||
| }, | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50}, | ||
| wantCode: connect.CodeNotFound, | ||
| }, | ||
| { | ||
| name: "any other store failure is internal", | ||
| setup: func(s *mocks.OrgServiceUserService) { | ||
| s.EXPECT().Search(mock.Anything, orgID, mock.Anything).Return( | ||
| orgserviceuser.OrganizationServiceUsers{}, fmt.Errorf("connection refused")) | ||
| }, | ||
| query: &frontierv1beta1.RQLRequest{Limit: 50}, | ||
| wantCode: connect.CodeInternal, | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| svc := mocks.NewOrgServiceUserService(t) | ||
| if tt.setup != nil { | ||
| tt.setup(svc) | ||
| } | ||
| orgSvc := mocks.NewOrganizationService(t) | ||
| if tt.orgSetup != nil { | ||
| tt.orgSetup(orgSvc) | ||
| } else if tt.setup != nil { | ||
| orgSvc.EXPECT().GetRaw(mock.Anything, mock.Anything).Return(organization.Organization{ID: orgID}, nil) | ||
| } | ||
| handler := &ConnectHandler{orgServiceUserService: svc, orgService: orgSvc} | ||
|
|
||
| id := tt.id | ||
| if id == "" { | ||
| id = orgID | ||
| } | ||
| resp, err := handler.SearchOrganizationServiceUsers(context.Background(), | ||
| connect.NewRequest(&frontierv1beta1.SearchOrganizationServiceUsersRequest{Id: id, Query: tt.query})) | ||
|
|
||
| if tt.wantMsg == "" && tt.wantCode == 0 { | ||
| assert.NoError(t, err) | ||
| assert.Len(t, resp.Msg.GetOrganizationServiceUsers(), tt.wantLen) | ||
| return | ||
| } | ||
|
|
||
| assert.Error(t, err) | ||
| assert.Equal(t, tt.wantCode, connect.CodeOf(err)) | ||
| if tt.wantMsg != "" { | ||
| assert.Contains(t, err.Error(), tt.wantMsg) | ||
| } | ||
| }) | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.