-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathfield.go
More file actions
272 lines (223 loc) · 8.06 KB
/
field.go
File metadata and controls
272 lines (223 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
package core
import (
"context"
"database/sql/driver"
"regexp"
"strings"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/pocketbase/pocketbase/core/validators"
"github.com/pocketbase/pocketbase/tools/list"
)
var fieldNameRegex = regexp.MustCompile(`^\w+$`)
const maxSafeJSONInt int64 = 1<<53 - 1
// Commonly used field names.
const (
FieldNameId = "id"
FieldNameCollectionId = "collectionId"
FieldNameCollectionName = "collectionName"
FieldNameExpand = "expand"
FieldNameEmail = "email"
FieldNameEmailVisibility = "emailVisibility"
FieldNameVerified = "verified"
FieldNameTokenKey = "tokenKey"
FieldNamePassword = "password"
)
// SystemFields returns special internal field names that are usually readonly.
var SystemDynamicFieldNames = []string{
FieldNameCollectionId,
FieldNameCollectionName,
FieldNameExpand,
}
// Common RecordInterceptor action names.
const (
InterceptorActionValidate = "validate"
InterceptorActionDelete = "delete"
InterceptorActionDeleteExecute = "deleteExecute"
InterceptorActionAfterDelete = "afterDelete"
InterceptorActionAfterDeleteError = "afterDeleteError"
InterceptorActionCreate = "create"
InterceptorActionCreateExecute = "createExecute"
InterceptorActionAfterCreate = "afterCreate"
InterceptorActionAfterCreateError = "afterCreateFailure"
InterceptorActionUpdate = "update"
InterceptorActionUpdateExecute = "updateExecute"
InterceptorActionAfterUpdate = "afterUpdate"
InterceptorActionAfterUpdateError = "afterUpdateError"
)
// Common field errors.
var (
ErrUnknownField = validation.NewError("validation_unknown_field", "Unknown or invalid field.")
ErrInvalidFieldValue = validation.NewError("validation_invalid_field_value", "Invalid field value.")
ErrMustBeSystemAndHidden = validation.NewError("validation_must_be_system_and_hidden", `The field must be marked as "System" and "Hidden".`)
ErrMustBeSystem = validation.NewError("validation_must_be_system", `The field must be marked as "System".`)
)
// FieldFactoryFunc defines a simple function to construct a specific Field instance.
type FieldFactoryFunc func() Field
// Fields holds all available collection fields.
var Fields = map[string]FieldFactoryFunc{}
// Field defines a common interface that all Collection fields should implement.
type Field interface {
// note: the getters has an explicit "Get" prefix to avoid conflicts with their related field members
// GetId returns the field id.
GetId() string
// SetId changes the field id.
SetId(id string)
// GetName returns the field name.
GetName() string
// SetName changes the field name.
SetName(name string)
// GetSystem returns the field system flag state.
GetSystem() bool
// SetSystem changes the field system flag state.
SetSystem(system bool)
// GetHidden returns the field hidden flag state.
GetHidden() bool
// SetHidden changes the field hidden flag state.
SetHidden(hidden bool)
// Type returns the unique type of the field.
Type() string
// ColumnType returns the DB column definition of the field.
ColumnType(app App) string
// PrepareValue returns a properly formatted field value based on the provided raw one.
//
// This method is also called on record construction to initialize its default field value.
PrepareValue(record *Record, raw any) (any, error)
// ValidateSettings validates the current field value associated with the provided record.
ValidateValue(ctx context.Context, app App, record *Record) error
// ValidateSettings validates the current field settings.
ValidateSettings(ctx context.Context, app App, collection *Collection) error
}
// MaxBodySizeCalculator defines an optional field interface for
// specifying the max size of a field value.
type MaxBodySizeCalculator interface {
// CalculateMaxBodySize returns the approximate max body size of a field value.
CalculateMaxBodySize() int64
}
type (
SetterFunc func(record *Record, raw any)
// SetterFinder defines a field interface for registering custom field value setters.
SetterFinder interface {
// FindSetter returns a single field value setter function
// by performing pattern-like field matching using the specified key.
//
// The key is usually just the field name but it could also
// contains "modifier" characters based on which you can perform custom set operations
// (ex. "users+" could be mapped to a function that will append new user to the existing field value).
//
// Return nil if you want to fallback to the default field value setter.
FindSetter(key string) SetterFunc
}
)
type (
GetterFunc func(record *Record) any
// GetterFinder defines a field interface for registering custom field value getters.
GetterFinder interface {
// FindGetter returns a single field value getter function
// by performing pattern-like field matching using the specified key.
//
// The key is usually just the field name but it could also
// contains "modifier" characters based on which you can perform custom get operations
// (ex. "description:excerpt" could be mapped to a function that will return an excerpt of the current field value).
//
// Return nil if you want to fallback to the default field value setter.
FindGetter(key string) GetterFunc
}
)
// DriverValuer defines a Field interface for exporting and formatting
// a field value for the database.
type DriverValuer interface {
// DriverValue exports a single field value for persistence in the database.
DriverValue(record *Record) (driver.Value, error)
}
// MultiValuer defines a field interface that every multi-valued (eg. with MaxSelect) field has.
type MultiValuer interface {
// IsMultiple checks whether the field is configured to support multiple or single values.
IsMultiple() bool
}
// RecordInterceptor defines a field interface for reacting to various
// Record related operations (create, delete, validate, etc.).
type RecordInterceptor interface {
// Interceptor is invoked when a specific record action occurs
// allowing you to perform extra validations and normalization
// (ex. uploading or deleting files).
//
// Note that users must call actionFunc() manually if they want to
// execute the specific record action.
Intercept(
ctx context.Context,
app App,
record *Record,
actionName string,
actionFunc func() error,
) error
}
// DefaultFieldHelpValidationRule performs base validation on a field's "help" value.
func DefaultFieldHelpValidationRule(value any) error {
v, ok := value.(string)
if !ok {
return validators.ErrUnsupportedValueType
}
rules := []validation.Rule{
validation.Length(1, 300),
}
for _, r := range rules {
if err := r.Validate(v); err != nil {
return err
}
}
return nil
}
// DefaultFieldIdValidationRule performs base validation on a field id value.
func DefaultFieldIdValidationRule(value any) error {
v, ok := value.(string)
if !ok {
return validators.ErrUnsupportedValueType
}
rules := []validation.Rule{
validation.Required,
validation.Length(1, 100),
}
for _, r := range rules {
if err := r.Validate(v); err != nil {
return err
}
}
return nil
}
// exclude special filter and system literals
var excludeNames = append([]any{
"null", "true", "false", "_rowid_",
}, list.ToInterfaceSlice(SystemDynamicFieldNames)...)
// DefaultFieldIdValidationRule performs base validation on a field name value.
func DefaultFieldNameValidationRule(value any) error {
v, ok := value.(string)
if !ok {
return validators.ErrUnsupportedValueType
}
rules := []validation.Rule{
validation.Required,
validation.Length(1, 100),
validation.Match(fieldNameRegex),
validation.NotIn(excludeNames...),
validation.By(checkForVia),
}
for _, r := range rules {
if err := r.Validate(v); err != nil {
return err
}
}
return nil
}
func checkForVia(value any) error {
v, _ := value.(string)
if v == "" {
return nil
}
if strings.Contains(strings.ToLower(v), "_via_") {
return validation.NewError("validation_found_via", `The value cannot contain "_via_".`)
}
return nil
}
func noopSetter(record *Record, raw any) {
// do nothing
}