From 5a1f873d58885f29ac2c85ccf6bd171efff5bec9 Mon Sep 17 00:00:00 2001 From: gearnode Date: Wed, 19 Mar 2025 00:20:05 +0100 Subject: [PATCH] Use dedicated type for each order field Signed-off-by: gearnode --- pkg/coredata/control.go | 11 ++++-- pkg/coredata/control_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/evidence.go | 11 ++++-- pkg/coredata/evidence_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/framework.go | 11 ++++-- pkg/coredata/framework_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/organization.go | 9 ++++- pkg/coredata/organization_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/people.go | 14 +++++-- pkg/coredata/people_order_field.go | 41 +++++++++++++++++++++ pkg/coredata/policy.go | 13 +++++-- pkg/coredata/policy_order_field.go | 41 +++++++++++++++++++++ pkg/coredata/session.go | 9 ++++- pkg/coredata/session_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/task.go | 11 ++++-- pkg/coredata/task_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/user.go | 11 ++++-- pkg/coredata/user_order_field.go | 40 ++++++++++++++++++++ pkg/coredata/vendor.go | 12 +++--- pkg/coredata/vendor_order_field.go | 41 +++++++++++++++++++++ pkg/page/cursor.go | 25 +++++-------- pkg/page/{cursorkey.go => cursor_key.go} | 0 pkg/page/order_field.go | 47 +----------------------- pkg/page/page.go | 18 ++++----- pkg/probo/control_service.go | 4 +- pkg/probo/evidence_service.go | 4 +- pkg/probo/framework_service.go | 4 +- pkg/probo/people_service.go | 4 +- pkg/probo/policy_service.go | 4 +- pkg/probo/task_service.go | 4 +- pkg/probo/vendor_service.go | 4 +- pkg/usrmgr/usrmgr.go | 4 +- 32 files changed, 518 insertions(+), 119 deletions(-) create mode 100644 pkg/coredata/control_order_field.go create mode 100644 pkg/coredata/evidence_order_field.go create mode 100644 pkg/coredata/framework_order_field.go create mode 100644 pkg/coredata/organization_order_field.go create mode 100644 pkg/coredata/people_order_field.go create mode 100644 pkg/coredata/policy_order_field.go create mode 100644 pkg/coredata/session_order_field.go create mode 100644 pkg/coredata/task_order_field.go create mode 100644 pkg/coredata/user_order_field.go create mode 100644 pkg/coredata/vendor_order_field.go rename pkg/page/{cursorkey.go => cursor_key.go} (100%) diff --git a/pkg/coredata/control.go b/pkg/coredata/control.go index 8b0c5a6cd..d8dcee1ec 100644 --- a/pkg/coredata/control.go +++ b/pkg/coredata/control.go @@ -55,8 +55,13 @@ type ( } ) -func (c Control) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(c.ID, c.CreatedAt) +func (c Control) CursorKey(orderBy ControlOrderField) page.CursorKey { + switch orderBy { + case ControlOrderFieldCreatedAt: + return page.NewCursorKey(c.ID, c.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (c *Control) LoadByID( @@ -170,7 +175,7 @@ func (c *Controls) LoadByFrameworkID( conn pg.Conn, scope Scoper, frameworkID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[ControlOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/control_order_field.go b/pkg/coredata/control_order_field.go new file mode 100644 index 000000000..8004efe45 --- /dev/null +++ b/pkg/coredata/control_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + ControlOrderField string +) + +const ( + ControlOrderFieldCreatedAt ControlOrderField = "CREATED_AT" +) + +func (p ControlOrderField) Column() string { + return string(p) +} + +func (p ControlOrderField) String() string { + return string(p) +} + +func (p ControlOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *ControlOrderField) UnmarshalText(text []byte) error { + *p = ControlOrderField(text) + return nil +} diff --git a/pkg/coredata/evidence.go b/pkg/coredata/evidence.go index cd3226526..fb83d1a7c 100644 --- a/pkg/coredata/evidence.go +++ b/pkg/coredata/evidence.go @@ -42,8 +42,13 @@ type ( Evidences []*Evidence ) -func (e Evidence) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(e.ID, e.CreatedAt) +func (e Evidence) CursorKey(orderBy EvidenceOrderField) page.CursorKey { + switch orderBy { + case EvidenceOrderFieldCreatedAt: + return page.NewCursorKey(e.ID, e.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (e Evidence) Insert( @@ -145,7 +150,7 @@ func (e *Evidences) LoadByTaskID( conn pg.Conn, scope Scoper, taskID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[EvidenceOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/evidence_order_field.go b/pkg/coredata/evidence_order_field.go new file mode 100644 index 000000000..6fdffb8cb --- /dev/null +++ b/pkg/coredata/evidence_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + EvidenceOrderField string +) + +const ( + EvidenceOrderFieldCreatedAt EvidenceOrderField = "CREATED_AT" +) + +func (p EvidenceOrderField) Column() string { + return string(p) +} + +func (p EvidenceOrderField) String() string { + return string(p) +} + +func (p EvidenceOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *EvidenceOrderField) UnmarshalText(text []byte) error { + *p = EvidenceOrderField(text) + return nil +} diff --git a/pkg/coredata/framework.go b/pkg/coredata/framework.go index 5503c232a..fa6e570d6 100644 --- a/pkg/coredata/framework.go +++ b/pkg/coredata/framework.go @@ -47,8 +47,13 @@ type ( } ) -func (f Framework) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(f.ID, f.CreatedAt) +func (f Framework) CursorKey(orderBy FrameworkOrderField) page.CursorKey { + switch orderBy { + case FrameworkOrderFieldCreatedAt: + return page.NewCursorKey(f.ID, f.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (f *Frameworks) LoadByOrganizationID( @@ -56,7 +61,7 @@ func (f *Frameworks) LoadByOrganizationID( conn pg.Conn, scope Scoper, organizationID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[FrameworkOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/framework_order_field.go b/pkg/coredata/framework_order_field.go new file mode 100644 index 000000000..fa66c3216 --- /dev/null +++ b/pkg/coredata/framework_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + FrameworkOrderField string +) + +const ( + FrameworkOrderFieldCreatedAt FrameworkOrderField = "CREATED_AT" +) + +func (p FrameworkOrderField) Column() string { + return string(p) +} + +func (p FrameworkOrderField) String() string { + return string(p) +} + +func (p FrameworkOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *FrameworkOrderField) UnmarshalText(text []byte) error { + *p = FrameworkOrderField(text) + return nil +} diff --git a/pkg/coredata/organization.go b/pkg/coredata/organization.go index 76434c05d..e3c03f85b 100644 --- a/pkg/coredata/organization.go +++ b/pkg/coredata/organization.go @@ -39,8 +39,13 @@ type ( Organizations []*Organization ) -func (o Organization) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(o.ID, o.CreatedAt) +func (o Organization) CursorKey(orderBy OrganizationOrderField) page.CursorKey { + switch orderBy { + case OrganizationOrderFieldCreatedAt: + return page.NewCursorKey(o.ID, o.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (o *Organization) LoadByID( diff --git a/pkg/coredata/organization_order_field.go b/pkg/coredata/organization_order_field.go new file mode 100644 index 000000000..ea4976d42 --- /dev/null +++ b/pkg/coredata/organization_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + OrganizationOrderField string +) + +const ( + OrganizationOrderFieldCreatedAt OrganizationOrderField = "CREATED_AT" +) + +func (p OrganizationOrderField) Column() string { + return string(p) +} + +func (p OrganizationOrderField) String() string { + return string(p) +} + +func (p OrganizationOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *OrganizationOrderField) UnmarshalText(text []byte) error { + *p = OrganizationOrderField(text) + return nil +} diff --git a/pkg/coredata/people.go b/pkg/coredata/people.go index 90579be4e..fa7ed8294 100644 --- a/pkg/coredata/people.go +++ b/pkg/coredata/people.go @@ -50,8 +50,15 @@ type ( } ) -func (p People) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(p.ID, p.CreatedAt) +func (p People) CursorKey(orderBy PeopleOrderField) page.CursorKey { + switch orderBy { + case PeopleOrderFieldCreatedAt: + return page.NewCursorKey(p.ID, p.CreatedAt) + case PeopleOrderFieldFullName: + return page.NewCursorKey(p.ID, p.FullName) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (p *People) LoadByID( @@ -171,8 +178,9 @@ func (p *Peoples) LoadByOrganizationID( conn pg.Conn, scope Scoper, organizationID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[PeopleOrderField], ) error { + // Base query q := ` SELECT id, diff --git a/pkg/coredata/people_order_field.go b/pkg/coredata/people_order_field.go new file mode 100644 index 000000000..842f37084 --- /dev/null +++ b/pkg/coredata/people_order_field.go @@ -0,0 +1,41 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + PeopleOrderField string +) + +const ( + PeopleOrderFieldCreatedAt PeopleOrderField = "CREATED_AT" + PeopleOrderFieldFullName PeopleOrderField = "FULL_NAME" +) + +func (p PeopleOrderField) Column() string { + return string(p) +} + +func (p PeopleOrderField) String() string { + return string(p) +} + +func (p PeopleOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *PeopleOrderField) UnmarshalText(text []byte) error { + *p = PeopleOrderField(text) + return nil +} diff --git a/pkg/coredata/policy.go b/pkg/coredata/policy.go index 9b191c2b4..105396892 100644 --- a/pkg/coredata/policy.go +++ b/pkg/coredata/policy.go @@ -38,8 +38,15 @@ type ( } ) -func (p Policy) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(p.ID, p.CreatedAt) +func (p Policy) CursorKey(orderBy PolicyOrderField) page.CursorKey { + switch orderBy { + case PolicyOrderFieldCreatedAt: + return page.NewCursorKey(p.ID, p.CreatedAt) + case PolicyOrderFieldName: + return page.NewCursorKey(p.ID, p.Name) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (p *Policy) LoadByID( @@ -93,7 +100,7 @@ func (p *Policies) LoadByOrganizationID( conn pg.Conn, scope Scoper, organizationID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[PolicyOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/policy_order_field.go b/pkg/coredata/policy_order_field.go new file mode 100644 index 000000000..a52686b20 --- /dev/null +++ b/pkg/coredata/policy_order_field.go @@ -0,0 +1,41 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + PolicyOrderField string +) + +const ( + PolicyOrderFieldCreatedAt PolicyOrderField = "CREATED_AT" + PolicyOrderFieldName PolicyOrderField = "NAME" +) + +func (p PolicyOrderField) Column() string { + return string(p) +} + +func (p PolicyOrderField) String() string { + return string(p) +} + +func (p PolicyOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *PolicyOrderField) UnmarshalText(text []byte) error { + *p = PolicyOrderField(text) + return nil +} diff --git a/pkg/coredata/session.go b/pkg/coredata/session.go index 19a1a40a3..06aaf5612 100644 --- a/pkg/coredata/session.go +++ b/pkg/coredata/session.go @@ -38,8 +38,13 @@ type ( SessionData struct{} ) -func (s Session) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(s.ID, s.CreatedAt) +func (s Session) CursorKey(orderBy SessionOrderField) page.CursorKey { + switch orderBy { + case SessionOrderFieldCreatedAt: + return page.NewCursorKey(s.ID, s.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (s *Session) LoadByID( diff --git a/pkg/coredata/session_order_field.go b/pkg/coredata/session_order_field.go new file mode 100644 index 000000000..2b517c97b --- /dev/null +++ b/pkg/coredata/session_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + SessionOrderField string +) + +const ( + SessionOrderFieldCreatedAt SessionOrderField = "CREATED_AT" +) + +func (p SessionOrderField) Column() string { + return string(p) +} + +func (p SessionOrderField) String() string { + return string(p) +} + +func (p SessionOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *SessionOrderField) UnmarshalText(text []byte) error { + *p = SessionOrderField(text) + return nil +} diff --git a/pkg/coredata/task.go b/pkg/coredata/task.go index 39e86c521..79744c95d 100644 --- a/pkg/coredata/task.go +++ b/pkg/coredata/task.go @@ -53,8 +53,13 @@ type ( } ) -func (t Task) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(t.ID, t.CreatedAt) +func (t Task) CursorKey(orderBy TaskOrderField) page.CursorKey { + switch orderBy { + case TaskOrderFieldCreatedAt: + return page.NewCursorKey(t.ID, t.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (t *Task) LoadByID( @@ -163,7 +168,7 @@ func (t *Tasks) LoadByControlID( conn pg.Conn, scope Scoper, controlID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[TaskOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/task_order_field.go b/pkg/coredata/task_order_field.go new file mode 100644 index 000000000..a22c44869 --- /dev/null +++ b/pkg/coredata/task_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + TaskOrderField string +) + +const ( + TaskOrderFieldCreatedAt TaskOrderField = "CREATED_AT" +) + +func (p TaskOrderField) Column() string { + return string(p) +} + +func (p TaskOrderField) String() string { + return string(p) +} + +func (p TaskOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *TaskOrderField) UnmarshalText(text []byte) error { + *p = TaskOrderField(text) + return nil +} diff --git a/pkg/coredata/user.go b/pkg/coredata/user.go index bfa1a550f..cb7323b98 100644 --- a/pkg/coredata/user.go +++ b/pkg/coredata/user.go @@ -59,15 +59,20 @@ func (e ErrUserAlreadyExists) Error() string { return e.message } -func (u User) CursorKey(orderBy page.OrderField) page.CursorKey { - return page.NewCursorKey(u.ID, u.CreatedAt) +func (u User) CursorKey(orderBy UserOrderField) page.CursorKey { + switch orderBy { + case UserOrderFieldCreatedAt: + return page.NewCursorKey(u.ID, u.CreatedAt) + } + + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (u *Users) LoadByOrganizationID( ctx context.Context, conn pg.Conn, organizationID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[UserOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/user_order_field.go b/pkg/coredata/user_order_field.go new file mode 100644 index 000000000..ab99a38f5 --- /dev/null +++ b/pkg/coredata/user_order_field.go @@ -0,0 +1,40 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + UserOrderField string +) + +const ( + UserOrderFieldCreatedAt UserOrderField = "CREATED_AT" +) + +func (p UserOrderField) Column() string { + return string(p) +} + +func (p UserOrderField) String() string { + return string(p) +} + +func (p UserOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *UserOrderField) UnmarshalText(text []byte) error { + *p = UserOrderField(text) + return nil +} diff --git a/pkg/coredata/vendor.go b/pkg/coredata/vendor.go index 53b2b61f3..8e5be63a4 100644 --- a/pkg/coredata/vendor.go +++ b/pkg/coredata/vendor.go @@ -63,17 +63,15 @@ type ( } ) -func (v Vendor) CursorKey(orderBy page.OrderField) page.CursorKey { +func (v Vendor) CursorKey(orderBy VendorOrderField) page.CursorKey { switch orderBy { - case page.OrderFieldCreatedAt: + case VendorOrderFieldCreatedAt: return page.NewCursorKey(v.ID, v.CreatedAt) - case page.OrderFieldUpdatedAt: - return page.NewCursorKey(v.ID, v.UpdatedAt) - case page.OrderFieldName: + case VendorOrderFieldName: return page.NewCursorKey(v.ID, v.Name) } - panic(fmt.Sprintf("unknown order by: %s", orderBy)) + panic(fmt.Sprintf("unsupported order by: %s", orderBy)) } func (v *Vendor) LoadByID( @@ -213,7 +211,7 @@ func (v *Vendors) LoadByOrganizationID( conn pg.Conn, scope Scoper, organizationID gid.GID, - cursor *page.Cursor, + cursor *page.Cursor[VendorOrderField], ) error { q := ` SELECT diff --git a/pkg/coredata/vendor_order_field.go b/pkg/coredata/vendor_order_field.go new file mode 100644 index 000000000..6fdb96207 --- /dev/null +++ b/pkg/coredata/vendor_order_field.go @@ -0,0 +1,41 @@ +// Copyright (c) 2025 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package coredata + +type ( + VendorOrderField string +) + +const ( + VendorOrderFieldCreatedAt VendorOrderField = "CREATED_AT" + VendorOrderFieldName VendorOrderField = "NAME" +) + +func (p VendorOrderField) Column() string { + return string(p) +} + +func (p VendorOrderField) String() string { + return string(p) +} + +func (p VendorOrderField) MarshalText() ([]byte, error) { + return []byte(p.String()), nil +} + +func (p *VendorOrderField) UnmarshalText(text []byte) error { + *p = VendorOrderField(text) + return nil +} diff --git a/pkg/page/cursor.go b/pkg/page/cursor.go index 2af52fedf..a231d7bb4 100644 --- a/pkg/page/cursor.go +++ b/pkg/page/cursor.go @@ -19,17 +19,17 @@ import ( ) type ( - Cursor struct { + Cursor[T OrderField] struct { Size int Position Position Key *CursorKey - OrderBy OrderBy + OrderBy OrderBy[T] } Position string - OrderBy struct { - Field OrderField + OrderBy[T OrderField] struct { + Field T Direction OrderDirection } ) @@ -41,27 +41,20 @@ const ( Head Position = "HEAD" ) -func NewCursor(size int, from *CursorKey, pos Position, orderBy *OrderBy) *Cursor { +func NewCursor[T OrderField](size int, from *CursorKey, pos Position, orderBy OrderBy[T]) *Cursor[T] { if size == 0 { size = DefaultCursorSize } - if orderBy == nil { - orderBy = &OrderBy{ - Field: OrderFieldCreatedAt, - Direction: OrderDirectionDesc, - } - } - - return &Cursor{ + return &Cursor[T]{ Size: size, Key: from, Position: pos, - OrderBy: *orderBy, + OrderBy: orderBy, } } -func (c *Cursor) SQLFragment() string { +func (c *Cursor[T]) SQLFragment() string { fieldName := c.OrderBy.Field.Column() var orderDirection string @@ -88,7 +81,7 @@ func (c *Cursor) SQLFragment() string { return whereClause + " ORDER BY " + orderByClause + " LIMIT @cursor_limit" } -func (c *Cursor) SQLArguments() pgx.NamedArgs { +func (c *Cursor[T]) SQLArguments() pgx.NamedArgs { var size = c.Size if c.Key == nil { size += 1 diff --git a/pkg/page/cursorkey.go b/pkg/page/cursor_key.go similarity index 100% rename from pkg/page/cursorkey.go rename to pkg/page/cursor_key.go diff --git a/pkg/page/order_field.go b/pkg/page/order_field.go index 864ecf2e2..54e3a0f27 100644 --- a/pkg/page/order_field.go +++ b/pkg/page/order_field.go @@ -21,51 +21,6 @@ import ( type ( OrderField interface { Column() string + fmt.Stringer } - - GenericOrderField string ) - -const ( - OrderFieldCreatedAt GenericOrderField = "CREATED_AT" - OrderFieldUpdatedAt GenericOrderField = "UPDATED_AT" - OrderFieldName GenericOrderField = "NAME" -) - -func (of GenericOrderField) String() string { - return string(of) -} - -func (of GenericOrderField) Column() string { - switch of { - case OrderFieldCreatedAt: - return "created_at" - case OrderFieldUpdatedAt: - return "updated_at" - case OrderFieldName: - return "name" - default: - return "" - } -} - -func (of GenericOrderField) MarshalText() ([]byte, error) { - return []byte(of.String()), nil -} - -func (of *GenericOrderField) UnmarshalText(data []byte) error { - val := string(data) - - switch val { - case OrderFieldCreatedAt.String(): - *of = OrderFieldCreatedAt - case OrderFieldUpdatedAt.String(): - *of = OrderFieldUpdatedAt - case OrderFieldName.String(): - *of = OrderFieldName - default: - return fmt.Errorf("invalid GenericOrderField value: %q", val) - } - - return nil -} diff --git a/pkg/page/page.go b/pkg/page/page.go index 01b63c274..f0813c254 100644 --- a/pkg/page/page.go +++ b/pkg/page/page.go @@ -15,8 +15,8 @@ package page type ( - Paginable interface { - CursorKey(orderBy OrderField) CursorKey + Paginable[T OrderField] interface { + CursorKey(orderBy T) CursorKey } PageInfo struct { @@ -24,14 +24,14 @@ type ( HasPrev bool } - Page[T Paginable] struct { + Page[T Paginable[U], U OrderField] struct { Info *PageInfo - Cursor *Cursor + Cursor *Cursor[U] Data []T } ) -func (p *Page[T]) First() T { +func (p *Page[T, U]) First() T { if len(p.Data) == 0 { var zero T return zero @@ -40,7 +40,7 @@ func (p *Page[T]) First() T { return p.Data[0] } -func (p *Page[T]) Last() T { +func (p *Page[T, U]) Last() T { if len(p.Data) == 0 { var zero T return zero @@ -49,11 +49,11 @@ func (p *Page[T]) Last() T { return p.Data[len(p.Data)-1] } -func NewPage[T Paginable](data []T, c *Cursor) *Page[T] { +func NewPage[T Paginable[U], U OrderField](data []T, c *Cursor[U]) *Page[T, U] { pi := &PageInfo{} if len(data) == 0 { - return &Page[T]{ + return &Page[T, U]{ Info: pi, Data: data, } @@ -109,7 +109,7 @@ func NewPage[T Paginable](data []T, c *Cursor) *Page[T] { } } - return &Page[T]{ + return &Page[T, U]{ Info: pi, Cursor: c, Data: edges, diff --git a/pkg/probo/control_service.go b/pkg/probo/control_service.go index 3c6a6427a..9d104c1a0 100644 --- a/pkg/probo/control_service.go +++ b/pkg/probo/control_service.go @@ -100,8 +100,8 @@ func (s ControlService) Update( func (s ControlService) ListForFrameworkID( ctx context.Context, frameworkID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Control], error) { + cursor *page.Cursor[coredata.ControlOrderField], +) (*page.Page[*coredata.Control, coredata.ControlOrderField], error) { var controls coredata.Controls err := s.svc.pg.WithConn( diff --git a/pkg/probo/evidence_service.go b/pkg/probo/evidence_service.go index 6f922eff2..18e9c49e4 100644 --- a/pkg/probo/evidence_service.go +++ b/pkg/probo/evidence_service.go @@ -171,8 +171,8 @@ func (s EvidenceService) GenerateFileURL( func (s EvidenceService) ListForTaskID( ctx context.Context, taskID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Evidence], error) { + cursor *page.Cursor[coredata.EvidenceOrderField], +) (*page.Page[*coredata.Evidence, coredata.EvidenceOrderField], error) { var evidences coredata.Evidences err := s.svc.pg.WithConn( diff --git a/pkg/probo/framework_service.go b/pkg/probo/framework_service.go index d61e4c7ff..12c419162 100644 --- a/pkg/probo/framework_service.go +++ b/pkg/probo/framework_service.go @@ -106,8 +106,8 @@ func (s FrameworkService) Create( func (s FrameworkService) ListForOrganizationID( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Framework], error) { + cursor *page.Cursor[coredata.FrameworkOrderField], +) (*page.Page[*coredata.Framework, coredata.FrameworkOrderField], error) { var frameworks coredata.Frameworks err := s.svc.pg.WithConn( diff --git a/pkg/probo/people_service.go b/pkg/probo/people_service.go index 956e8de31..d174e3c64 100644 --- a/pkg/probo/people_service.go +++ b/pkg/probo/people_service.go @@ -71,8 +71,8 @@ func (s PeopleService) Get( func (s PeopleService) ListForOrganizationID( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.People], error) { + cursor *page.Cursor[coredata.PeopleOrderField], +) (*page.Page[*coredata.People, coredata.PeopleOrderField], error) { var peoples coredata.Peoples err := s.svc.pg.WithConn( diff --git a/pkg/probo/policy_service.go b/pkg/probo/policy_service.go index e3f649977..83597b08d 100644 --- a/pkg/probo/policy_service.go +++ b/pkg/probo/policy_service.go @@ -145,8 +145,8 @@ func (s *PolicyService) Delete( func (s *PolicyService) ListByOrganizationID( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Policy], error) { + cursor *page.Cursor[coredata.PolicyOrderField], +) (*page.Page[*coredata.Policy, coredata.PolicyOrderField], error) { var policies coredata.Policies err := s.svc.pg.WithConn( diff --git a/pkg/probo/task_service.go b/pkg/probo/task_service.go index 7078a5553..a448c26c8 100644 --- a/pkg/probo/task_service.go +++ b/pkg/probo/task_service.go @@ -185,8 +185,8 @@ func (s TaskService) Update( func (s TaskService) ListForControlID( ctx context.Context, controlID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Task], error) { + cursor *page.Cursor[coredata.TaskOrderField], +) (*page.Page[*coredata.Task, coredata.TaskOrderField], error) { var tasks coredata.Tasks err := s.svc.pg.WithConn( diff --git a/pkg/probo/vendor_service.go b/pkg/probo/vendor_service.go index 340dfff46..0062938d1 100644 --- a/pkg/probo/vendor_service.go +++ b/pkg/probo/vendor_service.go @@ -61,8 +61,8 @@ type ( func (s VendorService) ListForOrganizationID( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.Vendor], error) { + cursor *page.Cursor[coredata.VendorOrderField], +) (*page.Page[*coredata.Vendor, coredata.VendorOrderField], error) { var vendors coredata.Vendors err := s.svc.pg.WithConn( diff --git a/pkg/usrmgr/usrmgr.go b/pkg/usrmgr/usrmgr.go index 4d9d4efa8..73363adfe 100644 --- a/pkg/usrmgr/usrmgr.go +++ b/pkg/usrmgr/usrmgr.go @@ -511,8 +511,8 @@ func (s Service) ConfirmEmail(ctx context.Context, tokenString string) error { func (s Service) ListUsersForTenant( ctx context.Context, organizationID gid.GID, - cursor *page.Cursor, -) (*page.Page[*coredata.User], error) { + cursor *page.Cursor[coredata.UserOrderField], +) (*page.Page[*coredata.User, coredata.UserOrderField], error) { users := coredata.Users{} err := s.pg.WithConn(