[#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.
 | 
					- 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
 | 
					## v0.16.10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,6 +173,15 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return dao.RunInTransaction(func(txDao *Dao) error {
 | 
						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() {
 | 
							for _, newField := range newCollection.Schema.Fields() {
 | 
				
			||||||
			// allow to continue even if there is no old field for the cases
 | 
								// 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
 | 
								// 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
 | 
									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 {
 | 
								if !isOldMultiple && isNewMultiple {
 | 
				
			||||||
				// single -> multiple (convert to array)
 | 
									// single -> multiple (convert to array)
 | 
				
			||||||
				updateQuery = txDao.DB().NewQuery(fmt.Sprintf(
 | 
									copyQuery = txDao.DB().NewQuery(fmt.Sprintf(
 | 
				
			||||||
					`UPDATE {{%s}} set [[%s]] = (
 | 
										`UPDATE {{%s}} set [[%s]] = (
 | 
				
			||||||
							CASE
 | 
												CASE
 | 
				
			||||||
								WHEN COALESCE([[%s]], '') = ''
 | 
													WHEN COALESCE([[%s]], '') = ''
 | 
				
			||||||
| 
						 | 
					@ -211,19 +235,19 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
 | 
				
			||||||
							END
 | 
												END
 | 
				
			||||||
						)`,
 | 
											)`,
 | 
				
			||||||
					newCollection.Name,
 | 
										newCollection.Name,
 | 
				
			||||||
					newField.Name,
 | 
										tempName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
				))
 | 
									))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				// multiple -> single (keep only the last element)
 | 
									// multiple -> single (keep only the last element)
 | 
				
			||||||
				//
 | 
									//
 | 
				
			||||||
				// note: for file fields the actual files are not deleted
 | 
									// note: for file fields the actual file objects are not
 | 
				
			||||||
				// allowing additional custom handling via migration.
 | 
									// deleted allowing additional custom handling via migration
 | 
				
			||||||
				updateQuery = txDao.DB().NewQuery(fmt.Sprintf(
 | 
									copyQuery = txDao.DB().NewQuery(fmt.Sprintf(
 | 
				
			||||||
					`UPDATE {{%s}} set [[%s]] = (
 | 
										`UPDATE {{%s}} set [[%s]] = (
 | 
				
			||||||
						CASE
 | 
											CASE
 | 
				
			||||||
							WHEN COALESCE([[%s]], '[]') = '[]'
 | 
												WHEN COALESCE([[%s]], '[]') = '[]'
 | 
				
			||||||
| 
						 | 
					@ -238,21 +262,35 @@ func (dao *Dao) normalizeSingleVsMultipleFieldChanges(newCollection, oldCollecti
 | 
				
			||||||
						END
 | 
											END
 | 
				
			||||||
					)`,
 | 
										)`,
 | 
				
			||||||
					newCollection.Name,
 | 
										newCollection.Name,
 | 
				
			||||||
					newField.Name,
 | 
										tempName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										originalName,
 | 
				
			||||||
					newField.Name,
 | 
										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 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)
 | 
							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"`
 | 
							SelectOne   string `db:"select_one"`
 | 
				
			||||||
		SelectMany  string `db:"select_many"`
 | 
							SelectMany  string `db:"select_many"`
 | 
				
			||||||
		FileOne     string `db:"file_one"`
 | 
							FileOne     string `db:"file_one"`
 | 
				
			||||||
| 
						 | 
					@ -198,13 +242,13 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
 | 
				
			||||||
		NewMultiple string `db:"new_multiple"`
 | 
							NewMultiple string `db:"new_multiple"`
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	scenarios := []struct {
 | 
						fieldsScenarios := []struct {
 | 
				
			||||||
		recordId string
 | 
							recordId string
 | 
				
			||||||
		expected expectation
 | 
							expected fieldsExpectation
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"imy661ixudk5izi",
 | 
								"imy661ixudk5izi",
 | 
				
			||||||
			expectation{
 | 
								fieldsExpectation{
 | 
				
			||||||
				SelectOne:   `[]`,
 | 
									SelectOne:   `[]`,
 | 
				
			||||||
				SelectMany:  ``,
 | 
									SelectMany:  ``,
 | 
				
			||||||
				FileOne:     `[]`,
 | 
									FileOne:     `[]`,
 | 
				
			||||||
| 
						 | 
					@ -216,7 +260,7 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"al1h9ijdeojtsjy",
 | 
								"al1h9ijdeojtsjy",
 | 
				
			||||||
			expectation{
 | 
								fieldsExpectation{
 | 
				
			||||||
				SelectOne:   `["optionB"]`,
 | 
									SelectOne:   `["optionB"]`,
 | 
				
			||||||
				SelectMany:  `optionB`,
 | 
									SelectMany:  `optionB`,
 | 
				
			||||||
				FileOne:     `["300_Jsjq7RdBgA.png"]`,
 | 
									FileOne:     `["300_Jsjq7RdBgA.png"]`,
 | 
				
			||||||
| 
						 | 
					@ -228,7 +272,7 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"84nmscqy84lsi1t",
 | 
								"84nmscqy84lsi1t",
 | 
				
			||||||
			expectation{
 | 
								fieldsExpectation{
 | 
				
			||||||
				SelectOne:   `["optionB"]`,
 | 
									SelectOne:   `["optionB"]`,
 | 
				
			||||||
				SelectMany:  `optionC`,
 | 
									SelectMany:  `optionC`,
 | 
				
			||||||
				FileOne:     `["test_d61b33QdDU.txt"]`,
 | 
									FileOne:     `["test_d61b33QdDU.txt"]`,
 | 
				
			||||||
| 
						 | 
					@ -240,37 +284,36 @@ func TestSingleVsMultipleValuesNormalization(t *testing.T) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, s := range scenarios {
 | 
						for _, s := range fieldsScenarios {
 | 
				
			||||||
		result := new(expectation)
 | 
							t.Run("check fields for record "+s.recordId, func(t *testing.T) {
 | 
				
			||||||
 | 
								result := new(fieldsExpectation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err := app.Dao().DB().Select(
 | 
								err := app.Dao().DB().Select(
 | 
				
			||||||
			"select_one",
 | 
									"select_one",
 | 
				
			||||||
			"select_many",
 | 
									"select_many",
 | 
				
			||||||
			"file_one",
 | 
									"file_one",
 | 
				
			||||||
			"file_many",
 | 
									"file_many",
 | 
				
			||||||
			"rel_one",
 | 
									"rel_one",
 | 
				
			||||||
			"rel_many",
 | 
									"rel_many",
 | 
				
			||||||
			"new_multiple",
 | 
									"new_multiple",
 | 
				
			||||||
		).From(collection.Name).Where(dbx.HashExp{"id": s.recordId}).One(result)
 | 
								).From(collection.Name).Where(dbx.HashExp{"id": s.recordId}).One(result)
 | 
				
			||||||
		if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
			t.Errorf("[%s] Failed to load record: %v", s.recordId, err)
 | 
									t.Fatalf("Failed to load record: %v", err)
 | 
				
			||||||
			continue
 | 
								}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		encodedResult, err := json.Marshal(result)
 | 
								encodedResult, err := json.Marshal(result)
 | 
				
			||||||
		if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
			t.Errorf("[%s] Failed to encode result: %v", s.recordId, err)
 | 
									t.Fatalf("Failed to encode result: %v", err)
 | 
				
			||||||
			continue
 | 
								}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		encodedExpectation, err := json.Marshal(s.expected)
 | 
								encodedExpectation, err := json.Marshal(s.expected)
 | 
				
			||||||
		if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
			t.Errorf("[%s] Failed to encode expectation: %v", s.recordId, err)
 | 
									t.Fatalf("Failed to encode expectation: %v", err)
 | 
				
			||||||
			continue
 | 
								}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !bytes.EqualFold(encodedExpectation, encodedResult) {
 | 
								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-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 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
 | 
				
			||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
 | 
					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 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 v1.44.307/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 | 
				
			||||||
github.com/aws/aws-sdk-go-v2 v1.19.0 h1:klAT+y3pGFBU/qVf1uzwttpBbiuozJYWzNLHioyDJ+k=
 | 
					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.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 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 | 
				
			||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 | 
					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 h1:FwuzbVh87iLiUQj1+uQUsuw9x5t9m5n5g7rG7o4svW4=
 | 
				
			||||||
github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61/go.mod h1:paQfF1YtHe+GrGg5fOgjsjoCX/UKDr9bc1DoWpZfns8=
 | 
					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=
 | 
					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.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.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.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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 | 
				
			||||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 | 
					github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 | 
				
			||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 | 
					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 {
 | 
					func (m *Record) Get(key string) any {
 | 
				
			||||||
	switch key {
 | 
						switch key {
 | 
				
			||||||
	case schema.FieldNameId:
 | 
						case schema.FieldNameId:
 | 
				
			||||||
| 
						 | 
					@ -334,11 +334,27 @@ func (m *Record) Get(key string) any {
 | 
				
			||||||
	case schema.FieldNameUpdated:
 | 
						case schema.FieldNameUpdated:
 | 
				
			||||||
		return m.Updated
 | 
							return m.Updated
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		if m.data == nil {
 | 
							var v any
 | 
				
			||||||
			return nil
 | 
							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",
 | 
									Name: "field2",
 | 
				
			||||||
				Type: schema.FieldTypeNumber,
 | 
									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("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
 | 
						m.Set("expand", map[string]any{"test": 123}) // should store the value in m.expand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.Get("id") != "test_id" {
 | 
						if v := m.Get("id"); v != "test_id" {
 | 
				
			||||||
		t.Fatalf("Expected id %q, got %q", "test_id", m.Get("id"))
 | 
							t.Fatalf("Expected id %q, got %q", "test_id", v)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.GetString("created") != "2022-09-15 00:00:00.123Z" {
 | 
						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", m.GetString("created"))
 | 
							t.Fatalf("Expected created %q, got %q", "2022-09-15 00:00:00.123Z", v)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.GetString("updated") != "" {
 | 
						if v := m.GetString("updated"); v != "" {
 | 
				
			||||||
		t.Fatalf("Expected updated to be empty, got %q", m.GetString("updated"))
 | 
							t.Fatalf("Expected updated to be empty, got %q", v)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.Get("field1") != "123" {
 | 
						if v, ok := m.Get("field1").(string); !ok || v != "123" {
 | 
				
			||||||
		t.Fatalf("Expected field1 %q, got %v", "123", m.Get("field1"))
 | 
							t.Fatalf("Expected field1 %#v, got %#v", "123", m.Get("field1"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.Get("field2") != 0.0 {
 | 
						if v, ok := m.Get("field2").(float64); !ok || v != 0.0 {
 | 
				
			||||||
		t.Fatalf("Expected field2 %v, got %v", 0.0, m.Get("field2"))
 | 
							t.Fatalf("Expected field2 %#v, got %#v", 0.0, m.Get("field2"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if m.Get("unknown") != 456 {
 | 
						if v, ok := m.Get("field3").(bool); !ok || v != false {
 | 
				
			||||||
		t.Fatalf("Expected unknown %v, got %v", 456, m.Get("unknown"))
 | 
							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 {
 | 
						if m.Expand()["test"] != 123 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,13 +143,17 @@ type SchemaField struct {
 | 
				
			||||||
func (f *SchemaField) ColDefinition() string {
 | 
					func (f *SchemaField) ColDefinition() string {
 | 
				
			||||||
	switch f.Type {
 | 
						switch f.Type {
 | 
				
			||||||
	case FieldTypeNumber:
 | 
						case FieldTypeNumber:
 | 
				
			||||||
		return "NUMERIC DEFAULT 0"
 | 
							return "NUMERIC DEFAULT 0 NOT NULL"
 | 
				
			||||||
	case FieldTypeBool:
 | 
						case FieldTypeBool:
 | 
				
			||||||
		return "BOOLEAN DEFAULT FALSE"
 | 
							return "BOOLEAN DEFAULT FALSE NOT NULL"
 | 
				
			||||||
	case FieldTypeJson:
 | 
						case FieldTypeJson:
 | 
				
			||||||
		return "JSON DEFAULT NULL"
 | 
							return "JSON DEFAULT NULL"
 | 
				
			||||||
	default:
 | 
						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"},
 | 
								schema.SchemaField{Type: schema.FieldTypeText, Name: "test"},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"TEXT DEFAULT '' NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeNumber, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeNumber, Name: "test"},
 | 
				
			||||||
			"NUMERIC DEFAULT 0",
 | 
								"NUMERIC DEFAULT 0 NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeBool, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeBool, Name: "test"},
 | 
				
			||||||
			"BOOLEAN DEFAULT FALSE",
 | 
								"BOOLEAN DEFAULT FALSE NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeEmail, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeEmail, Name: "test"},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"TEXT DEFAULT '' NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeUrl, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeUrl, Name: "test"},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"TEXT DEFAULT '' NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeEditor, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeEditor, Name: "test"},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"TEXT DEFAULT '' NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeDate, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeDate, Name: "test"},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"TEXT DEFAULT '' NOT NULL",
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test"},
 | 
					 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeJson, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeJson, Name: "test"},
 | 
				
			||||||
			"JSON DEFAULT NULL",
 | 
								"JSON DEFAULT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeFile, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test"},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"TEXT DEFAULT '' NOT NULL",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			schema.SchemaField{Type: schema.FieldTypeRelation, Name: "test"},
 | 
								schema.SchemaField{Type: schema.FieldTypeSelect, Name: "test_multiple", Options: &schema.SelectOptions{MaxSelect: 2}},
 | 
				
			||||||
			"TEXT DEFAULT ''",
 | 
								"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
 | 
					// 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
 | 
					// skip on* hook methods as they are registered via the global on* method
 | 
				
			||||||
type appWithoutHooks = Omit<pocketbase.PocketBase, ` + "`on${string}`" + `>
 | 
					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
 | 
					 * ` + "`$app`" + ` is the current running PocketBase instance that is globally
 | 
				
			||||||
 * available in each .pb.js file.
 | 
					 * available in each .pb.js file.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 | 
					 * _Note that this variable is available only in pb_hooks context._
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 * @namespace
 | 
					 * @namespace
 | 
				
			||||||
 * @group PocketBase
 | 
					 * @group PocketBase
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -162,14 +171,10 @@ declare var $app: appWithoutHooks
 | 
				
			||||||
 * Example:
 | 
					 * Example:
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * ` + "```" + `js
 | 
					 * ` + "```" + `js
 | 
				
			||||||
 * routerAdd("get", "/hello", (c) => {
 | 
					 * const html = $template.loadFiles(
 | 
				
			||||||
 *     const html = $template.loadFiles(
 | 
					 *     "views/layout.html",
 | 
				
			||||||
 *         "views/layout.html",
 | 
					 *     "views/content.html",
 | 
				
			||||||
 *         "views/content.html",
 | 
					 * ).render({"name": "John"})
 | 
				
			||||||
 *     ).render({"name": "John"})
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 *     return c.html(200, html)
 | 
					 | 
				
			||||||
 * })
 | 
					 | 
				
			||||||
 * ` + "```" + `
 | 
					 * ` + "```" + `
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * _Note that this method is available only in pb_hooks context._
 | 
					 * _Note that this method is available only in pb_hooks context._
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -201,6 +201,11 @@ func (p *plugin) registerHooks() error {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						absHooksDir, err := filepath.Abs(p.config.HooksDir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p.app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
 | 
						p.app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
 | 
				
			||||||
		e.Router.HTTPErrorHandler = p.normalizeServeExceptions(e.Router.HTTPErrorHandler)
 | 
							e.Router.HTTPErrorHandler = p.normalizeServeExceptions(e.Router.HTTPErrorHandler)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
| 
						 | 
					@ -225,6 +230,7 @@ func (p *plugin) registerHooks() error {
 | 
				
			||||||
		apisBinds(vm)
 | 
							apisBinds(vm)
 | 
				
			||||||
		vm.Set("$app", p.app)
 | 
							vm.Set("$app", p.app)
 | 
				
			||||||
		vm.Set("$template", templateRegistry)
 | 
							vm.Set("$template", templateRegistry)
 | 
				
			||||||
 | 
							vm.Set("__hooks", absHooksDir)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// initiliaze the executor vms
 | 
						// initiliaze the executor vms
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue