Skip to content

Commit

Permalink
Push role sorting logic to repo layer
Browse files Browse the repository at this point in the history
  • Loading branch information
gogolok authored and georgethebeatle committed Oct 24, 2024
1 parent 1a13a0f commit 4eed90e
Show file tree
Hide file tree
Showing 9 changed files with 260 additions and 52 deletions.
18 changes: 10 additions & 8 deletions api/handlers/fake/cfrole_repository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 5 additions & 21 deletions api/handlers/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"net/http"
"net/url"
"sort"

"code.cloudfoundry.org/korifi/api/authorization"
apierrors "code.cloudfoundry.org/korifi/api/errors"
Expand All @@ -25,7 +24,7 @@ const (

type CFRoleRepository interface {
CreateRole(context.Context, authorization.Info, repositories.CreateRoleMessage) (repositories.RoleRecord, error)
ListRoles(context.Context, authorization.Info) ([]repositories.RoleRecord, error)
ListRoles(context.Context, authorization.Info, repositories.ListRolesMessage) ([]repositories.RoleRecord, error)
GetRole(context.Context, authorization.Info, string) (repositories.RoleRecord, error)
DeleteRole(context.Context, authorization.Info, repositories.DeleteRoleMessage) error
}
Expand Down Expand Up @@ -68,19 +67,18 @@ func (h *Role) list(r *http.Request) (*routing.Response, error) {
authInfo, _ := authorization.InfoFromContext(r.Context())
logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.role.list")

roleListFilter := new(payloads.RoleList)
err := h.requestValidator.DecodeAndValidateURLValues(r, roleListFilter)
payload := new(payloads.RoleList)
err := h.requestValidator.DecodeAndValidateURLValues(r, payload)
if err != nil {
return nil, apierrors.LogAndReturn(logger, err, "Unable to decode request query parameters")
}

roles, err := h.roleRepo.ListRoles(r.Context(), authInfo)
roles, err := h.roleRepo.ListRoles(r.Context(), authInfo, payload.ToMessage())
if err != nil {
return nil, apierrors.LogAndReturn(logger, err, "failed to list roles")
}

filteredRoles := filterRoles(roleListFilter, roles)
h.sortList(filteredRoles, roleListFilter.OrderBy)
filteredRoles := filterRoles(payload, roles)

return routing.NewResponse(http.StatusOK).WithBody(presenter.ForList(presenter.ForRole, filteredRoles, h.apiBaseURL, *r.URL)), nil
}
Expand All @@ -103,20 +101,6 @@ func match(allowedValues map[string]bool, val string) bool {
return len(allowedValues) == 0 || allowedValues[val]
}

func (h *Role) sortList(roles []repositories.RoleRecord, order string) {
switch order {
case "":
case "created_at":
sort.Slice(roles, func(i, j int) bool { return timePtrAfter(&roles[j].CreatedAt, &roles[i].CreatedAt) })
case "-created_at":
sort.Slice(roles, func(i, j int) bool { return timePtrAfter(&roles[i].CreatedAt, &roles[j].CreatedAt) })
case "updated_at":
sort.Slice(roles, func(i, j int) bool { return timePtrAfter(roles[j].UpdatedAt, roles[i].UpdatedAt) })
case "-updated_at":
sort.Slice(roles, func(i, j int) bool { return timePtrAfter(roles[i].UpdatedAt, roles[j].UpdatedAt) })
}
}

func (h *Role) delete(r *http.Request) (*routing.Response, error) {
authInfo, _ := authorization.InfoFromContext(r.Context())
logger := logr.FromContextOrDiscard(r.Context()).WithName("handlers.role.delete")
Expand Down
21 changes: 2 additions & 19 deletions api/handlers/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ var _ = Describe("Role", func() {
Expect(req.URL.String()).To(HaveSuffix(rolesBase + "?foo=bar"))

Expect(roleRepo.ListRolesCallCount()).To(Equal(1))
_, actualAuthInfo := roleRepo.ListRolesArgsForCall(0)
_, actualAuthInfo, _ := roleRepo.ListRolesArgsForCall(0)
Expect(actualAuthInfo).To(Equal(authInfo))

Expect(rr).To(HaveHTTPStatus(http.StatusOK))
Expand All @@ -194,7 +194,7 @@ var _ = Describe("Role", func() {
)))
})

Describe("filtering and ordering", func() {
Describe("filtering", func() {
BeforeEach(func() {
roleRepo.ListRolesReturns([]repositories.RoleRecord{
{GUID: "1", CreatedAt: time.UnixMilli(5000), UpdatedAt: tools.PtrTo(time.UnixMilli(4000)), Type: "a", Space: "space1", Org: "", User: "user1"},
Expand Down Expand Up @@ -227,23 +227,6 @@ var _ = Describe("Role", func() {
Entry("user_guids1", payloads.RoleList{UserGUIDs: map[string]bool{"user1": true}}, "1", "2", "3"),
Entry("user_guids2", payloads.RoleList{UserGUIDs: map[string]bool{"user1": true, "user2": true}}, "1", "2", "3", "4"),
)

DescribeTable("ordering", func(order string, expectedGUIDs ...any) {
req, err := http.NewRequestWithContext(ctx, "GET", rolesBase+"?order_by=not-used-in-test", nil)
Expect(err).NotTo(HaveOccurred())

requestValidator.DecodeAndValidateURLValuesStub = decodeAndValidateURLValuesStub(&payloads.RoleList{OrderBy: order})
rr = httptest.NewRecorder()
routerBuilder.Build().ServeHTTP(rr, req)

Expect(rr).To(HaveHTTPStatus(http.StatusOK))
Expect(rr).To(HaveHTTPBody(MatchJSONPath("$.resources[*].guid", expectedGUIDs)))
},
Entry("created_at ASC", "created_at", "4", "3", "1", "2"),
Entry("created_at DESC", "-created_at", "2", "1", "3", "4"),
Entry("updated_at ASC", "updated_at", "2", "1", "4", "3"),
Entry("updated_at DESC", "-updated_at", "3", "4", "1", "2"),
)
})

When("decoding the url values fails", func() {
Expand Down
1 change: 1 addition & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ func main() {
cfg.RootNamespace,
cfg.RoleMappings,
namespaceRetriever,
repositories.NewRoleSorter(),
)
imageClient := image.NewClient(privilegedK8sClient)
imageRepo := repositories.NewImageRepository(
Expand Down
6 changes: 6 additions & 0 deletions api/payloads/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,12 @@ type RoleList struct {
OrderBy string
}

func (r RoleList) ToMessage() repositories.ListRolesMessage {
return repositories.ListRolesMessage{
OrderBy: r.OrderBy,
}
}

func (r RoleList) SupportedKeys() []string {
return []string{"guids", "types", "space_guids", "organization_guids", "user_guids", "order_by", "include", "per_page", "page"}
}
Expand Down
11 changes: 11 additions & 0 deletions api/payloads/role_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"code.cloudfoundry.org/korifi/api/errors"
"code.cloudfoundry.org/korifi/api/payloads"
"code.cloudfoundry.org/korifi/api/repositories"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -206,4 +207,14 @@ var _ = Describe("role list", func() {
},
Entry("invalid order_by", "order_by=foo", "value must be one of"),
)

DescribeTable("ToMessage",
func(roleList payloads.RoleList, expectedListRolesMessage repositories.ListRolesMessage) {
actualListRolesMessage := roleList.ToMessage()

Expect(actualListRolesMessage).To(Equal(expectedListRolesMessage))
},
Entry("created_at", payloads.RoleList{OrderBy: "created_at"}, repositories.ListRolesMessage{OrderBy: "created_at"}),
)

})
118 changes: 118 additions & 0 deletions api/repositories/fake/role_sorter.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4eed90e

Please sign in to comment.