Files
paralus/pkg/query/options.go
2024-04-25 12:16:15 +05:30

333 lines
7.4 KiB
Go

package query
import (
"errors"
"fmt"
"time"
"github.com/paralus/paralus/internal/random"
commonv3 "github.com/paralus/paralus/proto/types/commonpb/v3"
"github.com/uptrace/bun"
)
const (
// DefaultLimit is the default limit if no limit is set in query options
DefaultLimit = 10
// MaxLimit is the max limit for page size
MaxLimit = 50
// MaxOffset is the max offset
MaxOffset = 100000
// DefaultOrderBy is the default column used to order results
DefaultOrderBy = "created_at"
orderASC = "ASC"
orderDESC = "DESC"
// DefaultOrder is the order of the results
DefaultOrder = orderASC
)
const (
nameWithAliasQ = "?TableAlias.name = ?"
idWithAliasQ = "?TableAlias.id = ?"
partnerIDWithAliasQ = "?TableAlias.partner_id = ?"
organizationIDWithAliasQ = "?TableAlias.organization_id = ?"
projectIDwithAliasQ = "?TableAlias.project_id = ?"
nameQ = "name = ?"
deletedAtQ = "deleted_at = ?"
modifiedAtQ = "modified_at = ?"
displayNameQ = "display_name = ?"
labelsQ = "labels = ?"
annotationsQ = "annotations = ?"
idQ = "id = ?"
)
var (
// ErrNoName is returned when name is not set in query option
// trying to build query for get/update/delete
ErrNoName = errors.New("name not set in options")
ErrNoNameOrID = errors.New("neither name nor id is set in options")
)
// Option is the functional query option signature
type Option func(*commonv3.QueryOptions)
type setOption func(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error)
// WithMeta sets meta in query options
func WithMeta(o *commonv3.Metadata) Option {
return func(opts *commonv3.QueryOptions) {
opts.Name = o.GetName()
opts.ID = o.GetId()
opts.Partner = o.GetPartner()
opts.Organization = o.GetOrganization()
opts.Project = o.GetProject()
opts.DisplayName = o.GetName()
opts.Labels = o.GetLabels()
opts.Annotations = o.GetAnnotations()
}
}
// WithOptions copies options to query options
func WithOptions(in *commonv3.QueryOptions) Option {
return func(opts *commonv3.QueryOptions) {
*opts = *in
opts.Limit = getLimit(opts)
opts.Offset = getOffset(opts)
}
}
// WithIgnoreScopeDefault ignores default values for scope when building queries
func WithIgnoreScopeDefault() Option {
return func(opts *commonv3.QueryOptions) {
opts.IgnoreScopeDefault = true
}
}
// WithExtended sets extended in query options
func WithExtended() Option {
return func(opts *commonv3.QueryOptions) {
opts.Extended = true
}
}
// WithGlobalScope should be used to query resources in global scope
// partnerID, orgID, projectID = 0, 0, 0
func WithGlobalScope() Option {
return func(opts *commonv3.QueryOptions) {
opts.GlobalScope = true
}
}
func getLimit(opts *commonv3.QueryOptions) int64 {
// if limit is < 0 send all records
if opts.Limit == 0 {
return DefaultLimit
}
if opts.Limit > MaxLimit {
return MaxLimit
}
return opts.Limit
}
func getOffset(opts *commonv3.QueryOptions) int64 {
if opts.Offset > MaxOffset {
return MaxOffset
}
return opts.Offset
}
func Paginate(q *bun.SelectQuery, opts *commonv3.QueryOptions) *bun.SelectQuery {
limit := getLimit(opts)
if limit > 0 {
offset := getOffset(opts)
q = q.Limit(int(limit)).Offset(int(offset))
}
return q
}
// Select builds query for selecting resources
func Select(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error) {
var err error
q, err = setRequestMeta(q, opts)
if err != nil {
return nil, err
}
q, err = FilterLabels(q, opts)
if err != nil {
return nil, err
}
//q = Order(q, opts)
return q, nil
}
// GetAccountID returns account ID from QueryOptions
func GetAccountID(opts *commonv3.QueryOptions) (string, error) {
switch {
case opts.GlobalScope:
return "", nil
default:
return opts.Account, nil
}
}
// GetOrganizationID returns organization id from QueryOptions
func GetOrganizationID(opts *commonv3.QueryOptions) (string, error) {
switch {
case opts.GlobalScope:
return "", nil
default:
return opts.Organization, nil
}
}
func setRequestMeta(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error) {
var err error
for _, f := range []setOption{
setPartner,
setOrganization,
setProject,
} {
q, err = f(q, opts)
if err != nil {
return nil, err
}
}
return q, nil
}
func setProject(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error) {
id := opts.Project
if !opts.GlobalScope && id != "" {
q = q.Where(projectIDwithAliasQ, id)
}
return q, nil
}
func setOrganization(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error) {
id := opts.Organization
if !opts.GlobalScope && id != "" {
q = q.Where(organizationIDWithAliasQ, id)
}
return q, nil
}
func setPartner(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error) {
id := opts.Partner
// global scope takes precedence over ignore scope default
if !opts.GlobalScope && id != "" {
q = q.Where(partnerIDWithAliasQ, id)
}
return q, nil
}
// WithName sets name in query options
func WithName(name string) Option {
return func(opts *commonv3.QueryOptions) {
opts.Name = name
}
}
// WithSelector sets selector in query options
func WithSelector(selector string) Option {
return func(opts *commonv3.QueryOptions) {
opts.Selector = selector
}
}
// WithDeleted sets deleted in query options
func WithDeleted() Option {
return func(opts *commonv3.QueryOptions) {
opts.Deleted = true
}
}
// WithPartnerID sets partner id in query options
func WithPartnerID(partnerID string) Option {
return func(opts *commonv3.QueryOptions) {
opts.Partner = partnerID
}
}
// WithOrganizationID sets organization id in query options
func WithOrganizationID(organizationID string) Option {
return func(opts *commonv3.QueryOptions) {
opts.Organization = organizationID
}
}
// WithProjectID sets project id in query options
func WithProjectID(projectID string) Option {
return func(opts *commonv3.QueryOptions) {
opts.Project = projectID
}
}
// GetClusterID returns cluster ID from QueryOptions
func GetClusterID(opts *commonv3.QueryOptions) (string, error) {
switch {
case opts.GlobalScope:
return "", nil
default:
return opts.ClusterID, nil
}
}
// Get builds query for getting resource
func Get(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.SelectQuery, error) {
var err error
if !opts.GlobalScope {
q, err = setRequestMeta(q, opts)
}
if err != nil {
return nil, err
}
if opts.Name == "" && opts.ID == "" {
return nil, ErrNoNameOrID
}
if opts.Name != "" {
q = q.Where(nameWithAliasQ, opts.Name)
} else if opts.ID != "" {
q = q.Where(idWithAliasQ, opts.ID)
}
return q, nil
}
// Update builds query for updating resource
func Update(uq *bun.UpdateQuery, opts *commonv3.QueryOptions) (*bun.UpdateQuery, error) {
uq = uq.Set(modifiedAtQ, time.Now())
if opts.DisplayName != "" {
uq = uq.Set(displayNameQ, opts.DisplayName)
}
if opts.Labels != nil {
uq = uq.Set(labelsQ, opts.Labels)
}
if opts.Annotations != nil {
uq = uq.Set(annotationsQ, opts.Annotations)
}
return uq, nil
}
// Delete builds query for deleting resource
func Delete(q *bun.SelectQuery, opts *commonv3.QueryOptions) (*bun.UpdateQuery, error) {
var err error
q, err = Get(q, opts)
if err != nil {
return nil, err
}
uq := q.DB().NewUpdate()
if opts.Deleted {
uq = uq.Set(nameQ, fmt.Sprintf("%s-%s", opts.Name, random.NewRandomString(10)))
}
now := time.Now()
uq = uq.Set(deletedAtQ, &now)
return uq, nil
}