diff --git a/server/pkg/envoyx/util_rbac.gen.go b/server/pkg/envoyx/util_rbac.gen.go index fadf7af6f..28a2d1abc 100644 --- a/server/pkg/envoyx/util_rbac.gen.go +++ b/server/pkg/envoyx/util_rbac.gen.go @@ -165,6 +165,19 @@ func SplitResourceIdentifier(ref string) (out map[string]Ref) { Scope: scope, } + case "corteza::system:dal-schema-alteration": + scope := Scope{} + + if gRef(pp, 0) == "" { + return + } + + out["Path.0"] = Ref{ + ResourceType: "corteza::system:dal-schema-alteration", + Identifiers: MakeIdentifiers(gRef(pp, 0)), + Scope: scope, + } + case "corteza::system:dal-sensitivity-level": scope := Scope{} diff --git a/server/store/adapters/rdbms/aux_types.gen.go b/server/store/adapters/rdbms/aux_types.gen.go index 2aee08a60..379504f35 100644 --- a/server/store/adapters/rdbms/aux_types.gen.go +++ b/server/store/adapters/rdbms/aux_types.gen.go @@ -351,6 +351,23 @@ type ( DeletedBy uint64 `db:"deleted_by"` } + // auxDalSchemaAlteration is an auxiliary structure used for transporting to/from RDBMS store + auxDalSchemaAlteration struct { + ID uint64 `db:"id"` + BatchID uint64 `db:"batchID"` + DependsOn uint64 `db:"dependsOn"` + Kind string `db:"kind"` + Params *systemType.DalSchemaAlterationParams `db:"params"` + CreatedAt time.Time `db:"created_at"` + UpdatedAt *time.Time `db:"updated_at"` + DeletedAt *time.Time `db:"deleted_at"` + CompletedAt *time.Time `db:"completed_at"` + CreatedBy uint64 `db:"created_by"` + UpdatedBy uint64 `db:"updated_by"` + DeletedBy uint64 `db:"deleted_by"` + CompletedBy uint64 `db:"completed_by"` + } + // auxDalSensitivityLevel is an auxiliary structure used for transporting to/from RDBMS store auxDalSensitivityLevel struct { ID uint64 `db:"id"` @@ -1851,6 +1868,68 @@ func (aux *auxDalConnection) scan(row scanner) error { ) } +// encodes DalSchemaAlteration to auxDalSchemaAlteration +// +// This function is auto-generated +func (aux *auxDalSchemaAlteration) encode(res *systemType.DalSchemaAlteration) (_ error) { + aux.ID = res.ID + aux.BatchID = res.BatchID + aux.DependsOn = res.DependsOn + aux.Kind = res.Kind + aux.Params = res.Params + aux.CreatedAt = res.CreatedAt + aux.UpdatedAt = res.UpdatedAt + aux.DeletedAt = res.DeletedAt + aux.CompletedAt = res.CompletedAt + aux.CreatedBy = res.CreatedBy + aux.UpdatedBy = res.UpdatedBy + aux.DeletedBy = res.DeletedBy + aux.CompletedBy = res.CompletedBy + return +} + +// decodes DalSchemaAlteration from auxDalSchemaAlteration +// +// This function is auto-generated +func (aux auxDalSchemaAlteration) decode() (res *systemType.DalSchemaAlteration, _ error) { + res = new(systemType.DalSchemaAlteration) + res.ID = aux.ID + res.BatchID = aux.BatchID + res.DependsOn = aux.DependsOn + res.Kind = aux.Kind + res.Params = aux.Params + res.CreatedAt = aux.CreatedAt + res.UpdatedAt = aux.UpdatedAt + res.DeletedAt = aux.DeletedAt + res.CompletedAt = aux.CompletedAt + res.CreatedBy = aux.CreatedBy + res.UpdatedBy = aux.UpdatedBy + res.DeletedBy = aux.DeletedBy + res.CompletedBy = aux.CompletedBy + return +} + +// scans row and fills auxDalSchemaAlteration fields +// +// This function is auto-generated +func (aux *auxDalSchemaAlteration) scan(row scanner) error { + return row.Scan( + &aux.ID, + &aux.BatchID, + &aux.DependsOn, + &aux.Kind, + &aux.Params, + &aux.CreatedAt, + &aux.UpdatedAt, + &aux.DeletedAt, + &aux.CompletedAt, + &aux.CreatedBy, + &aux.UpdatedBy, + &aux.DeletedBy, + &aux.CompletedBy, + ) +} + // encodes DalSensitivityLevel to auxDalSensitivityLevel // // This function is auto-generated diff --git a/server/store/adapters/rdbms/filters.gen.go b/server/store/adapters/rdbms/filters.gen.go index ce39ffd88..79dce7ee9 100644 --- a/server/store/adapters/rdbms/filters.gen.go +++ b/server/store/adapters/rdbms/filters.gen.go @@ -93,6 +93,9 @@ type ( // optional dalConnection filter function called after the generated function DalConnection func(*Store, systemType.DalConnectionFilter) ([]goqu.Expression, systemType.DalConnectionFilter, error) + // optional dalSchemaAlteration filter function called after the generated function + DalSchemaAlteration func(*Store, systemType.DalSchemaAlterationFilter) ([]goqu.Expression, systemType.DalSchemaAlterationFilter, error) + // optional dalSensitivityLevel filter function called after the generated function DalSensitivityLevel func(*Store, systemType.DalSensitivityLevelFilter) ([]goqu.Expression, systemType.DalSensitivityLevelFilter, error) @@ -791,6 +794,30 @@ func DalConnectionFilter(d drivers.Dialect, f systemType.DalConnectionFilter) (e return ee, f, err } +// DalSchemaAlterationFilter returns logical expressions +// +// This function is called from Store.QueryDalSchemaAlterations() and can be extended +// by setting Store.Filters.DalSchemaAlteration. Extension is called after all expressions +// are generated and can choose to ignore or alter them. +// +// This function is auto-generated +func DalSchemaAlterationFilter(d drivers.Dialect, f systemType.DalSchemaAlterationFilter) (ee []goqu.Expression, _ systemType.DalSchemaAlterationFilter, err error) { + + if expr := stateNilComparison(d, "deleted_at", f.Deleted); expr != nil { + ee = append(ee, expr) + } + + if val := strings.TrimSpace(f.Kind); len(val) > 0 { + ee = append(ee, goqu.C("kind").Eq(f.Kind)) + } + + if len(f.AlterationID) > 0 { + ee = append(ee, goqu.C("alteration_id").In(f.AlterationID)) + } + + return ee, f, err +} + // DalSensitivityLevelFilter returns logical expressions // // This function is called from Store.QueryDalSensitivityLevels() and can be extended diff --git a/server/store/adapters/rdbms/queries.gen.go b/server/store/adapters/rdbms/queries.gen.go index 4cf9b7c3b..2f8d14507 100644 --- a/server/store/adapters/rdbms/queries.gen.go +++ b/server/store/adapters/rdbms/queries.gen.go @@ -2427,6 +2427,126 @@ var ( } } + // dalSchemaAlterationTable represents dalSchemaAlterations store table + // + // This value is auto-generated + dalSchemaAlterationTable = goqu.T("dal_schema_alterations") + + // dalSchemaAlterationSelectQuery assembles select query for fetching dalSchemaAlterations + // + // This function is auto-generated + dalSchemaAlterationSelectQuery = func(d goqu.DialectWrapper) *goqu.SelectDataset { + return d.Select( + "id", + "batchID", + "dependsOn", + "kind", + "params", + "created_at", + "updated_at", + "deleted_at", + "completed_at", + "created_by", + "updated_by", + "deleted_by", + "completed_by", + ).From(dalSchemaAlterationTable) + } + + // dalSchemaAlterationInsertQuery assembles query inserting dalSchemaAlterations + // + // This function is auto-generated + dalSchemaAlterationInsertQuery = func(d goqu.DialectWrapper, res *systemType.DalSchemaAlteration) *goqu.InsertDataset { + return d.Insert(dalSchemaAlterationTable). + Rows(goqu.Record{ + "id": res.ID, + "batchID": res.BatchID, + "dependsOn": res.DependsOn, + "kind": res.Kind, + "params": res.Params, + "created_at": res.CreatedAt, + "updated_at": res.UpdatedAt, + "deleted_at": res.DeletedAt, + "completed_at": res.CompletedAt, + "created_by": res.CreatedBy, + "updated_by": res.UpdatedBy, + "deleted_by": res.DeletedBy, + "completed_by": res.CompletedBy, + }) + } + + // dalSchemaAlterationUpsertQuery assembles (insert+on-conflict) query for replacing dalSchemaAlterations + // + // This function is auto-generated + dalSchemaAlterationUpsertQuery = func(d goqu.DialectWrapper, res *systemType.DalSchemaAlteration) *goqu.InsertDataset { + var target = `,id` + + return dalSchemaAlterationInsertQuery(d, res). + OnConflict( + goqu.DoUpdate(target[1:], + goqu.Record{ + "batchID": res.BatchID, + "dependsOn": res.DependsOn, + "kind": res.Kind, + "params": res.Params, + "created_at": res.CreatedAt, + "updated_at": res.UpdatedAt, + "deleted_at": res.DeletedAt, + "completed_at": res.CompletedAt, + "created_by": res.CreatedBy, + "updated_by": res.UpdatedBy, + "deleted_by": res.DeletedBy, + "completed_by": res.CompletedBy, + }, + ), + ) + } + + // dalSchemaAlterationUpdateQuery assembles query for updating dalSchemaAlterations + // + // This function is auto-generated + dalSchemaAlterationUpdateQuery = func(d goqu.DialectWrapper, res *systemType.DalSchemaAlteration) *goqu.UpdateDataset { + return d.Update(dalSchemaAlterationTable). + Set(goqu.Record{ + "batchID": res.BatchID, + "dependsOn": res.DependsOn, + "kind": res.Kind, + "params": res.Params, + "created_at": res.CreatedAt, + "updated_at": res.UpdatedAt, + "deleted_at": res.DeletedAt, + "completed_at": res.CompletedAt, + "created_by": res.CreatedBy, + "updated_by": res.UpdatedBy, + "deleted_by": res.DeletedBy, + "completed_by": res.CompletedBy, + }). + Where(dalSchemaAlterationPrimaryKeys(res)) + } + + // dalSchemaAlterationDeleteQuery assembles delete query for removing dalSchemaAlterations + // + // This function is auto-generated + dalSchemaAlterationDeleteQuery = func(d goqu.DialectWrapper, ee ...goqu.Expression) *goqu.DeleteDataset { + return d.Delete(dalSchemaAlterationTable).Where(ee...) + } + + // dalSchemaAlterationDeleteQuery assembles delete query for removing dalSchemaAlterations + // + // This function is auto-generated + dalSchemaAlterationTruncateQuery = func(d goqu.DialectWrapper) *goqu.TruncateDataset { + return d.Truncate(dalSchemaAlterationTable) + } + + // dalSchemaAlterationPrimaryKeys assembles set of conditions for all primary keys + // + // This function is auto-generated + dalSchemaAlterationPrimaryKeys = func(res *systemType.DalSchemaAlteration) goqu.Ex { + return goqu.Ex{ + "id": res.ID, + } + } + // dalSensitivityLevelTable represents dalSensitivityLevels store table // // This value is auto-generated diff --git a/server/store/adapters/rdbms/rdbms.gen.go b/server/store/adapters/rdbms/rdbms.gen.go index 9cc661613..dd1767082 100644 --- a/server/store/adapters/rdbms/rdbms.gen.go +++ b/server/store/adapters/rdbms/rdbms.gen.go @@ -51,6 +51,7 @@ var ( _ store.ComposePageLayouts = &Store{} _ store.Credentials = &Store{} _ store.DalConnections = &Store{} + _ store.DalSchemaAlterations = &Store{} _ store.DalSensitivityLevels = &Store{} _ store.DataPrivacyRequests = &Store{} _ store.DataPrivacyRequestComments = &Store{} @@ -11329,6 +11330,545 @@ func (s *Store) checkDalConnectionConstraints(ctx context.Context, res *systemTy return nil } +// CreateDalSchemaAlteration creates one or more rows in dalSchemaAlteration collection +// +// This function is auto-generated +func (s *Store) CreateDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) (err error) { + for i := range rr { + if err = s.checkDalSchemaAlterationConstraints(ctx, rr[i]); err != nil { + return + } + + if err = s.Exec(ctx, dalSchemaAlterationInsertQuery(s.Dialect.GOQU(), rr[i])); err != nil { + return + } + } + + return +} + +// UpdateDalSchemaAlteration updates one or more existing entries in dalSchemaAlteration collection +// +// This function is auto-generated +func (s *Store) UpdateDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) (err error) { + for i := range rr { + if err = s.checkDalSchemaAlterationConstraints(ctx, rr[i]); err != nil { + return + } + + if err = s.Exec(ctx, dalSchemaAlterationUpdateQuery(s.Dialect.GOQU(), rr[i])); err != nil { + return + } + } + + return +} + +// UpsertDalSchemaAlteration updates one or more existing entries in dalSchemaAlteration collection +// +// This function is auto-generated +func (s *Store) UpsertDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) (err error) { + for i := range rr { + if err = s.checkDalSchemaAlterationConstraints(ctx, rr[i]); err != nil { + return + } + + // @todo this solution is ok for now but could be problematic when we start + // batching together DB operations. + if s.Dialect.Nuances().TwoStepUpsert { + var rsp sql.Result + rsp, err = s.ExecR(ctx, dalSchemaAlterationUpdateQuery(s.Dialect.GOQU(), rr[i])) + if err != nil { + return + } + if c, err := rsp.RowsAffected(); err != nil { + return err + } else if c > 0 { + continue + } + + err = s.Exec(ctx, dalSchemaAlterationInsertQuery(s.Dialect.GOQU(), rr[i])) + if err != nil { + return + } + } else { + err = s.Exec(ctx, dalSchemaAlterationUpsertQuery(s.Dialect.GOQU(), rr[i])) + if err != nil { + return + } + } + } + + return +} + +// DeleteDalSchemaAlteration Deletes one or more entries from dalSchemaAlteration collection +// +// This function is auto-generated +func (s *Store) DeleteDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) (err error) { + for i := range rr { + if err = s.Exec(ctx, dalSchemaAlterationDeleteQuery(s.Dialect.GOQU(), dalSchemaAlterationPrimaryKeys(rr[i]))); err != nil { + return + } + } + + return nil +} + +// DeleteDalSchemaAlterationByID deletes single entry from dalSchemaAlteration collection +// +// This function is auto-generated +func (s *Store) DeleteDalSchemaAlterationByID(ctx context.Context, id uint64) error { + return s.Exec(ctx, dalSchemaAlterationDeleteQuery(s.Dialect.GOQU(), goqu.Ex{ + "id": id, + })) +} + +// TruncateDalSchemaAlterations Deletes all rows from the dalSchemaAlteration collection +func (s *Store) TruncateDalSchemaAlterations(ctx context.Context) error { + return s.Exec(ctx, dalSchemaAlterationTruncateQuery(s.Dialect.GOQU())) +} + +// SearchDalSchemaAlterations returns (filtered) set of DalSchemaAlterations +// +// This function is auto-generated +func (s *Store) SearchDalSchemaAlterations(ctx context.Context, f systemType.DalSchemaAlterationFilter) (set systemType.DalSchemaAlterationSet, _ systemType.DalSchemaAlterationFilter, err error) { + + // Cleanup unwanted cursor values (only relevant is f.PageCursor, next&prev are reset and returned) + f.PrevPage, f.NextPage = nil, nil + + if f.PageCursor != nil { + if f.IncPageNavigation || f.IncTotal { + return nil, f, fmt.Errorf("not allowed to fetch page navigation or total item count with page cursor") + } + + // Page cursor exists; we need to validate it against used sort + // To cover the case when paging cursor is set but sorting is empty, we collect the sorting instructions + // from the cursor. + // This (extracted sorting info) is then returned as part of response + if f.Sort, err = f.PageCursor.Sort(f.Sort); err != nil { + return + } + } + + // Make sure results are always sorted at least by primary keys + if f.Sort.Get("id") == nil { + f.Sort = append(f.Sort, &filter.SortExpr{ + Column: "id", + Descending: f.Sort.LastDescending(), + }) + } + + // Cloned sorting instructions for the actual sorting + // Original are passed to the etchFullPageOfDalSchemaAlterations fn used for cursor creation; + // direction information it MUST keep the initial + sort := f.Sort.Clone() + + // When cursor for a previous page is used it's marked as reversed + // This tells us to flip the descending flag on all used sort keys + if f.PageCursor != nil && f.PageCursor.ROrder { + sort.Reverse() + } + + set, f.PrevPage, f.NextPage, err = s.fetchFullPageOfDalSchemaAlterations(ctx, f, sort) + + f.PageCursor = nil + if err != nil { + return nil, f, err + } + + if f.IncTotal { + // Calc total from the number of items fetched + // even if we do build the page navigation + f.Total = uint(len(set)) + + if f.Limit > 0 && uint(len(set)) == f.Limit { + // there are fewer items fetched then requested limit + limit := f.Limit + f.Limit = 0 + var navSet systemType.DalSchemaAlterationSet + if navSet, _, _, err = s.fetchFullPageOfDalSchemaAlterations(ctx, f, sort); err != nil { + return + } else { + f.Total = uint(len(navSet)) + f.Limit = limit + } + } + } + + return set, f, nil +} + +// fetchFullPageOfDalSchemaAlterations collects all requested results. +// +// Function applies: +// - cursor conditions (where ...) +// - limit +// +// Main responsibility of this function is to perform additional sequential queries in case when not enough results +// are collected due to failed check on a specific row (by check fn). +// +// Function then moves cursor to the last item fetched +// +// This function is auto-generated +func (s *Store) fetchFullPageOfDalSchemaAlterations( + ctx context.Context, + filter systemType.DalSchemaAlterationFilter, + sort filter.SortExprSet, +) (set []*systemType.DalSchemaAlteration, prev, next *filter.PagingCursor, err error) { + var ( + aux []*systemType.DalSchemaAlteration + + // When cursor for a previous page is used it's marked as reversed + // This tells us to flip the descending flag on all used sort keys + reversedOrder = filter.PageCursor != nil && filter.PageCursor.ROrder + + // Copy no. of required items to limit + // Limit will change when doing subsequent queries to fill + // the set with all required items + limit = filter.Limit + + reqItems = filter.Limit + + // cursor to prev. page is only calculated when cursor is used + hasPrev = filter.PageCursor != nil + + // next cursor is calculated when there are more pages to come + hasNext bool + + tryFilter systemType.DalSchemaAlterationFilter + ) + + set = make([]*systemType.DalSchemaAlteration, 0, DefaultSliceCapacity) + + for try := 0; try < MaxRefetches; try++ { + // Copy filter & apply custom sorting that might be affected by cursor + tryFilter = filter + tryFilter.Sort = sort + + if limit > 0 { + // fetching + 1 to peak ahead if there are more items + // we can fetch (next-page cursor) + tryFilter.Limit = limit + 1 + } + + if aux, hasNext, err = s.QueryDalSchemaAlterations(ctx, tryFilter); err != nil { + return nil, nil, nil, err + } + + if len(aux) == 0 { + // nothing fetched + break + } + + // append fetched items + set = append(set, aux...) + + if reqItems == 0 || !hasNext { + // no max requested items specified, break out + break + } + + collected := uint(len(set)) + + if reqItems > collected { + // not enough items fetched, try again with adjusted limit + limit = reqItems - collected + + if limit < MinEnsureFetchLimit { + // In case limit is set very low and we've missed records in the first fetch, + // make sure next fetch limit is a bit higher + limit = MinEnsureFetchLimit + } + + // Update cursor so that it points to the last item fetched + tryFilter.PageCursor = s.collectDalSchemaAlterationCursorValues(set[collected-1], filter.Sort...) + + // Copy reverse flag from sorting + tryFilter.PageCursor.LThen = filter.Sort.Reversed() + continue + } + + if reqItems < collected { + set = set[:reqItems] + } + + break + } + + collected := len(set) + + if collected == 0 { + return nil, nil, nil, nil + } + + if reversedOrder { + // Fetched set needs to be reversed because we've forced a descending order to get the previous page + for i, j := 0, collected-1; i < j; i, j = i+1, j-1 { + set[i], set[j] = set[j], set[i] + } + + // when in reverse-order rules on what cursor to return change + hasPrev, hasNext = hasNext, hasPrev + } + + if hasPrev { + prev = s.collectDalSchemaAlterationCursorValues(set[0], filter.Sort...) + prev.ROrder = true + prev.LThen = !filter.Sort.Reversed() + } + + if hasNext { + next = s.collectDalSchemaAlterationCursorValues(set[collected-1], filter.Sort...) + next.LThen = filter.Sort.Reversed() + } + + return set, prev, next, nil +} + +// QueryDalSchemaAlterations queries the database, converts and checks each row and returns collected set +// +// With generics, we can remove this per-resource-generated function +// and replace it with a single utility fetcher +// +// This function is auto-generated +func (s *Store) QueryDalSchemaAlterations( + ctx context.Context, + f systemType.DalSchemaAlterationFilter, +) (_ []*systemType.DalSchemaAlteration, more bool, err error) { + var ( + set = make([]*systemType.DalSchemaAlteration, 0, DefaultSliceCapacity) + res *systemType.DalSchemaAlteration + aux *auxDalSchemaAlteration + rows *sql.Rows + count uint + expr, tExpr []goqu.Expression + + sortExpr []exp.OrderedExpression + ) + + if s.Filters.DalSchemaAlteration != nil { + // extended filter set + tExpr, f, err = s.Filters.DalSchemaAlteration(s, f) + } else { + // using generated filter + tExpr, f, err = DalSchemaAlterationFilter(s.Dialect, f) + } + + if err != nil { + err = fmt.Errorf("could generate filter expression for DalSchemaAlteration: %w", err) + return + } + + expr = append(expr, tExpr...) + + // paging feature is enabled + if f.PageCursor != nil { + if tExpr, err = cursorWithSorting(f.PageCursor, s.sortableDalSchemaAlterationFields()); err != nil { + return + } else { + expr = append(expr, tExpr...) + } + } + + query := dalSchemaAlterationSelectQuery(s.Dialect.GOQU()).Where(expr...) + + // sorting feature is enabled + if sortExpr, err = order(f.Sort, s.sortableDalSchemaAlterationFields()); err != nil { + err = fmt.Errorf("could generate order expression for DalSchemaAlteration: %w", err) + return + } + + if len(sortExpr) > 0 { + query = query.Order(sortExpr...) + } + + if f.Limit > 0 { + query = query.Limit(f.Limit) + } + + rows, err = s.Query(ctx, query) + if err != nil { + err = fmt.Errorf("could not query DalSchemaAlteration: %w", err) + return + } + + if err = rows.Err(); err != nil { + err = fmt.Errorf("could not query DalSchemaAlteration: %w", err) + return + } + + defer func() { + closeError := rows.Close() + if err == nil { + // return error from close + err = closeError + } + }() + + for rows.Next() { + if err = rows.Err(); err != nil { + err = fmt.Errorf("could not query DalSchemaAlteration: %w", err) + return + } + + aux = new(auxDalSchemaAlteration) + if err = aux.scan(rows); err != nil { + err = fmt.Errorf("could not scan rows for DalSchemaAlteration: %w", err) + return + } + + count++ + if res, err = aux.decode(); err != nil { + err = fmt.Errorf("could not decode DalSchemaAlteration: %w", err) + return + } + + set = append(set, res) + } + + return set, f.Limit > 0 && count >= f.Limit, err + +} + +// LookupDalSchemaAlterationByID searches for resource translation by ID +// It also returns deleted resource translations. +// +// This function is auto-generated +func (s *Store) LookupDalSchemaAlterationByID(ctx context.Context, id uint64) (_ *systemType.DalSchemaAlteration, err error) { + var ( + rows *sql.Rows + aux = new(auxDalSchemaAlteration) + lookup = dalSchemaAlterationSelectQuery(s.Dialect.GOQU()).Where( + goqu.I("id").Eq(id), + ).Limit(1) + ) + + rows, err = s.Query(ctx, lookup) + if err != nil { + return + } + + defer func() { + closeError := rows.Close() + if err == nil { + // return error from close + err = closeError + } + }() + + if err = rows.Err(); err != nil { + return + } + + if !rows.Next() { + return nil, store.ErrNotFound.Stack(1) + } + + if err = aux.scan(rows); err != nil { + return + } + + return aux.decode() +} + +// sortableDalSchemaAlterationFields returns all columns flagged as sortable +// +// # Notes +// With optional string arg, all columns are returned aliased +// +// This function is auto-generated +func (Store) sortableDalSchemaAlterationFields() map[string]string { + return map[string]string{ + "completed_at": "completed_at", + "completedat": "completed_at", + "created_at": "created_at", + "createdat": "created_at", + "deleted_at": "deleted_at", + "deletedat": "deleted_at", + "id": "id", + "updated_at": "updated_at", + "updatedat": "updated_at", + } +} + +// collectDalSchemaAlterationCursorValues collects values from the given resource that and sets them to the cursor +// to be used for pagination +// +// Values that are collected must come from sortable, unique or primary columns/fields +// At least one of the collected columns must be flagged as unique, otherwise fn appends primary keys at the end +// +// # Known issues: +// +// When collecting cursor values for query that sorts by unique column with partial index (ie: unique handle on +// undeleted items) +// +// This function is auto-generated +func (s *Store) collectDalSchemaAlterationCursorValues(res *systemType.DalSchemaAlteration, cc ...*filter.SortExpr) *filter.PagingCursor { + var ( + cur = &filter.PagingCursor{LThen: filter.SortExprSet(cc).Reversed()} + + hasUnique bool + + pkID bool + + collect = func(cc ...*filter.SortExpr) { + getVal := func(col string) interface{} { + switch col { + case "id": + pkID = true + return res.ID + case "createdAt": + return res.CreatedAt + case "updatedAt": + return res.UpdatedAt + case "deletedAt": + return res.DeletedAt + case "completedAt": + return res.CompletedAt + } + return nil + } + + for _, c := range cc { + switch c.Modifier() { + case filter.COALESCE: + var val interface{} + for _, col := range c.Columns() { + if reflect2.IsNil(val) { + val = getVal(col) + } + } + cur.SetModifier(c.Column, val, c.Descending, c.Modifier(), c.Columns()...) + default: + cur.Set(c.Column, getVal(c.Column), c.Descending) + } + } + } + ) + + _ = hasUnique + + collect(cc...) + if !hasUnique || !pkID { + collect(&filter.SortExpr{Column: "id", Descending: false}) + } + + return cur + +} + +// checkDalSchemaAlterationConstraints performs lookups (on valid) resource to check if any of the values on unique fields +// already exists in the store +// +// Using built-in constraint checking would be more performant, but unfortunately we cannot rely +// on the full support (MySQL does not support conditional indexes) +// +// This function is auto-generated +func (s *Store) checkDalSchemaAlterationConstraints(ctx context.Context, res *systemType.DalSchemaAlteration) (err error) { + return nil +} + // CreateDalSensitivityLevel creates one or more rows in dalSensitivityLevel collection // // This function is auto-generated diff --git a/server/store/interfaces.gen.go b/server/store/interfaces.gen.go index 02ee26967..1c4640849 100644 --- a/server/store/interfaces.gen.go +++ b/server/store/interfaces.gen.go @@ -65,6 +65,7 @@ type ( ComposePageLayouts Credentials DalConnections + DalSchemaAlterations DalSensitivityLevels DataPrivacyRequests DataPrivacyRequestComments @@ -369,6 +370,18 @@ type ( LookupDalConnectionByHandle(ctx context.Context, handle string) (*systemType.DalConnection, error) } + DalSchemaAlterations interface { + SearchDalSchemaAlterations(ctx context.Context, f systemType.DalSchemaAlterationFilter) (systemType.DalSchemaAlterationSet, systemType.DalSchemaAlterationFilter, error) + CreateDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) error + UpdateDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) error + UpsertDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) error + DeleteDalSchemaAlteration(ctx context.Context, rr ...*systemType.DalSchemaAlteration) error + + DeleteDalSchemaAlterationByID(ctx context.Context, id uint64) error + TruncateDalSchemaAlterations(ctx context.Context) error + LookupDalSchemaAlterationByID(ctx context.Context, id uint64) (*systemType.DalSchemaAlteration, error) + } + DalSensitivityLevels interface { SearchDalSensitivityLevels(ctx context.Context, f systemType.DalSensitivityLevelFilter) (systemType.DalSensitivityLevelSet, systemType.DalSensitivityLevelFilter, error) CreateDalSensitivityLevel(ctx context.Context, rr ...*systemType.DalSensitivityLevel) error @@ -2053,6 +2066,63 @@ func LookupDalConnectionByHandle(ctx context.Context, s DalConnections, handle s return s.LookupDalConnectionByHandle(ctx, handle) } +// SearchDalSchemaAlterations returns all matching DalSchemaAlterations from store +// +// This function is auto-generated +func SearchDalSchemaAlterations(ctx context.Context, s DalSchemaAlterations, f systemType.DalSchemaAlterationFilter) (systemType.DalSchemaAlterationSet, systemType.DalSchemaAlterationFilter, error) { + return s.SearchDalSchemaAlterations(ctx, f) +} + +// CreateDalSchemaAlteration creates one or more DalSchemaAlterations in store +// +// This function is auto-generated +func CreateDalSchemaAlteration(ctx context.Context, s DalSchemaAlterations, rr ...*systemType.DalSchemaAlteration) error { + return s.CreateDalSchemaAlteration(ctx, rr...) +} + +// UpdateDalSchemaAlteration updates one or more (existing) DalSchemaAlterations in store +// +// This function is auto-generated +func UpdateDalSchemaAlteration(ctx context.Context, s DalSchemaAlterations, rr ...*systemType.DalSchemaAlteration) error { + return s.UpdateDalSchemaAlteration(ctx, rr...) +} + +// UpsertDalSchemaAlteration creates new or updates existing one or more DalSchemaAlterations in store +// +// This function is auto-generated +func UpsertDalSchemaAlteration(ctx context.Context, s DalSchemaAlterations, rr ...*systemType.DalSchemaAlteration) error { + return s.UpsertDalSchemaAlteration(ctx, rr...) +} + +// DeleteDalSchemaAlteration deletes one or more DalSchemaAlterations from store +// +// This function is auto-generated +func DeleteDalSchemaAlteration(ctx context.Context, s DalSchemaAlterations, rr ...*systemType.DalSchemaAlteration) error { + return s.DeleteDalSchemaAlteration(ctx, rr...) +} + +// DeleteDalSchemaAlterationByID deletes one or more DalSchemaAlterations from store +// +// This function is auto-generated +func DeleteDalSchemaAlterationByID(ctx context.Context, s DalSchemaAlterations, id uint64) error { + return s.DeleteDalSchemaAlterationByID(ctx, id) +} + +// TruncateDalSchemaAlterations Deletes all DalSchemaAlterations from store +// +// This function is auto-generated +func TruncateDalSchemaAlterations(ctx context.Context, s DalSchemaAlterations) error { + return s.TruncateDalSchemaAlterations(ctx) +} + +// LookupDalSchemaAlterationByID searches for resource translation by ID +// It also returns deleted resource translations. +// +// This function is auto-generated +func LookupDalSchemaAlterationByID(ctx context.Context, s DalSchemaAlterations, id uint64) (*systemType.DalSchemaAlteration, error) { + return s.LookupDalSchemaAlterationByID(ctx, id) +} + // SearchDalSensitivityLevels returns all matching DalSensitivityLevels from store // // This function is auto-generated diff --git a/server/store/tests/all_test.go b/server/store/tests/all_test.go index 48df77a17..58e6dfd43 100644 --- a/server/store/tests/all_test.go +++ b/server/store/tests/all_test.go @@ -77,6 +77,9 @@ func testAllGenerated(t *testing.T, s store.Storer) { t.Run("dalConnection", func(t *testing.T) { testDalConnections(t, s) }) + t.Run("dalSchemaAlteration", func(t *testing.T) { + testDalSchemaAlterations(t, s) + }) t.Run("dalSensitivityLevel", func(t *testing.T) { testDalSensitivityLevels(t, s) }) diff --git a/server/store/tests/dal_schema_alterations.go b/server/store/tests/dal_schema_alterations.go new file mode 100644 index 000000000..59ce9a5c3 --- /dev/null +++ b/server/store/tests/dal_schema_alterations.go @@ -0,0 +1,10 @@ +package tests + +import ( + "testing" + + "github.com/cortezaproject/corteza/server/store" +) + +func testDalSchemaAlterations(t *testing.T, s store.DalSensitivityLevels) { +} diff --git a/server/system/component.cue b/server/system/component.cue index dc1949c59..8491bfac3 100644 --- a/server/system/component.cue +++ b/server/system/component.cue @@ -31,6 +31,7 @@ component: schema.#component & { "user": user "dal-connection": dal_connection "dal-sensitivity-level": dal_sensitivity_level + "dal-schema-alteration": dal_schema_alteration } rbac: operations: { diff --git a/server/system/dal_schema_alteration.cue b/server/system/dal_schema_alteration.cue new file mode 100644 index 000000000..4e08bba9c --- /dev/null +++ b/server/system/dal_schema_alteration.cue @@ -0,0 +1,89 @@ +package system + +import ( + "github.com/cortezaproject/corteza/server/codegen/schema" +) + +dal_schema_alteration: { + features: { + labels: false + checkFn: false + } + + model: { + // lengths for the lang, resource fields are now a bit shorter + // Reason for that is supported index length in MySQL + attributes: { + id: schema.IdField + batchID: { + goType: "uint64" + dal: { type: "ID" } + } + dependsOn: { + goType: "uint64" + dal: { type: "Ref", refModelResType: "corteza::system:dal-schema-alteration" } + } + + kind: { + dal: { type: "Text", length: 256 } + } + params: { + goType: "*types.DalSchemaAlterationParams" + dal: { type: "JSON", defaultEmptyObject: true } + omitSetter: true + omitGetter: true + } + + created_at: schema.SortableTimestampNowField + updated_at: schema.SortableTimestampNilField + deleted_at: schema.SortableTimestampNilField + completed_at: schema.SortableTimestampNilField + created_by: schema.AttributeUserRef + updated_by: schema.AttributeUserRef + deleted_by: schema.AttributeUserRef + completed_by: schema.AttributeUserRef + } + + indexes: { + "primary": { attribute: "id" } + "unique_alteration": { + fields: [ + { attribute: "id", modifiers: [ "LOWERCASE" ] }, + { attribute: "batchID", modifiers: [ "LOWERCASE" ] }, + ] + } + } + } + + envoy: { + // Not needed for this resource (yet) + omit: true + } + + filter: { + struct: { + alteration_id: {goType: "[]uint64", ident: "alterationID" } + batch_id: {goType: "[]uint64", ident: "batchID" } + kind: {} + deleted: {goType: "filter.State", storeIdent: "deleted_at"} + completed: {goType: "filter.State", storeIdent: "completed_at"} + } + + byValue: ["kind", "alteration_id"] + byNilState: ["deleted"] + } + + store: { + api: { + lookups: [ + { + fields: ["id"] + description: """ + searches for resource translation by ID + It also returns deleted resource translations. + """ + }, + ] + } + } +} diff --git a/server/system/envoy/store_encode.gen.go b/server/system/envoy/store_encode.gen.go index 83fce2b23..9fe83844e 100644 --- a/server/system/envoy/store_encode.gen.go +++ b/server/system/envoy/store_encode.gen.go @@ -71,6 +71,7 @@ func (e StoreEncoder) Prepare(ctx context.Context, p envoyx.EncodeParams, rt str return e.prepareDalConnection(ctx, p, s, nn) case types.DalSensitivityLevelResourceType: return e.prepareDalSensitivityLevel(ctx, p, s, nn) + default: return e.prepare(ctx, p, s, rt, nn) } diff --git a/server/system/envoy/yaml_encode.gen.go b/server/system/envoy/yaml_encode.gen.go index ca1c20de7..6f71e090c 100644 --- a/server/system/envoy/yaml_encode.gen.go +++ b/server/system/envoy/yaml_encode.gen.go @@ -160,6 +160,7 @@ func (e YamlEncoder) Encode(ctx context.Context, p envoyx.EncodeParams, rt strin if err != nil { return } + default: out, err = e.encode(ctx, out, p, rt, nodes, tt) if err != nil { diff --git a/server/system/model/models.gen.go b/server/system/model/models.gen.go index be7f2036a..db30b3c92 100644 --- a/server/system/model/models.gen.go +++ b/server/system/model/models.gen.go @@ -1117,6 +1117,159 @@ var DalConnection = &dal.Model{ }, } +var DalSchemaAlteration = &dal.Model{ + Ident: "dal_schema_alterations", + ResourceType: types.DalSchemaAlterationResourceType, + + Attributes: dal.AttributeSet{ + &dal.Attribute{ + Ident: "ID", + Type: &dal.TypeID{}, + Store: &dal.CodecAlias{Ident: "id"}, + }, + + &dal.Attribute{ + Ident: "BatchID", + Type: &dal.TypeID{}, + Store: &dal.CodecAlias{Ident: "batchID"}, + }, + + &dal.Attribute{ + Ident: "DependsOn", + Type: &dal.TypeRef{ + RefAttribute: "id", + RefModel: &dal.ModelRef{ + ResourceType: "corteza::system:dal-schema-alteration", + }, + }, + Store: &dal.CodecAlias{Ident: "dependsOn"}, + }, + + &dal.Attribute{ + Ident: "Kind", + Type: &dal.TypeText{Length: 256}, + Store: &dal.CodecAlias{Ident: "kind"}, + }, + + &dal.Attribute{ + Ident: "Params", + Type: &dal.TypeJSON{ + DefaultValue: "{}", + }, + Store: &dal.CodecAlias{Ident: "params"}, + }, + + &dal.Attribute{ + Ident: "CreatedAt", Sortable: true, + Type: &dal.TypeTimestamp{ + DefaultCurrentTimestamp: true, Timezone: true, Precision: -1, + }, + Store: &dal.CodecAlias{Ident: "created_at"}, + }, + + &dal.Attribute{ + Ident: "UpdatedAt", Sortable: true, + Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1}, + Store: &dal.CodecAlias{Ident: "updated_at"}, + }, + + &dal.Attribute{ + Ident: "DeletedAt", Sortable: true, + Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1}, + Store: &dal.CodecAlias{Ident: "deleted_at"}, + }, + + &dal.Attribute{ + Ident: "CompletedAt", Sortable: true, + Type: &dal.TypeTimestamp{Nullable: true, Timezone: true, Precision: -1}, + Store: &dal.CodecAlias{Ident: "completed_at"}, + }, + + &dal.Attribute{ + Ident: "CreatedBy", + Type: &dal.TypeRef{HasDefault: true, + DefaultValue: 0, + + RefAttribute: "id", + RefModel: &dal.ModelRef{ + ResourceType: "corteza::system:user", + }, + }, + Store: &dal.CodecAlias{Ident: "created_by"}, + }, + + &dal.Attribute{ + Ident: "UpdatedBy", + Type: &dal.TypeRef{HasDefault: true, + DefaultValue: 0, + + RefAttribute: "id", + RefModel: &dal.ModelRef{ + ResourceType: "corteza::system:user", + }, + }, + Store: &dal.CodecAlias{Ident: "updated_by"}, + }, + + &dal.Attribute{ + Ident: "DeletedBy", + Type: &dal.TypeRef{HasDefault: true, + DefaultValue: 0, + + RefAttribute: "id", + RefModel: &dal.ModelRef{ + ResourceType: "corteza::system:user", + }, + }, + Store: &dal.CodecAlias{Ident: "deleted_by"}, + }, + + &dal.Attribute{ + Ident: "CompletedBy", + Type: &dal.TypeRef{HasDefault: true, + DefaultValue: 0, + + RefAttribute: "id", + RefModel: &dal.ModelRef{ + ResourceType: "corteza::system:user", + }, + }, + Store: &dal.CodecAlias{Ident: "completed_by"}, + }, + }, + + Indexes: dal.IndexSet{ + &dal.Index{ + Ident: "PRIMARY", + Type: "BTREE", + + Fields: []*dal.IndexField{ + { + AttributeIdent: "ID", + }, + }, + }, + + &dal.Index{ + Ident: "dal_schema_alterations_uniqueAlteration", + Type: "BTREE", + Unique: true, + + Fields: []*dal.IndexField{ + { + AttributeIdent: "ID", + Modifiers: []dal.IndexFieldModifier{"LOWERCASE"}, + }, + + { + AttributeIdent: "BatchID", + Modifiers: []dal.IndexFieldModifier{"LOWERCASE"}, + }, + }, + }, + }, +} + var DalSensitivityLevel = &dal.Model{ Ident: "dal_sensitivity_levels", ResourceType: types.DalSensitivityLevelResourceType, @@ -2493,6 +2646,7 @@ func init() { AuthSession, Credential, DalConnection, + DalSchemaAlteration, DalSensitivityLevel, DataPrivacyRequest, DataPrivacyRequestComment, diff --git a/server/system/rest.yaml b/server/system/rest.yaml index e63d870af..db46ac947 100644 --- a/server/system/rest.yaml +++ b/server/system/rest.yaml @@ -914,6 +914,65 @@ endpoints: required: true title: Connection ID +- title: Data access layer schema alterations + path: "/dal/schema/alterations" + entrypoint: dalSchemaAlteration + authentication: + - Client ID + - Session ID + imports: + apis: + - name: list + method: GET + title: Search schema alterations + path: "/" + parameters: + get: + - name: alterationID + type: "[]string" + title: Filter by alteration ID + - name: batchID + type: "uint64" + title: Filter by batch ID + - type: string + name: kind + title: Search by kind + - name: deleted + title: Exclude (0, default), include (1) or return only (2) deleted alterations + type: uint + - name: completed + title: Exclude (0, default), include (1) or return only (2) completed alterations + type: uint + - type: bool + name: incTotal + title: Include total counter + + # We'll drop these two because they don't make sense to be here + # - name: create + # method: POST + + # - name: update + # method: PUT + + - name: read + method: GET + title: Read alteration details + path: "/{alterationID}" + parameters: + path: [ { type: uint64, name: alterationID, required: true, title: "Alteration ID" } ] + - name: delete + method: DELETE + title: Remove alteration + path: "/{alterationID}" + parameters: + path: [ { type: uint64, name: alterationID, required: true, title: "Alteration ID" } ] + - name: undelete + method: POST + title: Undelete alteration + path: "/{alterationID}/undelete" + parameters: + path: [ { type: uint64, name: alterationID, required: true, title: "Alteration ID" } ] + - title: Data access layer connections path: "/dal/connections" diff --git a/server/system/rest/dal_schema_alteration.go b/server/system/rest/dal_schema_alteration.go new file mode 100644 index 000000000..41eb79d95 --- /dev/null +++ b/server/system/rest/dal_schema_alteration.go @@ -0,0 +1,122 @@ +package rest + +import ( + "context" + + "github.com/cortezaproject/corteza/server/pkg/api" + "github.com/cortezaproject/corteza/server/pkg/filter" + "github.com/cortezaproject/corteza/server/system/rest/request" + "github.com/cortezaproject/corteza/server/system/service" + "github.com/cortezaproject/corteza/server/system/types" +) + +type ( + DalSchemaAlteration struct { + svc alterationService + federationSvc federationNodeService + + alterationAc alterationAccessController + } + + alterationPayload struct { + *types.DalSchemaAlteration + + CanGrant bool `json:"canGrant"` + CanUpdateSchemaAlteration bool `json:"canUpdateSchemaAlteration"` + CanDeleteSchemaAlteration bool `json:"canDeleteSchemaAlteration"` + CanManageDalConfig bool `json:"canManageDalConfig"` + } + + alterationSetPayload struct { + Filter types.DalSchemaAlterationFilter `json:"filter"` + Set []*alterationPayload `json:"set"` + } + + alterationAccessController interface { + CanGrant(context.Context) bool + // @todo? + } + + alterationService interface { + Search(ctx context.Context, filter types.DalSchemaAlterationFilter) (types.DalSchemaAlterationSet, types.DalSchemaAlterationFilter, error) + FindByID(ctx context.Context, ID uint64) (*types.DalSchemaAlteration, error) + DeleteByID(ctx context.Context, ID uint64) error + UndeleteByID(ctx context.Context, ID uint64) error + } +) + +func (DalSchemaAlteration) New() *DalSchemaAlteration { + return &DalSchemaAlteration{ + svc: service.DefaultDalSchemaAlteration, + + alterationAc: service.DefaultAccessControl, + } +} + +func (ctrl DalSchemaAlteration) List(ctx context.Context, r *request.DalSchemaAlterationList) (interface{}, error) { + var ( + err error + set types.DalSchemaAlterationSet + + f = types.DalSchemaAlterationFilter{ + AlterationID: r.AlterationID, + BatchID: r.BatchID, + Kind: r.Kind, + + Deleted: filter.State(r.Deleted), + Completed: filter.State(r.Completed), + } + ) + + if f.Deleted == 0 { + f.Deleted = filter.StateExcluded + } + if f.Completed == 0 { + f.Completed = filter.StateExcluded + } + + f.IncTotal = r.IncTotal + + set, f, err = ctrl.svc.Search(ctx, f) + return ctrl.makeFilterPayload(ctx, set, f, err) +} + +func (ctrl DalSchemaAlteration) Read(ctx context.Context, r *request.DalSchemaAlterationRead) (interface{}, error) { + res, err := ctrl.svc.FindByID(ctx, r.AlterationID) + return ctrl.makePayload(ctx, res, err) +} + +func (ctrl DalSchemaAlteration) Delete(ctx context.Context, r *request.DalSchemaAlterationDelete) (interface{}, error) { + return api.OK(), ctrl.svc.DeleteByID(ctx, r.AlterationID) +} + +func (ctrl DalSchemaAlteration) Undelete(ctx context.Context, r *request.DalSchemaAlterationUndelete) (interface{}, error) { + return api.OK(), ctrl.svc.UndeleteByID(ctx, r.AlterationID) +} + +func (ctrl DalSchemaAlteration) makePayload(ctx context.Context, res *types.DalSchemaAlteration, err error) (*alterationPayload, error) { + if err != nil || res == nil { + return nil, err + } + + pl := &alterationPayload{ + DalSchemaAlteration: res, + + CanGrant: ctrl.alterationAc.CanGrant(ctx), + } + + return pl, nil +} + +func (ctrl DalSchemaAlteration) makeFilterPayload(ctx context.Context, rr types.DalSchemaAlterationSet, f types.DalSchemaAlterationFilter, err error) (*alterationSetPayload, error) { + if err != nil { + return nil, err + } + + out := &alterationSetPayload{Filter: f, Set: make([]*alterationPayload, len(rr))} + for i := range rr { + out.Set[i], _ = ctrl.makePayload(ctx, rr[i], nil) + } + + return out, nil +} diff --git a/server/system/rest/handlers/dalSchemaAlteration.go b/server/system/rest/handlers/dalSchemaAlteration.go new file mode 100644 index 000000000..5ebdb5687 --- /dev/null +++ b/server/system/rest/handlers/dalSchemaAlteration.go @@ -0,0 +1,114 @@ +package handlers + +// This file is auto-generated. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// Definitions file that controls how this file is generated: +// + +import ( + "context" + "github.com/cortezaproject/corteza/server/pkg/api" + "github.com/cortezaproject/corteza/server/system/rest/request" + "github.com/go-chi/chi/v5" + "net/http" +) + +type ( + // Internal API interface + DalSchemaAlterationAPI interface { + List(context.Context, *request.DalSchemaAlterationList) (interface{}, error) + Read(context.Context, *request.DalSchemaAlterationRead) (interface{}, error) + Delete(context.Context, *request.DalSchemaAlterationDelete) (interface{}, error) + Undelete(context.Context, *request.DalSchemaAlterationUndelete) (interface{}, error) + } + + // HTTP API interface + DalSchemaAlteration struct { + List func(http.ResponseWriter, *http.Request) + Read func(http.ResponseWriter, *http.Request) + Delete func(http.ResponseWriter, *http.Request) + Undelete func(http.ResponseWriter, *http.Request) + } +) + +func NewDalSchemaAlteration(h DalSchemaAlterationAPI) *DalSchemaAlteration { + return &DalSchemaAlteration{ + List: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewDalSchemaAlterationList() + if err := params.Fill(r); err != nil { + api.Send(w, r, err) + return + } + + value, err := h.List(r.Context(), params) + if err != nil { + api.Send(w, r, err) + return + } + + api.Send(w, r, value) + }, + Read: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewDalSchemaAlterationRead() + if err := params.Fill(r); err != nil { + api.Send(w, r, err) + return + } + + value, err := h.Read(r.Context(), params) + if err != nil { + api.Send(w, r, err) + return + } + + api.Send(w, r, value) + }, + Delete: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewDalSchemaAlterationDelete() + if err := params.Fill(r); err != nil { + api.Send(w, r, err) + return + } + + value, err := h.Delete(r.Context(), params) + if err != nil { + api.Send(w, r, err) + return + } + + api.Send(w, r, value) + }, + Undelete: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewDalSchemaAlterationUndelete() + if err := params.Fill(r); err != nil { + api.Send(w, r, err) + return + } + + value, err := h.Undelete(r.Context(), params) + if err != nil { + api.Send(w, r, err) + return + } + + api.Send(w, r, value) + }, + } +} + +func (h DalSchemaAlteration) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http.Handler) { + r.Group(func(r chi.Router) { + r.Use(middlewares...) + r.Get("/dal/schema/alterations/", h.List) + r.Get("/dal/schema/alterations/{alterationID}", h.Read) + r.Delete("/dal/schema/alterations/{alterationID}", h.Delete) + r.Post("/dal/schema/alterations/{alterationID}/undelete", h.Undelete) + }) +} diff --git a/server/system/rest/request/dalSchemaAlteration.go b/server/system/rest/request/dalSchemaAlteration.go new file mode 100644 index 000000000..bc18bd62e --- /dev/null +++ b/server/system/rest/request/dalSchemaAlteration.go @@ -0,0 +1,293 @@ +package request + +// This file is auto-generated. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// Definitions file that controls how this file is generated: +// + +import ( + "encoding/json" + "fmt" + "github.com/cortezaproject/corteza/server/pkg/payload" + "github.com/go-chi/chi/v5" + "io" + "mime/multipart" + "net/http" + "strings" +) + +// dummy vars to prevent +// unused imports complain +var ( + _ = chi.URLParam + _ = multipart.ErrMessageTooLarge + _ = payload.ParseUint64s + _ = strings.ToLower + _ = io.EOF + _ = fmt.Errorf + _ = json.NewEncoder +) + +type ( + // Internal API interface + DalSchemaAlterationList struct { + // AlterationID GET parameter + // + // Filter by alteration ID + AlterationID []string + + // BatchID GET parameter + // + // Filter by batch ID + BatchID uint64 `json:",string"` + + // Kind GET parameter + // + // Search by kind + Kind string + + // Deleted GET parameter + // + // Exclude (0, default), include (1) or return only (2) deleted alterations + Deleted uint + + // Completed GET parameter + // + // Exclude (0, default), include (1) or return only (2) completed alterations + Completed uint + + // IncTotal GET parameter + // + // Include total counter + IncTotal bool + } + + DalSchemaAlterationRead struct { + // AlterationID PATH parameter + // + // Alteration ID + AlterationID uint64 `json:",string"` + } + + DalSchemaAlterationDelete struct { + // AlterationID PATH parameter + // + // Alteration ID + AlterationID uint64 `json:",string"` + } + + DalSchemaAlterationUndelete struct { + // AlterationID PATH parameter + // + // Alteration ID + AlterationID uint64 `json:",string"` + } +) + +// NewDalSchemaAlterationList request +func NewDalSchemaAlterationList() *DalSchemaAlterationList { + return &DalSchemaAlterationList{} +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) Auditable() map[string]interface{} { + return map[string]interface{}{ + "alterationID": r.AlterationID, + "batchID": r.BatchID, + "kind": r.Kind, + "deleted": r.Deleted, + "completed": r.Completed, + "incTotal": r.IncTotal, + } +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) GetAlterationID() []string { + return r.AlterationID +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) GetBatchID() uint64 { + return r.BatchID +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) GetKind() string { + return r.Kind +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) GetDeleted() uint { + return r.Deleted +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) GetCompleted() uint { + return r.Completed +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationList) GetIncTotal() bool { + return r.IncTotal +} + +// Fill processes request and fills internal variables +func (r *DalSchemaAlterationList) Fill(req *http.Request) (err error) { + + { + // GET params + tmp := req.URL.Query() + + if val, ok := tmp["alterationID[]"]; ok { + r.AlterationID, err = val, nil + if err != nil { + return err + } + } else if val, ok := tmp["alterationID"]; ok { + r.AlterationID, err = val, nil + if err != nil { + return err + } + } + if val, ok := tmp["batchID"]; ok && len(val) > 0 { + r.BatchID, err = payload.ParseUint64(val[0]), nil + if err != nil { + return err + } + } + if val, ok := tmp["kind"]; ok && len(val) > 0 { + r.Kind, err = val[0], nil + if err != nil { + return err + } + } + if val, ok := tmp["deleted"]; ok && len(val) > 0 { + r.Deleted, err = payload.ParseUint(val[0]), nil + if err != nil { + return err + } + } + if val, ok := tmp["completed"]; ok && len(val) > 0 { + r.Completed, err = payload.ParseUint(val[0]), nil + if err != nil { + return err + } + } + if val, ok := tmp["incTotal"]; ok && len(val) > 0 { + r.IncTotal, err = payload.ParseBool(val[0]), nil + if err != nil { + return err + } + } + } + + return err +} + +// NewDalSchemaAlterationRead request +func NewDalSchemaAlterationRead() *DalSchemaAlterationRead { + return &DalSchemaAlterationRead{} +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationRead) Auditable() map[string]interface{} { + return map[string]interface{}{ + "alterationID": r.AlterationID, + } +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationRead) GetAlterationID() uint64 { + return r.AlterationID +} + +// Fill processes request and fills internal variables +func (r *DalSchemaAlterationRead) Fill(req *http.Request) (err error) { + + { + var val string + // path params + + val = chi.URLParam(req, "alterationID") + r.AlterationID, err = payload.ParseUint64(val), nil + if err != nil { + return err + } + + } + + return err +} + +// NewDalSchemaAlterationDelete request +func NewDalSchemaAlterationDelete() *DalSchemaAlterationDelete { + return &DalSchemaAlterationDelete{} +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationDelete) Auditable() map[string]interface{} { + return map[string]interface{}{ + "alterationID": r.AlterationID, + } +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationDelete) GetAlterationID() uint64 { + return r.AlterationID +} + +// Fill processes request and fills internal variables +func (r *DalSchemaAlterationDelete) Fill(req *http.Request) (err error) { + + { + var val string + // path params + + val = chi.URLParam(req, "alterationID") + r.AlterationID, err = payload.ParseUint64(val), nil + if err != nil { + return err + } + + } + + return err +} + +// NewDalSchemaAlterationUndelete request +func NewDalSchemaAlterationUndelete() *DalSchemaAlterationUndelete { + return &DalSchemaAlterationUndelete{} +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationUndelete) Auditable() map[string]interface{} { + return map[string]interface{}{ + "alterationID": r.AlterationID, + } +} + +// Auditable returns all auditable/loggable parameters +func (r DalSchemaAlterationUndelete) GetAlterationID() uint64 { + return r.AlterationID +} + +// Fill processes request and fills internal variables +func (r *DalSchemaAlterationUndelete) Fill(req *http.Request) (err error) { + + { + var val string + // path params + + val = chi.URLParam(req, "alterationID") + r.AlterationID, err = payload.ParseUint64(val), nil + if err != nil { + return err + } + + } + + return err +} diff --git a/server/system/service/dal_schema_alteration.go b/server/system/service/dal_schema_alteration.go new file mode 100644 index 000000000..f1a726e96 --- /dev/null +++ b/server/system/service/dal_schema_alteration.go @@ -0,0 +1,216 @@ +package service + +import ( + "context" + + "github.com/cortezaproject/corteza/server/pkg/actionlog" + "github.com/cortezaproject/corteza/server/pkg/errors" + "github.com/cortezaproject/corteza/server/store" + "github.com/cortezaproject/corteza/server/system/types" +) + +type ( + dalSchemaAlteration struct { + actionlog actionlog.Recorder + ac dalSchemaAlterationAccessController + + store store.Storer + } + + dalSchemaAlterationAccessController interface { + } +) + +func DalSchemaAlteration() *dalSchemaAlteration { + return &dalSchemaAlteration{ + ac: DefaultAccessControl, + store: DefaultStore, + actionlog: DefaultActionlog, + } +} + +func (svc dalSchemaAlteration) FindByID(ctx context.Context, dalSchemaAlterationID uint64) (a *types.DalSchemaAlteration, err error) { + var ( + uaProps = &dalSchemaAlterationActionProps{dalSchemaAlteration: &types.DalSchemaAlteration{ID: dalSchemaAlterationID}} + ) + + err = func() error { + a, err = loadDalSchemaAlteration(ctx, svc.store, dalSchemaAlterationID) + if err != nil { + return err + } + + uaProps.setDalSchemaAlteration(a) + + // if !svc.ac.CanReadDalSchemaAlteration(ctx, u) { + // return DalSchemaAlterationErrNotAllowedToRead() + // } + + return nil + }() + + return a, svc.recordAction(ctx, uaProps, DalSchemaAlterationActionLookup, err) +} + +// Search interacts with backend storage and +// +// @todo rename to Search() for consistency +func (svc dalSchemaAlteration) Search(ctx context.Context, filter types.DalSchemaAlterationFilter) (aa types.DalSchemaAlterationSet, f types.DalSchemaAlterationFilter, err error) { + var ( + uaProps = &dalSchemaAlterationActionProps{filter: &filter} + ) + + // For each fetched item, store backend will check if it is valid or not + // if !svc.ac.CanReadDalSchemaAlteration(ctx, res) { + // return false, nil + // } + + // return true, nil + // } + + err = func() error { + // if !svc.ac.CanSearchDalSchemaAlterations(ctx) { + // return DalSchemaAlterationErrNotAllowedToSearch() + // } + + aa, f, err = store.SearchDalSchemaAlterations(ctx, svc.store, filter) + return err + }() + + return aa, f, svc.recordAction(ctx, uaProps, DalSchemaAlterationActionSearch, err) +} + +func (svc dalSchemaAlteration) DeleteByID(ctx context.Context, dalSchemaAlterationID uint64) (err error) { + var ( + u *types.DalSchemaAlteration + uaProps = &dalSchemaAlterationActionProps{dalSchemaAlteration: &types.DalSchemaAlteration{ID: dalSchemaAlterationID}} + ) + + err = func() (err error) { + if u, err = loadDalSchemaAlteration(ctx, svc.store, dalSchemaAlterationID); err != nil { + return + } + + // if !svc.ac.CanDeleteDalSchemaAlteration(ctx, u) { + // return DalSchemaAlterationErrNotAllowedToDelete() + // } + + // if err = svc.eventbus.WaitFor(ctx, event.DalSchemaAlterationBeforeDelete(nil, u)); err != nil { + // return + // } + + u.DeletedAt = now() + if err = store.UpdateDalSchemaAlteration(ctx, svc.store, u); err != nil { + return + } + + // _ = svc.eventbus.WaitFor(ctx, event.DalSchemaAlterationAfterDelete(nil, u)) + return nil + }() + + return svc.recordAction(ctx, uaProps, DalSchemaAlterationActionDelete, err) +} + +func (svc dalSchemaAlteration) UndeleteByID(ctx context.Context, dalSchemaAlterationID uint64) (err error) { + var ( + u *types.DalSchemaAlteration + uaProps = &dalSchemaAlterationActionProps{dalSchemaAlteration: &types.DalSchemaAlteration{ID: dalSchemaAlterationID}} + ) + + err = func() (err error) { + if u, err = loadDalSchemaAlteration(ctx, svc.store, dalSchemaAlterationID); err != nil { + return + } + + uaProps.setDalSchemaAlteration(u) + + // if err = uniqueDalSchemaAlterationCheck(ctx, svc.store, u); err != nil { + // return err + // } + + // if !svc.ac.CanDeleteDalSchemaAlteration(ctx, u) { + // return DalSchemaAlterationErrNotAllowedToDelete() + // } + + u.DeletedAt = nil + if err = store.UpdateDalSchemaAlteration(ctx, svc.store, u); err != nil { + return + } + + return nil + }() + + return svc.recordAction(ctx, uaProps, DalSchemaAlterationActionUndelete, err) + +} + +func loadDalSchemaAlteration(ctx context.Context, s store.DalSchemaAlterations, ID uint64) (res *types.DalSchemaAlteration, err error) { + if ID == 0 { + return nil, DalSchemaAlterationErrInvalidID() + } + + if res, err = store.LookupDalSchemaAlterationByID(ctx, s, ID); errors.IsNotFound(err) { + return nil, DalSchemaAlterationErrNotFound() + } + + return +} + +// // uniqueDalSchemaAlterationCheck verifies dalSchemaAlteration's email, dalSchemaAlterationname and handle +// func uniqueDalSchemaAlterationCheck(ctx context.Context, s store.Storer, u *types.DalSchemaAlteration) (err error) { +// isUnique := func(field string) bool { +// f := types.DalSchemaAlterationFilter{ +// // If dalSchemaAlteration exists and is deleted -- not a dup +// Deleted: filter.StateExcluded, + +// // If dalSchemaAlteration exists and is suspended -- duplicate +// Suspended: filter.StateInclusive, +// } + +// f.Limit = 1 + +// switch field { +// case "email": +// if u.Email == "" { +// return true +// } + +// f.Email = u.Email + +// case "dalSchemaAlterationname": +// if u.DalSchemaAlterationname == "" { +// return true +// } + +// f.DalSchemaAlterationname = u.DalSchemaAlterationname +// case "handle": +// if u.Handle == "" { +// return true +// } + +// f.Handle = u.Handle +// } + +// set, _, err := store.SearchDalSchemaAlterations(ctx, s, f) +// if err != nil || len(set) > 1 { +// // In case of error or multiple dalSchemaAlterations returned +// return false +// } + +// return len(set) == 0 || set[0].ID == u.ID +// } + +// if !isUnique("email") { +// return DalSchemaAlterationErrEmailNotUnique() +// } + +// if !isUnique("dalSchemaAlterationname") { +// return DalSchemaAlterationErrDalSchemaAlterationnameNotUnique() +// } + +// if !isUnique("handle") { +// return DalSchemaAlterationErrHandleNotUnique() +// } + +// return nil +// } diff --git a/server/system/service/dal_schema_alteration_actions.gen.go b/server/system/service/dal_schema_alteration_actions.gen.go new file mode 100644 index 000000000..4be21fc82 --- /dev/null +++ b/server/system/service/dal_schema_alteration_actions.gen.go @@ -0,0 +1,671 @@ +package service + +// This file is auto-generated. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// Definitions file that controls how this file is generated: +// system/service/dal_schema_alteration_actions.yaml + +import ( + "context" + "fmt" + "github.com/cortezaproject/corteza/server/pkg/actionlog" + "github.com/cortezaproject/corteza/server/pkg/errors" + "github.com/cortezaproject/corteza/server/pkg/locale" + "github.com/cortezaproject/corteza/server/system/types" + "strings" + "time" +) + +type ( + dalSchemaAlterationActionProps struct { + dalSchemaAlteration *types.DalSchemaAlteration + new *types.DalSchemaAlteration + update *types.DalSchemaAlteration + existing *types.DalSchemaAlteration + filter *types.DalSchemaAlterationFilter + } + + dalSchemaAlterationAction struct { + timestamp time.Time + resource string + action string + log string + severity actionlog.Severity + + // prefix for error when action fails + errorMessage string + + props *dalSchemaAlterationActionProps + } + + dalSchemaAlterationLogMetaKey struct{} + dalSchemaAlterationPropsMetaKey struct{} +) + +var ( + // just a placeholder to cover template cases w/o fmt package use + _ = fmt.Println +) + +// ********************************************************************************************************************* +// ********************************************************************************************************************* +// Props methods +// setDalSchemaAlteration updates dalSchemaAlterationActionProps's dalSchemaAlteration +// +// This function is auto-generated. +// +func (p *dalSchemaAlterationActionProps) setDalSchemaAlteration(dalSchemaAlteration *types.DalSchemaAlteration) *dalSchemaAlterationActionProps { + p.dalSchemaAlteration = dalSchemaAlteration + return p +} + +// setNew updates dalSchemaAlterationActionProps's new +// +// This function is auto-generated. +// +func (p *dalSchemaAlterationActionProps) setNew(new *types.DalSchemaAlteration) *dalSchemaAlterationActionProps { + p.new = new + return p +} + +// setUpdate updates dalSchemaAlterationActionProps's update +// +// This function is auto-generated. +// +func (p *dalSchemaAlterationActionProps) setUpdate(update *types.DalSchemaAlteration) *dalSchemaAlterationActionProps { + p.update = update + return p +} + +// setExisting updates dalSchemaAlterationActionProps's existing +// +// This function is auto-generated. +// +func (p *dalSchemaAlterationActionProps) setExisting(existing *types.DalSchemaAlteration) *dalSchemaAlterationActionProps { + p.existing = existing + return p +} + +// setFilter updates dalSchemaAlterationActionProps's filter +// +// This function is auto-generated. +// +func (p *dalSchemaAlterationActionProps) setFilter(filter *types.DalSchemaAlterationFilter) *dalSchemaAlterationActionProps { + p.filter = filter + return p +} + +// Serialize converts dalSchemaAlterationActionProps to actionlog.Meta +// +// This function is auto-generated. +// +func (p dalSchemaAlterationActionProps) Serialize() actionlog.Meta { + var ( + m = make(actionlog.Meta) + ) + + if p.dalSchemaAlteration != nil { + m.Set("dalSchemaAlteration.ID", p.dalSchemaAlteration.ID, true) + } + if p.new != nil { + m.Set("new.ID", p.new.ID, true) + } + if p.update != nil { + m.Set("update.ID", p.update.ID, true) + } + if p.existing != nil { + m.Set("existing.ID", p.existing.ID, true) + } + if p.filter != nil { + m.Set("filter.alterationID", p.filter.AlterationID, true) + m.Set("filter.batchID", p.filter.BatchID, true) + m.Set("filter.kind", p.filter.Kind, true) + m.Set("filter.deleted", p.filter.Deleted, true) + m.Set("filter.completed", p.filter.Completed, true) + } + + return m +} + +// tr translates string and replaces meta value placeholder with values +// +// This function is auto-generated. +// +func (p dalSchemaAlterationActionProps) Format(in string, err error) string { + var ( + pairs = []string{"{{err}}"} + // first non-empty string + fns = func(ii ...interface{}) string { + for _, i := range ii { + if s := fmt.Sprintf("%v", i); len(s) > 0 { + return s + } + } + + return "" + } + ) + + if err != nil { + pairs = append(pairs, err.Error()) + } else { + pairs = append(pairs, "nil") + } + + if p.dalSchemaAlteration != nil { + // replacement for "{{dalSchemaAlteration}}" (in order how fields are defined) + pairs = append( + pairs, + "{{dalSchemaAlteration}}", + fns( + p.dalSchemaAlteration.ID, + ), + ) + pairs = append(pairs, "{{dalSchemaAlteration.ID}}", fns(p.dalSchemaAlteration.ID)) + } + + if p.new != nil { + // replacement for "{{new}}" (in order how fields are defined) + pairs = append( + pairs, + "{{new}}", + fns( + p.new.ID, + ), + ) + pairs = append(pairs, "{{new.ID}}", fns(p.new.ID)) + } + + if p.update != nil { + // replacement for "{{update}}" (in order how fields are defined) + pairs = append( + pairs, + "{{update}}", + fns( + p.update.ID, + ), + ) + pairs = append(pairs, "{{update.ID}}", fns(p.update.ID)) + } + + if p.existing != nil { + // replacement for "{{existing}}" (in order how fields are defined) + pairs = append( + pairs, + "{{existing}}", + fns( + p.existing.ID, + ), + ) + pairs = append(pairs, "{{existing.ID}}", fns(p.existing.ID)) + } + + if p.filter != nil { + // replacement for "{{filter}}" (in order how fields are defined) + pairs = append( + pairs, + "{{filter}}", + fns( + p.filter.AlterationID, + p.filter.BatchID, + p.filter.Kind, + p.filter.Deleted, + p.filter.Completed, + ), + ) + pairs = append(pairs, "{{filter.alterationID}}", fns(p.filter.AlterationID)) + pairs = append(pairs, "{{filter.batchID}}", fns(p.filter.BatchID)) + pairs = append(pairs, "{{filter.kind}}", fns(p.filter.Kind)) + pairs = append(pairs, "{{filter.deleted}}", fns(p.filter.Deleted)) + pairs = append(pairs, "{{filter.completed}}", fns(p.filter.Completed)) + } + return strings.NewReplacer(pairs...).Replace(in) +} + +// ********************************************************************************************************************* +// ********************************************************************************************************************* +// Action methods + +// String returns loggable description as string +// +// This function is auto-generated. +// +func (a *dalSchemaAlterationAction) String() string { + var props = &dalSchemaAlterationActionProps{} + + if a.props != nil { + props = a.props + } + + return props.Format(a.log, nil) +} + +func (e *dalSchemaAlterationAction) ToAction() *actionlog.Action { + return &actionlog.Action{ + Resource: e.resource, + Action: e.action, + Severity: e.severity, + Description: e.String(), + Meta: e.props.Serialize(), + } +} + +// ********************************************************************************************************************* +// ********************************************************************************************************************* +// Action constructors + +// DalSchemaAlterationActionSearch returns "system:dal-schema-alteration.search" action +// +// This function is auto-generated. +// +func DalSchemaAlterationActionSearch(props ...*dalSchemaAlterationActionProps) *dalSchemaAlterationAction { + a := &dalSchemaAlterationAction{ + timestamp: time.Now(), + resource: "system:dal-schema-alteration", + action: "search", + log: "searched for matching dalSchemaAlterations", + severity: actionlog.Info, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + +// DalSchemaAlterationActionLookup returns "system:dal-schema-alteration.lookup" action +// +// This function is auto-generated. +// +func DalSchemaAlterationActionLookup(props ...*dalSchemaAlterationActionProps) *dalSchemaAlterationAction { + a := &dalSchemaAlterationAction{ + timestamp: time.Now(), + resource: "system:dal-schema-alteration", + action: "lookup", + log: "looked-up for a {{dalSchemaAlteration}}", + severity: actionlog.Info, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + +// DalSchemaAlterationActionDelete returns "system:dal-schema-alteration.delete" action +// +// This function is auto-generated. +// +func DalSchemaAlterationActionDelete(props ...*dalSchemaAlterationActionProps) *dalSchemaAlterationAction { + a := &dalSchemaAlterationAction{ + timestamp: time.Now(), + resource: "system:dal-schema-alteration", + action: "delete", + log: "deleted {{dalSchemaAlteration}}", + severity: actionlog.Notice, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + +// DalSchemaAlterationActionUndelete returns "system:dal-schema-alteration.undelete" action +// +// This function is auto-generated. +// +func DalSchemaAlterationActionUndelete(props ...*dalSchemaAlterationActionProps) *dalSchemaAlterationAction { + a := &dalSchemaAlterationAction{ + timestamp: time.Now(), + resource: "system:dal-schema-alteration", + action: "undelete", + log: "undeleted {{dalSchemaAlteration}}", + severity: actionlog.Notice, + } + + if len(props) > 0 { + a.props = props[0] + } + + return a +} + +// ********************************************************************************************************************* +// ********************************************************************************************************************* +// Error constructors + +// DalSchemaAlterationErrGeneric returns "system:dal-schema-alteration.generic" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrGeneric(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("failed to complete request due to internal error", nil), + + errors.Meta("type", "generic"), + errors.Meta("resource", "system:dal-schema-alteration"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(dalSchemaAlterationLogMetaKey{}, "{err}"), + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.generic"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrNotFound returns "system:dal-schema-alteration.notFound" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrNotFound(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("dalSchemaAlteration not found", nil), + + errors.Meta("type", "notFound"), + errors.Meta("resource", "system:dal-schema-alteration"), + + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.notFound"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrInvalidID returns "system:dal-schema-alteration.invalidID" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrInvalidID(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("invalid ID", nil), + + errors.Meta("type", "invalidID"), + errors.Meta("resource", "system:dal-schema-alteration"), + + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.invalidID"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrNotAllowedToRead returns "system:dal-schema-alteration.notAllowedToRead" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrNotAllowedToRead(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to read this dal schema alteration", nil), + + errors.Meta("type", "notAllowedToRead"), + errors.Meta("resource", "system:dal-schema-alteration"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(dalSchemaAlterationLogMetaKey{}, "failed to read {{dalSchemaAlteration.ID}}; insufficient permissions"), + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.notAllowedToRead"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrNotAllowedToSearch returns "system:dal-schema-alteration.notAllowedToSearch" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrNotAllowedToSearch(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to list or search dal schema alterations", nil), + + errors.Meta("type", "notAllowedToSearch"), + errors.Meta("resource", "system:dal-schema-alteration"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(dalSchemaAlterationLogMetaKey{}, "failed to search for dal schema alterations; insufficient permissions"), + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.notAllowedToSearch"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrNotAllowedToListUsers returns "system:dal-schema-alteration.notAllowedToListUsers" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrNotAllowedToListUsers(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to list dal schema alterations", nil), + + errors.Meta("type", "notAllowedToListUsers"), + errors.Meta("resource", "system:dal-schema-alteration"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(dalSchemaAlterationLogMetaKey{}, "failed to list dalSchemaAlteration; insufficient permissions"), + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.notAllowedToListUsers"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrNotAllowedToDelete returns "system:dal-schema-alteration.notAllowedToDelete" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrNotAllowedToDelete(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to delete this dal schema alteration", nil), + + errors.Meta("type", "notAllowedToDelete"), + errors.Meta("resource", "system:dal-schema-alteration"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(dalSchemaAlterationLogMetaKey{}, "failed to delete {{dalSchemaAlteration.ID}}; insufficient permissions"), + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.notAllowedToDelete"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// DalSchemaAlterationErrNotAllowedToUndelete returns "system:dal-schema-alteration.notAllowedToUndelete" as *errors.Error +// +// +// This function is auto-generated. +// +func DalSchemaAlterationErrNotAllowedToUndelete(mm ...*dalSchemaAlterationActionProps) *errors.Error { + var p = &dalSchemaAlterationActionProps{} + if len(mm) > 0 { + p = mm[0] + } + + var e = errors.New( + errors.KindInternal, + + p.Format("not allowed to undelete this dal schema alteration", nil), + + errors.Meta("type", "notAllowedToUndelete"), + errors.Meta("resource", "system:dal-schema-alteration"), + + // action log entry; no formatting, it will be applied inside recordAction fn. + errors.Meta(dalSchemaAlterationLogMetaKey{}, "failed to undelete {{dalSchemaAlteration.ID}}; insufficient permissions"), + errors.Meta(dalSchemaAlterationPropsMetaKey{}, p), + + // translation namespace & key + errors.Meta(locale.ErrorMetaNamespace{}, "system"), + errors.Meta(locale.ErrorMetaKey{}, "dal-schema-alteration.errors.notAllowedToUndelete"), + + errors.StackSkip(1), + ) + + if len(mm) > 0 { + } + + return e +} + +// ********************************************************************************************************************* +// ********************************************************************************************************************* + +// recordAction is a service helper function wraps function that can return error +// +// It will wrap unrecognized/internal errors with generic errors. +// +// This function is auto-generated. +// +func (svc dalSchemaAlteration) recordAction(ctx context.Context, props *dalSchemaAlterationActionProps, actionFn func(...*dalSchemaAlterationActionProps) *dalSchemaAlterationAction, err error) error { + if svc.actionlog == nil || actionFn == nil { + // action log disabled or no action fn passed, return error as-is + return err + } else if err == nil { + // action completed w/o error, record it + svc.actionlog.Record(ctx, actionFn(props).ToAction()) + return nil + } + + a := actionFn(props).ToAction() + + // Extracting error information and recording it as action + a.Error = err.Error() + + switch c := err.(type) { + case *errors.Error: + m := c.Meta() + + a.Error = err.Error() + a.Severity = actionlog.Severity(m.AsInt("severity")) + a.Description = props.Format(m.AsString(dalSchemaAlterationLogMetaKey{}), err) + + if p, has := m[dalSchemaAlterationPropsMetaKey{}]; has { + a.Meta = p.(*dalSchemaAlterationActionProps).Serialize() + } + + svc.actionlog.Record(ctx, a) + default: + svc.actionlog.Record(ctx, a) + } + + // Original error is passed on + return err +} diff --git a/server/system/service/dal_schema_alteration_actions.yaml b/server/system/service/dal_schema_alteration_actions.yaml new file mode 100644 index 000000000..d36e88a7d --- /dev/null +++ b/server/system/service/dal_schema_alteration_actions.yaml @@ -0,0 +1,74 @@ +# List of loggable service actions + +resource: system:dal-schema-alteration +service: dalSchemaAlteration + +# Default sensitivity for actions +defaultActionSeverity: notice + +# default severity for errors +defaultErrorSeverity: alert + +import: + - github.com/cortezaproject/corteza/server/system/types + +props: + - name: dalSchemaAlteration + type: "*types.DalSchemaAlteration" + fields: [ ID ] + - name: new + type: "*types.DalSchemaAlteration" + fields: [ ID ] + - name: update + type: "*types.DalSchemaAlteration" + fields: [ ID ] + - name: existing + type: "*types.DalSchemaAlteration" + fields: [ ID ] + - name: filter + type: "*types.DalSchemaAlterationFilter" + fields: [ alterationID, batchID, kind, deleted, completed ] + +actions: + - action: search + log: "searched for matching dalSchemaAlterations" + severity: info + + - action: lookup + log: "looked-up for a {{dalSchemaAlteration}}" + severity: info + + - action: delete + log: "deleted {{dalSchemaAlteration}}" + + - action: undelete + log: "undeleted {{dalSchemaAlteration}}" + +errors: + - error: notFound + message: "dalSchemaAlteration not found" + severity: warning + + - error: invalidID + message: "invalid ID" + severity: warning + + - error: notAllowedToRead + message: "not allowed to read this dal schema alteration" + log: "failed to read {{dalSchemaAlteration.ID}}; insufficient permissions" + + - error: notAllowedToSearch + message: "not allowed to list or search dal schema alterations" + log: "failed to search for dal schema alterations; insufficient permissions" + + - error: notAllowedToListUsers + message: "not allowed to list dal schema alterations" + log: "failed to list dalSchemaAlteration; insufficient permissions" + + - error: notAllowedToDelete + message: "not allowed to delete this dal schema alteration" + log: "failed to delete {{dalSchemaAlteration.ID}}; insufficient permissions" + + - error: notAllowedToUndelete + message: "not allowed to undelete this dal schema alteration" + log: "failed to undelete {{dalSchemaAlteration.ID}}; insufficient permissions" diff --git a/server/system/service/service.go b/server/system/service/service.go index bb0f91dd7..ce061e891 100644 --- a/server/system/service/service.go +++ b/server/system/service/service.go @@ -79,6 +79,7 @@ var ( DefaultCredentials *credentials DefaultDalConnection *dalConnection DefaultDalSensitivityLevel *dalSensitivityLevel + DefaultDalSchemaAlteration *dalSchemaAlteration DefaultRole *role DefaultApplication *application DefaultReminder ReminderService @@ -157,9 +158,8 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, ws websock DefaultDalConnection = Connection(ctx, dal.Service(), c.DB) DefaultDalSensitivityLevel = SensitivityLevel(ctx, dal.Service()) - if err != nil { - return - } + + DefaultDalSchemaAlteration = DalSchemaAlteration() if DefaultObjectStore == nil { var ( diff --git a/server/system/types/dal_schema_ateration.go b/server/system/types/dal_schema_ateration.go new file mode 100644 index 000000000..2bc9b4c6c --- /dev/null +++ b/server/system/types/dal_schema_ateration.go @@ -0,0 +1,51 @@ +package types + +import ( + "database/sql/driver" + "encoding/json" + "time" + + "github.com/cortezaproject/corteza/server/pkg/sql" + + "github.com/cortezaproject/corteza/server/pkg/filter" +) + +type ( + DalSchemaAlteration struct { + ID uint64 `json:"alterationID,string"` + BatchID uint64 `json:"batchID,string"` + DependsOn uint64 `json:"dependsOn,string"` + + Kind string `json:"kind"` + Params *DalSchemaAlterationParams `json:"params"` + + CreatedAt time.Time `json:"createdAt,omitempty"` + CreatedBy uint64 `json:"createdBy,string" ` + UpdatedAt *time.Time `json:"updatedAt,omitempty"` + UpdatedBy uint64 `json:"updatedBy,string,omitempty"` + DeletedAt *time.Time `json:"deletedAt,omitempty"` + DeletedBy uint64 `json:"deletedBy,string,omitempty"` + + CompletedAt *time.Time `json:"completedAt,omitempty"` + CompletedBy uint64 `json:"completedBy,string,omitempty"` + } + + DalSchemaAlterationParams struct { + } + + DalSchemaAlterationFilter struct { + AlterationID []string `json:"alterationID"` + BatchID uint64 `json:"batchID,string"` + Kind string `json:"kind"` + + Deleted filter.State `json:"deleted"` + Completed filter.State `json:"completed"` + + // Standard helpers for paging and sorting + filter.Sorting + filter.Paging + } +) + +func (meta *DalSchemaAlterationParams) Scan(src any) error { return sql.ParseJSON(src, meta) } +func (meta *DalSchemaAlterationParams) Value() (driver.Value, error) { return json.Marshal(meta) } diff --git a/server/system/types/getters_setters.gen.go b/server/system/types/getters_setters.gen.go index f2c520a66..f364f1b35 100644 --- a/server/system/types/getters_setters.gen.go +++ b/server/system/types/getters_setters.gen.go @@ -823,3 +823,67 @@ func (r *DalSensitivityLevel) SetValue(name string, pos uint, value any) (err er } return nil } + +func (r DalSchemaAlteration) GetID() uint64 { return r.ID } + +func (r *DalSchemaAlteration) GetValue(name string, pos uint) (any, error) { + switch name { + case "batchID", "BatchID": + return r.BatchID, nil + case "completedAt", "CompletedAt": + return r.CompletedAt, nil + case "completedBy", "CompletedBy": + return r.CompletedBy, nil + case "createdAt", "CreatedAt": + return r.CreatedAt, nil + case "createdBy", "CreatedBy": + return r.CreatedBy, nil + case "deletedAt", "DeletedAt": + return r.DeletedAt, nil + case "deletedBy", "DeletedBy": + return r.DeletedBy, nil + case "dependsOn", "DependsOn": + return r.DependsOn, nil + case "id", "ID": + return r.ID, nil + case "kind", "Kind": + return r.Kind, nil + case "updatedAt", "UpdatedAt": + return r.UpdatedAt, nil + case "updatedBy", "UpdatedBy": + return r.UpdatedBy, nil + + } + return nil, nil +} + +func (r *DalSchemaAlteration) SetValue(name string, pos uint, value any) (err error) { + switch name { + case "batchID", "BatchID": + return cast2.Uint64(value, &r.BatchID) + case "completedAt", "CompletedAt": + return cast2.TimePtr(value, &r.CompletedAt) + case "completedBy", "CompletedBy": + return cast2.Uint64(value, &r.CompletedBy) + case "createdAt", "CreatedAt": + return cast2.Time(value, &r.CreatedAt) + case "createdBy", "CreatedBy": + return cast2.Uint64(value, &r.CreatedBy) + case "deletedAt", "DeletedAt": + return cast2.TimePtr(value, &r.DeletedAt) + case "deletedBy", "DeletedBy": + return cast2.Uint64(value, &r.DeletedBy) + case "dependsOn", "DependsOn": + return cast2.Uint64(value, &r.DependsOn) + case "id", "ID": + return cast2.Uint64(value, &r.ID) + case "kind", "Kind": + return cast2.String(value, &r.Kind) + case "updatedAt", "UpdatedAt": + return cast2.TimePtr(value, &r.UpdatedAt) + case "updatedBy", "UpdatedBy": + return cast2.Uint64(value, &r.UpdatedBy) + + } + return nil +} diff --git a/server/system/types/resources.gen.go b/server/system/types/resources.gen.go index 3bb584d3b..8339d1081 100644 --- a/server/system/types/resources.gen.go +++ b/server/system/types/resources.gen.go @@ -30,5 +30,6 @@ const ( UserResourceType = "corteza::system:user" DalConnectionResourceType = "corteza::system:dal-connection" DalSensitivityLevelResourceType = "corteza::system:dal-sensitivity-level" + DalSchemaAlterationResourceType = "corteza::system:dal-schema-alteration" ComponentResourceType = "corteza::system" ) diff --git a/server/system/types/type_set.gen.go b/server/system/types/type_set.gen.go index 0c2286c8d..e313f4142 100644 --- a/server/system/types/type_set.gen.go +++ b/server/system/types/type_set.gen.go @@ -70,6 +70,11 @@ type ( // This type is auto-generated. DalConnectionSet []*DalConnection + // DalSchemaAlterationSet slice of DalSchemaAlteration + // + // This type is auto-generated. + DalSchemaAlterationSet []*DalSchemaAlteration + // DalSensitivityLevelSet slice of DalSensitivityLevel // // This type is auto-generated. @@ -709,6 +714,62 @@ func (set DalConnectionSet) IDs() (IDs []uint64) { return } +// Walk iterates through every slice item and calls w(DalSchemaAlteration) err +// +// This function is auto-generated. +func (set DalSchemaAlterationSet) Walk(w func(*DalSchemaAlteration) error) (err error) { + for i := range set { + if err = w(set[i]); err != nil { + return + } + } + + return +} + +// Filter iterates through every slice item, calls f(DalSchemaAlteration) (bool, err) and return filtered slice +// +// This function is auto-generated. +func (set DalSchemaAlterationSet) Filter(f func(*DalSchemaAlteration) (bool, error)) (out DalSchemaAlterationSet, err error) { + var ok bool + out = DalSchemaAlterationSet{} + for i := range set { + if ok, err = f(set[i]); err != nil { + return + } else if ok { + out = append(out, set[i]) + } + } + + return +} + +// FindByID finds items from slice by its ID property +// +// This function is auto-generated. +func (set DalSchemaAlterationSet) FindByID(ID uint64) *DalSchemaAlteration { + for i := range set { + if set[i].ID == ID { + return set[i] + } + } + + return nil +} + +// IDs returns a slice of uint64s from all items in the set +// +// This function is auto-generated. +func (set DalSchemaAlterationSet) IDs() (IDs []uint64) { + IDs = make([]uint64, len(set)) + + for i := range set { + IDs[i] = set[i].ID + } + + return +} + // Walk iterates through every slice item and calls w(DalSensitivityLevel) err // // This function is auto-generated. diff --git a/server/system/types/type_set.gen_test.go b/server/system/types/type_set.gen_test.go index bd38dd74e..ab778404a 100644 --- a/server/system/types/type_set.gen_test.go +++ b/server/system/types/type_set.gen_test.go @@ -958,6 +958,96 @@ func TestDalConnectionSetIDs(t *testing.T) { } } +func TestDalSchemaAlterationSetWalk(t *testing.T) { + var ( + value = make(DalSchemaAlterationSet, 3) + req = require.New(t) + ) + + // check walk with no errors + { + err := value.Walk(func(*DalSchemaAlteration) error { + return nil + }) + req.NoError(err) + } + + // check walk with error + req.Error(value.Walk(func(*DalSchemaAlteration) error { return fmt.Errorf("walk error") })) +} + +func TestDalSchemaAlterationSetFilter(t *testing.T) { + var ( + value = make(DalSchemaAlterationSet, 3) + req = require.New(t) + ) + + // filter nothing + { + set, err := value.Filter(func(*DalSchemaAlteration) (bool, error) { + return true, nil + }) + req.NoError(err) + req.Equal(len(set), len(value)) + } + + // filter one item + { + found := false + set, err := value.Filter(func(*DalSchemaAlteration) (bool, error) { + if !found { + found = true + return found, nil + } + return false, nil + }) + req.NoError(err) + req.Len(set, 1) + } + + // filter error + { + _, err := value.Filter(func(*DalSchemaAlteration) (bool, error) { + return false, fmt.Errorf("filter error") + }) + req.Error(err) + } +} + +func TestDalSchemaAlterationSetIDs(t *testing.T) { + var ( + value = make(DalSchemaAlterationSet, 3) + req = require.New(t) + ) + + // construct objects + value[0] = new(DalSchemaAlteration) + value[1] = new(DalSchemaAlteration) + value[2] = new(DalSchemaAlteration) + // set ids + value[0].ID = 1 + value[1].ID = 2 + value[2].ID = 3 + + // Find existing + { + val := value.FindByID(2) + req.Equal(uint64(2), val.ID) + } + + // Find non-existing + { + val := value.FindByID(4) + req.Nil(val) + } + + // List IDs from set + { + val := value.IDs() + req.Equal(len(val), len(value)) + } +} + func TestDalSensitivityLevelSetWalk(t *testing.T) { var ( value = make(DalSensitivityLevelSet, 3) diff --git a/server/system/types/types.yaml b/server/system/types/types.yaml index 837dd1504..26d1cd354 100644 --- a/server/system/types/types.yaml +++ b/server/system/types/types.yaml @@ -10,6 +10,7 @@ types: labelResourceType: user DalConnection: DalSensitivityLevel: + DalSchemaAlteration: Application: labelResourceType: application Role: