3
0

Remove obsolete compose/repository

This commit is contained in:
Denis Arh
2020-08-29 15:00:59 +02:00
parent 720dc38da6
commit 6041f26d04
11 changed files with 0 additions and 1752 deletions
-177
View File
@@ -1,177 +0,0 @@
package repository
//import (
// "context"
// "time"
//
// "github.com/Masterminds/squirrel"
// "github.com/pkg/errors"
// "github.com/titpetric/factory"
//
// "github.com/cortezaproject/corteza-server/compose/types"
// "github.com/cortezaproject/corteza-server/pkg/rh"
//)
//
//type (
// AttachmentRepository interface {
// With(ctx context.Context, db *factory.DB) AttachmentRepository
//
// Find(filter types.AttachmentFilter) (types.AttachmentSet, types.AttachmentFilter, error)
// FindByID(namespaceID, attachmentID uint64) (*types.Attachment, error)
// Create(mod *types.Attachment) (*types.Attachment, error)
// DeleteByID(namespaceID, attachmentID uint64) error
// }
//
// attachment struct {
// *repository
// }
//)
//
//const (
// ErrAttachmentNotFound = repositoryError("AttachmentNotFound")
//)
//
//func Attachment(ctx context.Context, db *factory.DB) AttachmentRepository {
// return (&attachment{}).With(ctx, db)
//}
//
//func (r attachment) With(ctx context.Context, db *factory.DB) AttachmentRepository {
// return &attachment{
// repository: r.repository.With(ctx, db),
// }
//}
//
//func (r attachment) table() string {
// return "compose_attachment"
//}
//
//func (r attachment) columns() []string {
// return []string{
// "a.id",
// "a.rel_namespace",
// "a.rel_owner",
// "a.kind",
// "a.url",
// "a.preview_url",
// "a.name",
// "a.meta",
// "a.created_at",
// "a.updated_at",
// "a.deleted_at",
// }
//}
//
//func (r attachment) query() squirrel.SelectBuilder {
// return squirrel.
// Select(r.columns()...).
// From(r.table() + " AS a").
// Where("a.deleted_at IS NULL")
//
//}
//
//func (r attachment) FindByID(namespaceID, attachmentID uint64) (*types.Attachment, error) {
// return r.findOneBy(namespaceID, "id", attachmentID)
//}
//
//func (r attachment) findOneBy(namespaceID uint64, field string, value interface{}) (*types.Attachment, error) {
// var (
// p = &types.Attachment{}
//
// q = r.query().
// Where(squirrel.Eq{field: value, "rel_namespace": namespaceID})
//
// err = rh.FetchOne(r.db(), q, p)
// )
//
// if err != nil {
// return nil, err
// } else if p.ID == 0 {
// return nil, ErrAttachmentNotFound
// }
//
// return p, nil
//}
//
//func (r attachment) Find(filter types.AttachmentFilter) (set types.AttachmentSet, f types.AttachmentFilter, err error) {
// f = filter
//
// if f.Sort == "" {
// f.Sort = "id ASC"
// }
//
// query := r.query().
// Where(squirrel.Eq{"a.kind": f.Kind})
//
// if filter.NamespaceID > 0 {
// query = query.Where("a.rel_namespace = ?", filter.NamespaceID)
// }
//
// switch f.Kind {
// case types.PageAttachment:
// // @todo implement filtering by page
// if f.PageID > 0 {
// err = errors.New("filtering by pageID not implemented")
// return
// }
//
// case types.RecordAttachment:
// query = query.
// Join("compose_record_value AS v ON (v.ref = a.id)")
//
// if f.ModuleID > 0 {
// query = query.
// Join("compose_record AS r ON (r.id = v.record_id)").
// Where(squirrel.Eq{"r.module_id": f.ModuleID})
// }
//
// if f.RecordID > 0 {
// query = query.Where(squirrel.Eq{"v.record_id": f.RecordID})
// }
//
// if f.FieldName != "" {
// query = query.Where(squirrel.Eq{"v.name": f.FieldName})
// }
//
// default:
// err = errors.New("unsupported kind value")
// return
// }
//
// if f.Filter != "" {
// err = errors.New("filtering by filter not implemented")
// return
// }
//
// var orderBy []string
// if orderBy, err = rh.ParseOrder(f.Sort, r.columns()...); err != nil {
// return
// } else {
// query = query.OrderBy(orderBy...)
// }
//
// if f.Count, err = rh.Count(r.db(), query); err != nil || f.Count == 0 {
// return
// }
//
// return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
//}
//
//func (r attachment) Create(mod *types.Attachment) (*types.Attachment, error) {
// if mod.ID == 0 {
// mod.ID = factory.Sonyflake.NextID()
// }
//
// mod.CreatedAt = time.Now()
//
// return mod, r.db().Insert(r.table(), mod)
//}
//
//func (r attachment) DeleteByID(namespaceID, attachmentID uint64) error {
// _, err := r.db().Exec(
// "UPDATE "+r.table()+" SET deleted_at = NOW() WHERE rel_namespace = ? AND id = ?",
// namespaceID,
// attachmentID,
// )
//
// return err
//}
-151
View File
@@ -1,151 +0,0 @@
package repository
//type (
// ChartRepository interface {
// With(ctx context.Context, db *factory.DB) ChartRepository
//
// FindByID(namespaceID, chartID uint64) (*types.Chart, error)
// FindByHandle(namespaceID uint64, handle string) (c *types.Chart, err error)
// Find(filter types.ChartFilter) (set types.ChartSet, f types.ChartFilter, err error)
// Create(mod *types.Chart) (*types.Chart, error)
// Update(mod *types.Chart) (*types.Chart, error)
// DeleteByID(namespaceID, chartID uint64) error
// }
//
// chart struct {
// *repository
// }
//)
//
//const (
// ErrChartNotFound = repositoryError("ChartNotFound")
// ErrChartHandleNotUnique = repositoryError("ChartHandleNotUnique")
//)
//
//func Chart(ctx context.Context, db *factory.DB) ChartRepository {
// return (&chart{}).With(ctx, db)
//}
//
//func (r chart) With(ctx context.Context, db *factory.DB) ChartRepository {
// return &chart{
// repository: r.repository.With(ctx, db),
// }
//}
//
//func (r chart) table() string {
// return "compose_chart"
//}
//
//func (r chart) columns() []string {
// return []string{
// "id",
// "rel_namespace",
// "handle",
// "name",
// "config",
// "created_at",
// "updated_at",
// "deleted_at",
// }
//}
//
//func (r chart) query() squirrel.SelectBuilder {
// return squirrel.
// Select(r.columns()...).
// From(r.table()).
// Where("deleted_at IS NULL")
//}
//
//func (r chart) FindByID(namespaceID, chartID uint64) (*types.Chart, error) {
// return r.findOneBy(namespaceID, "id", chartID)
//}
//
//func (r chart) FindByHandle(namespaceID uint64, handle string) (*types.Chart, error) {
// return r.findOneBy(namespaceID, "LOWER(handle)", strings.ToLower(strings.TrimSpace(handle)))
//}
//
//func (r chart) findOneBy(namespaceID uint64, field string, value interface{}) (*types.Chart, error) {
// var (
// c = &types.Chart{}
//
// q = r.query().
// Where(squirrel.Eq{field: value, "rel_namespace": namespaceID})
//
// err = rh.FetchOne(r.db(), q, c)
// )
//
// if err != nil {
// return nil, err
// } else if c.ID == 0 {
// return nil, ErrChartNotFound
// }
//
// return c, nil
//}
//
//func (r chart) Find(filter types.ChartFilter) (set types.ChartSet, f types.ChartFilter, err error) {
// //f = filter
// //
// //if f.Sort == "" {
// // f.Sort = "id ASC"
// //}
// //
// //query := r.query()
// //
// //if filter.NamespaceID > 0 {
// // query = query.Where(squirrel.Eq{"rel_namespace": filter.NamespaceID})
// //}
// //
// //if f.Query != "" {
// // q := "%" + strings.ToLower(f.Query) + "%"
// // query = query.Where(squirrel.Or{
// // squirrel.Like{"LOWER(name)": q},
// // })
// //}
// //
// //if f.Handle != "" {
// // query = query.Where("LOWER(handle) = LOWER(?)", f.Handle)
// //}
// //
// //if f.IsReadable != nil {
// // query = query.Where(f.IsReadable)
// //}
// //
// //var orderBy []string
// //if orderBy, err = rh.ParseOrder(f.Sort, r.columns()...); err != nil {
// // return
// //} else {
// // query = query.OrderBy(orderBy...)
// //}
// //
// //if f.Count, err = rh.Count(r.db(), query); err != nil || f.Count == 0 {
// // return
// //}
// //
// //return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
// return nil, f, fmt.Errorf("deprecated")
//}
//
//func (r chart) Create(mod *types.Chart) (*types.Chart, error) {
// mod.ID = factory.Sonyflake.NextID()
// rh.SetCurrentTimeRounded(&mod.CreatedAt)
// mod.UpdatedAt = nil
//
// return mod, r.db().Insert(r.table(), mod)
//}
//
//func (r chart) Update(mod *types.Chart) (*types.Chart, error) {
// rh.SetCurrentTimeRounded(&mod.UpdatedAt)
//
// return mod, r.db().Update(r.table(), mod, "id")
//}
//
//func (r chart) DeleteByID(namespaceID, chartID uint64) error {
// _, err := r.db().Exec(
// "UPDATE "+r.table()+" SET deleted_at = NOW() WHERE rel_namespace = ? AND id = ?",
// namespaceID,
// chartID,
// )
//
// return err
//}
-21
View File
@@ -1,21 +0,0 @@
package repository
type (
repositoryError string
)
const (
ErrNotImplemented = repositoryError("NotImplemented")
)
func (e repositoryError) Error() string {
return e.String()
}
func (e repositoryError) String() string {
return "compose.repository." + string(e)
}
func (e repositoryError) Eq(err error) bool {
return err != nil && e.Error() == err.Error()
}
-275
View File
@@ -1,275 +0,0 @@
package repository
import (
"context"
"fmt"
"strings"
"github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/rh"
)
type (
ModuleRepository interface {
With(ctx context.Context, db *factory.DB) ModuleRepository
FindByID(namespaceID, moduleID uint64) (*types.Module, error)
FindByName(namespaceID uint64, name string) (*types.Module, error)
FindByHandle(namespaceID uint64, handle string) (*types.Module, error)
Find(filter types.ModuleFilter) (set types.ModuleSet, f types.ModuleFilter, err error)
FindFields(moduleIDs ...uint64) (ff types.ModuleFieldSet, err error)
Create(mod *types.Module) (*types.Module, error)
Update(mod *types.Module) (*types.Module, error)
UpdateFields(moduleID uint64, ff types.ModuleFieldSet, hasRecords bool) (err error)
DeleteByID(namespaceID, moduleID uint64) error
}
module struct {
*repository
}
)
const (
ErrModuleNotFound = repositoryError("ModuleNotFound")
//ErrModuleNameNotUnique = repositoryError("ModuleNameNotUnique")
//ErrModuleHandleNotUnique = repositoryError("ModuleHandleNotUnique")
)
func Module(ctx context.Context, db *factory.DB) ModuleRepository {
return (&module{}).With(ctx, db)
}
func (r module) With(ctx context.Context, db *factory.DB) ModuleRepository {
return &module{
repository: r.repository.With(ctx, db),
}
}
func (r module) table() string {
return "compose_module"
}
func (r module) tableFields() string {
return "compose_module_field"
}
func (r module) columns() []string {
return []string{
"id",
"rel_namespace",
"handle",
"name",
"json",
"created_at",
"updated_at",
"deleted_at",
}
}
func (r module) query() squirrel.SelectBuilder {
return squirrel.
Select(r.columns()...).
From(r.table()).
Where("deleted_at IS NULL")
}
func (r module) FindByID(namespaceID, moduleID uint64) (*types.Module, error) {
return r.findOneBy(namespaceID, "id", moduleID)
}
func (r module) FindByHandle(namespaceID uint64, handle string) (*types.Module, error) {
return r.findOneBy(namespaceID, "LOWER(handle)", strings.ToLower(strings.TrimSpace(handle)))
}
func (r module) FindByName(namespaceID uint64, name string) (*types.Module, error) {
return r.findOneBy(namespaceID, "LOWER(name)", strings.ToLower(strings.TrimSpace(name)))
}
func (r module) findOneBy(namespaceID uint64, field string, value interface{}) (*types.Module, error) {
var (
m = &types.Module{}
q = r.query().
Where(squirrel.Eq{field: value, "rel_namespace": namespaceID})
err = rh.FetchOne(r.db(), q, m)
)
if err != nil {
return nil, err
} else if m.ID == 0 {
return nil, ErrModuleNotFound
}
return m, nil
}
func (r module) Find(filter types.ModuleFilter) (set types.ModuleSet, f types.ModuleFilter, err error) {
//f = filter
//
//if f.Sort == "" {
// f.Sort = "id ASC"
//}
//
//query := r.query()
//
//if filter.NamespaceID > 0 {
// query = query.Where("rel_namespace = ?", filter.NamespaceID)
//}
//
//if f.Query != "" {
// q := "%" + strings.ToLower(f.Query) + "%"
// query = query.Where(squirrel.Or{
// squirrel.Like{"LOWER(name)": q},
// squirrel.Like{"LOWER(handle)": q},
// })
//}
//
//if f.Name != "" {
// query = query.Where(squirrel.Eq{"LOWER(name)": strings.ToLower(f.Name)})
//}
//
//if f.Handle != "" {
// query = query.Where(squirrel.Eq{"LOWER(handle)": strings.ToLower(f.Handle)})
//}
//
//if f.IsReadable != nil {
// query = query.Where(f.IsReadable)
//}
//
//var orderBy []string
//if orderBy, err = rh.ParseOrder(f.Sort, r.columns()...); err != nil {
// return
//} else {
// query = query.OrderBy(orderBy...)
//}
//
//if f.Count, err = rh.Count(r.db(), query); err != nil || f.Count == 0 {
// return
//}
//
//return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
return nil, f, fmt.Errorf("deprecated")
}
func (r module) Create(mod *types.Module) (*types.Module, error) {
var err error
mod.ID = factory.Sonyflake.NextID()
rh.SetCurrentTimeRounded(&mod.CreatedAt)
mod.UpdatedAt = nil
if err = r.db().Insert(r.table(), mod); err != nil {
return nil, err
}
return mod, nil
}
func (r module) Update(mod *types.Module) (*types.Module, error) {
rh.SetCurrentTimeRounded(&mod.UpdatedAt)
return mod, r.db().Update(r.table(), mod, "id")
}
func (r module) UpdateFields(moduleID uint64, ff types.ModuleFieldSet, hasRecords bool) error {
if existing, err := r.FindFields(moduleID); err != nil {
return err
} else {
// Remove fields that do not exist anymore
err = existing.Walk(func(e *types.ModuleField) error {
if ff.FindByID(e.ID) == nil {
return r.deleteFieldByID(moduleID, e.ID)
}
return nil
})
if err != nil {
return err
}
for idx, f := range ff {
if e := existing.FindByID(f.ID); e != nil {
f.CreatedAt = e.CreatedAt
// We do not have any other code in place that would handle changes of field name and kind, so we need
// to reset any changes made to the field.
// @todo remove when we are able to handle field rename & type change
if hasRecords {
f.Name = e.Name
f.Kind = e.Kind
} else {
rh.SetCurrentTimeRounded(&f.UpdatedAt)
}
} else {
f.ID = 0
}
if f.ID == 0 {
f.ID = factory.Sonyflake.NextID()
rh.SetCurrentTimeRounded(&f.CreatedAt)
}
f.ModuleID = moduleID
f.Place = idx
f.DeletedAt = nil
if err := r.db().Replace(r.tableFields(), f); err != nil {
return errors.Wrap(err, "Error updating module fields")
}
}
}
return nil
}
func (r module) deleteFieldByID(moduleID, fieldID uint64) error {
_, err := r.db().Exec(
fmt.Sprintf("DELETE FROM %s WHERE rel_module = ? AND id = ?", r.tableFields()),
moduleID,
fieldID,
)
return err
}
func (r module) DeleteByID(namespaceID, moduleID uint64) error {
_, err := r.db().Exec(
fmt.Sprintf("UPDATE %s SET deleted_at = NOW() WHERE rel_namespace = ? AND id = ?", r.table()),
namespaceID,
moduleID,
)
return err
}
func (r module) FindFields(moduleIDs ...uint64) (ff types.ModuleFieldSet, err error) {
if len(moduleIDs) == 0 {
return
}
query := `SELECT id, rel_module, place,
kind, name, label, options,
is_private, is_required, is_visible, is_multi, default_value,
created_at, updated_at, deleted_at
FROM %s
WHERE rel_module IN (?)
AND deleted_at IS NULL
ORDER BY rel_module, place`
query = fmt.Sprintf(query, r.tableFields())
if sql, args, err := sqlx.In(query, moduleIDs); err != nil {
return nil, err
} else {
return ff, r.db().Select(&ff, sql, args...)
}
}
-154
View File
@@ -1,154 +0,0 @@
package repository
import (
"context"
"fmt"
"github.com/Masterminds/squirrel"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/rh"
)
type (
NamespaceRepository interface {
With(ctx context.Context, db *factory.DB) NamespaceRepository
FindByID(id uint64) (*types.Namespace, error)
FindBySlug(slug string) (*types.Namespace, error)
Find(filter types.NamespaceFilter) (types.NamespaceSet, types.NamespaceFilter, error)
Create(mod *types.Namespace) (*types.Namespace, error)
Update(mod *types.Namespace) (*types.Namespace, error)
DeleteByID(id uint64) error
}
namespace struct {
*repository
}
)
const (
ErrNamespaceNotFound = repositoryError("NamespaceNotFound")
ErrNamespaceSlugNotUnique = repositoryError("NamespaceSlugNotUnique")
ErrNamespaceInvalidSlugFormat = repositoryError("NamespaceInvalidSlugFormat")
)
func Namespace(ctx context.Context, db *factory.DB) NamespaceRepository {
return (&namespace{}).With(ctx, db)
}
func (r namespace) table() string {
return "compose_namespace"
}
func (r namespace) columns() []string {
return []string{
"id",
"name",
"slug",
"enabled",
"meta",
"created_at",
"updated_at",
"deleted_at",
}
}
func (r namespace) query() squirrel.SelectBuilder {
return squirrel.
Select(r.columns()...).
From(r.table()).
Where("deleted_at IS NULL")
}
func (r *namespace) With(ctx context.Context, db *factory.DB) NamespaceRepository {
return &namespace{
repository: r.repository.With(ctx, db),
}
}
func (r *namespace) FindByID(namespaceID uint64) (*types.Namespace, error) {
return r.findOneBy("id", namespaceID)
}
func (r *namespace) FindBySlug(slug string) (*types.Namespace, error) {
return r.findOneBy("slug", slug)
}
func (r *namespace) findOneBy(field string, value interface{}) (*types.Namespace, error) {
var (
ns = &types.Namespace{}
q = r.query().
Where(squirrel.Eq{field: value})
err = rh.FetchOne(r.db(), q, ns)
)
if err != nil {
return nil, err
} else if ns.ID == 0 {
return nil, ErrNamespaceNotFound
}
return ns, nil
}
func (r *namespace) Find(filter types.NamespaceFilter) (set types.NamespaceSet, f types.NamespaceFilter, err error) {
//f = filter
//
//if f.Sort == "" {
// f.Sort = "id ASC"
//}
//
//query := r.query()
//if f.Query != "" {
// q := "%" + strings.ToLower(f.Query) + "%"
// query = query.Where(squirrel.Or{
// squirrel.Like{"LOWER(name)": q},
// squirrel.Like{"LOWER(slug)": q},
// })
//}
//
//if f.Slug != "" {
// query = query.Where(squirrel.Eq{"LOWER(slug)": strings.ToLower(f.Slug)})
//}
//
//if f.IsReadable != nil {
// query = query.Where(f.IsReadable)
//}
//
//var orderBy []string
//if orderBy, err = rh.ParseOrder(f.Sort, r.columns()...); err != nil {
// return
//} else {
// query = query.OrderBy(orderBy...)
//}
//
//if f.Count, err = rh.Count(r.db(), query); err != nil || f.Count == 0 {
// return
//}
//
//return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
return nil, f, fmt.Errorf("deprecated")
}
func (r *namespace) Create(mod *types.Namespace) (*types.Namespace, error) {
mod.ID = factory.Sonyflake.NextID()
rh.SetCurrentTimeRounded(&mod.CreatedAt)
mod.UpdatedAt = nil
return mod, r.db().Insert(r.table(), mod)
}
func (r *namespace) Update(mod *types.Namespace) (*types.Namespace, error) {
rh.SetCurrentTimeRounded(&mod.UpdatedAt)
return mod, r.db().Update(r.table(), mod, "id")
}
func (r *namespace) DeleteByID(namespaceID uint64) error {
_, err := r.db().Exec("UPDATE "+r.table()+" SET deleted_at = NOW() WHERE id = ?", namespaceID)
return err
}
-217
View File
@@ -1,217 +0,0 @@
package repository
import (
"context"
"fmt"
"github.com/Masterminds/squirrel"
"github.com/titpetric/factory"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/rh"
)
type (
PageRepository interface {
With(ctx context.Context, db *factory.DB) PageRepository
FindByID(namespaceID, pageID uint64) (*types.Page, error)
FindByHandle(namespaceID uint64, handle string) (*types.Page, error)
FindByModuleID(namespaceID, moduleID uint64) (*types.Page, error)
Find(filter types.PageFilter) (set types.PageSet, f types.PageFilter, err error)
Create(mod *types.Page) (*types.Page, error)
Update(mod *types.Page) (*types.Page, error)
DeleteByID(namespaceID, pageID uint64) error
Reorder(namespaceID, selfID uint64, pageIDs []uint64) error
}
page struct {
*repository
}
)
const (
ErrPageNotFound = repositoryError("PageNotFound")
ErrPageHandleNotUnique = repositoryError("PageHandleNotUnique")
)
func Page(ctx context.Context, db *factory.DB) PageRepository {
return (&page{}).With(ctx, db)
}
func (r page) With(ctx context.Context, db *factory.DB) PageRepository {
return &page{
repository: r.repository.With(ctx, db),
}
}
func (r page) table() string {
return "compose_page"
}
func (r page) columns() []string {
return []string{
"id",
"rel_namespace",
"self_id",
"rel_module",
"handle",
"title",
"blocks",
"description",
"visible",
"weight",
"created_at",
"updated_at",
"deleted_at",
}
}
func (r page) query() squirrel.SelectBuilder {
return squirrel.
Select(r.columns()...).
From(r.table()).
Where("deleted_at IS NULL")
}
func (r page) FindByID(namespaceID, pageID uint64) (*types.Page, error) {
return r.findOneBy(namespaceID, "id", pageID)
}
func (r page) FindByHandle(namespaceID uint64, handle string) (*types.Page, error) {
return r.findOneBy(namespaceID, "handle", handle)
}
func (r page) FindByModuleID(namespaceID, moduleID uint64) (*types.Page, error) {
return r.findOneBy(namespaceID, "rel_module", moduleID)
}
func (r page) findOneBy(namespaceID uint64, field string, value interface{}) (*types.Page, error) {
var (
p = &types.Page{}
q = r.query().
Where(squirrel.Eq{field: value, "rel_namespace": namespaceID})
err = rh.FetchOne(r.db(), q, p)
)
if err != nil {
return nil, err
} else if p.ID == 0 {
return nil, ErrPageNotFound
}
return p, nil
}
func (r page) Find(filter types.PageFilter) (set types.PageSet, f types.PageFilter, err error) {
//f = filter
//
//if f.Sort == "" {
// f.Sort = "id ASC"
//}
//
//query := r.query()
//
//if filter.NamespaceID > 0 {
// query = query.Where("rel_namespace = ?", filter.NamespaceID)
//}
//
//if filter.ParentID > 0 {
// query = query.Where("self_id = ?", filter.ParentID)
//} else if filter.Root {
// query = query.Where("self_id = 0")
//}
//
//if f.Handle != "" {
// query = query.Where("LOWER(handle) = ?", strings.ToLower(f.Handle))
//}
//
//if f.Query != "" {
// q := "%" + strings.ToLower(f.Query) + "%"
// query = query.Where(squirrel.Or{
// squirrel.Like{"LOWER(title)": q},
// squirrel.Like{"LOWER(description)": q},
// })
//}
//
//if f.IsReadable != nil {
// query = query.Where(f.IsReadable)
//}
//
//var orderBy []string
//if orderBy, err = rh.ParseOrder(f.Sort, r.columns()...); err != nil {
// return
//} else {
// query = query.OrderBy(orderBy...)
//}
//
//if f.Count, err = rh.Count(r.db(), query); err != nil || f.Count == 0 {
// return
//}
//
//return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
return nil, f, fmt.Errorf("deprecated")
}
func (r page) Reorder(namespaceID, parentID uint64, pageIDs []uint64) error {
var (
pageMap = map[uint64]bool{}
filter = types.PageFilter{NamespaceID: namespaceID, ParentID: parentID}
)
if pages, _, err := r.Find(filter); err != nil {
return nil
} else {
for _, page := range pages {
pageMap[page.ID] = true
}
}
weight := 1
db := r.db()
// honor parameter first
for _, pageID := range pageIDs {
if pageMap[pageID] {
pageMap[pageID] = false
if _, err := db.Exec("UPDATE compose_page SET weight = ? WHERE id = ? AND self_id = ?", weight, pageID, parentID); err != nil {
return err
}
weight++
}
}
for pageID, update := range pageMap {
if update {
if _, err := db.Exec("UPDATE compose_page SET weight = ? WHERE id = ? AND self_id = ?", weight, pageID, parentID); err != nil {
return err
}
weight++
}
}
return nil
}
func (r page) Create(mod *types.Page) (*types.Page, error) {
mod.ID = factory.Sonyflake.NextID()
rh.SetCurrentTimeRounded(&mod.CreatedAt)
mod.UpdatedAt = nil
return mod, r.db().Insert(r.table(), mod)
}
func (r page) Update(mod *types.Page) (*types.Page, error) {
rh.SetCurrentTimeRounded(&mod.UpdatedAt)
return mod, r.db().Update(r.table(), mod, "id")
}
func (r page) DeleteByID(namespaceID, pageID uint64) error {
_, err := r.db().Exec(
"UPDATE "+r.table()+" SET deleted_at = NOW() WHERE rel_namespace = ? AND id = ?",
namespaceID,
pageID,
)
return err
}
-387
View File
@@ -1,387 +0,0 @@
package repository
import (
"context"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/titpetric/factory"
"strings"
)
type (
RecordRepository interface {
With(ctx context.Context, db *factory.DB) RecordRepository
FindByID(namespaceID, recordID uint64) (*types.Record, error)
Report(module *types.Module, metrics, dimensions, filter string) (results interface{}, err error)
Find(module *types.Module, filter types.RecordFilter) (set types.RecordSet, f types.RecordFilter, err error)
Export(module *types.Module, filter types.RecordFilter) (set types.RecordSet, err error)
Create(record *types.Record) (*types.Record, error)
Update(record *types.Record) (*types.Record, error)
Delete(record *types.Record) error
RefValueLookup(moduleID uint64, field string, ref uint64) (recordID uint64, err error)
LoadValues(fieldNames []string, IDs []uint64) (rvs types.RecordValueSet, err error)
DeleteValues(record *types.Record) error
UpdateValues(recordID uint64, rvs types.RecordValueSet) (err error)
PartialUpdateValues(rvs ...*types.RecordValue) (err error)
}
record struct {
*repository
}
)
//
const (
ErrRecordNotFound = repositoryError("RecordNotFound")
)
//
func Record(ctx context.Context, db *factory.DB) RecordRepository {
return nil //.With(ctx, db)
}
//
//func (r record) With(ctx context.Context, db *factory.DB) RecordRepository {
// return &record{
// repository: r.repository.With(ctx, db),
// }
//}
//
//func (r record) table() string {
// return "compose_record"
//}
//
//func (r record) columns() []string {
// return []string{
// "r.id",
// "r.module_id",
// "r.rel_namespace",
// "r.owned_by",
// "r.created_at",
// "r.created_by",
// "r.updated_at",
// "r.updated_by",
// "r.deleted_at",
// "r.deleted_by",
// }
//}
//
//func (r record) query() squirrel.SelectBuilder {
// return squirrel.
// Select(r.columns()...).
// From(r.table() + " AS r")
//}
//
//// @todo: update to accepted DeletedAt column semantics from Messaging
//
//func (r record) FindByID(namespaceID, recordID uint64) (*types.Record, error) {
// return r.findOneBy(namespaceID, "id", recordID)
//}
//
//func (r record) findOneBy(namespaceID uint64, field string, value interface{}) (*types.Record, error) {
// var (
// rec = &types.Record{}
//
// q = r.query().
// Where("r.deleted_at IS NULL").
// Where(squirrel.Eq{field: value, "rel_namespace": namespaceID})
//
// err = rh.FetchOne(r.db(), q, rec)
// )
//
// if err != nil {
// return nil, err
// } else if rec.ID == 0 {
// return nil, ErrRecordNotFound
// }
//
// return rec, nil
//}
//
//func (r record) Report(module *types.Module, metrics, dimensions, filter string) (results interface{}, err error) {
// crb := NewRecordReportBuilder(module)
//
// var result = make([]map[string]interface{}, 0)
//
// if query, args, err := crb.Build(metrics, dimensions, filter); err != nil {
// return nil, errors.Wrap(err, "can not generate report query")
// } else if rows, err := r.db().Query(query, args...); err != nil {
// return nil, errors.Wrapf(err, "can not execute report query (%s)", query)
// } else {
// for rows.Next() {
// result = append(result, crb.Cast(rows))
// }
//
// return result, nil
// }
//}
//
//func (r record) Find(module *types.Module, filter types.RecordFilter) (set types.RecordSet, f types.RecordFilter, err error) {
// var query squirrel.SelectBuilder
// f = filter
//
// query, err = r.buildQuery(module, filter)
// if err != nil {
// return
// }
//
// if f.Count, err = rh.Count(r.db(), query); err != nil || f.Count == 0 {
// return
// }
//
// return set, f, rh.FetchPaged(r.db(), query, f.PageFilter, &set)
//}
//
//// Export ignores paging and does not return filter
////
//// @todo optimize and include value loading
//func (r record) Export(module *types.Module, filter types.RecordFilter) (set types.RecordSet, err error) {
// filter.PageFilter = rh.PageFilter{}
//
// query, err := r.buildQuery(module, filter)
// if err != nil {
// return
// }
//
// return set, rh.FetchAll(r.db(), query, &set)
//}
//
//func (r record) buildQuery(module *types.Module, f types.RecordFilter) (query squirrel.SelectBuilder, err error) {
// var (
// joinedFields = []string{}
// alreadyJoined = func(f string) bool {
// for _, a := range joinedFields {
// if a == f {
// return true
// }
// }
//
// joinedFields = append(joinedFields, f)
// return false
// }
//
// identResolver = func(i ql.Ident) (ql.Ident, error) {
// var is bool
// if i.Value, is = isRealRecordCol(i.Value); is {
// i.Value += " "
// return i, nil
// }
//
// if !module.Fields.HasName(i.Value) {
// return i, errors.Errorf("unknown field %q", i.Value)
// }
//
// if !alreadyJoined(i.Value) {
// query = query.LeftJoin(fmt.Sprintf(
// "compose_record_value AS rv_%s ON (rv_%s.record_id = r.id AND rv_%s.name = ? AND rv_%s.deleted_at IS NULL)",
// i.Value, i.Value, i.Value, i.Value,
// ), i.Value)
// }
//
// field := module.Fields.FindByName(i.Value)
//
// switch true {
// case field.IsBoolean():
// i.Value = fmt.Sprintf("(rv_%s.value NOT IN ('', '0', 'false', 'f', 'FALSE', 'F', false))", i.Value)
// case field.IsNumeric():
// i.Value = fmt.Sprintf("CAST(rv_%s.value AS SIGNED)", i.Value)
// case field.IsDateTime():
// i.Value = fmt.Sprintf("CAST(rv_%s.value AS DATETIME)", i.Value)
// case field.IsRef():
// i.Value = fmt.Sprintf("rv_%s.ref ", i.Value)
// default:
// i.Value = fmt.Sprintf("rv_%s.value ", i.Value)
// }
//
// return i, nil
// }
// )
//
// // Create query for fetching and counting records.
// query = r.query().
// Where("r.module_id = ?", module.ID).
// Where("r.rel_namespace = ?", module.NamespaceID)
//
// // Inc/exclude deleted records according to filter settings
// query = rh.FilterNullByState(query, "r.deleted_at", f.Deleted)
//
// // Parse filters.
// if f.Query != "" {
// var (
// // Filter parser
// fp = ql.NewParser()
//
// // Filter node
// fn ql.ASTNode
// )
//
// // Resolve all identifiers found in the query
// // into their table/column counterparts
// fp.OnIdent = identResolver
//
// if fn, err = fp.ParseExpression(f.Query); err != nil {
// return
// } else if filterSql, filterArgs, err := fn.ToSql(); err != nil {
// return query, err
// } else {
// query = query.Where("("+filterSql+")", filterArgs...)
// }
// }
//
// if f.Sort != "" {
// var (
// // Sort parser
// sp = ql.NewParser()
//
// // Sort columns
// sc ql.Columns
// )
//
// // Resolve all identifiers found in sort
// // into their table/column counterparts
// sp.OnIdent = identResolver
//
// if sc, err = sp.ParseColumns(f.Sort); err != nil {
// return
// }
//
// query = query.OrderBy(sc.Strings()...)
// }
//
// return
//}
//
//func (r record) Create(record *types.Record) (*types.Record, error) {
// record.ID = factory.Sonyflake.NextID()
//
// if err := r.db().Insert("compose_record", record); err != nil {
// return nil, errors.Wrap(err, "could not update record")
// }
//
// return record, nil
//}
//
//func (r record) Update(record *types.Record) (*types.Record, error) {
// if err := r.db().Update("compose_record", record, "id"); err != nil {
// return nil, errors.Wrap(err, "could not update record")
// }
//
// return record, nil
//}
//
//func (r record) Delete(record *types.Record) error {
// _, err := r.db().Exec(
// "UPDATE compose_record SET deleted_at = ?, deleted_by = ? WHERE rel_namespace = ? AND id = ?",
// record.DeletedAt,
// record.DeletedBy,
// record.NamespaceID,
// record.ID,
// )
//
// return err
//}
//
//func (r record) DeleteValues(record *types.Record) error {
// _, err := r.db().Exec(
// "UPDATE compose_record_value SET deleted_at = ? WHERE record_id = ?",
// record.DeletedAt,
// record.ID)
//
// return err
//}
//
//func (r record) UpdateValues(recordID uint64, rvs types.RecordValueSet) (err error) {
// // Remove all records and prepare to be updated
// // @todo be more selective and delete only removed and update/insert changed/new values
// if _, err = r.db().Exec("DELETE FROM compose_record_value WHERE record_id = ?", recordID); err != nil {
// return errors.Wrap(err, "could not remove record values")
// }
//
// err = rvs.Walk(func(value *types.RecordValue) error {
// if value.DeletedAt != nil {
// return nil
// }
//
// value.RecordID = recordID
// return r.db().Insert("compose_record_value", value)
// })
//
// return errors.Wrap(err, "could not insert record values")
//
//}
//
//func (r record) PartialUpdateValues(rvs ...*types.RecordValue) (err error) {
// err = types.RecordValueSet(rvs).Walk(func(value *types.RecordValue) error {
// return r.db().Replace("compose_record_value", value)
// })
//
// return errors.Wrap(err, "could not replace record values")
//}
//
//func (r record) RefValueLookup(moduleID uint64, field string, ref uint64) (recordID uint64, err error) {
// var sql = "SELECT record_id" +
// " FROM compose_record AS r INNER JOIN compose_record_value AS v " +
// " WHERE r.module_id = ? " +
// " AND v.name = ? " +
// " AND v.ref = ? " +
// " AND r.deleted_at IS NULL " +
// " AND v.deleted_at IS NULL " +
// " LIMIT 1"
//
// return recordID, r.db().Get(&recordID, sql, moduleID, field, ref)
//}
//
//func (r record) LoadValues(fieldNames []string, IDs []uint64) (rvs types.RecordValueSet, err error) {
// if len(fieldNames) == 0 || len(IDs) == 0 {
// return
// }
//
// var sql = "SELECT record_id, name, value, ref, place, deleted_at " +
// " FROM compose_record_value " +
// " WHERE record_id IN (?) " +
// " AND name IN (?) " +
// " AND deleted_at IS NULL " +
// " ORDER BY record_id, place"
//
// if sql, args, err := sqlx.In(sql, IDs, fieldNames); err != nil {
// return nil, err
// } else {
// return rvs, r.db().Select(&rvs, sql, args...)
// }
//}
//
// Checks if field name is "real column", reformats it and returns
func isRealRecordCol(name string) (string, bool) {
switch name {
case
"recordID",
"id":
return "r.id", true
case
"module_id",
"owned_by",
"created_by",
"created_at",
"updated_by",
"updated_at",
"deleted_by",
"deleted_at":
return "r." + name, true
case
"moduleID",
"ownedBy",
"createdBy",
"createdAt",
"updatedBy",
"updatedAt",
"deletedBy",
"deletedAt":
return "r." + name[0:len(name)-2] + "_" + strings.ToLower(name[len(name)-2:]), true
}
return name, false
}
-183
View File
@@ -1,183 +0,0 @@
package repository
import (
"fmt"
"strconv"
"strings"
"github.com/Masterminds/squirrel"
"github.com/jmoiron/sqlx"
"github.com/pkg/errors"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/ql"
"github.com/cortezaproject/corteza-server/pkg/rh"
)
type (
recordReportBuilder struct {
module *types.Module
// This is set by metric/column building to assist Cast()
numerics []string
report squirrel.SelectBuilder
parser *ql.Parser
}
)
// Identifiers should be names of the fields (physical table columns OR json fields, defined in module)
func stdAggregationHandler(f ql.Function) (ql.Function, error) {
switch strings.ToUpper(f.Name) {
// case "COUNTD":
// return SqlConcatExpr("COUNT(DISTINCT ", aggrFuncArgs, ")")
//
case "COUNT", "SUM", "MAX", "MIN", "AVG", "STD":
return f, nil
default:
return f, fmt.Errorf("unsupported aggregate function %q", f.Name)
}
}
// Identifiers should be names of the fields (physical table columns OR json fields, defined in module)
func stdFilterFuncHandler(f ql.Function) (ql.Function, error) {
switch strings.ToUpper(f.Name) {
case "CONCAT", "QUARTER", "YEAR", "DATE", "NOW", "DATE_ADD", "DATE_SUB", "DATE_FORMAT":
return f, nil
default:
return f, fmt.Errorf("unsupported group-by function %q", f.Name)
}
}
func NewRecordReportBuilder(module *types.Module) *recordReportBuilder {
var report = squirrel.
Select().
Column(squirrel.Alias(squirrel.Expr("COUNT(*)"), "count")).
From("compose_record AS r").
Where("r.deleted_at IS NULL").
Where("r.module_id = ?", module.ID)
return &recordReportBuilder{
parser: ql.NewParser(),
module: module,
report: report,
}
}
func (b *recordReportBuilder) Build(metrics, dimensions, filters string) (sql string, args []interface{}, err error) {
var joinedFields = []string{}
var alreadyJoined = func(f string) bool {
for _, a := range joinedFields {
if a == f {
return true
}
}
joinedFields = append(joinedFields, f)
return false
}
b.parser.OnIdent = func(i ql.Ident) (ql.Ident, error) {
var is bool
if i.Value, is = isRealRecordCol(i.Value); is {
return i, nil
}
if !b.module.Fields.HasName(i.Value) {
return i, errors.Errorf("unknown field %q", i.Value)
}
if !alreadyJoined(i.Value) {
b.report = b.report.LeftJoin(fmt.Sprintf(
"compose_record_value AS rv_%s ON (rv_%s.record_id = r.id AND rv_%s.name = ? AND rv_%s.deleted_at IS NULL)",
i.Value, i.Value, i.Value, i.Value,
), i.Value)
}
// @todo switch value for ref when doing Record/Owner lookup
i.Value = fmt.Sprintf("rv_%s.value", i.Value)
return i, nil
}
var columns ql.Columns
b.parser.OnFunction = stdAggregationHandler
if columns, err = b.parser.ParseColumns(metrics); err != nil {
err = errors.Wrapf(err, "could not parse metrics %q", metrics)
return
}
// Add all metrics to columns
for i, m := range columns {
if m.Alias == "" {
// Generate alias
m.Alias = fmt.Sprintf("metric_%d", i)
}
// Wrap to cast func to ensure numeric output
col := squirrel.Alias(rh.SquirrelConcatExpr("CAST(", m.Expr, " AS DECIMAL(14,2))"), m.Alias)
b.report = b.report.Column(col)
b.numerics = append(b.numerics, m.Alias)
}
b.parser.OnFunction = stdFilterFuncHandler
if columns, err = b.parser.ParseColumns(dimensions); err != nil {
err = errors.Wrapf(err, "could not parse dimensions %q", dimensions)
return
}
// Add dimensions
for i, d := range columns {
if d.Alias == "" {
d.Alias = fmt.Sprintf("dimension_%d", i)
}
b.report = b.report.
Column(d).
GroupBy(d.Alias).
OrderBy(d.Alias)
}
// Use a different handler for filter functions for this
b.parser.OnFunction = stdFilterFuncHandler
if len(filters) > 0 {
var filter ql.ASTNode
if filter, err = b.parser.ParseExpression(filters); err != nil {
err = errors.Wrapf(err, "could not parse filters %q", filters)
return
}
// We need to wrap this one level deeper, since additional filters should
// be evaluated as a whole.
// For example A AND B OR C =should be> (A AND B OR C)
// so the output becomes BASE AND (ADDITIONAL)
b.report = b.report.Where(ql.ASTNodes{filter})
}
return b.report.ToSql()
}
func (b recordReportBuilder) Cast(row sqlx.ColScanner) map[string]interface{} {
out := map[string]interface{}{}
sqlx.MapScan(row, out)
for k, v := range out {
switch cv := v.(type) {
case []uint8:
out[k] = string(cv)
default:
}
}
// Cast all metrics to float64
for _, fname := range b.numerics {
switch num := out[fname].(type) {
case string:
out[fname], _ = strconv.ParseFloat(num, 64)
}
}
return out
}
@@ -1,34 +0,0 @@
package repository
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/cortezaproject/corteza-server/compose/types"
)
func TestRecordReportBuilder2(t *testing.T) {
builder := NewRecordReportBuilder(&types.Module{
ID: 1000,
Fields: types.ModuleFieldSet{
&types.ModuleField{Name: "single1"},
&types.ModuleField{Name: "multi1", Multi: true},
&types.ModuleField{Name: "ref1", Kind: "Record"},
&types.ModuleField{Name: "multiRef1", Kind: "Record", Multi: true},
}},
)
expected := "SELECT (COUNT(*)) AS count, (CAST(max(rv_single1.value) AS DECIMAL(14,2))) AS metric_0, " +
"(QUARTER(rv_ref1.value)) AS dimension_0 " +
"FROM compose_record AS r " +
"LEFT JOIN compose_record_value AS rv_single1 ON (rv_single1.record_id = r.id AND rv_single1.name = ? AND rv_single1.deleted_at IS NULL) " +
"LEFT JOIN compose_record_value AS rv_ref1 ON (rv_ref1.record_id = r.id AND rv_ref1.name = ? AND rv_ref1.deleted_at IS NULL) " +
"WHERE r.deleted_at IS NULL AND r.module_id = ? AND (rv_ref1.value = 2) " +
"GROUP BY dimension_0 " +
"ORDER BY dimension_0"
sql, _, err := builder.Build("max(single1)", "QUARTER(ref1)", "ref1 = 2")
require.NoError(t, err)
require.Equal(t, expected, sql)
}
-113
View File
@@ -1,113 +0,0 @@
package repository
import (
"fmt"
"github.com/cortezaproject/corteza-server/pkg/rh"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/cortezaproject/corteza-server/compose/types"
)
func TestRecordFinder(t *testing.T) {
r := record{}
m := &types.Module{
ID: 123,
NamespaceID: 456,
Fields: types.ModuleFieldSet{
&types.ModuleField{Name: "foo"},
&types.ModuleField{Name: "bar"},
&types.ModuleField{Name: "booly", Kind: "Bool"},
},
}
ttc := []struct {
name string
f types.RecordFilter
match []string
noMatch []string
args []interface{}
err error
}{
{
name: "default filter",
match: []string{
"SELECT r.id, r.module_id, r.rel_namespace, r.owned_by, r.created_at, " +
"r.created_by, r.updated_at, r.updated_by, r.deleted_at, r.deleted_by " +
"FROM compose_record AS r " +
"WHERE r.module_id = ? AND r.rel_namespace = ? AND r.deleted_at IS NULL",
},
},
{
name: "simple query",
f: types.RecordFilter{Query: "id = 5 AND foo = 7"},
match: []string{
"r.id = 5",
"rv_foo.value = 7"},
args: []interface{}{"foo"},
},
{
name: "sorting",
f: types.RecordFilter{Sort: "id ASC, bar DESC"},
match: []string{
" r.id ASC",
" rv_bar.value DESC",
},
args: []interface{}{"bar"},
},
{
name: "exclude deleted records (def. behaviour)",
f: types.RecordFilter{Deleted: rh.FilterStateExcluded},
match: []string{" r.deleted_at IS "},
},
{
name: "include deleted records",
f: types.RecordFilter{Deleted: rh.FilterStateInclusive},
noMatch: []string{" r.deleted_at IS NULL "},
},
{
name: "only deleted record",
f: types.RecordFilter{Deleted: rh.FilterStateExclusive},
match: []string{" r.deleted_at IS NOT NULL"},
},
{
name: "boolean",
f: types.RecordFilter{Query: "booly"},
match: []string{"(rv_booly.value NOT IN ("},
args: []interface{}{"booly"},
},
}
for _, tc := range ttc {
t.Run(tc.name, func(t *testing.T) {
sb, err := r.buildQuery(m, tc.f)
if tc.err != nil {
require.True(t, tc.err.Error() == fmt.Sprintf("%v", err), "buildQuery(%+v) did not return an expected error %q but %q", tc.f, tc.err, err)
} else {
require.True(t, err == nil, "buildQuery(%+v) returned an unexpected error: %v", tc.f, err)
}
sql, args, err := sb.ToSql()
for _, m := range tc.match {
require.True(t, strings.Contains(sql, m),
"assertion failed; query %q \n "+
" did not contain %q", sql, m)
}
for _, m := range tc.noMatch {
require.False(t, strings.Contains(sql, m),
"assertion failed; query %q \n "+
" must not contain %q", sql, m)
}
tc.args = append(tc.args, m.ID, m.NamespaceID)
require.True(t, fmt.Sprintf("%+v", args) == fmt.Sprintf("%+v", tc.args),
"assertion failed; args %+v \n "+
" do not match expected %+v", args, tc.args)
})
}
}
-40
View File
@@ -1,40 +0,0 @@
package repository
import (
"context"
"github.com/titpetric/factory"
)
type (
repository struct {
ctx context.Context
dbh *factory.DB
}
)
// DB produces a contextual DB handle
func DB(ctx context.Context) *factory.DB {
return factory.Database.MustGet().With(ctx)
}
// With updates repository and database contexts
func (r *repository) With(ctx context.Context, db *factory.DB) *repository {
return &repository{
ctx: ctx,
dbh: db,
}
}
// Context returns current active repository context
func (r *repository) Context() context.Context {
return r.ctx
}
// db returns context-aware db handle
func (r *repository) db() *factory.DB {
if r.dbh != nil {
return r.dbh
}
return DB(r.ctx)
}