github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/internal/repo/list.go (about) 1 package repo 2 3 import ( 4 "context" 5 "strings" 6 7 "github.com/kyma-incubator/compass/components/director/pkg/apperrors" 8 9 "github.com/kyma-incubator/compass/components/director/pkg/log" 10 11 "github.com/pkg/errors" 12 13 "github.com/kyma-incubator/compass/components/director/pkg/persistence" 14 "github.com/kyma-incubator/compass/components/director/pkg/resource" 15 ) 16 17 // Lister is an interface for listing tenant scoped entities with either externally managed tenant accesses (m2m table or view) or embedded tenant in them. 18 type Lister interface { 19 List(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error 20 ListWithSelectForUpdate(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error 21 SetSelectedColumns(selectedColumns []string) 22 Clone() *universalLister 23 } 24 25 // ConditionTreeLister is an interface for listing tenant scoped entities with either externally managed tenant accesses (m2m table or view) or embedded tenant in them based on provided conditionTree. 26 type ConditionTreeLister interface { 27 ListConditionTree(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, conditionTree *ConditionTree) error 28 } 29 30 // ConditionTreeListerGlobal is an interface for listing entities by conditionTree globally. 31 type ConditionTreeListerGlobal interface { 32 ListConditionTreeGlobal(ctx context.Context, resourceType resource.Type, dest Collection, conditionTree *ConditionTree) error 33 } 34 35 // ListerGlobal is an interface for listing global entities. 36 type ListerGlobal interface { 37 ListGlobal(ctx context.Context, dest Collection, additionalConditions ...Condition) error 38 ListGlobalWithSelectForUpdate(ctx context.Context, dest Collection, additionalConditions ...Condition) error 39 SetSelectedColumns(selectedColumns []string) 40 Clone() *universalLister 41 } 42 43 type universalLister struct { 44 tableName string 45 selectedColumns string 46 tenantColumn *string 47 resourceType resource.Type 48 ownerCheck bool 49 50 orderByParams OrderByParams 51 } 52 53 // NewListerWithEmbeddedTenant is a constructor for Lister about entities with tenant embedded in them. 54 func NewListerWithEmbeddedTenant(tableName string, tenantColumn string, selectedColumns []string) Lister { 55 return &universalLister{ 56 tableName: tableName, 57 selectedColumns: strings.Join(selectedColumns, ", "), 58 tenantColumn: &tenantColumn, 59 orderByParams: NoOrderBy, 60 } 61 } 62 63 // NewLister is a constructor for Lister about entities with externally managed tenant accesses (m2m table or view) 64 func NewLister(tableName string, selectedColumns []string) Lister { 65 return &universalLister{ 66 tableName: tableName, 67 selectedColumns: strings.Join(selectedColumns, ", "), 68 orderByParams: NoOrderBy, 69 } 70 } 71 72 // NewConditionTreeListerWithEmbeddedTenant is a constructor for ConditionTreeLister about entities with tenant embedded in them. 73 func NewConditionTreeListerWithEmbeddedTenant(tableName string, tenantColumn string, selectedColumns []string) ConditionTreeLister { 74 return &universalLister{ 75 tableName: tableName, 76 selectedColumns: strings.Join(selectedColumns, ", "), 77 tenantColumn: &tenantColumn, 78 orderByParams: NoOrderBy, 79 } 80 } 81 82 // NewConditionTreeLister is a constructor for ConditionTreeLister about entities with externally managed tenant accesses (m2m table or view) 83 func NewConditionTreeLister(tableName string, selectedColumns []string) ConditionTreeLister { 84 return &universalLister{ 85 tableName: tableName, 86 selectedColumns: strings.Join(selectedColumns, ", "), 87 orderByParams: NoOrderBy, 88 } 89 } 90 91 // NewConditionTreeListerGlobal is a constructor for ConditionTreeListerGlobal. 92 func NewConditionTreeListerGlobal(tableName string, selectedColumns []string) ConditionTreeListerGlobal { 93 return &universalLister{ 94 tableName: tableName, 95 selectedColumns: strings.Join(selectedColumns, ", "), 96 orderByParams: NoOrderBy, 97 } 98 } 99 100 // NewOwnerLister is a constructor for Lister about entities with externally managed tenant accesses (m2m table or view) with owner access check 101 func NewOwnerLister(tableName string, selectedColumns []string, ownerCheck bool) Lister { 102 return &universalLister{ 103 tableName: tableName, 104 selectedColumns: strings.Join(selectedColumns, ", "), 105 ownerCheck: ownerCheck, 106 orderByParams: NoOrderBy, 107 } 108 } 109 110 // NewListerWithOrderBy is a constructor for Lister about entities with externally managed tenant accesses (m2m table or view) with additional order by clause. 111 func NewListerWithOrderBy(tableName string, selectedColumns []string, orderByParams OrderByParams) Lister { 112 return &universalLister{ 113 tableName: tableName, 114 selectedColumns: strings.Join(selectedColumns, ", "), 115 orderByParams: orderByParams, 116 } 117 } 118 119 // NewListerGlobalWithOrderBy is a constructor for ListerGlobal about global entities with additional order by clause. 120 func NewListerGlobalWithOrderBy(resourceType resource.Type, tableName string, selectedColumns []string, orderByParams OrderByParams) ListerGlobal { 121 return &universalLister{ 122 resourceType: resourceType, 123 tableName: tableName, 124 selectedColumns: strings.Join(selectedColumns, ", "), 125 orderByParams: orderByParams, 126 } 127 } 128 129 // NewListerGlobal is a constructor for ListerGlobal about global entities. 130 func NewListerGlobal(resourceType resource.Type, tableName string, selectedColumns []string) ListerGlobal { 131 return &universalLister{ 132 resourceType: resourceType, 133 tableName: tableName, 134 selectedColumns: strings.Join(selectedColumns, ", "), 135 orderByParams: NoOrderBy, 136 } 137 } 138 139 // List lists tenant scoped entities with tenant isolation subquery. 140 // If the tenantColumn is configured the isolation is based on equal condition on tenantColumn. 141 // If the tenantColumn is not configured an entity with externally managed tenant accesses in m2m table / view is assumed. 142 func (l *universalLister) List(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error { 143 return l.listWithTenantScope(ctx, resourceType, tenant, dest, NoLock, additionalConditions) 144 } 145 146 // ListConditionTree lists tenant scoped entities matching the provided condition tree with tenant isolation. 147 // If the tenantColumn is configured the isolation is based on equal condition on tenantColumn. 148 // If the tenantColumn is not configured an entity with externally managed tenant accesses in m2m table / view is assumed. 149 func (l *universalLister) ListConditionTree(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, conditionTree *ConditionTree) error { 150 return l.listConditionTreeWithTenantScope(ctx, resourceType, tenant, dest, NoLock, conditionTree) 151 } 152 153 // ListConditionTreeGlobal lists entities matching the provided condition tree globally. 154 func (l *universalLister) ListConditionTreeGlobal(ctx context.Context, resourceType resource.Type, dest Collection, conditionTree *ConditionTree) error { 155 return l.listWithConditionTree(ctx, resourceType, dest, NoLock, conditionTree) 156 } 157 158 // ListWithSelectForUpdate lists tenant scoped entities with tenant isolation subquery and 159 func (l *universalLister) ListWithSelectForUpdate(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, additionalConditions ...Condition) error { 160 return l.listWithTenantScope(ctx, resourceType, tenant, dest, ForUpdateLock, additionalConditions) 161 } 162 163 func (l *universalLister) listWithTenantScope(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, lockClause string, additionalConditions []Condition) error { 164 if tenant == "" { 165 return apperrors.NewTenantRequiredError() 166 } 167 168 if l.tenantColumn != nil { 169 additionalConditions = append(Conditions{NewEqualCondition(*l.tenantColumn, tenant)}, additionalConditions...) 170 return l.list(ctx, resourceType, dest, lockClause, additionalConditions...) 171 } 172 173 tenantIsolation, err := 174 newTenantIsolationConditionWithPlaceholder(resourceType, tenant, l.ownerCheck, true) 175 if err != nil { 176 return err 177 } 178 179 additionalConditions = append(additionalConditions, tenantIsolation) 180 181 return l.list(ctx, resourceType, dest, lockClause, additionalConditions...) 182 } 183 184 func (l *universalLister) listConditionTreeWithTenantScope(ctx context.Context, resourceType resource.Type, tenant string, dest Collection, lockClause string, conditionTree *ConditionTree) error { 185 if tenant == "" { 186 return apperrors.NewTenantRequiredError() 187 } 188 189 if l.tenantColumn != nil { 190 conditions := And(&ConditionTree{Operand: NewEqualCondition(*l.tenantColumn, tenant)}, conditionTree) 191 return l.listWithConditionTree(ctx, resourceType, dest, lockClause, conditions) 192 } 193 194 tenantIsolation, err := newTenantIsolationConditionWithPlaceholder(resourceType, tenant, l.ownerCheck, true) 195 if err != nil { 196 return err 197 } 198 199 conditions := And(&ConditionTree{Operand: tenantIsolation}, conditionTree) 200 201 return l.listWithConditionTree(ctx, resourceType, dest, lockClause, conditions) 202 } 203 204 // SetSelectedColumns sets the selected columns for the query. 205 func (l *universalLister) SetSelectedColumns(selectedColumns []string) { 206 l.selectedColumns = strings.Join(selectedColumns, ", ") 207 } 208 209 // Clone creates a new instance of the lister with the same configuration. 210 func (l *universalLister) Clone() *universalLister { 211 var clonedLister universalLister 212 213 clonedLister.resourceType = l.resourceType 214 clonedLister.tableName = l.tableName 215 clonedLister.selectedColumns = l.selectedColumns 216 clonedLister.tenantColumn = l.tenantColumn 217 clonedLister.orderByParams = append(clonedLister.orderByParams, l.orderByParams...) 218 219 return &clonedLister 220 } 221 222 // ListGlobal lists global entities without tenant isolation. 223 func (l *universalLister) ListGlobal(ctx context.Context, dest Collection, additionalConditions ...Condition) error { 224 return l.list(ctx, l.resourceType, dest, NoLock, additionalConditions...) 225 } 226 227 // ListGlobalWithSelectForUpdate lists global entities without tenant isolation. 228 func (l *universalLister) ListGlobalWithSelectForUpdate(ctx context.Context, dest Collection, additionalConditions ...Condition) error { 229 return l.list(ctx, l.resourceType, dest, ForUpdateLock, additionalConditions...) 230 } 231 232 func (l *universalLister) list(ctx context.Context, resourceType resource.Type, dest Collection, lockClause string, conditions ...Condition) error { 233 persist, err := persistence.FromCtx(ctx) 234 if err != nil { 235 return err 236 } 237 238 query, args, err := buildSelectQuery(l.tableName, l.selectedColumns, conditions, l.orderByParams, lockClause, true) 239 if err != nil { 240 return errors.Wrap(err, "while building list query") 241 } 242 243 log.C(ctx).Debugf("Executing DB query: %s", query) 244 err = persist.SelectContext(ctx, dest, query, args...) 245 246 return persistence.MapSQLError(ctx, err, resourceType, resource.List, "while fetching list of objects from '%s' table '%+v'", l.tableName, err) 247 } 248 249 func (l *universalLister) listWithConditionTree(ctx context.Context, resourceType resource.Type, dest Collection, lockClause string, conditionTree *ConditionTree) error { 250 persist, err := persistence.FromCtx(ctx) 251 if err != nil { 252 return err 253 } 254 255 query, args, err := buildSelectQueryFromTree(l.tableName, l.selectedColumns, conditionTree, l.orderByParams, lockClause, true) 256 if err != nil { 257 return errors.Wrap(err, "while building list query") 258 } 259 260 log.C(ctx).Debugf("Executing DB query: %s", query) 261 err = persist.SelectContext(ctx, dest, query, args...) 262 263 return persistence.MapSQLError(ctx, err, resourceType, resource.List, "while fetching list of objects from '%s' table", l.tableName) 264 }