soft deprecated apis.RequestData(c) in favor of apis.RequestInfo(c) and updated jsvm bindings
This commit is contained in:
parent
7d4017225c
commit
0110869c89
|
@ -31,7 +31,7 @@
|
||||||
```
|
```
|
||||||
app.Dao().FindRecordsByFilter("posts", "title ~ 'lorem ipsum' && visible = true", "-created", 10)
|
app.Dao().FindRecordsByFilter("posts", "title ~ 'lorem ipsum' && visible = true", "-created", 10)
|
||||||
app.Dao().FindFirstRecordByFilter("posts", "slug='test' && active=true")
|
app.Dao().FindFirstRecordByFilter("posts", "slug='test' && active=true")
|
||||||
app.Dao().CanAccessRecord(record, requestData, rule)
|
app.Dao().CanAccessRecord(record, requestInfo, rule)
|
||||||
```
|
```
|
||||||
|
|
||||||
- (@todo docs) Added `Dao.WithoutHooks()` helper to create a new `Dao` from the current one but without the create/update/delete hooks.
|
- (@todo docs) Added `Dao.WithoutHooks()` helper to create a new `Dao` from the current one but without the create/update/delete hooks.
|
||||||
|
@ -84,6 +84,9 @@
|
||||||
|
|
||||||
- (@todo docs) Added `record.ExpandedOne(rel)` and `record.ExpandedAll(rel)` helpers to retrieve casted single or multiple expand relations from the already loaded "expand" Record data.
|
- (@todo docs) Added `record.ExpandedOne(rel)` and `record.ExpandedAll(rel)` helpers to retrieve casted single or multiple expand relations from the already loaded "expand" Record data.
|
||||||
|
|
||||||
|
- **!** renamed `models.RequestData` to `models.RequestInfo` and soft-deprecated `apis.RequestData(c)` to `apis.RequestInfo(c)` to avoid the stuttering with the `Data` field.
|
||||||
|
_The old `apis.RequestData()` method still works to minimize the breaking changes but it is recommended to replace it with `apis.RequestInfo(c)`._
|
||||||
|
|
||||||
|
|
||||||
## v0.16.10
|
## v0.16.10
|
||||||
|
|
||||||
|
@ -92,7 +95,7 @@
|
||||||
|
|
||||||
## v0.16.9
|
## v0.16.9
|
||||||
|
|
||||||
- Register the `eagerRequestDataCache` middleware only for the internal `api` group routes to avoid conflicts with custom route handlers ([#2914](https://github.com/pocketbase/pocketbase/issues/2914)).
|
- Register the `eagerRequestInfoCache` middleware only for the internal `api` group routes to avoid conflicts with custom route handlers ([#2914](https://github.com/pocketbase/pocketbase/issues/2914)).
|
||||||
|
|
||||||
|
|
||||||
## v0.16.8
|
## v0.16.8
|
||||||
|
|
|
@ -120,7 +120,7 @@ func InitApi(app core.App) (*echo.Echo, error) {
|
||||||
bindStaticAdminUI(app, e)
|
bindStaticAdminUI(app, e)
|
||||||
|
|
||||||
// default routes
|
// default routes
|
||||||
api := e.Group("/api", eagerRequestDataCache(app))
|
api := e.Group("/api", eagerRequestInfoCache(app))
|
||||||
bindSettingsApi(app, api)
|
bindSettingsApi(app, api)
|
||||||
bindAdminApi(app, api)
|
bindAdminApi(app, api)
|
||||||
bindCollectionApi(app, api)
|
bindCollectionApi(app, api)
|
||||||
|
|
|
@ -213,7 +213,7 @@ func TestRemoveTrailingSlashMiddleware(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEagerRequestDataCache(t *testing.T) {
|
func TestEagerRequestInfoCache(t *testing.T) {
|
||||||
|
|
||||||
scenarios := []tests.ApiScenario{
|
scenarios := []tests.ApiScenario{
|
||||||
{
|
{
|
||||||
|
@ -236,7 +236,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
||||||
|
|
||||||
// since the unknown method is not eager cache support
|
// since the unknown method is not eager cache support
|
||||||
// it should fail reading the json body twice
|
// it should fail reading the json body twice
|
||||||
r := apis.RequestData(c)
|
r := apis.RequestInfo(c)
|
||||||
if v := cast.ToString(r.Data["name"]); v != "" {
|
if v := cast.ToString(r.Data["name"]); v != "" {
|
||||||
t.Fatalf("Expected empty request data body, got, %v", r.Data)
|
t.Fatalf("Expected empty request data body, got, %v", r.Data)
|
||||||
}
|
}
|
||||||
|
@ -256,7 +256,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
||||||
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
// it is not important whether the route handler return an error since
|
// it is not important whether the route handler return an error since
|
||||||
// we just need to ensure that the eagerRequestDataCache was registered
|
// we just need to ensure that the eagerRequestInfoCache was registered
|
||||||
next(c)
|
next(c)
|
||||||
|
|
||||||
// ensure that the body was read at least once
|
// ensure that the body was read at least once
|
||||||
|
@ -267,7 +267,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
||||||
|
|
||||||
// since the unknown method is not eager cache support
|
// since the unknown method is not eager cache support
|
||||||
// it should fail reading the json body twice
|
// it should fail reading the json body twice
|
||||||
r := apis.RequestData(c)
|
r := apis.RequestInfo(c)
|
||||||
if v := cast.ToString(r.Data["name"]); v != "" {
|
if v := cast.ToString(r.Data["name"]); v != "" {
|
||||||
t.Fatalf("Expected empty request data body, got, %v", r.Data)
|
t.Fatalf("Expected empty request data body, got, %v", r.Data)
|
||||||
}
|
}
|
||||||
|
@ -287,7 +287,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
||||||
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
// it is not important whether the route handler return an error since
|
// it is not important whether the route handler return an error since
|
||||||
// we just need to ensure that the eagerRequestDataCache was registered
|
// we just need to ensure that the eagerRequestInfoCache was registered
|
||||||
next(c)
|
next(c)
|
||||||
|
|
||||||
// ensure that the body was read at least once
|
// ensure that the body was read at least once
|
||||||
|
@ -297,7 +297,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
||||||
c.Bind(data)
|
c.Bind(data)
|
||||||
|
|
||||||
// try to read the body again
|
// try to read the body again
|
||||||
r := apis.RequestData(c)
|
r := apis.RequestInfo(c)
|
||||||
fmt.Println(r)
|
fmt.Println(r)
|
||||||
if v := cast.ToString(r.Data["name"]); v != "test123" {
|
if v := cast.ToString(r.Data["name"]); v != "test123" {
|
||||||
t.Fatalf("Expected request data with name %q, got, %q", "test123", v)
|
t.Fatalf("Expected request data with name %q, got, %q", "test123", v)
|
||||||
|
|
12
apis/file.go
12
apis/file.go
|
@ -95,18 +95,18 @@ func (api *fileApi) download(c echo.Context) error {
|
||||||
adminOrAuthRecord, _ := api.findAdminOrAuthRecordByFileToken(token)
|
adminOrAuthRecord, _ := api.findAdminOrAuthRecordByFileToken(token)
|
||||||
|
|
||||||
// create a copy of the cached request data and adjust it for the current auth model
|
// create a copy of the cached request data and adjust it for the current auth model
|
||||||
requestData := *RequestData(c)
|
requestInfo := *RequestInfo(c)
|
||||||
requestData.Admin = nil
|
requestInfo.Admin = nil
|
||||||
requestData.AuthRecord = nil
|
requestInfo.AuthRecord = nil
|
||||||
if adminOrAuthRecord != nil {
|
if adminOrAuthRecord != nil {
|
||||||
if admin, _ := adminOrAuthRecord.(*models.Admin); admin != nil {
|
if admin, _ := adminOrAuthRecord.(*models.Admin); admin != nil {
|
||||||
requestData.Admin = admin
|
requestInfo.Admin = admin
|
||||||
} else if record, _ := adminOrAuthRecord.(*models.Record); record != nil {
|
} else if record, _ := adminOrAuthRecord.(*models.Record); record != nil {
|
||||||
requestData.AuthRecord = record
|
requestInfo.AuthRecord = record
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, _ := api.app.Dao().CanAccessRecord(record, &requestData, record.Collection().ViewRule); !ok {
|
if ok, _ := api.app.Dao().CanAccessRecord(record, &requestInfo, record.Collection().ViewRule); !ok {
|
||||||
return NewForbiddenError("Insufficient permissions to access the file resource.", nil)
|
return NewForbiddenError("Insufficient permissions to access the file resource.", nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,15 +393,15 @@ func realUserIp(r *http.Request, fallbackIp string) string {
|
||||||
return fallbackIp
|
return fallbackIp
|
||||||
}
|
}
|
||||||
|
|
||||||
// eagerRequestDataCache ensures that the request data is cached in the request
|
// eagerRequestInfoCache ensures that the request data is cached in the request
|
||||||
// context to allow reading for example the json request body data more than once.
|
// context to allow reading for example the json request body data more than once.
|
||||||
func eagerRequestDataCache(app core.App) echo.MiddlewareFunc {
|
func eagerRequestInfoCache(app core.App) echo.MiddlewareFunc {
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
switch c.Request().Method {
|
switch c.Request().Method {
|
||||||
// currently we are eagerly caching only the requests with body
|
// currently we are eagerly caching only the requests with body
|
||||||
case "POST", "PUT", "PATCH", "DELETE":
|
case "POST", "PUT", "PATCH", "DELETE":
|
||||||
RequestData(c)
|
RequestInfo(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(c)
|
return next(c)
|
||||||
|
|
|
@ -347,12 +347,12 @@ func (api *realtimeApi) canAccessRecord(client subscriptions.Client, record *mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// mock request data
|
// mock request data
|
||||||
requestData := &models.RequestData{
|
requestInfo := &models.RequestInfo{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
}
|
}
|
||||||
requestData.AuthRecord, _ = client.Get(ContextAuthRecordKey).(*models.Record)
|
requestInfo.AuthRecord, _ = client.Get(ContextAuthRecordKey).(*models.Record)
|
||||||
|
|
||||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), record.Collection(), requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), record.Collection(), requestInfo, true)
|
||||||
expr, err := search.FilterData(*accessRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*accessRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -191,8 +191,8 @@ func (api *recordAuthApi) authWithOAuth2(c echo.Context) error {
|
||||||
return createForm.DrySubmit(func(txDao *daos.Dao) error {
|
return createForm.DrySubmit(func(txDao *daos.Dao) error {
|
||||||
event.IsNewRecord = true
|
event.IsNewRecord = true
|
||||||
// clone the current request data and assign the form create data as its body data
|
// clone the current request data and assign the form create data as its body data
|
||||||
requestData := *RequestData(c)
|
requestInfo := *RequestInfo(c)
|
||||||
requestData.Data = form.CreateData
|
requestInfo.Data = form.CreateData
|
||||||
|
|
||||||
createRuleFunc := func(q *dbx.SelectQuery) error {
|
createRuleFunc := func(q *dbx.SelectQuery) error {
|
||||||
admin, _ := c.Get(ContextAdminKey).(*models.Admin)
|
admin, _ := c.Get(ContextAdminKey).(*models.Admin)
|
||||||
|
@ -205,7 +205,7 @@ func (api *recordAuthApi) authWithOAuth2(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *collection.CreateRule != "" {
|
if *collection.CreateRule != "" {
|
||||||
resolver := resolvers.NewRecordFieldResolver(txDao, collection, &requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(txDao, collection, &requestInfo, true)
|
||||||
expr, err := search.FilterData(*collection.CreateRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*collection.CreateRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -50,9 +50,9 @@ func (api *recordApi) list(c echo.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := RequestData(c)
|
requestInfo := RequestInfo(c)
|
||||||
|
|
||||||
if requestData.Admin == nil && collection.ListRule == nil {
|
if requestInfo.Admin == nil && collection.ListRule == nil {
|
||||||
// only admins can access if the rule is nil
|
// only admins can access if the rule is nil
|
||||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,9 @@ func (api *recordApi) list(c echo.Context) error {
|
||||||
fieldsResolver := resolvers.NewRecordFieldResolver(
|
fieldsResolver := resolvers.NewRecordFieldResolver(
|
||||||
api.app.Dao(),
|
api.app.Dao(),
|
||||||
collection,
|
collection,
|
||||||
requestData,
|
requestInfo,
|
||||||
// hidden fields are searchable only by admins
|
// hidden fields are searchable only by admins
|
||||||
requestData.Admin != nil,
|
requestInfo.Admin != nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
searchProvider := search.NewProvider(fieldsResolver).
|
searchProvider := search.NewProvider(fieldsResolver).
|
||||||
|
@ -73,7 +73,7 @@ func (api *recordApi) list(c echo.Context) error {
|
||||||
searchProvider.CountCol("id")
|
searchProvider.CountCol("id")
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestData.Admin == nil && collection.ListRule != nil {
|
if requestInfo.Admin == nil && collection.ListRule != nil {
|
||||||
searchProvider.AddFilter(search.FilterData(*collection.ListRule))
|
searchProvider.AddFilter(search.FilterData(*collection.ListRule))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,16 +110,16 @@ func (api *recordApi) view(c echo.Context) error {
|
||||||
return NewNotFoundError("", nil)
|
return NewNotFoundError("", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := RequestData(c)
|
requestInfo := RequestInfo(c)
|
||||||
|
|
||||||
if requestData.Admin == nil && collection.ViewRule == nil {
|
if requestInfo.Admin == nil && collection.ViewRule == nil {
|
||||||
// only admins can access if the rule is nil
|
// only admins can access if the rule is nil
|
||||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||||
if requestData.Admin == nil && collection.ViewRule != nil && *collection.ViewRule != "" {
|
if requestInfo.Admin == nil && collection.ViewRule != nil && *collection.ViewRule != "" {
|
||||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||||
expr, err := search.FilterData(*collection.ViewRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*collection.ViewRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,23 +155,23 @@ func (api *recordApi) create(c echo.Context) error {
|
||||||
return NewNotFoundError("", "Missing collection context.")
|
return NewNotFoundError("", "Missing collection context.")
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := RequestData(c)
|
requestInfo := RequestInfo(c)
|
||||||
|
|
||||||
if requestData.Admin == nil && collection.CreateRule == nil {
|
if requestInfo.Admin == nil && collection.CreateRule == nil {
|
||||||
// only admins can access if the rule is nil
|
// only admins can access if the rule is nil
|
||||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFullManageAccess := requestData.Admin != nil
|
hasFullManageAccess := requestInfo.Admin != nil
|
||||||
|
|
||||||
// temporary save the record and check it against the create rule
|
// temporary save the record and check it against the create rule
|
||||||
if requestData.Admin == nil && collection.CreateRule != nil {
|
if requestInfo.Admin == nil && collection.CreateRule != nil {
|
||||||
testRecord := models.NewRecord(collection)
|
testRecord := models.NewRecord(collection)
|
||||||
|
|
||||||
// replace modifiers fields so that the resolved value is always
|
// replace modifiers fields so that the resolved value is always
|
||||||
// available when accessing requestData.Data using just the field name
|
// available when accessing requestInfo.Data using just the field name
|
||||||
if requestData.HasModifierDataKeys() {
|
if requestInfo.HasModifierDataKeys() {
|
||||||
requestData.Data = testRecord.ReplaceModifers(requestData.Data)
|
requestInfo.Data = testRecord.ReplaceModifers(requestInfo.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
testForm := forms.NewRecordUpsert(api.app, testRecord)
|
testForm := forms.NewRecordUpsert(api.app, testRecord)
|
||||||
|
@ -185,7 +185,7 @@ func (api *recordApi) create(c echo.Context) error {
|
||||||
return nil // no create rule to resolve
|
return nil // no create rule to resolve
|
||||||
}
|
}
|
||||||
|
|
||||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||||
expr, err := search.FilterData(*collection.CreateRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*collection.CreateRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -200,7 +200,7 @@ func (api *recordApi) create(c echo.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("DrySubmit create rule failure: %w", err)
|
return fmt.Errorf("DrySubmit create rule failure: %w", err)
|
||||||
}
|
}
|
||||||
hasFullManageAccess = hasAuthManageAccess(txDao, foundRecord, requestData)
|
hasFullManageAccess = hasAuthManageAccess(txDao, foundRecord, requestInfo)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -259,26 +259,26 @@ func (api *recordApi) update(c echo.Context) error {
|
||||||
return NewNotFoundError("", nil)
|
return NewNotFoundError("", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := RequestData(c)
|
requestInfo := RequestInfo(c)
|
||||||
|
|
||||||
if requestData.Admin == nil && collection.UpdateRule == nil {
|
if requestInfo.Admin == nil && collection.UpdateRule == nil {
|
||||||
// only admins can access if the rule is nil
|
// only admins can access if the rule is nil
|
||||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eager fetch the record so that the modifier field values are replaced
|
// eager fetch the record so that the modifier field values are replaced
|
||||||
// and available when accessing requestData.Data using just the field name
|
// and available when accessing requestInfo.Data using just the field name
|
||||||
if requestData.HasModifierDataKeys() {
|
if requestInfo.HasModifierDataKeys() {
|
||||||
record, err := api.app.Dao().FindRecordById(collection.Id, recordId)
|
record, err := api.app.Dao().FindRecordById(collection.Id, recordId)
|
||||||
if err != nil || record == nil {
|
if err != nil || record == nil {
|
||||||
return NewNotFoundError("", err)
|
return NewNotFoundError("", err)
|
||||||
}
|
}
|
||||||
requestData.Data = record.ReplaceModifers(requestData.Data)
|
requestInfo.Data = record.ReplaceModifers(requestInfo.Data)
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||||
if requestData.Admin == nil && collection.UpdateRule != nil && *collection.UpdateRule != "" {
|
if requestInfo.Admin == nil && collection.UpdateRule != nil && *collection.UpdateRule != "" {
|
||||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||||
expr, err := search.FilterData(*collection.UpdateRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*collection.UpdateRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -296,7 +296,7 @@ func (api *recordApi) update(c echo.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
form := forms.NewRecordUpsert(api.app, record)
|
form := forms.NewRecordUpsert(api.app, record)
|
||||||
form.SetFullManageAccess(requestData.Admin != nil || hasAuthManageAccess(api.app.Dao(), record, requestData))
|
form.SetFullManageAccess(requestInfo.Admin != nil || hasAuthManageAccess(api.app.Dao(), record, requestInfo))
|
||||||
|
|
||||||
// load request
|
// load request
|
||||||
if err := form.LoadRequest(c.Request(), ""); err != nil {
|
if err := form.LoadRequest(c.Request(), ""); err != nil {
|
||||||
|
@ -344,16 +344,16 @@ func (api *recordApi) delete(c echo.Context) error {
|
||||||
return NewNotFoundError("", nil)
|
return NewNotFoundError("", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := RequestData(c)
|
requestInfo := RequestInfo(c)
|
||||||
|
|
||||||
if requestData.Admin == nil && collection.DeleteRule == nil {
|
if requestInfo.Admin == nil && collection.DeleteRule == nil {
|
||||||
// only admins can access if the rule is nil
|
// only admins can access if the rule is nil
|
||||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||||
if requestData.Admin == nil && collection.DeleteRule != nil && *collection.DeleteRule != "" {
|
if requestInfo.Admin == nil && collection.DeleteRule != nil && *collection.DeleteRule != "" {
|
||||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||||
expr, err := search.FilterData(*collection.DeleteRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*collection.DeleteRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -17,14 +17,20 @@ import (
|
||||||
"github.com/pocketbase/pocketbase/tools/search"
|
"github.com/pocketbase/pocketbase/tools/search"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ContextRequestDataKey = "requestData"
|
const ContextRequestInfoKey = "requestInfo"
|
||||||
|
|
||||||
// RequestData exports cached common request data fields
|
// Deprecated: Use RequestInfo instead.
|
||||||
|
func RequestData(c echo.Context) *models.RequestInfo {
|
||||||
|
log.Println("RequestInfo(c) is depracated and will be removed in the future! You can replace it with RequestInfo(c).")
|
||||||
|
return RequestInfo(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestInfo exports cached common request data fields
|
||||||
// (query, body, logged auth state, etc.) from the provided context.
|
// (query, body, logged auth state, etc.) from the provided context.
|
||||||
func RequestData(c echo.Context) *models.RequestData {
|
func RequestInfo(c echo.Context) *models.RequestInfo {
|
||||||
// return cached to avoid copying the body multiple times
|
// return cached to avoid copying the body multiple times
|
||||||
if v := c.Get(ContextRequestDataKey); v != nil {
|
if v := c.Get(ContextRequestInfoKey); v != nil {
|
||||||
if data, ok := v.(*models.RequestData); ok {
|
if data, ok := v.(*models.RequestInfo); ok {
|
||||||
// refresh auth state
|
// refresh auth state
|
||||||
data.AuthRecord, _ = c.Get(ContextAuthRecordKey).(*models.Record)
|
data.AuthRecord, _ = c.Get(ContextAuthRecordKey).(*models.Record)
|
||||||
data.Admin, _ = c.Get(ContextAdminKey).(*models.Admin)
|
data.Admin, _ = c.Get(ContextAdminKey).(*models.Admin)
|
||||||
|
@ -32,7 +38,7 @@ func RequestData(c echo.Context) *models.RequestData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := &models.RequestData{
|
result := &models.RequestInfo{
|
||||||
Method: c.Request().Method,
|
Method: c.Request().Method,
|
||||||
Query: map[string]any{},
|
Query: map[string]any{},
|
||||||
Data: map[string]any{},
|
Data: map[string]any{},
|
||||||
|
@ -52,7 +58,7 @@ func RequestData(c echo.Context) *models.RequestData {
|
||||||
echo.BindQueryParams(c, &result.Query)
|
echo.BindQueryParams(c, &result.Query)
|
||||||
rest.BindBody(c, &result.Data)
|
rest.BindBody(c, &result.Data)
|
||||||
|
|
||||||
c.Set(ContextRequestDataKey, result)
|
c.Set(ContextRequestInfoKey, result)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -86,13 +92,13 @@ func RecordAuthResponse(
|
||||||
expands := strings.Split(c.QueryParam(expandQueryParam), ",")
|
expands := strings.Split(c.QueryParam(expandQueryParam), ",")
|
||||||
if len(expands) > 0 {
|
if len(expands) > 0 {
|
||||||
// create a copy of the cached request data and adjust it to the current auth record
|
// create a copy of the cached request data and adjust it to the current auth record
|
||||||
requestData := *RequestData(e.HttpContext)
|
requestInfo := *RequestInfo(e.HttpContext)
|
||||||
requestData.Admin = nil
|
requestInfo.Admin = nil
|
||||||
requestData.AuthRecord = e.Record
|
requestInfo.AuthRecord = e.Record
|
||||||
failed := app.Dao().ExpandRecord(
|
failed := app.Dao().ExpandRecord(
|
||||||
e.Record,
|
e.Record,
|
||||||
expands,
|
expands,
|
||||||
expandFetch(app.Dao(), &requestData),
|
expandFetch(app.Dao(), &requestInfo),
|
||||||
)
|
)
|
||||||
if len(failed) > 0 && app.IsDebug() {
|
if len(failed) > 0 && app.IsDebug() {
|
||||||
log.Println("Failed to expand relations: ", failed)
|
log.Println("Failed to expand relations: ", failed)
|
||||||
|
@ -131,9 +137,9 @@ func EnrichRecord(c echo.Context, dao *daos.Dao, record *models.Record, defaultE
|
||||||
// - ensures that the emails of the auth records and their expanded auth relations
|
// - ensures that the emails of the auth records and their expanded auth relations
|
||||||
// are visibe only for the current logged admin, record owner or record with manage access
|
// are visibe only for the current logged admin, record owner or record with manage access
|
||||||
func EnrichRecords(c echo.Context, dao *daos.Dao, records []*models.Record, defaultExpands ...string) error {
|
func EnrichRecords(c echo.Context, dao *daos.Dao, records []*models.Record, defaultExpands ...string) error {
|
||||||
requestData := RequestData(c)
|
requestInfo := RequestInfo(c)
|
||||||
|
|
||||||
if err := autoIgnoreAuthRecordsEmailVisibility(dao, records, requestData); err != nil {
|
if err := autoIgnoreAuthRecordsEmailVisibility(dao, records, requestInfo); err != nil {
|
||||||
return fmt.Errorf("Failed to resolve email visibility: %w", err)
|
return fmt.Errorf("Failed to resolve email visibility: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +151,7 @@ func EnrichRecords(c echo.Context, dao *daos.Dao, records []*models.Record, defa
|
||||||
return nil // nothing to expand
|
return nil // nothing to expand
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := dao.ExpandRecords(records, expands, expandFetch(dao, requestData))
|
errs := dao.ExpandRecords(records, expands, expandFetch(dao, requestInfo))
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return fmt.Errorf("Failed to expand: %v", errs)
|
return fmt.Errorf("Failed to expand: %v", errs)
|
||||||
}
|
}
|
||||||
|
@ -156,11 +162,11 @@ func EnrichRecords(c echo.Context, dao *daos.Dao, records []*models.Record, defa
|
||||||
// expandFetch is the records fetch function that is used to expand related records.
|
// expandFetch is the records fetch function that is used to expand related records.
|
||||||
func expandFetch(
|
func expandFetch(
|
||||||
dao *daos.Dao,
|
dao *daos.Dao,
|
||||||
requestData *models.RequestData,
|
requestInfo *models.RequestInfo,
|
||||||
) daos.ExpandFetchFunc {
|
) daos.ExpandFetchFunc {
|
||||||
return func(relCollection *models.Collection, relIds []string) ([]*models.Record, error) {
|
return func(relCollection *models.Collection, relIds []string) ([]*models.Record, error) {
|
||||||
records, err := dao.FindRecordsByIds(relCollection.Id, relIds, func(q *dbx.SelectQuery) error {
|
records, err := dao.FindRecordsByIds(relCollection.Id, relIds, func(q *dbx.SelectQuery) error {
|
||||||
if requestData.Admin != nil {
|
if requestInfo.Admin != nil {
|
||||||
return nil // admins can access everything
|
return nil // admins can access everything
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +175,7 @@ func expandFetch(
|
||||||
}
|
}
|
||||||
|
|
||||||
if *relCollection.ViewRule != "" {
|
if *relCollection.ViewRule != "" {
|
||||||
resolver := resolvers.NewRecordFieldResolver(dao, relCollection, requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(dao, relCollection, requestInfo, true)
|
||||||
expr, err := search.FilterData(*(relCollection.ViewRule)).BuildExpr(resolver)
|
expr, err := search.FilterData(*(relCollection.ViewRule)).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -182,7 +188,7 @@ func expandFetch(
|
||||||
})
|
})
|
||||||
|
|
||||||
if err == nil && len(records) > 0 {
|
if err == nil && len(records) > 0 {
|
||||||
autoIgnoreAuthRecordsEmailVisibility(dao, records, requestData)
|
autoIgnoreAuthRecordsEmailVisibility(dao, records, requestInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
return records, err
|
return records, err
|
||||||
|
@ -196,13 +202,13 @@ func expandFetch(
|
||||||
func autoIgnoreAuthRecordsEmailVisibility(
|
func autoIgnoreAuthRecordsEmailVisibility(
|
||||||
dao *daos.Dao,
|
dao *daos.Dao,
|
||||||
records []*models.Record,
|
records []*models.Record,
|
||||||
requestData *models.RequestData,
|
requestInfo *models.RequestInfo,
|
||||||
) error {
|
) error {
|
||||||
if len(records) == 0 || !records[0].Collection().IsAuth() {
|
if len(records) == 0 || !records[0].Collection().IsAuth() {
|
||||||
return nil // nothing to check
|
return nil // nothing to check
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestData.Admin != nil {
|
if requestInfo.Admin != nil {
|
||||||
for _, rec := range records {
|
for _, rec := range records {
|
||||||
rec.IgnoreEmailVisibility(true)
|
rec.IgnoreEmailVisibility(true)
|
||||||
}
|
}
|
||||||
|
@ -218,8 +224,8 @@ func autoIgnoreAuthRecordsEmailVisibility(
|
||||||
recordIds[i] = rec.Id
|
recordIds[i] = rec.Id
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestData != nil && requestData.AuthRecord != nil && mappedRecords[requestData.AuthRecord.Id] != nil {
|
if requestInfo != nil && requestInfo.AuthRecord != nil && mappedRecords[requestInfo.AuthRecord.Id] != nil {
|
||||||
mappedRecords[requestData.AuthRecord.Id].IgnoreEmailVisibility(true)
|
mappedRecords[requestInfo.AuthRecord.Id].IgnoreEmailVisibility(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
authOptions := collection.AuthOptions()
|
authOptions := collection.AuthOptions()
|
||||||
|
@ -235,7 +241,7 @@ func autoIgnoreAuthRecordsEmailVisibility(
|
||||||
Select(dao.DB().QuoteSimpleColumnName(collection.Name) + ".id").
|
Select(dao.DB().QuoteSimpleColumnName(collection.Name) + ".id").
|
||||||
AndWhere(dbx.In(dao.DB().QuoteSimpleColumnName(collection.Name)+".id", recordIds...))
|
AndWhere(dbx.In(dao.DB().QuoteSimpleColumnName(collection.Name)+".id", recordIds...))
|
||||||
|
|
||||||
resolver := resolvers.NewRecordFieldResolver(dao, collection, requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(dao, collection, requestInfo, true)
|
||||||
expr, err := search.FilterData(*authOptions.ManageRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*authOptions.ManageRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -264,7 +270,7 @@ func autoIgnoreAuthRecordsEmailVisibility(
|
||||||
func hasAuthManageAccess(
|
func hasAuthManageAccess(
|
||||||
dao *daos.Dao,
|
dao *daos.Dao,
|
||||||
record *models.Record,
|
record *models.Record,
|
||||||
requestData *models.RequestData,
|
requestInfo *models.RequestInfo,
|
||||||
) bool {
|
) bool {
|
||||||
if !record.Collection().IsAuth() {
|
if !record.Collection().IsAuth() {
|
||||||
return false
|
return false
|
||||||
|
@ -276,12 +282,12 @@ func hasAuthManageAccess(
|
||||||
return false // only for admins (manageRule can't be empty)
|
return false // only for admins (manageRule can't be empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestData == nil || requestData.AuthRecord == nil {
|
if requestInfo == nil || requestInfo.AuthRecord == nil {
|
||||||
return false // no auth record
|
return false // no auth record
|
||||||
}
|
}
|
||||||
|
|
||||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||||
resolver := resolvers.NewRecordFieldResolver(dao, record.Collection(), requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(dao, record.Collection(), requestInfo, true)
|
||||||
expr, err := search.FilterData(*manageRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*manageRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/pocketbase/pocketbase/tests"
|
"github.com/pocketbase/pocketbase/tests"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRequestData(t *testing.T) {
|
func TestRequestInfo(t *testing.T) {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
req := httptest.NewRequest(http.MethodPost, "/?test=123", strings.NewReader(`{"test":456}`))
|
req := httptest.NewRequest(http.MethodPost, "/?test=123", strings.NewReader(`{"test":456}`))
|
||||||
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
||||||
|
@ -29,10 +29,10 @@ func TestRequestData(t *testing.T) {
|
||||||
dummyAdmin.Id = "id2"
|
dummyAdmin.Id = "id2"
|
||||||
c.Set(apis.ContextAdminKey, dummyAdmin)
|
c.Set(apis.ContextAdminKey, dummyAdmin)
|
||||||
|
|
||||||
result := apis.RequestData(c)
|
result := apis.RequestInfo(c)
|
||||||
|
|
||||||
if result == nil {
|
if result == nil {
|
||||||
t.Fatal("Expected *models.RequestData instance, got nil")
|
t.Fatal("Expected *models.RequestInfo instance, got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if result.Method != http.MethodPost {
|
if result.Method != http.MethodPost {
|
||||||
|
|
|
@ -520,9 +520,9 @@ func (dao *Dao) SuggestUniqueAuthRecordUsername(
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanAccessRecord checks if a record is allowed to be accessed by the
|
// CanAccessRecord checks if a record is allowed to be accessed by the
|
||||||
// specified requestData and accessRule.
|
// specified requestInfo and accessRule.
|
||||||
//
|
//
|
||||||
// Rule and db checks are ignored in case requestData.Admin is set.
|
// Rule and db checks are ignored in case requestInfo.Admin is set.
|
||||||
//
|
//
|
||||||
// The returned error indicate that something unexpected happened during
|
// The returned error indicate that something unexpected happened during
|
||||||
// the check (eg. invalid rule or db error).
|
// the check (eg. invalid rule or db error).
|
||||||
|
@ -531,14 +531,14 @@ func (dao *Dao) SuggestUniqueAuthRecordUsername(
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// requestData := apis.RequestData(c /* echo.Context */)
|
// requestInfo := apis.RequestInfo(c /* echo.Context */)
|
||||||
// record, _ := dao.FindRecordById("example", "RECORD_ID")
|
// record, _ := dao.FindRecordById("example", "RECORD_ID")
|
||||||
// rule := types.Pointer("@request.auth.id != '' || status = 'public'")
|
// rule := types.Pointer("@request.auth.id != '' || status = 'public'")
|
||||||
// // ... or use one of the record collection's rule, eg. record.Collection().ViewRule
|
// // ... or use one of the record collection's rule, eg. record.Collection().ViewRule
|
||||||
//
|
//
|
||||||
// if ok, _ := dao.CanAccessRecord(record, requestData, rule); ok { ... }
|
// if ok, _ := dao.CanAccessRecord(record, requestInfo, rule); ok { ... }
|
||||||
func (dao *Dao) CanAccessRecord(record *models.Record, requestData *models.RequestData, accessRule *string) (bool, error) {
|
func (dao *Dao) CanAccessRecord(record *models.Record, requestInfo *models.RequestInfo, accessRule *string) (bool, error) {
|
||||||
if requestData.Admin != nil {
|
if requestInfo.Admin != nil {
|
||||||
// admins can access everything
|
// admins can access everything
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -560,7 +560,7 @@ func (dao *Dao) CanAccessRecord(record *models.Record, requestData *models.Reque
|
||||||
AndWhere(dbx.HashExp{record.Collection().Name + ".id": record.Id})
|
AndWhere(dbx.HashExp{record.Collection().Name + ".id": record.Id})
|
||||||
|
|
||||||
// parse and apply the access rule filter
|
// parse and apply the access rule filter
|
||||||
resolver := resolvers.NewRecordFieldResolver(dao, record.Collection(), requestData, true)
|
resolver := resolvers.NewRecordFieldResolver(dao, record.Collection(), requestInfo, true)
|
||||||
expr, err := search.FilterData(*accessRule).BuildExpr(resolver)
|
expr, err := search.FilterData(*accessRule).BuildExpr(resolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
@ -625,7 +625,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
record *models.Record
|
record *models.Record
|
||||||
requestData *models.RequestData
|
requestInfo *models.RequestInfo
|
||||||
rule *string
|
rule *string
|
||||||
expected bool
|
expected bool
|
||||||
expectError bool
|
expectError bool
|
||||||
|
@ -633,7 +633,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as admin with nil rule",
|
"as admin with nil rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Admin: admin,
|
Admin: admin,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -643,7 +643,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as admin with non-empty rule",
|
"as admin with non-empty rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Admin: admin,
|
Admin: admin,
|
||||||
},
|
},
|
||||||
types.Pointer("id = ''"), // the filter rule should be ignored
|
types.Pointer("id = ''"), // the filter rule should be ignored
|
||||||
|
@ -653,7 +653,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as admin with invalid rule",
|
"as admin with invalid rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Admin: admin,
|
Admin: admin,
|
||||||
},
|
},
|
||||||
types.Pointer("id ?!@ 1"), // the filter rule should be ignored
|
types.Pointer("id ?!@ 1"), // the filter rule should be ignored
|
||||||
|
@ -663,7 +663,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as guest with nil rule",
|
"as guest with nil rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{},
|
&models.RequestInfo{},
|
||||||
nil,
|
nil,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -671,7 +671,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as guest with empty rule",
|
"as guest with empty rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{},
|
&models.RequestInfo{},
|
||||||
types.Pointer(""),
|
types.Pointer(""),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
|
@ -679,7 +679,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as guest with invalid rule",
|
"as guest with invalid rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{},
|
&models.RequestInfo{},
|
||||||
types.Pointer("id ?!@ 1"),
|
types.Pointer("id ?!@ 1"),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
@ -687,7 +687,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as guest with mismatched rule",
|
"as guest with mismatched rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{},
|
&models.RequestInfo{},
|
||||||
types.Pointer("@request.auth.id != ''"),
|
types.Pointer("@request.auth.id != ''"),
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -695,7 +695,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as guest with matched rule",
|
"as guest with matched rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Data: map[string]any{"test": 1},
|
Data: map[string]any{"test": 1},
|
||||||
},
|
},
|
||||||
types.Pointer("@request.auth.id != '' || @request.data.test = 1"),
|
types.Pointer("@request.auth.id != '' || @request.data.test = 1"),
|
||||||
|
@ -705,7 +705,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as auth record with nil rule",
|
"as auth record with nil rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -715,7 +715,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as auth record with empty rule",
|
"as auth record with empty rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
},
|
},
|
||||||
types.Pointer(""),
|
types.Pointer(""),
|
||||||
|
@ -725,7 +725,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as auth record with invalid rule",
|
"as auth record with invalid rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
},
|
},
|
||||||
types.Pointer("id ?!@ 1"),
|
types.Pointer("id ?!@ 1"),
|
||||||
|
@ -735,7 +735,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as auth record with mismatched rule",
|
"as auth record with mismatched rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
Data: map[string]any{"test": 1},
|
Data: map[string]any{"test": 1},
|
||||||
},
|
},
|
||||||
|
@ -746,7 +746,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
{
|
{
|
||||||
"as auth record with matched rule",
|
"as auth record with matched rule",
|
||||||
record,
|
record,
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
Data: map[string]any{"test": 2},
|
Data: map[string]any{"test": 2},
|
||||||
},
|
},
|
||||||
|
@ -757,7 +757,7 @@ func TestCanAccessRecord(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
result, err := app.Dao().CanAccessRecord(s.record, s.requestData, s.rule)
|
result, err := app.Dao().CanAccessRecord(s.record, s.requestInfo, s.rule)
|
||||||
|
|
||||||
if result != s.expected {
|
if result != s.expected {
|
||||||
t.Errorf("[%s] Expected %v, got %v", s.name, s.expected, result)
|
t.Errorf("[%s] Expected %v, got %v", s.name, s.expected, result)
|
||||||
|
|
|
@ -117,7 +117,7 @@ func (form *RecordUpsert) getContentType(r *http.Request) string {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
func (form *RecordUpsert) extractRequestData(
|
func (form *RecordUpsert) extractRequestInfo(
|
||||||
r *http.Request,
|
r *http.Request,
|
||||||
keyPrefix string,
|
keyPrefix string,
|
||||||
) (map[string]any, map[string][]*filesystem.File, error) {
|
) (map[string]any, map[string][]*filesystem.File, error) {
|
||||||
|
@ -219,12 +219,12 @@ func (form *RecordUpsert) extractMultipartFormData(
|
||||||
//
|
//
|
||||||
// File upload is supported only via multipart/form-data.
|
// File upload is supported only via multipart/form-data.
|
||||||
func (form *RecordUpsert) LoadRequest(r *http.Request, keyPrefix string) error {
|
func (form *RecordUpsert) LoadRequest(r *http.Request, keyPrefix string) error {
|
||||||
requestData, uploadedFiles, err := form.extractRequestData(r, keyPrefix)
|
requestInfo, uploadedFiles, err := form.extractRequestInfo(r, keyPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := form.LoadData(requestData); err != nil {
|
if err := form.LoadData(requestInfo); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,39 +349,39 @@ func (form *RecordUpsert) RemoveFiles(key string, toDelete ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadData loads and normalizes the provided regular record data fields into the form.
|
// LoadData loads and normalizes the provided regular record data fields into the form.
|
||||||
func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
func (form *RecordUpsert) LoadData(requestInfo map[string]any) error {
|
||||||
// load base system fields
|
// load base system fields
|
||||||
if v, ok := requestData[schema.FieldNameId]; ok {
|
if v, ok := requestInfo[schema.FieldNameId]; ok {
|
||||||
form.Id = cast.ToString(v)
|
form.Id = cast.ToString(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// load auth system fields
|
// load auth system fields
|
||||||
if form.record.Collection().IsAuth() {
|
if form.record.Collection().IsAuth() {
|
||||||
if v, ok := requestData[schema.FieldNameUsername]; ok {
|
if v, ok := requestInfo[schema.FieldNameUsername]; ok {
|
||||||
form.Username = cast.ToString(v)
|
form.Username = cast.ToString(v)
|
||||||
}
|
}
|
||||||
if v, ok := requestData[schema.FieldNameEmail]; ok {
|
if v, ok := requestInfo[schema.FieldNameEmail]; ok {
|
||||||
form.Email = cast.ToString(v)
|
form.Email = cast.ToString(v)
|
||||||
}
|
}
|
||||||
if v, ok := requestData[schema.FieldNameEmailVisibility]; ok {
|
if v, ok := requestInfo[schema.FieldNameEmailVisibility]; ok {
|
||||||
form.EmailVisibility = cast.ToBool(v)
|
form.EmailVisibility = cast.ToBool(v)
|
||||||
}
|
}
|
||||||
if v, ok := requestData[schema.FieldNameVerified]; ok {
|
if v, ok := requestInfo[schema.FieldNameVerified]; ok {
|
||||||
form.Verified = cast.ToBool(v)
|
form.Verified = cast.ToBool(v)
|
||||||
}
|
}
|
||||||
if v, ok := requestData["password"]; ok {
|
if v, ok := requestInfo["password"]; ok {
|
||||||
form.Password = cast.ToString(v)
|
form.Password = cast.ToString(v)
|
||||||
}
|
}
|
||||||
if v, ok := requestData["passwordConfirm"]; ok {
|
if v, ok := requestInfo["passwordConfirm"]; ok {
|
||||||
form.PasswordConfirm = cast.ToString(v)
|
form.PasswordConfirm = cast.ToString(v)
|
||||||
}
|
}
|
||||||
if v, ok := requestData["oldPassword"]; ok {
|
if v, ok := requestInfo["oldPassword"]; ok {
|
||||||
form.OldPassword = cast.ToString(v)
|
form.OldPassword = cast.ToString(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace modifiers (if any)
|
// replace modifiers (if any)
|
||||||
requestData = form.record.ReplaceModifers(requestData)
|
requestInfo = form.record.ReplaceModifers(requestInfo)
|
||||||
|
|
||||||
// create a shallow copy of form.data
|
// create a shallow copy of form.data
|
||||||
var extendedData = make(map[string]any, len(form.data))
|
var extendedData = make(map[string]any, len(form.data))
|
||||||
|
@ -390,7 +390,7 @@ func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// extend form.data with the request data
|
// extend form.data with the request data
|
||||||
rawData, err := json.Marshal(requestData)
|
rawData, err := json.Marshal(requestInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,11 @@ import (
|
||||||
"github.com/pocketbase/pocketbase/models/schema"
|
"github.com/pocketbase/pocketbase/models/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RequestData defines a HTTP request data struct, usually used
|
// RequestInfo defines a HTTP request data struct, usually used
|
||||||
// as part of the `@request.*` filter resolver.
|
// as part of the `@request.*` filter resolver.
|
||||||
type RequestData struct {
|
type RequestInfo struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Query map[string]any `json:"query"`
|
Query map[string]any `json:"query"`
|
||||||
// @todo consider changing to Body?
|
|
||||||
Data map[string]any `json:"data"`
|
Data map[string]any `json:"data"`
|
||||||
Headers map[string]any `json:"headers"`
|
Headers map[string]any `json:"headers"`
|
||||||
AuthRecord *Record `json:"authRecord"`
|
AuthRecord *Record `json:"authRecord"`
|
||||||
|
@ -19,7 +18,7 @@ type RequestData struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasModifierDataKeys loosely checks if the current struct has any modifier Data keys.
|
// HasModifierDataKeys loosely checks if the current struct has any modifier Data keys.
|
||||||
func (r *RequestData) HasModifierDataKeys() bool {
|
func (r *RequestInfo) HasModifierDataKeys() bool {
|
||||||
allModifiers := schema.FieldValueModifiers()
|
allModifiers := schema.FieldValueModifiers()
|
||||||
|
|
||||||
for key := range r.Data {
|
for key := range r.Data {
|
|
@ -6,20 +6,20 @@ import (
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
func TestRequestInfoHasModifierDataKeys(t *testing.T) {
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
name string
|
name string
|
||||||
requestData *models.RequestData
|
requestInfo *models.RequestInfo
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"empty",
|
"empty",
|
||||||
&models.RequestData{},
|
&models.RequestInfo{},
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Data with regular fields",
|
"Data with regular fields",
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Query: map[string]any{"data+": "demo"}, // should be ignored
|
Query: map[string]any{"data+": "demo"}, // should be ignored
|
||||||
Data: map[string]any{"a": 123, "b": "test", "c.d": false},
|
Data: map[string]any{"a": 123, "b": "test", "c.d": false},
|
||||||
},
|
},
|
||||||
|
@ -27,21 +27,21 @@ func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Data with +modifier fields",
|
"Data with +modifier fields",
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Data: map[string]any{"a+": 123, "b": "test", "c.d": false},
|
Data: map[string]any{"a+": 123, "b": "test", "c.d": false},
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Data with -modifier fields",
|
"Data with -modifier fields",
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Data: map[string]any{"a": 123, "b-": "test", "c.d": false},
|
Data: map[string]any{"a": 123, "b-": "test", "c.d": false},
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Data with mixed modifier fields",
|
"Data with mixed modifier fields",
|
||||||
&models.RequestData{
|
&models.RequestInfo{
|
||||||
Data: map[string]any{"a": 123, "b-": "test", "c.d+": false},
|
Data: map[string]any{"a": 123, "b-": "test", "c.d+": false},
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
|
@ -49,7 +49,7 @@ func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
result := s.requestData.HasModifierDataKeys()
|
result := s.requestInfo.HasModifierDataKeys()
|
||||||
|
|
||||||
if result != s.expected {
|
if result != s.expected {
|
||||||
t.Fatalf("[%s] Expected %v, got %v", s.name, s.expected, result)
|
t.Fatalf("[%s] Expected %v, got %v", s.name, s.expected, result)
|
|
@ -319,6 +319,25 @@ func baseBinds(vm *goja.Runtime) {
|
||||||
return structConstructor(vm, call, instance)
|
return structConstructor(vm, call, instance)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
vm.Set("RequestInfo", func(call goja.ConstructorCall) *goja.Object {
|
||||||
|
instance := &models.RequestInfo{}
|
||||||
|
return structConstructor(vm, call, instance)
|
||||||
|
})
|
||||||
|
|
||||||
|
vm.Set("DateTime", func(call goja.ConstructorCall) *goja.Object {
|
||||||
|
instance := types.NowDateTime()
|
||||||
|
|
||||||
|
val, _ := call.Argument(0).Export().(string)
|
||||||
|
if val != "" {
|
||||||
|
instance, _ = types.ParseDateTime(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
instanceValue := vm.ToValue(instance).(*goja.Object)
|
||||||
|
instanceValue.SetPrototype(call.This.Prototype())
|
||||||
|
|
||||||
|
return structConstructor(vm, call, instance)
|
||||||
|
})
|
||||||
|
|
||||||
vm.Set("ValidationError", func(call goja.ConstructorCall) *goja.Object {
|
vm.Set("ValidationError", func(call goja.ConstructorCall) *goja.Object {
|
||||||
code, _ := call.Argument(0).Export().(string)
|
code, _ := call.Argument(0).Export().(string)
|
||||||
message, _ := call.Argument(1).Export().(string)
|
message, _ := call.Argument(1).Export().(string)
|
||||||
|
@ -462,7 +481,7 @@ func apisBinds(vm *goja.Runtime) {
|
||||||
obj.Set("activityLogger", apis.ActivityLogger)
|
obj.Set("activityLogger", apis.ActivityLogger)
|
||||||
|
|
||||||
// record helpers
|
// record helpers
|
||||||
obj.Set("requestData", apis.RequestData)
|
obj.Set("requestInfo", apis.RequestInfo)
|
||||||
obj.Set("recordAuthResponse", apis.RecordAuthResponse)
|
obj.Set("recordAuthResponse", apis.RecordAuthResponse)
|
||||||
obj.Set("enrichRecord", apis.EnrichRecord)
|
obj.Set("enrichRecord", apis.EnrichRecord)
|
||||||
obj.Set("enrichRecords", apis.EnrichRecords)
|
obj.Set("enrichRecords", apis.EnrichRecords)
|
||||||
|
|
|
@ -45,7 +45,7 @@ func TestBaseBindsCount(t *testing.T) {
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
baseBinds(vm)
|
baseBinds(vm)
|
||||||
|
|
||||||
testBindsCount(vm, "this", 14, t)
|
testBindsCount(vm, "this", 16, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBaseBindsRecord(t *testing.T) {
|
func TestBaseBindsRecord(t *testing.T) {
|
||||||
|
@ -248,6 +248,50 @@ func TestBaseBindsCommand(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBaseBindsRequestInfo(t *testing.T) {
|
||||||
|
vm := goja.New()
|
||||||
|
baseBinds(vm)
|
||||||
|
|
||||||
|
_, err := vm.RunString(`
|
||||||
|
let info = new RequestInfo({
|
||||||
|
admin: new Admin({id: "test1"}),
|
||||||
|
data: {"name": "test2"}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (info.admin?.id != "test1") {
|
||||||
|
throw new Error('Expected info.admin.id to be test1, got: ' + info.admin?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.data?.name != "test2") {
|
||||||
|
throw new Error('Expected info.data.name to be test2, got: ' + info.data?.name);
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBaseBindsDateTime(t *testing.T) {
|
||||||
|
vm := goja.New()
|
||||||
|
baseBinds(vm)
|
||||||
|
|
||||||
|
_, err := vm.RunString(`
|
||||||
|
const v0 = new DateTime();
|
||||||
|
if (v0.isZero()) {
|
||||||
|
throw new Error('Expected to fallback to now, got zero value');
|
||||||
|
}
|
||||||
|
|
||||||
|
const v1 = new DateTime('2023-01-01 00:00:00.000Z');
|
||||||
|
const expected = "2023-01-01 00:00:00.000Z"
|
||||||
|
if (v1.string() != expected) {
|
||||||
|
throw new Error('Expected ' + expected + ', got ', v1.string());
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBaseBindsValidationError(t *testing.T) {
|
func TestBaseBindsValidationError(t *testing.T) {
|
||||||
vm := goja.New()
|
vm := goja.New()
|
||||||
baseBinds(vm)
|
baseBinds(vm)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -308,6 +308,8 @@ interface Command extends cobra.Command{} // merge
|
||||||
/**
|
/**
|
||||||
* Command defines a single console command.
|
* Command defines a single console command.
|
||||||
*
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
* ` + "```" + `js
|
* ` + "```" + `js
|
||||||
* const command = new Command({
|
* const command = new Command({
|
||||||
* use: "hello",
|
* use: "hello",
|
||||||
|
@ -323,6 +325,51 @@ declare class Command implements cobra.Command {
|
||||||
constructor(cmd?: Partial<cobra.Command>)
|
constructor(cmd?: Partial<cobra.Command>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RequestInfo extends models.RequestInfo{} // merge
|
||||||
|
/**
|
||||||
|
* RequestInfo defines a single models.RequestInfo instance, usually used
|
||||||
|
* as part of various filter checks.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ` + "```" + `js
|
||||||
|
* const authRecord = $app.dao().findAuthRecordByEmail("users", "test@example.com")
|
||||||
|
*
|
||||||
|
* const info = new RequestInfo({
|
||||||
|
* authRecord: authRecord,
|
||||||
|
* data: {"name": 123},
|
||||||
|
* headers: {"x-token": "..."},
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* const record = $app.dao().findFirstRecordByData("articles", "slug", "hello")
|
||||||
|
*
|
||||||
|
* const canAccess = $app.dao().canAccessRecord(record, info, "@request.auth.id != '' && @request.data.name = 123")
|
||||||
|
* ` + "```" + `
|
||||||
|
*
|
||||||
|
* @group PocketBase
|
||||||
|
*/
|
||||||
|
declare class RequestInfo implements models.RequestInfo {
|
||||||
|
constructor(date?: Partial<models.RequestInfo>)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DateTime extends types.DateTime{} // merge
|
||||||
|
/**
|
||||||
|
* DateTime defines a single DateTime type instance.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* ` + "```" + `js
|
||||||
|
* const dt0 = new DateTime() // now
|
||||||
|
*
|
||||||
|
* const dt1 = new DateTime('2023-07-01 00:00:00.000Z')
|
||||||
|
* ` + "```" + `
|
||||||
|
*
|
||||||
|
* @group PocketBase
|
||||||
|
*/
|
||||||
|
declare class DateTime implements types.DateTime {
|
||||||
|
constructor(date?: string)
|
||||||
|
}
|
||||||
|
|
||||||
interface ValidationError extends ozzo_validation.Error{} // merge
|
interface ValidationError extends ozzo_validation.Error{} // merge
|
||||||
/**
|
/**
|
||||||
* ValidationError defines a single formatted data validation error,
|
* ValidationError defines a single formatted data validation error,
|
||||||
|
@ -690,7 +737,7 @@ declare namespace $apis {
|
||||||
let requireAdminOrRecordAuth: apis.requireAdminOrRecordAuth
|
let requireAdminOrRecordAuth: apis.requireAdminOrRecordAuth
|
||||||
let requireAdminOrOwnerAuth: apis.requireAdminOrOwnerAuth
|
let requireAdminOrOwnerAuth: apis.requireAdminOrOwnerAuth
|
||||||
let activityLogger: apis.activityLogger
|
let activityLogger: apis.activityLogger
|
||||||
let requestData: apis.requestData
|
let requestInfo: apis.requestInfo
|
||||||
let recordAuthResponse: apis.recordAuthResponse
|
let recordAuthResponse: apis.recordAuthResponse
|
||||||
let enrichRecord: apis.enrichRecord
|
let enrichRecord: apis.enrichRecord
|
||||||
let enrichRecords: apis.enrichRecords
|
let enrichRecords: apis.enrichRecords
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (r *runner) run() (*search.ResolverResult, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.activeProps[0] == "@request" {
|
if r.activeProps[0] == "@request" {
|
||||||
if r.resolver.requestData == nil {
|
if r.resolver.requestInfo == nil {
|
||||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,17 +87,17 @@ func (r *runner) run() (*search.ResolverResult, error) {
|
||||||
|
|
||||||
// check for data relation field
|
// check for data relation field
|
||||||
if dataField.Type == schema.FieldTypeRelation && len(r.activeProps) > 3 {
|
if dataField.Type == schema.FieldTypeRelation && len(r.activeProps) > 3 {
|
||||||
return r.processRequestDataRelationField(dataField)
|
return r.processRequestInfoRelationField(dataField)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for select:each field
|
// check for select:each field
|
||||||
if modifier == eachModifier && dataField.Type == schema.FieldTypeSelect && len(r.activeProps) == 3 {
|
if modifier == eachModifier && dataField.Type == schema.FieldTypeSelect && len(r.activeProps) == 3 {
|
||||||
return r.processRequestDataSelectEachModifier(dataField)
|
return r.processRequestInfoSelectEachModifier(dataField)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for data arrayble fields ":length" modifier
|
// check for data arrayble fields ":length" modifier
|
||||||
if modifier == lengthModifier && list.ExistInSlice(dataField.Type, schema.ArraybleFieldTypes()) && len(r.activeProps) == 3 {
|
if modifier == lengthModifier && list.ExistInSlice(dataField.Type, schema.ArraybleFieldTypes()) && len(r.activeProps) == 3 {
|
||||||
return r.processRequestDataLengthModifier(dataField)
|
return r.processRequestInfoLengthModifier(dataField)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,11 +177,11 @@ func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
|
||||||
|
|
||||||
// resolve the auth collection field
|
// resolve the auth collection field
|
||||||
// ---
|
// ---
|
||||||
if r.resolver.requestData == nil || r.resolver.requestData.AuthRecord == nil || r.resolver.requestData.AuthRecord.Collection() == nil {
|
if r.resolver.requestInfo == nil || r.resolver.requestInfo.AuthRecord == nil || r.resolver.requestInfo.AuthRecord.Collection() == nil {
|
||||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
collection := r.resolver.requestData.AuthRecord.Collection()
|
collection := r.resolver.requestInfo.AuthRecord.Collection()
|
||||||
r.resolver.loadedCollections = append(r.resolver.loadedCollections, collection)
|
r.resolver.loadedCollections = append(r.resolver.loadedCollections, collection)
|
||||||
|
|
||||||
r.activeCollectionName = collection.Name
|
r.activeCollectionName = collection.Name
|
||||||
|
@ -193,7 +193,7 @@ func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
|
||||||
r.activeTableAlias,
|
r.activeTableAlias,
|
||||||
dbx.HashExp{
|
dbx.HashExp{
|
||||||
// aka. __auth_users.id = :userId
|
// aka. __auth_users.id = :userId
|
||||||
(r.activeTableAlias + ".id"): r.resolver.requestData.AuthRecord.Id,
|
(r.activeTableAlias + ".id"): r.resolver.requestInfo.AuthRecord.Id,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
|
||||||
tableName: inflector.Columnify(r.activeCollectionName),
|
tableName: inflector.Columnify(r.activeCollectionName),
|
||||||
tableAlias: r.multiMatchActiveTableAlias,
|
tableAlias: r.multiMatchActiveTableAlias,
|
||||||
on: dbx.HashExp{
|
on: dbx.HashExp{
|
||||||
(r.multiMatchActiveTableAlias + ".id"): r.resolver.requestData.AuthRecord.Id,
|
(r.multiMatchActiveTableAlias + ".id"): r.resolver.requestInfo.AuthRecord.Id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -217,8 +217,8 @@ func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
|
||||||
return r.processActiveProps()
|
return r.processActiveProps()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runner) processRequestDataLengthModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
func (r *runner) processRequestInfoLengthModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||||
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
dataItems := list.ToUniqueStringSlice(r.resolver.requestInfo.Data[dataField.Name])
|
||||||
|
|
||||||
result := &search.ResolverResult{
|
result := &search.ResolverResult{
|
||||||
Identifier: fmt.Sprintf("%d", len(dataItems)),
|
Identifier: fmt.Sprintf("%d", len(dataItems)),
|
||||||
|
@ -227,13 +227,13 @@ func (r *runner) processRequestDataLengthModifier(dataField *schema.SchemaField)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runner) processRequestDataSelectEachModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
func (r *runner) processRequestInfoSelectEachModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||||
options, ok := dataField.Options.(*schema.SelectOptions)
|
options, ok := dataField.Options.(*schema.SelectOptions)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("failed to initialize field %q options", dataField.Name)
|
return nil, fmt.Errorf("failed to initialize field %q options", dataField.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
dataItems := list.ToUniqueStringSlice(r.resolver.requestInfo.Data[dataField.Name])
|
||||||
rawJson, err := json.Marshal(dataItems)
|
rawJson, err := json.Marshal(dataItems)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("cannot marshalize the data select item for field %q", r.activeProps[2])
|
return nil, fmt.Errorf("cannot marshalize the data select item for field %q", r.activeProps[2])
|
||||||
|
@ -272,7 +272,7 @@ func (r *runner) processRequestDataSelectEachModifier(dataField *schema.SchemaFi
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *runner) processRequestDataRelationField(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
func (r *runner) processRequestInfoRelationField(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||||
options, ok := dataField.Options.(*schema.RelationOptions)
|
options, ok := dataField.Options.(*schema.RelationOptions)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("failed to initialize data field %q options", dataField.Name)
|
return nil, fmt.Errorf("failed to initialize data field %q options", dataField.Name)
|
||||||
|
@ -284,8 +284,8 @@ func (r *runner) processRequestDataRelationField(dataField *schema.SchemaField)
|
||||||
}
|
}
|
||||||
|
|
||||||
var dataRelIds []string
|
var dataRelIds []string
|
||||||
if r.resolver.requestData != nil && len(r.resolver.requestData.Data) != 0 {
|
if r.resolver.requestInfo != nil && len(r.resolver.requestInfo.Data) != 0 {
|
||||||
dataRelIds = list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
dataRelIds = list.ToUniqueStringSlice(r.resolver.requestInfo.Data[dataField.Name])
|
||||||
}
|
}
|
||||||
if len(dataRelIds) == 0 {
|
if len(dataRelIds) == 0 {
|
||||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||||
|
|
|
@ -56,7 +56,7 @@ type CollectionsFinder interface {
|
||||||
// resolver := resolvers.NewRecordFieldResolver(
|
// resolver := resolvers.NewRecordFieldResolver(
|
||||||
// app.Dao(),
|
// app.Dao(),
|
||||||
// myCollection,
|
// myCollection,
|
||||||
// &models.RequestData{...},
|
// &models.RequestInfo{...},
|
||||||
// true,
|
// true,
|
||||||
// )
|
// )
|
||||||
// provider := search.NewProvider(resolver)
|
// provider := search.NewProvider(resolver)
|
||||||
|
@ -68,21 +68,21 @@ type RecordFieldResolver struct {
|
||||||
allowedFields []string
|
allowedFields []string
|
||||||
loadedCollections []*models.Collection
|
loadedCollections []*models.Collection
|
||||||
joins []*join // we cannot use a map because the insertion order is not preserved
|
joins []*join // we cannot use a map because the insertion order is not preserved
|
||||||
requestData *models.RequestData
|
requestInfo *models.RequestInfo
|
||||||
staticRequestData map[string]any
|
staticRequestInfo map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRecordFieldResolver creates and initializes a new `RecordFieldResolver`.
|
// NewRecordFieldResolver creates and initializes a new `RecordFieldResolver`.
|
||||||
func NewRecordFieldResolver(
|
func NewRecordFieldResolver(
|
||||||
dao CollectionsFinder,
|
dao CollectionsFinder,
|
||||||
baseCollection *models.Collection,
|
baseCollection *models.Collection,
|
||||||
requestData *models.RequestData,
|
requestInfo *models.RequestInfo,
|
||||||
allowHiddenFields bool,
|
allowHiddenFields bool,
|
||||||
) *RecordFieldResolver {
|
) *RecordFieldResolver {
|
||||||
r := &RecordFieldResolver{
|
r := &RecordFieldResolver{
|
||||||
dao: dao,
|
dao: dao,
|
||||||
baseCollection: baseCollection,
|
baseCollection: baseCollection,
|
||||||
requestData: requestData,
|
requestInfo: requestInfo,
|
||||||
allowHiddenFields: allowHiddenFields,
|
allowHiddenFields: allowHiddenFields,
|
||||||
joins: []*join{},
|
joins: []*join{},
|
||||||
loadedCollections: []*models.Collection{baseCollection},
|
loadedCollections: []*models.Collection{baseCollection},
|
||||||
|
@ -97,17 +97,17 @@ func NewRecordFieldResolver(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
r.staticRequestData = map[string]any{}
|
r.staticRequestInfo = map[string]any{}
|
||||||
if r.requestData != nil {
|
if r.requestInfo != nil {
|
||||||
r.staticRequestData["method"] = r.requestData.Method
|
r.staticRequestInfo["method"] = r.requestInfo.Method
|
||||||
r.staticRequestData["query"] = r.requestData.Query
|
r.staticRequestInfo["query"] = r.requestInfo.Query
|
||||||
r.staticRequestData["headers"] = r.requestData.Headers
|
r.staticRequestInfo["headers"] = r.requestInfo.Headers
|
||||||
r.staticRequestData["data"] = r.requestData.Data
|
r.staticRequestInfo["data"] = r.requestInfo.Data
|
||||||
r.staticRequestData["auth"] = nil
|
r.staticRequestInfo["auth"] = nil
|
||||||
if r.requestData.AuthRecord != nil {
|
if r.requestInfo.AuthRecord != nil {
|
||||||
r.requestData.AuthRecord.IgnoreEmailVisibility(true)
|
r.requestInfo.AuthRecord.IgnoreEmailVisibility(true)
|
||||||
r.staticRequestData["auth"] = r.requestData.AuthRecord.PublicExport()
|
r.staticRequestInfo["auth"] = r.requestInfo.AuthRecord.PublicExport()
|
||||||
r.requestData.AuthRecord.IgnoreEmailVisibility(false)
|
r.requestInfo.AuthRecord.IgnoreEmailVisibility(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +166,7 @@ func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (*search
|
||||||
path[len(path)-1] = lastProp
|
path[len(path)-1] = lastProp
|
||||||
|
|
||||||
// extract value
|
// extract value
|
||||||
resultVal, err := extractNestedMapVal(r.staticRequestData, path...)
|
resultVal, err := extractNestedMapVal(r.staticRequestInfo, path...)
|
||||||
|
|
||||||
if modifier == issetModifier {
|
if modifier == issetModifier {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -175,7 +175,7 @@ func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (*search
|
||||||
return &search.ResolverResult{Identifier: "TRUE"}, nil
|
return &search.ResolverResult{Identifier: "TRUE"}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: we are ignoring the error because requestData is dynamic
|
// note: we are ignoring the error because requestInfo is dynamic
|
||||||
// and some of the lookup keys may not be defined for the request
|
// and some of the lookup keys may not be defined for the request
|
||||||
|
|
||||||
switch v := resultVal.(type) {
|
switch v := resultVal.(type) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := &models.RequestData{
|
requestInfo := &models.RequestInfo{
|
||||||
Headers: map[string]any{
|
Headers: map[string]any{
|
||||||
"a": "123",
|
"a": "123",
|
||||||
"b": "456",
|
"b": "456",
|
||||||
|
@ -313,7 +313,7 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
|
||||||
|
|
||||||
query := app.Dao().RecordQuery(collection)
|
query := app.Dao().RecordQuery(collection)
|
||||||
|
|
||||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestData, s.allowHiddenFields)
|
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestInfo, s.allowHiddenFields)
|
||||||
|
|
||||||
expr, err := search.FilterData(s.rule).BuildExpr(r)
|
expr, err := search.FilterData(s.rule).BuildExpr(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -353,11 +353,11 @@ func TestRecordFieldResolverResolveSchemaFields(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := &models.RequestData{
|
requestInfo := &models.RequestInfo{
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestData, true)
|
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestInfo, true)
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
fieldName string
|
fieldName string
|
||||||
|
@ -424,7 +424,7 @@ func TestRecordFieldResolverResolveSchemaFields(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
|
func TestRecordFieldResolverResolveStaticRequestInfoFields(t *testing.T) {
|
||||||
app, _ := tests.NewTestApp()
|
app, _ := tests.NewTestApp()
|
||||||
defer app.Cleanup()
|
defer app.Cleanup()
|
||||||
|
|
||||||
|
@ -438,7 +438,7 @@ func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData := &models.RequestData{
|
requestInfo := &models.RequestInfo{
|
||||||
Method: "get",
|
Method: "get",
|
||||||
Query: map[string]any{
|
Query: map[string]any{
|
||||||
"a": 123,
|
"a": 123,
|
||||||
|
@ -455,7 +455,7 @@ func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
|
||||||
AuthRecord: authRecord,
|
AuthRecord: authRecord,
|
||||||
}
|
}
|
||||||
|
|
||||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestData, true)
|
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestInfo, true)
|
||||||
|
|
||||||
scenarios := []struct {
|
scenarios := []struct {
|
||||||
fieldName string
|
fieldName string
|
||||||
|
|
Loading…
Reference in New Issue