mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-02-09 20:24:02 +02:00
added option to insert/move fields at specific position
This commit is contained in:
parent
e9ece220d6
commit
1e92b51cf7
@ -4,6 +4,7 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@ -12,7 +13,7 @@ func NewFieldsList(fields ...Field) FieldsList {
|
||||
l := make(FieldsList, 0, len(fields))
|
||||
|
||||
for _, f := range fields {
|
||||
l.Add(f)
|
||||
l.add(-1, f)
|
||||
}
|
||||
|
||||
return l
|
||||
@ -116,7 +117,26 @@ func (l *FieldsList) RemoveByName(fieldName string) {
|
||||
// (the id value doesn't really matter and it is mostly used as a stable identifier in case of a field rename).
|
||||
func (l *FieldsList) Add(fields ...Field) {
|
||||
for _, f := range fields {
|
||||
l.add(f)
|
||||
l.add(-1, f)
|
||||
}
|
||||
}
|
||||
|
||||
// AddAt is the same as Add but insert/move the fields at the specific position.
|
||||
//
|
||||
// If pos < 0, then this method acts the same as calling Add.
|
||||
//
|
||||
// If pos > FieldsList total items, then the specified fields are inserted/moved at the end of the list.
|
||||
func (l *FieldsList) AddAt(pos int, fields ...Field) {
|
||||
total := len(*l)
|
||||
|
||||
for i, f := range fields {
|
||||
if pos < 0 {
|
||||
l.add(-1, f)
|
||||
} else if pos > total {
|
||||
l.add(total+i, f)
|
||||
} else {
|
||||
l.add(pos+i, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,13 +152,42 @@ func (l *FieldsList) Add(fields ...Field) {
|
||||
// l.AddMarshaledJSON([]byte{`{"type":"text", name: "test"}`})
|
||||
// l.AddMarshaledJSON([]byte{`[{"type":"text", name: "test1"}, {"type":"text", name: "test2"}]`})
|
||||
func (l *FieldsList) AddMarshaledJSON(rawJSON []byte) error {
|
||||
extractedFields, err := marshaledJSONtoFieldsList(rawJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Add(extractedFields...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddMarshaledJSONAt is the same as AddMarshaledJSON but insert/move the fields at the specific position.
|
||||
//
|
||||
// If pos < 0, then this method acts the same as calling AddMarshaledJSON.
|
||||
//
|
||||
// If pos > FieldsList total items, then the specified fields are inserted/moved at the end of the list.
|
||||
func (l *FieldsList) AddMarshaledJSONAt(pos int, rawJSON []byte) error {
|
||||
extractedFields, err := marshaledJSONtoFieldsList(rawJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.AddAt(pos, extractedFields...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func marshaledJSONtoFieldsList(rawJSON []byte) (FieldsList, error) {
|
||||
extractedFields := FieldsList{}
|
||||
|
||||
// nothing to add
|
||||
if len(rawJSON) == 0 {
|
||||
return nil // nothing to add
|
||||
return extractedFields, nil
|
||||
}
|
||||
|
||||
// try to unmarshal first into a new fieds list
|
||||
// (assuming that rawJSON is array of objects)
|
||||
extractedFields := FieldsList{}
|
||||
err := json.Unmarshal(rawJSON, &extractedFields)
|
||||
if err != nil {
|
||||
// try again but wrap the rawJSON in []
|
||||
@ -149,21 +198,25 @@ func (l *FieldsList) AddMarshaledJSON(rawJSON []byte) error {
|
||||
wrapped = append(wrapped, ']')
|
||||
err = json.Unmarshal(wrapped, &extractedFields)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal the provided JSON - expects array of objects or just single object: %w", err)
|
||||
return nil, fmt.Errorf("failed to unmarshal the provided JSON - expects array of objects or just single object: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range extractedFields {
|
||||
l.add(f)
|
||||
}
|
||||
|
||||
return nil
|
||||
return extractedFields, nil
|
||||
}
|
||||
|
||||
func (l *FieldsList) add(newField Field) {
|
||||
func (l *FieldsList) add(pos int, newField Field) {
|
||||
fields := *l
|
||||
|
||||
var replaceByName bool
|
||||
var replaceInPlace bool
|
||||
|
||||
if pos < 0 {
|
||||
replaceInPlace = true
|
||||
pos = len(fields)
|
||||
} else if pos > len(fields) {
|
||||
pos = len(fields)
|
||||
}
|
||||
|
||||
newFieldId := newField.GetId()
|
||||
|
||||
@ -182,24 +235,44 @@ func (l *FieldsList) add(newField Field) {
|
||||
newField.SetId(newFieldId)
|
||||
}
|
||||
|
||||
// replace existing
|
||||
// try to replace existing
|
||||
for i, field := range fields {
|
||||
if replaceByName {
|
||||
if name := newField.GetName(); name != "" && field.GetName() == name {
|
||||
// reuse the original id
|
||||
newField.SetId(field.GetId())
|
||||
(*l)[i] = newField
|
||||
return
|
||||
|
||||
if replaceInPlace {
|
||||
(*l)[i] = newField
|
||||
return
|
||||
} else {
|
||||
// remove the current field and insert it later at the specific position
|
||||
*l = slices.Delete(*l, i, i+1)
|
||||
if total := len(*l); pos > total {
|
||||
pos = total
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if field.GetId() == newFieldId {
|
||||
(*l)[i] = newField
|
||||
return
|
||||
if replaceInPlace {
|
||||
(*l)[i] = newField
|
||||
return
|
||||
} else {
|
||||
// remove the current field and insert it later at the specific position
|
||||
*l = slices.Delete(*l, i, i+1)
|
||||
if total := len(*l); pos > total {
|
||||
pos = total
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add new field
|
||||
*l = append(fields, newField)
|
||||
// insert the new field
|
||||
*l = slices.Insert(*l, pos, newField)
|
||||
}
|
||||
|
||||
// String returns the string representation of the current list.
|
||||
@ -252,7 +325,7 @@ func (l *FieldsList) UnmarshalJSON(data []byte) error {
|
||||
*l = []Field{} // reset
|
||||
|
||||
for _, fwt := range fwts {
|
||||
l.Add(fwt.Field)
|
||||
l.add(-1, fwt.Field)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -1,7 +1,10 @@
|
||||
package core_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -301,6 +304,96 @@ func TestFieldsListAddMarshaledJSON(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsListAddAt(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
position int
|
||||
expected []string
|
||||
}{
|
||||
{-2, []string{"test1", "test2_new", "test3", "test4"}},
|
||||
{-1, []string{"test1", "test2_new", "test3", "test4"}},
|
||||
{0, []string{"test2_new", "test4", "test1", "test3"}},
|
||||
{1, []string{"test1", "test2_new", "test4", "test3"}},
|
||||
{2, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
{3, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
{4, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
{5, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(strconv.Itoa(s.position), func(t *testing.T) {
|
||||
f1 := &core.TextField{Id: "f1Id", Name: "test1"}
|
||||
f2 := &core.TextField{Id: "f2Id", Name: "test2"}
|
||||
f3 := &core.TextField{Id: "f3Id", Name: "test3"}
|
||||
testFieldsList := core.NewFieldsList(f1, f2, f3)
|
||||
|
||||
f2New := &core.EmailField{Id: "f2Id", Name: "test2_new"}
|
||||
f4 := &core.URLField{Name: "test4"}
|
||||
testFieldsList.AddAt(s.position, f2New, f4)
|
||||
|
||||
rawNames, err := json.Marshal(testFieldsList.FieldNames())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rawExpected, err := json.Marshal(s.expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(rawNames, rawExpected) {
|
||||
t.Fatalf("Expected fields\n%s\ngot\n%s", rawExpected, rawNames)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsListAddMarshaledJSONAt(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
position int
|
||||
expected []string
|
||||
}{
|
||||
{-2, []string{"test1", "test2_new", "test3", "test4"}},
|
||||
{-1, []string{"test1", "test2_new", "test3", "test4"}},
|
||||
{0, []string{"test2_new", "test4", "test1", "test3"}},
|
||||
{1, []string{"test1", "test2_new", "test4", "test3"}},
|
||||
{2, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
{3, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
{4, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
{5, []string{"test1", "test3", "test2_new", "test4"}},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(strconv.Itoa(s.position), func(t *testing.T) {
|
||||
f1 := &core.TextField{Id: "f1Id", Name: "test1"}
|
||||
f2 := &core.TextField{Id: "f2Id", Name: "test2"}
|
||||
f3 := &core.TextField{Id: "f3Id", Name: "test3"}
|
||||
testFieldsList := core.NewFieldsList(f1, f2, f3)
|
||||
|
||||
err := testFieldsList.AddMarshaledJSONAt(s.position, []byte(`[
|
||||
{"id":"f2Id", "name":"test2_new", "type": "text"},
|
||||
{"name": "test4", "type": "text"}
|
||||
]`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rawNames, err := json.Marshal(testFieldsList.FieldNames())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rawExpected, err := json.Marshal(s.expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(rawNames, rawExpected) {
|
||||
t.Fatalf("Expected fields\n%s\ngot\n%s", rawExpected, rawNames)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldsListStringAndValue(t *testing.T) {
|
||||
t.Run("empty list", func(t *testing.T) {
|
||||
testFieldsList := core.NewFieldsList()
|
||||
|
7242
plugins/jsvm/internal/types/generated/types.d.ts
vendored
7242
plugins/jsvm/internal/types/generated/types.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
// Example:
|
||||
//
|
||||
// jsvm.MustRegister(app, jsvm.Config{
|
||||
// WatchHooks: true,
|
||||
// HooksWatch: true,
|
||||
// })
|
||||
package jsvm
|
||||
|
||||
|
@ -921,7 +921,7 @@ migrate((app) => {
|
||||
collection.fields.removeById("f3_id")
|
||||
|
||||
// add field
|
||||
collection.fields.add(new Field({
|
||||
collection.fields.addAt(8, new Field({
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "f4_id",
|
||||
@ -975,7 +975,7 @@ migrate((app) => {
|
||||
}, collection)
|
||||
|
||||
// add field
|
||||
collection.fields.add(new Field({
|
||||
collection.fields.addAt(8, new Field({
|
||||
"hidden": false,
|
||||
"id": "f3_id",
|
||||
"name": "f3_name",
|
||||
@ -1052,7 +1052,7 @@ func init() {
|
||||
collection.Fields.RemoveById("f3_id")
|
||||
|
||||
// add field
|
||||
if err := collection.Fields.AddMarshaledJSON([]byte(` + "`" + `{
|
||||
if err := collection.Fields.AddMarshaledJSONAt(8, []byte(` + "`" + `{
|
||||
"autogeneratePattern": "",
|
||||
"hidden": false,
|
||||
"id": "f4_id",
|
||||
@ -1115,7 +1115,7 @@ func init() {
|
||||
}
|
||||
|
||||
// add field
|
||||
if err := collection.Fields.AddMarshaledJSON([]byte(` + "`" + `{
|
||||
if err := collection.Fields.AddMarshaledJSONAt(8, []byte(` + "`" + `{
|
||||
"hidden": false,
|
||||
"id": "f3_id",
|
||||
"name": "f3_name",
|
||||
|
@ -208,7 +208,7 @@ func (p *plugin) jsDiffTemplate(new *core.Collection, old *core.Collection) (str
|
||||
upParts = append(upParts, fmt.Sprintf("%s.fields.removeById(%q)\n", varName, oldField.GetId()))
|
||||
|
||||
downParts = append(downParts, "// add field")
|
||||
downParts = append(downParts, fmt.Sprintf("%s.fields.add(new Field(%s))\n", varName, rawOldField))
|
||||
downParts = append(downParts, fmt.Sprintf("%s.fields.addAt(%d, new Field(%s))\n", varName, i, rawOldField))
|
||||
}
|
||||
|
||||
// created fields
|
||||
@ -223,13 +223,14 @@ func (p *plugin) jsDiffTemplate(new *core.Collection, old *core.Collection) (str
|
||||
}
|
||||
|
||||
upParts = append(upParts, "// add field")
|
||||
upParts = append(upParts, fmt.Sprintf("%s.fields.add(new Field(%s))\n", varName, rawNewField))
|
||||
upParts = append(upParts, fmt.Sprintf("%s.fields.addAt(%d, new Field(%s))\n", varName, i, rawNewField))
|
||||
|
||||
downParts = append(downParts, "// remove field")
|
||||
downParts = append(downParts, fmt.Sprintf("%s.fields.removeById(%q)\n", varName, newField.GetId()))
|
||||
}
|
||||
|
||||
// modified fields
|
||||
// (note currently ignoring order-only changes as it comes with too many edge-cases)
|
||||
for i, newField := range new.Fields {
|
||||
var rawNewField, rawOldField []byte
|
||||
|
||||
@ -546,7 +547,7 @@ func (p *plugin) goDiffTemplate(new *core.Collection, old *core.Collection) (str
|
||||
upParts = append(upParts, fmt.Sprintf("%s.Fields.RemoveById(%q)\n", varName, oldField.GetId()))
|
||||
|
||||
downParts = append(downParts, "// add field")
|
||||
downParts = append(downParts, goErrIf(fmt.Sprintf("%s.Fields.AddMarshaledJSON([]byte(`%s`))", varName, escapeBacktick(string(rawOldField)))))
|
||||
downParts = append(downParts, goErrIf(fmt.Sprintf("%s.Fields.AddMarshaledJSONAt(%d, []byte(`%s`))", varName, i, escapeBacktick(string(rawOldField)))))
|
||||
}
|
||||
|
||||
// created fields
|
||||
@ -561,13 +562,14 @@ func (p *plugin) goDiffTemplate(new *core.Collection, old *core.Collection) (str
|
||||
}
|
||||
|
||||
upParts = append(upParts, "// add field")
|
||||
upParts = append(upParts, goErrIf(fmt.Sprintf("%s.Fields.AddMarshaledJSON([]byte(`%s`))", varName, escapeBacktick(string(rawNewField)))))
|
||||
upParts = append(upParts, goErrIf(fmt.Sprintf("%s.Fields.AddMarshaledJSONAt(%d, []byte(`%s`))", varName, i, escapeBacktick(string(rawNewField)))))
|
||||
|
||||
downParts = append(downParts, "// remove field")
|
||||
downParts = append(downParts, fmt.Sprintf("%s.Fields.RemoveById(%q)\n", varName, newField.GetId()))
|
||||
}
|
||||
|
||||
// modified fields
|
||||
// (note currently ignoring order-only changes as it comes with too many edge-cases)
|
||||
for i, newField := range new.Fields {
|
||||
var rawNewField, rawOldField []byte
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user