[#2992] fixed zero-default value not being used if the field is not explicitly set when manually creating records
This commit is contained in:
parent
34fe55686d
commit
1330e2e1e7
|
@ -110,6 +110,10 @@
|
|||
|
||||
- Added new utility `github.com/pocketbase/pocketbase/tools/template` package to assist with rendering HTML templates using the standard Go `html/template` and `text/template` syntax.
|
||||
|
||||
- Fixed zero-default value not being used if the field is not explicitly set when manually creating records ([#2992](https://github.com/pocketbase/pocketbase/issues/2992)).
|
||||
Additionally, `record.Get(field)` will now always return normalized value (the same as in the json serialization) for consistency and to avoid ambiguities what is stored in the related DB table.
|
||||
The schema fields columns `DEFAULT` definition was also updated for new collections to ensure that `NULL` values can't be accidentally inserted.
|
||||
|
||||
|
||||
## v0.16.10
|
||||
|
||||
|
|
|
@ -173,6 +173,15 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
|||
}
|
||||
|
||||
return dao.RunInTransaction(func(txDao *Dao) error {
|
||||
// temporary disable the schema error checks to prevent view and trigger errors
|
||||
// when "altering" (aka. deleting and recreating) the non-normalized columns
|
||||
if _, err := txDao.DB().NewQuery("PRAGMA writable_schema = ON").Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
// executed with defer to make sure that the pragma is always reverted
|
||||
// in case of an error and when nested transactions are used
|
||||
defer txDao.DB().NewQuery("PRAGMA writable_schema = RESET").Execute()
|
||||
|
||||
for _, newField := range newCollection.Schema.Fields() {
|
||||
// allow to continue even if there is no old field for the cases
|
||||
// when a new field is added and there are already inserted data
|
||||
|
@ -192,11 +201,26 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
|||
continue // no change
|
||||
}
|
||||
|
||||
var updateQuery *dbx.Query
|
||||
// update the column definition by:
|
||||
// 1. inserting a new column with the new definition
|
||||
// 2. copy normalized values from the original column to the new one
|
||||
// 3. drop the original column
|
||||
// 4. rename the new column to the original column
|
||||
// -------------------------------------------------------
|
||||
|
||||
originalName := newField.Name
|
||||
tempName := "_" + newField.Name + security.PseudorandomString(5)
|
||||
|
||||
_, err := txDao.DB().AddColumn(newCollection.Name, tempName, newField.ColDefinition()).Execute()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var copyQuery *dbx.Query
|
||||
|
||||
if !isOldMultiple && isNewMultiple {
|
||||
// single -> multiple (convert to array)
|
||||
updateQuery = txDao.DB().NewQuery(fmt.Sprintf(
|
||||
copyQuery = txDao.DB().NewQuery(fmt.Sprintf(
|
||||
`UPDATE {{%s}} set [[%s]] = (
|
||||
CASE
|
||||
WHEN COALESCE([[%s]], '') = ''
|
||||
|
@ -211,19 +235,19 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
|||
END
|
||||
)`,
|
||||
newCollection.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
tempName,
|
||||
originalName,
|
||||
originalName,
|
||||
originalName,
|
||||
originalName,
|
||||
originalName,
|
||||
))
|
||||
} else {
|
||||
// multiple -> single (keep only the last element)
|
||||
//
|
||||
// note: for file fields the actual files are not deleted
|
||||
// allowing additional custom handling via migration.
|
||||
updateQuery = txDao.DB().NewQuery(fmt.Sprintf(
|
||||
// note: for file fields the actual file objects are not
|
||||
// deleted allowing additional custom handling via migration
|
||||
copyQuery = txDao.DB().NewQuery(fmt.Sprintf(
|
||||
`UPDATE {{%s}} set [[%s]] = (
|
||||
CASE
|
||||
WHEN COALESCE([[%s]], '[]') = '[]'
|
||||
|
@ -238,21 +262,35 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
|
|||
END
|
||||
)`,
|
||||
newCollection.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
newField.Name,
|
||||
tempName,
|
||||
originalName,
|
||||
originalName,
|
||||
originalName,
|
||||
originalName,
|
||||
originalName,
|
||||
))
|
||||
}
|
||||
|
||||
if _, err := updateQuery.Execute(); err != nil {
|
||||
// copy the normalized values
|
||||
if _, err := copyQuery.Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// drop the original column
|
||||
if _, err := txDao.DB().DropColumn(newCollection.Name, originalName).Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// rename the new column back to the original
|
||||
if _, err := txDao.DB().RenameColumn(newCollection.Name, tempName, originalName).Execute(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
// revert the pragma and reload the schema
|
||||
_, revertErr := txDao.DB().NewQuery("PRAGMA writable_schema = RESET").Execute()
|
||||
|
||||
return revertErr
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -188,7 +188,51 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
type expectation struct {
|
||||
// ensures that the writable schema was reverted to its expected default
|
||||
var writableSchema bool
|
||||
app.Dao().DB().NewQuery("PRAGMA writable_schema").Row(&writableSchema)
|
||||
if writableSchema == true {
|
||||
t.Fatalf("Expected writable_schema to be OFF, got %v", writableSchema)
|
||||
}
|
||||
|
||||
// check whether the columns DEFAULT definition was updated
|
||||
// ---------------------------------------------------------------
|
||||
tableInfo, err := app.Dao().TableInfo(collection.Name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tableInfoExpectations := map[string]string{
|
||||
"select_one": `'[]'`,
|
||||
"select_many": `''`,
|
||||
"file_one": `'[]'`,
|
||||
"file_many": `''`,
|
||||
"rel_one": `'[]'`,
|
||||
"rel_many": `''`,
|
||||
"new_multiple": `'[]'`,
|
||||
}
|
||||
for col, dflt := range tableInfoExpectations {
|
||||
t.Run("check default for "+col, func(t *testing.T) {
|
||||
var row *models.TableInfoRow
|
||||
for _, r := range tableInfo {
|
||||
if r.Name == col {
|
||||
row = r
|
||||
break
|
||||
}
|
||||
}
|
||||
if row == nil {
|
||||
t.Fatalf("Missing info for column %q", col)
|
||||
}
|
||||
|
||||
if v := row.DefaultValue.String(); v != dflt {
|
||||
t.Fatalf("Expected default value %q, got %q", dflt, v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// check whether the values were normalized
|
||||
// ---------------------------------------------------------------
|
||||
type fieldsExpectation struct {
|
||||
SelectOne string `db:"select_one"`
|
||||
SelectMany string `db:"select_many"`
|
||||
FileOne string `db:"file_one"`
|
||||
|
@ -198,13 +242,13 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|||
NewMultiple string `db:"new_multiple"`
|
||||
}
|
||||
|
||||
scenarios := []struct {
|
||||
fieldsScenarios := []struct {
|
||||
recordId string
|
||||
expected expectation
|
||||
expected fieldsExpectation
|
||||
}{
|
||||
{
|
||||
"imy661ixudk5izi",
|
||||
expectation{
|
||||
fieldsExpectation{
|
||||
SelectOne: `[]`,
|
||||
SelectMany: ``,
|
||||
FileOne: `[]`,
|
||||
|
@ -216,7 +260,7 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"al1h9ijdeojtsjy",
|
||||
expectation{
|
||||
fieldsExpectation{
|
||||
SelectOne: `["optionB"]`,
|
||||
SelectMany: `optionB`,
|
||||
FileOne: `["300_Jsjq7RdBgA.png"]`,
|
||||
|
@ -228,7 +272,7 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"84nmscqy84lsi1t",
|
||||
expectation{
|
||||
fieldsExpectation{
|
||||
SelectOne: `["optionB"]`,
|
||||
SelectMany: `optionC`,
|
||||
FileOne: `["test_d61b33QdDU.txt"]`,
|
||||
|
@ -240,8 +284,9 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
result := new(expectation)
|
||||
for _, s := range fieldsScenarios {
|
||||
t.Run("check fields for record "+s.recordId, func(t *testing.T) {
|
||||
result := new(fieldsExpectation)
|
||||
|
||||
err := app.Dao().DB().Select(
|
||||
"select_one",
|
||||
|
@ -253,24 +298,22 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
|
|||
"new_multiple",
|
||||
).From(collection.Name).Where(dbx.HashExp{"id": s.recordId}).One(result)
|
||||
if err != nil {
|
||||
t.Errorf("[%s] Failed to load record: %v", s.recordId, err)
|
||||
continue
|
||||
t.Fatalf("Failed to load record: %v", err)
|
||||
}
|
||||
|
||||
encodedResult, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
t.Errorf("[%s] Failed to encode result: %v", s.recordId, err)
|
||||
continue
|
||||
t.Fatalf("Failed to encode result: %v", err)
|
||||
}
|
||||
|
||||
encodedExpectation, err := json.Marshal(s.expected)
|
||||
if err != nil {
|
||||
t.Errorf("[%s] Failed to encode expectation: %v", s.recordId, err)
|
||||
continue
|
||||
t.Fatalf("Failed to encode expectation: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.EqualFold(encodedExpectation, encodedResult) {
|
||||
t.Errorf("[%s] Expected \n%s, \ngot \n%s", s.recordId, encodedExpectation, encodedResult)
|
||||
}
|
||||
t.Fatalf("Expected \n%s, \ngot \n%s", encodedExpectation, encodedResult)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
5
go.sum
5
go.sum
|
@ -12,8 +12,6 @@ github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDe
|
|||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aws/aws-sdk-go v1.44.306 h1:H487V/1N09BDxeGR7oR+LloC2uUpmf4atmqJaBgQOIs=
|
||||
github.com/aws/aws-sdk-go v1.44.306/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go v1.44.307 h1:2R0/EPgpZcFSUwZhYImq/srjaOrOfLv5MNRzrFyAM38=
|
||||
github.com/aws/aws-sdk-go v1.44.307/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k=
|
||||
|
@ -168,8 +166,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198 h1:lFz33AOOXwTpqOiHvrN8nmTdkxSfuNLHLPjgQ1muPpU=
|
||||
github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198/go.mod h1:uh3YlzsEJj7OG57rDWj6c3WEkOF1ZHGBQkDuUZw3rE8=
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4=
|
||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
|
@ -210,7 +206,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
|||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
|
|
|
@ -324,7 +324,7 @@ func (m *Record) Set(key string, value any) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get returns a single record model data value for "key".
|
||||
// Get returns a normalized single record model data value for "key".
|
||||
func (m *Record) Get(key string) any {
|
||||
switch key {
|
||||
case schema.FieldNameId:
|
||||
|
@ -334,11 +334,27 @@ func (m *Record) Get(key string) any {
|
|||
case schema.FieldNameUpdated:
|
||||
return m.Updated
|
||||
default:
|
||||
if m.data == nil {
|
||||
return nil
|
||||
var v any
|
||||
if m.data != nil {
|
||||
v = m.data.Get(key)
|
||||
}
|
||||
|
||||
return m.data.Get(key)
|
||||
// normalize the field value in case it is missing or an incorrect type was set
|
||||
// to ensure that the DB will always have normalized columns value.
|
||||
if field := m.Collection().Schema.GetFieldByName(key); field != nil {
|
||||
v = field.PrepareValue(v)
|
||||
} else if m.collection.IsAuth() {
|
||||
switch key {
|
||||
case schema.FieldNameEmailVisibility, schema.FieldNameVerified:
|
||||
v = cast.ToBool(v)
|
||||
case schema.FieldNameLastResetSentAt, schema.FieldNameLastVerificationSentAt:
|
||||
v, _ = types.ParseDateTime(v)
|
||||
case schema.FieldNameUsername, schema.FieldNameEmail, schema.FieldNameTokenKey, schema.FieldNamePasswordHash:
|
||||
v = cast.ToString(v)
|
||||
}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -729,6 +729,22 @@ func TestRecordSetAndGet(t *testing.T) {
|
|||
Name: "field2",
|
||||
Type: schema.FieldTypeNumber,
|
||||
},
|
||||
// fields that are not explicitly set to check
|
||||
// the default retrieval value (single and multiple)
|
||||
&schema.SchemaField{
|
||||
Name: "field3",
|
||||
Type: schema.FieldTypeBool,
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "field4",
|
||||
Type: schema.FieldTypeSelect,
|
||||
Options: &schema.SelectOptions{MaxSelect: 2},
|
||||
},
|
||||
&schema.SchemaField{
|
||||
Name: "field5",
|
||||
Type: schema.FieldTypeRelation,
|
||||
Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)},
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -741,28 +757,40 @@ func TestRecordSetAndGet(t *testing.T) {
|
|||
m.Set("unknown", 456) // undefined fields are allowed but not exported by default
|
||||
m.Set("expand", map[string]any{"test": 123}) // should store the value in m.expand
|
||||
|
||||
if m.Get("id") != "test_id" {
|
||||
t.Fatalf("Expected id %q, got %q", "test_id", m.Get("id"))
|
||||
if v := m.Get("id"); v != "test_id" {
|
||||
t.Fatalf("Expected id %q, got %q", "test_id", v)
|
||||
}
|
||||
|
||||
if m.GetString("created") != "2022-09-15 00:00:00.123Z" {
|
||||
t.Fatalf("Expected created %q, got %q", "2022-09-15 00:00:00.123Z", m.GetString("created"))
|
||||
if v := m.GetString("created"); v != "2022-09-15 00:00:00.123Z" {
|
||||
t.Fatalf("Expected created %q, got %q", "2022-09-15 00:00:00.123Z", v)
|
||||
}
|
||||
|
||||
if m.GetString("updated") != "" {
|
||||
t.Fatalf("Expected updated to be empty, got %q", m.GetString("updated"))
|
||||
if v := m.GetString("updated"); v != "" {
|
||||
t.Fatalf("Expected updated to be empty, got %q", v)
|
||||
}
|
||||
|
||||
if m.Get("field1") != "123" {
|
||||
t.Fatalf("Expected field1 %q, got %v", "123", m.Get("field1"))
|
||||
if v, ok := m.Get("field1").(string); !ok || v != "123" {
|
||||
t.Fatalf("Expected field1 %#v, got %#v", "123", m.Get("field1"))
|
||||
}
|
||||
|
||||
if m.Get("field2") != 0.0 {
|
||||
t.Fatalf("Expected field2 %v, got %v", 0.0, m.Get("field2"))
|
||||
if v, ok := m.Get("field2").(float64); !ok || v != 0.0 {
|
||||
t.Fatalf("Expected field2 %#v, got %#v", 0.0, m.Get("field2"))
|
||||
}
|
||||
|
||||
if m.Get("unknown") != 456 {
|
||||
t.Fatalf("Expected unknown %v, got %v", 456, m.Get("unknown"))
|
||||
if v, ok := m.Get("field3").(bool); !ok || v != false {
|
||||
t.Fatalf("Expected field3 %#v, got %#v", false, m.Get("field3"))
|
||||
}
|
||||
|
||||
if v, ok := m.Get("field4").([]string); !ok || len(v) != 0 {
|
||||
t.Fatalf("Expected field4 %#v, got %#v", "[]", m.Get("field4"))
|
||||
}
|
||||
|
||||
if v, ok := m.Get("field5").(string); !ok || len(v) != 0 {
|
||||
t.Fatalf("Expected field5 %#v, got %#v", "", m.Get("field5"))
|
||||
}
|
||||
|
||||
if v := m.Get("unknown"); v != 456 {
|
||||
t.Fatalf("Expected unknown %v, got %v", 456, v)
|
||||
}
|
||||
|
||||
if m.Expand()["test"] != 123 {
|
||||
|
|
|
@ -143,13 +143,17 @@ type SchemaField struct {
|
|||
func (f *SchemaField) ColDefinition() string {
|
||||
switch f.Type {
|
||||
case FieldTypeNumber:
|
||||
return "NUMERIC DEFAULT 0"
|
||||
return "NUMERIC DEFAULT 0 NOT NULL"
|
||||
case FieldTypeBool:
|
||||
return "BOOLEAN DEFAULT FALSE"
|
||||
return "BOOLEAN DEFAULT FALSE NOT NULL"
|
||||
case FieldTypeJson:
|
||||
return "JSON DEFAULT NULL"
|
||||
default:
|
||||
return "TEXT DEFAULT ''"
|
||||
if opt, ok := f.Options.(MultiValuer); ok && opt.IsMultiple() {
|
||||
return "JSON DEFAULT '[]' NOT NULL"
|
||||
}
|
||||
|
||||
return "TEXT DEFAULT '' NOT NULL"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,47 +63,59 @@ func TestSchemaFieldColDefinition(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeText, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeNumber, Name: "test"},
|
||||
"NUMERIC DEFAULT 0",
|
||||
"NUMERIC DEFAULT 0 NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeBool, Name: "test"},
|
||||
"BOOLEAN DEFAULT FALSE",
|
||||
"BOOLEAN DEFAULT FALSE NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeEmail, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeUrl, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeEditor, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeDate, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeJson, Name: "test"},
|
||||
"JSON DEFAULT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test"},
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Name: "test"},
|
||||
"TEXT DEFAULT ''",
|
||||
schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test_multiple", Options: &schema.SelectOptions{MaxSelect: 2}},
|
||||
"JSON DEFAULT '[]' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Name: "test"},
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeFile, Name: "test_multiple", Options: &schema.FileOptions{MaxSelect: 2}},
|
||||
"JSON DEFAULT '[]' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Name: "test", Options: &schema.RelationOptions{MaxSelect: types.Pointer(1)}},
|
||||
"TEXT DEFAULT '' NOT NULL",
|
||||
},
|
||||
{
|
||||
schema.SchemaField{Type: schema.FieldTypeRelation, Name: "test_multiple", Options: &schema.RelationOptions{MaxSelect: nil}},
|
||||
"JSON DEFAULT '[]' NOT NULL",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -141,6 +141,13 @@ declare function routerPre(...middlewares: Array<string|echo.MiddlewareFunc>): v
|
|||
// baseBinds
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Global helper variable that contains the absolute path to the app pb_hooks directory.
|
||||
*
|
||||
* @group PocketBase
|
||||
*/
|
||||
declare var __hooks: string
|
||||
|
||||
// skip on* hook methods as they are registered via the global on* method
|
||||
type appWithoutHooks = Omit<pocketbase.PocketBase, ` + "`on${string}`" + `>
|
||||
|
||||
|
@ -148,6 +155,8 @@ type appWithoutHooks = Omit<pocketbase.PocketBase, ` + "`on${string}`" + `>
|
|||
* ` + "`$app`" + ` is the current running PocketBase instance that is globally
|
||||
* available in each .pb.js file.
|
||||
*
|
||||
* _Note that this variable is available only in pb_hooks context._
|
||||
*
|
||||
* @namespace
|
||||
* @group PocketBase
|
||||
*/
|
||||
|
@ -162,14 +171,10 @@ declare var $app: appWithoutHooks
|
|||
* Example:
|
||||
*
|
||||
* ` + "```" + `js
|
||||
* routerAdd("get", "/hello", (c) => {
|
||||
* const html = $template.loadFiles(
|
||||
* "views/layout.html",
|
||||
* "views/content.html",
|
||||
* ).render({"name": "John"})
|
||||
*
|
||||
* return c.html(200, html)
|
||||
* })
|
||||
* ` + "```" + `
|
||||
*
|
||||
* _Note that this method is available only in pb_hooks context._
|
||||
|
|
|
@ -201,6 +201,11 @@ func (p *plugin) registerHooks() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
absHooksDir, err := filepath.Abs(p.config.HooksDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
e.Router.HTTPErrorHandler = p.normalizeServeExceptions(e.Router.HTTPErrorHandler)
|
||||
return nil
|
||||
|
@ -225,6 +230,7 @@ func (p *plugin) registerHooks() error {
|
|||
apisBinds(vm)
|
||||
vm.Set("$app", p.app)
|
||||
vm.Set("$template", templateRegistry)
|
||||
vm.Set("__hooks", absHooksDir)
|
||||
}
|
||||
|
||||
// initiliaze the executor vms
|
||||
|
|
Loading…
Reference in New Issue