snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
This commit is contained in:
415
server/system/service/dal_connection.go
Normal file
415
server/system/service/dal_connection.go
Normal file
@@ -0,0 +1,415 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/errors"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/pkg/actionlog"
|
||||
a "github.com/cortezaproject/corteza/server/pkg/auth"
|
||||
"github.com/cortezaproject/corteza/server/pkg/dal"
|
||||
"github.com/cortezaproject/corteza/server/pkg/options"
|
||||
|
||||
"github.com/cortezaproject/corteza/server/store"
|
||||
"github.com/cortezaproject/corteza/server/system/types"
|
||||
)
|
||||
|
||||
type (
|
||||
dalConnection struct {
|
||||
actionlog actionlog.Recorder
|
||||
store store.Storer
|
||||
ac connectionAccessController
|
||||
dal dalConnManager
|
||||
dbConf options.DBOpt
|
||||
}
|
||||
|
||||
connectionAccessController interface {
|
||||
CanGrant(context.Context) bool
|
||||
CanSearchDalConnections(ctx context.Context) bool
|
||||
|
||||
CanCreateDalConnection(context.Context) bool
|
||||
CanReadDalConnection(context.Context, *types.DalConnection) bool
|
||||
CanUpdateDalConnection(context.Context, *types.DalConnection) bool
|
||||
CanDeleteDalConnection(context.Context, *types.DalConnection) bool
|
||||
CanManageDalConfigOnDalConnection(context.Context, *types.DalConnection) bool
|
||||
}
|
||||
|
||||
// Connection management on DAL Service
|
||||
dalConnManager interface {
|
||||
ReplaceConnection(context.Context, *dal.ConnectionWrap, bool) error
|
||||
RemoveConnection(context.Context, uint64) error
|
||||
SearchConnectionIssues(uint64) []dal.Issue
|
||||
}
|
||||
)
|
||||
|
||||
func Connection(ctx context.Context, dal dalConnManager, dbConf options.DBOpt) *dalConnection {
|
||||
return &dalConnection{
|
||||
ac: DefaultAccessControl,
|
||||
actionlog: DefaultActionlog,
|
||||
store: DefaultStore,
|
||||
dal: dal,
|
||||
dbConf: dbConf,
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *dalConnection) FindByID(ctx context.Context, ID uint64) (q *types.DalConnection, err error) {
|
||||
var (
|
||||
rProps = &dalConnectionActionProps{}
|
||||
)
|
||||
|
||||
err = func() error {
|
||||
if q, err = loadDalConnection(ctx, svc.store, ID); err != nil {
|
||||
return DalConnectionErrInvalidID().Wrap(err)
|
||||
}
|
||||
|
||||
rProps.setConnection(q)
|
||||
|
||||
if !svc.ac.CanReadDalConnection(ctx, q) {
|
||||
return DalConnectionErrNotAllowedToRead(rProps)
|
||||
}
|
||||
|
||||
svc.proc(ctx, q)
|
||||
return nil
|
||||
}()
|
||||
return q, svc.recordAction(ctx, rProps, DalConnectionActionLookup, err)
|
||||
}
|
||||
|
||||
func (svc *dalConnection) Create(ctx context.Context, new *types.DalConnection) (q *types.DalConnection, err error) {
|
||||
var (
|
||||
qProps = &dalConnectionActionProps{new: new}
|
||||
)
|
||||
|
||||
err = func() (err error) {
|
||||
if new.Meta.Name == "" {
|
||||
return DalConnectionErrMissingName()
|
||||
}
|
||||
|
||||
if !svc.ac.CanCreateDalConnection(ctx) {
|
||||
return DalConnectionErrNotAllowedToCreate(qProps)
|
||||
}
|
||||
|
||||
new.ID = nextID()
|
||||
new.CreatedAt = *now()
|
||||
new.CreatedBy = a.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
// validation
|
||||
{
|
||||
if new.Type != types.DalConnectionResourceType {
|
||||
// @todo error
|
||||
err = fmt.Errorf("cannot create connection: unsupported connection type %s", new.Type)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err = store.CreateDalConnection(ctx, svc.store, new); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q = new
|
||||
|
||||
if err = dalConnectionReplace(ctx, svc.store.ToDalConn(), svc.dal, new); err != nil {
|
||||
return err
|
||||
}
|
||||
svc.proc(ctx, q)
|
||||
return
|
||||
}()
|
||||
|
||||
return q, svc.recordAction(ctx, qProps, DalConnectionActionCreate, err)
|
||||
}
|
||||
|
||||
func (svc *dalConnection) Update(ctx context.Context, upd *types.DalConnection) (q *types.DalConnection, err error) {
|
||||
var (
|
||||
cProps = &dalConnectionActionProps{update: upd}
|
||||
old *types.DalConnection
|
||||
)
|
||||
|
||||
err = func() (err error) {
|
||||
if upd.Meta.Name == "" {
|
||||
return DalConnectionErrMissingName()
|
||||
}
|
||||
|
||||
if old, err = loadDalConnection(ctx, svc.store, upd.ID); err != nil {
|
||||
return DalConnectionErrNotFound(cProps)
|
||||
}
|
||||
|
||||
if !svc.ac.CanUpdateDalConnection(ctx, old) {
|
||||
return DalConnectionErrNotAllowedToUpdate(cProps)
|
||||
}
|
||||
|
||||
// Test if stale (update has an older version of data)
|
||||
if isStale(upd.UpdatedAt, old.UpdatedAt, old.CreatedAt) {
|
||||
return DalConnectionErrStaleData()
|
||||
}
|
||||
|
||||
upd.UpdatedAt = now()
|
||||
upd.CreatedAt = old.CreatedAt
|
||||
upd.UpdatedBy = a.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
// validate
|
||||
{
|
||||
if old.Type == types.DalPrimaryConnectionResourceType {
|
||||
// when primary connection is updated,
|
||||
// ignore connection & DAL config changes
|
||||
//
|
||||
// see Test_dal_connection_update_primary
|
||||
// for more details
|
||||
upd.Config.DAL = old.Config.DAL
|
||||
} else if upd.Config.DAL == nil {
|
||||
upd.Config.DAL = old.Config.DAL
|
||||
} else if !svc.ac.CanManageDalConfigOnDalConnection(ctx, old) {
|
||||
return DalConnectionErrNotAllowedToUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
if err = store.UpdateDalConnection(ctx, svc.store, upd); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
q = upd
|
||||
|
||||
return dalConnectionReplace(ctx, svc.store.ToDalConn(), svc.dal, upd)
|
||||
}()
|
||||
|
||||
if q != nil {
|
||||
svc.proc(ctx, q)
|
||||
}
|
||||
|
||||
return q, svc.recordAction(ctx, cProps, DalConnectionActionUpdate, err)
|
||||
}
|
||||
|
||||
func (svc *dalConnection) DeleteByID(ctx context.Context, ID uint64) (err error) {
|
||||
var (
|
||||
cProps = &dalConnectionActionProps{}
|
||||
c *types.DalConnection
|
||||
)
|
||||
|
||||
err = func() (err error) {
|
||||
if c, err = loadDalConnection(ctx, svc.store, ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if c.Type == types.DalPrimaryConnectionResourceType {
|
||||
return fmt.Errorf("not allowed to delete primary connections")
|
||||
}
|
||||
|
||||
if !svc.ac.CanDeleteDalConnection(ctx, c) {
|
||||
return DalConnectionErrNotAllowedToDelete(cProps)
|
||||
}
|
||||
|
||||
cProps.setConnection(c)
|
||||
|
||||
c.DeletedAt = now()
|
||||
c.DeletedBy = a.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
if err = store.UpdateDalConnection(ctx, svc.store, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return dalConnectionRemove(ctx, svc.dal, c)
|
||||
}()
|
||||
|
||||
return svc.recordAction(ctx, cProps, DalConnectionActionDelete, err)
|
||||
}
|
||||
|
||||
func (svc *dalConnection) UndeleteByID(ctx context.Context, ID uint64) (err error) {
|
||||
var (
|
||||
cProps = &dalConnectionActionProps{}
|
||||
c *types.DalConnection
|
||||
)
|
||||
|
||||
err = func() (err error) {
|
||||
if c, err = loadDalConnection(ctx, svc.store, ID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !svc.ac.CanDeleteDalConnection(ctx, c) {
|
||||
return DalConnectionErrNotAllowedToUndelete(cProps)
|
||||
}
|
||||
|
||||
cProps.setConnection(c)
|
||||
|
||||
c.DeletedAt = nil
|
||||
c.UpdatedBy = a.GetIdentityFromContext(ctx).Identity()
|
||||
|
||||
if err = store.UpdateDalConnection(ctx, svc.store, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// We're creating it here since it was removed on delete
|
||||
// primary connection can't be deleted we're just using nil here.
|
||||
return dalConnectionReplace(ctx, nil, svc.dal, c)
|
||||
}()
|
||||
|
||||
return svc.recordAction(ctx, cProps, DalConnectionActionDelete, err)
|
||||
}
|
||||
|
||||
func (svc *dalConnection) Search(ctx context.Context, filter types.DalConnectionFilter) (r types.DalConnectionSet, f types.DalConnectionFilter, err error) {
|
||||
var (
|
||||
aProps = &dalConnectionActionProps{search: &filter}
|
||||
)
|
||||
|
||||
// For each fetched item, store backend will check if it is valid or not
|
||||
filter.Check = func(res *types.DalConnection) (bool, error) {
|
||||
if !svc.ac.CanReadDalConnection(ctx, res) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
err = func() error {
|
||||
if !svc.ac.CanSearchDalConnections(ctx) {
|
||||
return DalConnectionErrNotAllowedToSearch()
|
||||
}
|
||||
|
||||
if r, f, err = store.SearchDalConnections(ctx, svc.store, filter); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
svc.proc(ctx, r...)
|
||||
return nil
|
||||
}()
|
||||
return r, f, svc.recordAction(ctx, aProps, DalConnectionActionSearch, err)
|
||||
}
|
||||
|
||||
func (svc *dalConnection) ReloadConnections(ctx context.Context) (err error) {
|
||||
return dalConnectionReload(ctx, svc.store, svc.dal)
|
||||
}
|
||||
|
||||
// proc is a helper function that processes the given connection set
|
||||
// before connections are returned to the caller.
|
||||
func (svc *dalConnection) proc(ctx context.Context, connections ...*types.DalConnection) {
|
||||
for _, c := range connections {
|
||||
svc.procPrimaryConnection(c)
|
||||
svc.procDal(ctx, c)
|
||||
svc.procLocale(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *dalConnection) procPrimaryConnection(c *types.DalConnection) {
|
||||
if c.Type == types.DalPrimaryConnectionResourceType {
|
||||
if c.Config.DAL == nil {
|
||||
c.Config.DAL = &types.ConnectionConfigDAL{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *dalConnection) procDal(ctx context.Context, c *types.DalConnection) {
|
||||
if !svc.ac.CanManageDalConfigOnDalConnection(ctx, c) {
|
||||
c.Config.DAL = nil
|
||||
return
|
||||
}
|
||||
|
||||
c.Issues = svc.dal.SearchConnectionIssues(c.ID)
|
||||
if len(c.Issues) == 0 {
|
||||
c.Issues = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *dalConnection) procLocale(c *types.DalConnection) {
|
||||
// @todo...
|
||||
}
|
||||
|
||||
func loadDalConnection(ctx context.Context, s store.DalConnections, ID uint64) (res *types.DalConnection, err error) {
|
||||
if ID == 0 {
|
||||
return nil, DalConnectionErrInvalidID()
|
||||
}
|
||||
|
||||
if res, err = store.LookupDalConnectionByID(ctx, s, ID); errors.IsNotFound(err) {
|
||||
return nil, DalConnectionErrNotFound()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func dalConnectionReload(ctx context.Context, s store.Storer, dcm dalConnManager) (err error) {
|
||||
// Get all available connections
|
||||
cc, _, err := store.SearchDalConnections(ctx, s, types.DalConnectionFilter{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return dalConnectionReplace(ctx, s.ToDalConn(), dcm, cc...)
|
||||
}
|
||||
|
||||
// Replaces all given connections
|
||||
func dalConnectionReplace(ctx context.Context, primary dal.Connection, dcm dalConnManager, cc ...*types.DalConnection) (err error) {
|
||||
var (
|
||||
cw *dal.ConnectionWrap
|
||||
isPrimary bool
|
||||
)
|
||||
|
||||
for _, c := range cc {
|
||||
isPrimary = c.Type == types.DalPrimaryConnectionResourceType
|
||||
|
||||
if isPrimary {
|
||||
// reuse primary connection
|
||||
cw, err = MakeDalConnection(c, primary)
|
||||
} else {
|
||||
cw, err = MakeDalConnection(c, nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = dcm.ReplaceConnection(ctx, cw, isPrimary); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// MakeDalConnection converts types.DalConnection to dal.ConnectionWrap and returns it.
|
||||
func MakeDalConnection(c *types.DalConnection, existing dal.Connection) (cw *dal.ConnectionWrap, err error) {
|
||||
var (
|
||||
connConfig = dal.ConnectionConfig{
|
||||
SensitivityLevelID: c.Config.Privacy.SensitivityLevelID,
|
||||
Label: c.Handle,
|
||||
}
|
||||
connParams dal.ConnectionParams
|
||||
)
|
||||
|
||||
if c.Config.DAL != nil {
|
||||
connConfig.ModelIdent = c.Config.DAL.ModelIdent
|
||||
|
||||
connParams = dal.ConnectionParams{
|
||||
Type: c.Config.DAL.Type,
|
||||
Params: c.Config.DAL.Params,
|
||||
}
|
||||
|
||||
if checks := len(c.Config.DAL.ModelIdentCheck); checks > 0 {
|
||||
connConfig.ModelIdentCheck = make([]*regexp.Regexp, checks)
|
||||
for i, m := range c.Config.DAL.ModelIdentCheck {
|
||||
if connConfig.ModelIdentCheck[i], err = regexp.Compile(m); err != nil {
|
||||
return nil, fmt.Errorf("could not prepare connection model ident check for %q: %w", c.Handle, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cw = dal.MakeConnection(
|
||||
c.ID,
|
||||
existing,
|
||||
connParams,
|
||||
connConfig,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Removes a connection from DAL service
|
||||
func dalConnectionRemove(ctx context.Context, dcm dalConnManager, cc ...*types.DalConnection) (err error) {
|
||||
for _, c := range cc {
|
||||
if err = dcm.RemoveConnection(ctx, c.ID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user