3
0
Files
corteza/pkg/seeder/seeder.go
2022-08-11 19:33:07 +02:00

450 lines
11 KiB
Go

package seeder
import (
"context"
"fmt"
"time"
"github.com/cortezaproject/corteza-server/compose/dalutils"
cService "github.com/cortezaproject/corteza-server/compose/service"
cTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/dal"
"github.com/cortezaproject/corteza-server/pkg/filter"
"github.com/cortezaproject/corteza-server/pkg/id"
lTypes "github.com/cortezaproject/corteza-server/pkg/label/types"
"github.com/cortezaproject/corteza-server/store"
sTypes "github.com/cortezaproject/corteza-server/system/types"
)
type (
seeder struct {
ctx context.Context
store storeService
faker fakerService
dal dalService
modSvc moduleService
}
Params struct {
// (optional) no record to be generate; Default value will be 1
Limit int
}
RecordParams struct {
NamespaceID uint64
NamespaceHandle string
ModuleID uint64
ModuleHandle string
Params
}
fakerService interface {
fakeValueByName(name string) (val string, ok bool)
fakeValue(name, kind string, opt valueOptions) (val string, err error)
fakeUserHandle(s string) string
}
storeService interface {
UpsertLabel(ctx context.Context, rr ...*lTypes.Label) error
SearchUsers(ctx context.Context, f sTypes.UserFilter) (sTypes.UserSet, sTypes.UserFilter, error)
CreateUser(ctx context.Context, rr ...*sTypes.User) error
DeleteUser(ctx context.Context, rr ...*sTypes.User) error
LookupComposeNamespaceBySlug(ctx context.Context, slug string) (*cTypes.Namespace, error)
LookupComposeNamespaceByID(ctx context.Context, id uint64) (*cTypes.Namespace, error)
LookupComposeModuleByNamespaceIDHandle(ctx context.Context, namespaceID uint64, name string) (*cTypes.Module, error)
LookupComposeModuleByID(ctx context.Context, id uint64) (*cTypes.Module, error)
SearchComposeModuleFields(ctx context.Context, f cTypes.ModuleFieldFilter) (cTypes.ModuleFieldSet, cTypes.ModuleFieldFilter, error)
}
dalService interface {
Create(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, vv ...dal.ValueGetter) error
Search(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, f filter.Filter) (dal.Iterator, error)
Delete(ctx context.Context, m dal.ModelRef, operations dal.OperationSet, pkv ...dal.ValueGetter) (err error)
}
moduleService interface {
FindByID(ctx context.Context, namespaceID uint64, moduleID uint64) (*cTypes.Module, error)
FindByHandle(ctx context.Context, namespaceID uint64, handle string) (*cTypes.Module, error)
Find(ctx context.Context, filter cTypes.ModuleFilter) (set cTypes.ModuleSet, f cTypes.ModuleFilter, err error)
}
)
var (
DefaultStore store.Storer
)
const (
FakeDataLabel = "generated"
)
func Seeder(ctx context.Context, store store.Storer, dal dalService, faker fakerService) *seeder {
DefaultStore = store
return &seeder{
ctx: ctx,
store: store,
faker: faker,
dal: dal,
modSvc: cService.DefaultModule,
}
}
// getLimit return data generation limit; It will return Default(1) if limit is 0
func (p Params) getLimit() int {
if p.Limit == 0 {
return 1
}
return p.Limit
}
// CreateLabel return the label for generate data
func (s seeder) CreateLabel(resourceID uint64, kind, name string) *lTypes.Label {
return &lTypes.Label{
Kind: kind,
ResourceID: resourceID,
Name: name,
Value: FakeDataLabel,
}
}
// CreateUser creates given no of users into DB
func (s seeder) CreateUser(params Params) (IDs []uint64, err error) {
var users []*sTypes.User
var labels []*lTypes.Label
for i := 0; i < params.getLimit(); i++ {
var user sTypes.User
user.ID = id.Next()
user.Email, _ = s.faker.fakeValueByName("Email")
user.Name, _ = s.faker.fakeValueByName("Name")
user.Handle = s.faker.fakeUserHandle(user.Name)
user.Kind = sTypes.NormalUser
user.CreatedAt = time.Now()
IDs = append(IDs, user.ID)
users = append(users, &user)
labels = append(labels, s.CreateLabel(
user.ID,
user.LabelResourceKind(),
FakeDataLabel,
))
}
err = s.store.CreateUser(s.ctx, users...)
if err != nil {
return
}
err = s.store.UpsertLabel(s.ctx, labels...)
if err != nil {
return
}
return
}
// DeleteAllUser deletes all the fake users from DB
func (s seeder) DeleteAllUser() (err error) {
filter := sTypes.UserFilter{
Labels: map[string]string{
FakeDataLabel: FakeDataLabel,
},
}
users, _, err := s.store.SearchUsers(s.ctx, filter)
if err != nil {
return
}
err = s.store.DeleteUser(s.ctx, users...)
if err != nil {
return
}
return
}
// LookupNamespaceByID will get namespace by ID
func (s seeder) LookupNamespaceByID(ID uint64) (ns *cTypes.Namespace, err error) {
if ID == 0 {
err = fmt.Errorf("invalid ID for namespace")
return
}
ns, err = s.store.LookupComposeNamespaceByID(s.ctx, ID)
if err != nil {
return
}
if ns == nil {
return ns, fmt.Errorf("namespace not found")
}
return
}
// LookupNamespaceByHandle will get namespace by handle
func (s seeder) LookupNamespaceByHandle(handle string) (ns *cTypes.Namespace, err error) {
if len(handle) == 0 {
err = fmt.Errorf("invalid handle for namespace")
return
}
ns, err = s.store.LookupComposeNamespaceBySlug(s.ctx, handle)
if err != nil {
return
}
if ns == nil {
return ns, fmt.Errorf("namespace not found")
}
return
}
// LookupModuleByID will get module by ID
func (s seeder) LookupModuleByID(ID uint64) (mod *cTypes.Module, err error) {
if ID == 0 {
err = fmt.Errorf("invalid ID for module")
return
}
mod, err = s.store.LookupComposeModuleByID(s.ctx, ID)
if err != nil {
return
}
if mod == nil {
return mod, fmt.Errorf("module not found")
}
mod.Fields, _, err = s.store.SearchComposeModuleFields(s.ctx, cTypes.ModuleFieldFilter{ModuleID: []uint64{mod.ID}})
if err != nil {
return
}
return
}
// LookupModuleByHandle will get module by handle
func (s seeder) LookupModuleByHandle(nsID uint64, handle string) (mod *cTypes.Module, err error) {
if nsID == 0 {
err = fmt.Errorf("invalid namespace for module")
return nil, err
}
if len(handle) == 0 {
err = fmt.Errorf("invalid handle for module")
return
}
mod, err = s.store.LookupComposeModuleByNamespaceIDHandle(s.ctx, nsID, handle)
if err != nil {
return
}
if mod == nil {
return mod, fmt.Errorf("module not found")
}
mod.Fields, _, err = s.store.SearchComposeModuleFields(s.ctx, cTypes.ModuleFieldFilter{ModuleID: []uint64{mod.ID}})
if err != nil {
return
}
return
}
// FindModuleByID will get module by ID
func (s seeder) FindModuleByID(nsID, mID uint64) (mod *cTypes.Module, err error) {
if nsID == 0 {
err = fmt.Errorf("invalid namespace ID")
return
}
if mID == 0 {
err = fmt.Errorf("invalid module ID")
return
}
mod, err = s.modSvc.FindByID(s.ctx, nsID, mID)
if err != nil {
return
}
if mod == nil {
return mod, fmt.Errorf("module not found")
}
return
}
// FindModuleByHandle will get module by handle
func (s seeder) FindModuleByHandle(nsID uint64, handle string) (mod *cTypes.Module, err error) {
if nsID == 0 {
err = fmt.Errorf("invalid namespace for module")
return nil, err
}
if len(handle) == 0 {
err = fmt.Errorf("invalid handle for module")
return
}
mod, err = s.modSvc.FindByHandle(s.ctx, nsID, handle)
if err != nil {
return
}
if mod == nil {
return mod, fmt.Errorf("module not found")
}
return
}
// GetModuleFromParams will get module of namespace using params
func (s seeder) GetModuleFromParams(params *RecordParams) (mod *cTypes.Module, err error) {
if params == nil {
return
}
var ns *cTypes.Namespace
if params.NamespaceID > 0 {
ns, err = s.LookupNamespaceByID(params.NamespaceID)
if err != nil {
return
}
}
if len(params.NamespaceHandle) > 0 {
ns, err = s.LookupNamespaceByHandle(params.NamespaceHandle)
if err != nil {
return
}
}
if params.ModuleID > 0 {
mod, err = s.LookupModuleByID(params.ModuleID)
if err != nil {
return
}
}
if len(params.ModuleHandle) > 0 {
mod, err = s.LookupModuleByHandle(ns.ID, params.ModuleHandle)
if err != nil {
return
}
}
if mod == nil {
return mod, fmt.Errorf("module not found")
}
return
}
// CreateRecord creates given no of record into DB
func (s seeder) CreateRecord(params RecordParams) (IDs []uint64, err error) {
var (
records []*cTypes.Record
labels []*lTypes.Label
)
m, err := s.GetModuleFromParams(&params)
if err != nil {
return
}
for i := 0; i < params.getLimit(); i++ {
rec := &cTypes.Record{
ID: id.Next(),
NamespaceID: m.NamespaceID,
ModuleID: m.ID,
CreatedAt: time.Now(),
Meta: map[string]any{FakeDataLabel: true},
}
for j, f := range m.Fields {
err = s.setRecordValues(rec, uint(j), f)
if err != nil {
return nil, err
}
}
IDs = append(IDs, rec.ID)
records = append(records, rec)
}
// @todo remove this .ctx ptrn
err = dalutils.ComposeRecordCreate(s.ctx, s.dal, m, records...)
if err != nil {
return
}
err = s.store.UpsertLabel(s.ctx, labels...)
if err != nil {
return
}
return
}
// setRecordValues will generate record values from third party and set to record
func (s seeder) setRecordValues(rec *cTypes.Record, place uint, field *cTypes.ModuleField) (err error) {
// @todo: is it multi-value field? how many values? then create some multi-values
var value string
// skip the non required fields
if !field.Required {
fmt.Println("Skipping it due to not required")
return
}
// return err for fields without name
if len(field.Name) == 0 {
return fmt.Errorf("invalid field name")
}
// check the type to fake the data
if len(field.Kind) > 0 {
value, err = s.faker.fakeValue(field.Name, field.Kind, valueOptions{})
if err != nil {
return fmt.Errorf("coudn't generate the value")
}
} else {
return fmt.Errorf("unknown kind for field")
}
rec.Values = rec.Values.Set(&cTypes.RecordValue{
RecordID: rec.ID,
Name: field.Name,
Value: value,
// Ref: 0, // @todo: fake data with ref
Place: place, // in case of multi-value field this is ++
})
return
}
// DeleteAllRecord clear all the fake user from DB
func (s seeder) DeleteAllRecord(mod *cTypes.Module) (err error) {
filter := cTypes.RecordFilter{
Meta: map[string]any{
FakeDataLabel: FakeDataLabel,
},
}
records, _, err := dalutils.ComposeRecordsList(s.ctx, s.dal, mod, filter)
if err != nil {
return
}
err = dalutils.ComposeRecordDelete(s.ctx, s.dal, mod, records...)
if err != nil {
return
}
return
}
// DeleteAll will delete all the fake data from the DB
func (s seeder) DeleteAll(params *RecordParams) (err error) {
err = s.DeleteAllUser()
if err != nil {
return
}
if params != nil {
m, err := s.GetModuleFromParams(params)
if err != nil {
err = s.DeleteAllRecord(m)
if err != nil {
return err
}
}
}
return
}