mirror of
https://github.com/pocketbase/pocketbase.git
synced 2025-04-20 15:08:05 +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"
|
"database/sql/driver"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ func NewFieldsList(fields ...Field) FieldsList {
|
|||||||
l := make(FieldsList, 0, len(fields))
|
l := make(FieldsList, 0, len(fields))
|
||||||
|
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
l.Add(f)
|
l.add(-1, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
return l
|
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).
|
// (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) {
|
func (l *FieldsList) Add(fields ...Field) {
|
||||||
for _, f := range fields {
|
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: "test"}`})
|
||||||
// l.AddMarshaledJSON([]byte{`[{"type":"text", name: "test1"}, {"type":"text", name: "test2"}]`})
|
// l.AddMarshaledJSON([]byte{`[{"type":"text", name: "test1"}, {"type":"text", name: "test2"}]`})
|
||||||
func (l *FieldsList) AddMarshaledJSON(rawJSON []byte) error {
|
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 {
|
if len(rawJSON) == 0 {
|
||||||
return nil // nothing to add
|
return extractedFields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to unmarshal first into a new fieds list
|
// try to unmarshal first into a new fieds list
|
||||||
// (assuming that rawJSON is array of objects)
|
// (assuming that rawJSON is array of objects)
|
||||||
extractedFields := FieldsList{}
|
|
||||||
err := json.Unmarshal(rawJSON, &extractedFields)
|
err := json.Unmarshal(rawJSON, &extractedFields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// try again but wrap the rawJSON in []
|
// try again but wrap the rawJSON in []
|
||||||
@ -149,21 +198,25 @@ func (l *FieldsList) AddMarshaledJSON(rawJSON []byte) error {
|
|||||||
wrapped = append(wrapped, ']')
|
wrapped = append(wrapped, ']')
|
||||||
err = json.Unmarshal(wrapped, &extractedFields)
|
err = json.Unmarshal(wrapped, &extractedFields)
|
||||||
if err != nil {
|
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 {
|
return extractedFields, nil
|
||||||
l.add(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *FieldsList) add(newField Field) {
|
func (l *FieldsList) add(pos int, newField Field) {
|
||||||
fields := *l
|
fields := *l
|
||||||
|
|
||||||
var replaceByName bool
|
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()
|
newFieldId := newField.GetId()
|
||||||
|
|
||||||
@ -182,24 +235,44 @@ func (l *FieldsList) add(newField Field) {
|
|||||||
newField.SetId(newFieldId)
|
newField.SetId(newFieldId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace existing
|
// try to replace existing
|
||||||
for i, field := range fields {
|
for i, field := range fields {
|
||||||
if replaceByName {
|
if replaceByName {
|
||||||
if name := newField.GetName(); name != "" && field.GetName() == name {
|
if name := newField.GetName(); name != "" && field.GetName() == name {
|
||||||
|
// reuse the original id
|
||||||
newField.SetId(field.GetId())
|
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 {
|
} else {
|
||||||
if field.GetId() == newFieldId {
|
if field.GetId() == newFieldId {
|
||||||
(*l)[i] = newField
|
if replaceInPlace {
|
||||||
return
|
(*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
|
// insert the new field
|
||||||
*l = append(fields, newField)
|
*l = slices.Insert(*l, pos, newField)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the string representation of the current list.
|
// String returns the string representation of the current list.
|
||||||
@ -252,7 +325,7 @@ func (l *FieldsList) UnmarshalJSON(data []byte) error {
|
|||||||
*l = []Field{} // reset
|
*l = []Field{} // reset
|
||||||
|
|
||||||
for _, fwt := range fwts {
|
for _, fwt := range fwts {
|
||||||
l.Add(fwt.Field)
|
l.add(-1, fwt.Field)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package core_test
|
package core_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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) {
|
func TestFieldsListStringAndValue(t *testing.T) {
|
||||||
t.Run("empty list", func(t *testing.T) {
|
t.Run("empty list", func(t *testing.T) {
|
||||||
testFieldsList := core.NewFieldsList()
|
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:
|
// Example:
|
||||||
//
|
//
|
||||||
// jsvm.MustRegister(app, jsvm.Config{
|
// jsvm.MustRegister(app, jsvm.Config{
|
||||||
// WatchHooks: true,
|
// HooksWatch: true,
|
||||||
// })
|
// })
|
||||||
package jsvm
|
package jsvm
|
||||||
|
|
||||||
|
@ -921,7 +921,7 @@ migrate((app) => {
|
|||||||
collection.fields.removeById("f3_id")
|
collection.fields.removeById("f3_id")
|
||||||
|
|
||||||
// add field
|
// add field
|
||||||
collection.fields.add(new Field({
|
collection.fields.addAt(8, new Field({
|
||||||
"autogeneratePattern": "",
|
"autogeneratePattern": "",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"id": "f4_id",
|
"id": "f4_id",
|
||||||
@ -975,7 +975,7 @@ migrate((app) => {
|
|||||||
}, collection)
|
}, collection)
|
||||||
|
|
||||||
// add field
|
// add field
|
||||||
collection.fields.add(new Field({
|
collection.fields.addAt(8, new Field({
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"id": "f3_id",
|
"id": "f3_id",
|
||||||
"name": "f3_name",
|
"name": "f3_name",
|
||||||
@ -1052,7 +1052,7 @@ func init() {
|
|||||||
collection.Fields.RemoveById("f3_id")
|
collection.Fields.RemoveById("f3_id")
|
||||||
|
|
||||||
// add field
|
// add field
|
||||||
if err := collection.Fields.AddMarshaledJSON([]byte(` + "`" + `{
|
if err := collection.Fields.AddMarshaledJSONAt(8, []byte(` + "`" + `{
|
||||||
"autogeneratePattern": "",
|
"autogeneratePattern": "",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"id": "f4_id",
|
"id": "f4_id",
|
||||||
@ -1115,7 +1115,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add field
|
// add field
|
||||||
if err := collection.Fields.AddMarshaledJSON([]byte(` + "`" + `{
|
if err := collection.Fields.AddMarshaledJSONAt(8, []byte(` + "`" + `{
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"id": "f3_id",
|
"id": "f3_id",
|
||||||
"name": "f3_name",
|
"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()))
|
upParts = append(upParts, fmt.Sprintf("%s.fields.removeById(%q)\n", varName, oldField.GetId()))
|
||||||
|
|
||||||
downParts = append(downParts, "// add field")
|
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
|
// 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, "// 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, "// remove field")
|
||||||
downParts = append(downParts, fmt.Sprintf("%s.fields.removeById(%q)\n", varName, newField.GetId()))
|
downParts = append(downParts, fmt.Sprintf("%s.fields.removeById(%q)\n", varName, newField.GetId()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// modified fields
|
// modified fields
|
||||||
|
// (note currently ignoring order-only changes as it comes with too many edge-cases)
|
||||||
for i, newField := range new.Fields {
|
for i, newField := range new.Fields {
|
||||||
var rawNewField, rawOldField []byte
|
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()))
|
upParts = append(upParts, fmt.Sprintf("%s.Fields.RemoveById(%q)\n", varName, oldField.GetId()))
|
||||||
|
|
||||||
downParts = append(downParts, "// add field")
|
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
|
// 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, "// 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, "// remove field")
|
||||||
downParts = append(downParts, fmt.Sprintf("%s.Fields.RemoveById(%q)\n", varName, newField.GetId()))
|
downParts = append(downParts, fmt.Sprintf("%s.Fields.RemoveById(%q)\n", varName, newField.GetId()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// modified fields
|
// modified fields
|
||||||
|
// (note currently ignoring order-only changes as it comes with too many edge-cases)
|
||||||
for i, newField := range new.Fields {
|
for i, newField := range new.Fields {
|
||||||
var rawNewField, rawOldField []byte
|
var rawNewField, rawOldField []byte
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user