updated CollectionsImport and CollectionUpsert forms
This commit is contained in:
+60
-26
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
validation "github.com/go-ozzo/ozzo-validation/v4"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/models/schema"
|
||||
"github.com/pocketbase/pocketbase/resolvers"
|
||||
@@ -14,12 +15,12 @@ import (
|
||||
|
||||
var collectionNameRegex = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_]*$`)
|
||||
|
||||
// CollectionUpsert defines a collection upsert (create/update) form.
|
||||
// CollectionUpsert specifies a [models.Collection] upsert (create/update) form.
|
||||
type CollectionUpsert struct {
|
||||
app core.App
|
||||
config CollectionUpsertConfig
|
||||
collection *models.Collection
|
||||
isCreate bool
|
||||
|
||||
Id string `form:"id" json:"id"`
|
||||
Name string `form:"name" json:"name"`
|
||||
System bool `form:"system" json:"system"`
|
||||
Schema schema.Schema `form:"schema" json:"schema"`
|
||||
@@ -30,25 +31,48 @@ type CollectionUpsert struct {
|
||||
DeleteRule *string `form:"deleteRule" json:"deleteRule"`
|
||||
}
|
||||
|
||||
// NewCollectionUpsert creates new collection upsert form for the provided Collection model
|
||||
// (pass an empty Collection model instance (`&models.Collection{}`) for create).
|
||||
// CollectionUpsertConfig is the [CollectionUpsert] factory initializer config.
|
||||
//
|
||||
// NB! Dao is a required struct member.
|
||||
type CollectionUpsertConfig struct {
|
||||
Dao *daos.Dao
|
||||
}
|
||||
|
||||
// NewCollectionUpsert creates a new [CollectionUpsert] form with initializer
|
||||
// config created from the provided [core.App] and [models.Collection] instances.
|
||||
//
|
||||
// This factory method is used primarily for convenience (and backward compatibility).
|
||||
// If you want to submit the form as part of another transaction, use
|
||||
// [NewCollectionUpsertWithConfig] with Dao configured to your txDao.
|
||||
func NewCollectionUpsert(app core.App, collection *models.Collection) *CollectionUpsert {
|
||||
form := &CollectionUpsert{
|
||||
app: app,
|
||||
collection: collection,
|
||||
isCreate: collection.IsNew(),
|
||||
form := NewCollectionUpsertWithConfig(CollectionUpsertConfig{
|
||||
Dao: app.Dao(),
|
||||
}, collection)
|
||||
|
||||
return form
|
||||
}
|
||||
|
||||
// NewCollectionUpsertWithConfig creates a new [CollectionUpsert] form
|
||||
// with the provided config and [models.Collection] instance or panics on invalid configuration
|
||||
// (for create you could pass a pointer to an empty Collection instance - `&models.Collection{}`).
|
||||
func NewCollectionUpsertWithConfig(config CollectionUpsertConfig, collection *models.Collection) *CollectionUpsert {
|
||||
form := &CollectionUpsert{config: config}
|
||||
|
||||
if form.config.Dao == nil || collection == nil {
|
||||
panic("Invalid initializer config or nil upsert model.")
|
||||
}
|
||||
|
||||
// load defaults
|
||||
form.Name = collection.Name
|
||||
form.System = collection.System
|
||||
form.ListRule = collection.ListRule
|
||||
form.ViewRule = collection.ViewRule
|
||||
form.CreateRule = collection.CreateRule
|
||||
form.UpdateRule = collection.UpdateRule
|
||||
form.DeleteRule = collection.DeleteRule
|
||||
form.Id = form.collection.Id
|
||||
form.Name = form.collection.Name
|
||||
form.System = form.collection.System
|
||||
form.ListRule = form.collection.ListRule
|
||||
form.ViewRule = form.collection.ViewRule
|
||||
form.CreateRule = form.collection.CreateRule
|
||||
form.UpdateRule = form.collection.UpdateRule
|
||||
form.DeleteRule = form.collection.DeleteRule
|
||||
|
||||
clone, _ := collection.Schema.Clone()
|
||||
clone, _ := form.collection.Schema.Clone()
|
||||
if clone != nil {
|
||||
form.Schema = *clone
|
||||
} else {
|
||||
@@ -61,6 +85,10 @@ func NewCollectionUpsert(app core.App, collection *models.Collection) *Collectio
|
||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||
func (form *CollectionUpsert) Validate() error {
|
||||
return validation.ValidateStruct(form,
|
||||
validation.Field(
|
||||
&form.Id,
|
||||
validation.Length(models.DefaultIdLength, models.DefaultIdLength),
|
||||
),
|
||||
validation.Field(
|
||||
&form.System,
|
||||
validation.By(form.ensureNoSystemFlagChange),
|
||||
@@ -90,11 +118,11 @@ func (form *CollectionUpsert) Validate() error {
|
||||
func (form *CollectionUpsert) checkUniqueName(value any) error {
|
||||
v, _ := value.(string)
|
||||
|
||||
if !form.app.Dao().IsCollectionNameUnique(v, form.collection.Id) {
|
||||
if !form.config.Dao.IsCollectionNameUnique(v, form.collection.Id) {
|
||||
return validation.NewError("validation_collection_name_exists", "Collection name must be unique (case insensitive).")
|
||||
}
|
||||
|
||||
if (form.isCreate || !strings.EqualFold(v, form.collection.Name)) && form.app.Dao().HasTable(v) {
|
||||
if (form.collection.IsNew() || !strings.EqualFold(v, form.collection.Name)) && form.config.Dao.HasTable(v) {
|
||||
return validation.NewError("validation_collection_name_table_exists", "The collection name must be also unique table name.")
|
||||
}
|
||||
|
||||
@@ -104,7 +132,7 @@ func (form *CollectionUpsert) checkUniqueName(value any) error {
|
||||
func (form *CollectionUpsert) ensureNoSystemNameChange(value any) error {
|
||||
v, _ := value.(string)
|
||||
|
||||
if form.isCreate || !form.collection.System || v == form.collection.Name {
|
||||
if form.collection.IsNew() || !form.collection.System || v == form.collection.Name {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -114,7 +142,7 @@ func (form *CollectionUpsert) ensureNoSystemNameChange(value any) error {
|
||||
func (form *CollectionUpsert) ensureNoSystemFlagChange(value any) error {
|
||||
v, _ := value.(bool)
|
||||
|
||||
if form.isCreate || v == form.collection.System {
|
||||
if form.collection.IsNew() || v == form.collection.System {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -161,7 +189,7 @@ func (form *CollectionUpsert) checkRule(value any) error {
|
||||
}
|
||||
|
||||
dummy := &models.Collection{Schema: form.Schema}
|
||||
r := resolvers.NewRecordFieldResolver(form.app.Dao(), dummy, nil)
|
||||
r := resolvers.NewRecordFieldResolver(form.config.Dao, dummy, nil)
|
||||
|
||||
_, err := search.FilterData(*v).BuildExpr(r)
|
||||
if err != nil {
|
||||
@@ -182,13 +210,19 @@ func (form *CollectionUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// system flag can be set only for create
|
||||
if form.isCreate {
|
||||
if form.collection.IsNew() {
|
||||
// system flag can be set only on create
|
||||
form.collection.System = form.System
|
||||
|
||||
// custom insertion id can be set only on create
|
||||
if form.Id != "" {
|
||||
form.collection.MarkAsNew()
|
||||
form.collection.SetId(form.Id)
|
||||
}
|
||||
}
|
||||
|
||||
// system collections cannot be renamed
|
||||
if form.isCreate || !form.collection.System {
|
||||
if form.collection.IsNew() || !form.collection.System {
|
||||
form.collection.Name = form.Name
|
||||
}
|
||||
|
||||
@@ -200,6 +234,6 @@ func (form *CollectionUpsert) Submit(interceptors ...InterceptorFunc) error {
|
||||
form.collection.DeleteRule = form.DeleteRule
|
||||
|
||||
return runInterceptors(func() error {
|
||||
return form.app.Dao().SaveCollection(form.collection)
|
||||
return form.config.Dao.SaveCollection(form.collection)
|
||||
}, interceptors...)
|
||||
}
|
||||
|
||||
+43
-18
@@ -11,24 +11,48 @@ import (
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
)
|
||||
|
||||
// CollectionsImport defines a bulk collections import form.
|
||||
// CollectionsImport specifies a form model to bulk import
|
||||
// (create, replace and delete) collections from a user provided list.
|
||||
type CollectionsImport struct {
|
||||
app core.App
|
||||
config CollectionsImportConfig
|
||||
|
||||
Collections []*models.Collection `form:"collections" json:"collections"`
|
||||
DeleteOthers bool `form:"deleteOthers" json:"deleteOthers"`
|
||||
}
|
||||
|
||||
// NewCollectionsImport bulk imports (create, replace and delete)
|
||||
// a user provided list with collections data.
|
||||
func NewCollectionsImport(app core.App) *CollectionsImport {
|
||||
form := &CollectionsImport{
|
||||
app: app,
|
||||
// CollectionsImportConfig is the [CollectionsImport] factory initializer config.
|
||||
//
|
||||
// NB! Dao is a required struct member.
|
||||
type CollectionsImportConfig struct {
|
||||
Dao *daos.Dao
|
||||
IsDebug bool
|
||||
}
|
||||
|
||||
// NewCollectionsImportWithConfig creates a new [CollectionsImport]
|
||||
// form with the provided config or panics on invalid configuration.
|
||||
func NewCollectionsImportWithConfig(config CollectionsImportConfig) *CollectionsImport {
|
||||
form := &CollectionsImport{config: config}
|
||||
|
||||
if form.config.Dao == nil {
|
||||
panic("Invalid initializer config.")
|
||||
}
|
||||
|
||||
return form
|
||||
}
|
||||
|
||||
// NewCollectionsImport creates a new [CollectionsImport] form with
|
||||
// initializer config created from the provided [core.App] instance.
|
||||
//
|
||||
// This factory method is used primarily for convenience (and backward compatibility).
|
||||
// If you want to submit the form as part of another transaction, use
|
||||
// [NewCollectionsImportWithConfig] with Dao configured to your txDao.
|
||||
func NewCollectionsImport(app core.App) *CollectionsImport {
|
||||
return NewCollectionsImportWithConfig(CollectionsImportConfig{
|
||||
Dao: app.Dao(),
|
||||
IsDebug: app.IsDebug(),
|
||||
})
|
||||
}
|
||||
|
||||
// Validate makes the form validatable by implementing [validation.Validatable] interface.
|
||||
func (form *CollectionsImport) Validate() error {
|
||||
return validation.ValidateStruct(form,
|
||||
@@ -49,12 +73,12 @@ func (form *CollectionsImport) Submit() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// @todo validate id length in the form
|
||||
return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error {
|
||||
return form.config.Dao.RunInTransaction(func(txDao *daos.Dao) error {
|
||||
oldCollections := []*models.Collection{}
|
||||
if err := txDao.CollectionQuery().All(&oldCollections); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mappedOldCollections := make(map[string]*models.Collection, len(oldCollections))
|
||||
for _, old := range oldCollections {
|
||||
mappedOldCollections[old.GetId()] = old
|
||||
@@ -71,7 +95,7 @@ func (form *CollectionsImport) Submit() error {
|
||||
if mappedFormCollections[old.GetId()] == nil {
|
||||
// delete the collection
|
||||
if err := txDao.DeleteCollection(old); err != nil {
|
||||
if form.app.IsDebug() {
|
||||
if form.config.IsDebug {
|
||||
log.Println("[CollectionsImport] DeleteOthers failure", old.Name, err)
|
||||
}
|
||||
return validation.Errors{"collections": validation.NewError(
|
||||
@@ -79,6 +103,8 @@ func (form *CollectionsImport) Submit() error {
|
||||
fmt.Sprintf("Failed to delete collection %q (%s). Make sure that the collection is not system or referenced by other collections.", old.Name, old.Id),
|
||||
)}
|
||||
}
|
||||
|
||||
delete(mappedOldCollections, old.GetId())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,7 +117,7 @@ func (form *CollectionsImport) Submit() error {
|
||||
}
|
||||
|
||||
if err := txDao.Save(collection); err != nil {
|
||||
if form.app.IsDebug() {
|
||||
if form.config.IsDebug {
|
||||
log.Println("[CollectionsImport] Save failure", collection.Name, err)
|
||||
}
|
||||
return validation.Errors{"collections": validation.NewError(
|
||||
@@ -112,10 +138,13 @@ func (form *CollectionsImport) Submit() error {
|
||||
for _, collection := range refreshedCollections {
|
||||
upsertModel := mappedOldCollections[collection.GetId()]
|
||||
if upsertModel == nil {
|
||||
upsertModel = &models.Collection{}
|
||||
upsertModel = collection
|
||||
}
|
||||
upsertForm := NewCollectionUpsert(form.app, upsertModel)
|
||||
upsertForm := NewCollectionUpsertWithConfig(CollectionUpsertConfig{
|
||||
Dao: txDao,
|
||||
}, upsertModel)
|
||||
// load form fields with the refreshed collection state
|
||||
upsertForm.Id = collection.Id
|
||||
upsertForm.Name = collection.Name
|
||||
upsertForm.System = collection.System
|
||||
upsertForm.ListRule = collection.ListRule
|
||||
@@ -125,10 +154,6 @@ func (form *CollectionsImport) Submit() error {
|
||||
upsertForm.DeleteRule = collection.DeleteRule
|
||||
upsertForm.Schema = collection.Schema
|
||||
if err := upsertForm.Validate(); err != nil {
|
||||
if form.app.IsDebug() {
|
||||
log.Println("[CollectionsImport] Validate failure", collection.Name, err)
|
||||
}
|
||||
|
||||
// serialize the validation error(s)
|
||||
serializedErr, _ := json.Marshal(err)
|
||||
|
||||
@@ -143,7 +168,7 @@ func (form *CollectionsImport) Submit() error {
|
||||
for _, collection := range form.Collections {
|
||||
oldCollection := mappedOldCollections[collection.GetId()]
|
||||
if err := txDao.SyncRecordTableSchema(collection, oldCollection); err != nil {
|
||||
if form.app.IsDebug() {
|
||||
if form.config.IsDebug {
|
||||
log.Println("[CollectionsImport] Records table sync failure", collection.Name, err)
|
||||
}
|
||||
return validation.Errors{"collections": validation.NewError(
|
||||
|
||||
Reference in New Issue
Block a user