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().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.
|
||||
|
@ -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.
|
||||
|
||||
- **!** 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
|
||||
|
||||
|
@ -92,7 +95,7 @@
|
|||
|
||||
## 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
|
||||
|
|
|
@ -120,7 +120,7 @@ func InitApi(app core.App) (*echo.Echo, error) {
|
|||
bindStaticAdminUI(app, e)
|
||||
|
||||
// default routes
|
||||
api := e.Group("/api", eagerRequestDataCache(app))
|
||||
api := e.Group("/api", eagerRequestInfoCache(app))
|
||||
bindSettingsApi(app, api)
|
||||
bindAdminApi(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{
|
||||
{
|
||||
|
@ -236,7 +236,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
|||
|
||||
// since the unknown method is not eager cache support
|
||||
// it should fail reading the json body twice
|
||||
r := apis.RequestData(c)
|
||||
r := apis.RequestInfo(c)
|
||||
if v := cast.ToString(r.Data["name"]); v != "" {
|
||||
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 {
|
||||
return func(c echo.Context) error {
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
// it should fail reading the json body twice
|
||||
r := apis.RequestData(c)
|
||||
r := apis.RequestInfo(c)
|
||||
if v := cast.ToString(r.Data["name"]); v != "" {
|
||||
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 {
|
||||
return func(c echo.Context) error {
|
||||
// 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)
|
||||
|
||||
// ensure that the body was read at least once
|
||||
|
@ -297,7 +297,7 @@ func TestEagerRequestDataCache(t *testing.T) {
|
|||
c.Bind(data)
|
||||
|
||||
// try to read the body again
|
||||
r := apis.RequestData(c)
|
||||
r := apis.RequestInfo(c)
|
||||
fmt.Println(r)
|
||||
if v := cast.ToString(r.Data["name"]); v != "test123" {
|
||||
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)
|
||||
|
||||
// create a copy of the cached request data and adjust it for the current auth model
|
||||
requestData := *RequestData(c)
|
||||
requestData.Admin = nil
|
||||
requestData.AuthRecord = nil
|
||||
requestInfo := *RequestInfo(c)
|
||||
requestInfo.Admin = nil
|
||||
requestInfo.AuthRecord = nil
|
||||
if adminOrAuthRecord != nil {
|
||||
if admin, _ := adminOrAuthRecord.(*models.Admin); admin != nil {
|
||||
requestData.Admin = admin
|
||||
requestInfo.Admin = admin
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,15 +393,15 @@ func realUserIp(r *http.Request, fallbackIp string) string {
|
|||
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.
|
||||
func eagerRequestDataCache(app core.App) echo.MiddlewareFunc {
|
||||
func eagerRequestInfoCache(app core.App) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
switch c.Request().Method {
|
||||
// currently we are eagerly caching only the requests with body
|
||||
case "POST", "PUT", "PATCH", "DELETE":
|
||||
RequestData(c)
|
||||
RequestInfo(c)
|
||||
}
|
||||
|
||||
return next(c)
|
||||
|
|
|
@ -347,12 +347,12 @@ func (api *realtimeApi) canAccessRecord(client subscriptions.Client, record *mod
|
|||
}
|
||||
|
||||
// mock request data
|
||||
requestData := &models.RequestData{
|
||||
requestInfo := &models.RequestInfo{
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -191,8 +191,8 @@ func (api *recordAuthApi) authWithOAuth2(c echo.Context) error {
|
|||
return createForm.DrySubmit(func(txDao *daos.Dao) error {
|
||||
event.IsNewRecord = true
|
||||
// clone the current request data and assign the form create data as its body data
|
||||
requestData := *RequestData(c)
|
||||
requestData.Data = form.CreateData
|
||||
requestInfo := *RequestInfo(c)
|
||||
requestInfo.Data = form.CreateData
|
||||
|
||||
createRuleFunc := func(q *dbx.SelectQuery) error {
|
||||
admin, _ := c.Get(ContextAdminKey).(*models.Admin)
|
||||
|
@ -205,7 +205,7 @@ func (api *recordAuthApi) authWithOAuth2(c echo.Context) error {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -50,9 +50,9 @@ func (api *recordApi) list(c echo.Context) error {
|
|||
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
|
||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||
}
|
||||
|
@ -60,9 +60,9 @@ func (api *recordApi) list(c echo.Context) error {
|
|||
fieldsResolver := resolvers.NewRecordFieldResolver(
|
||||
api.app.Dao(),
|
||||
collection,
|
||||
requestData,
|
||||
requestInfo,
|
||||
// hidden fields are searchable only by admins
|
||||
requestData.Admin != nil,
|
||||
requestInfo.Admin != nil,
|
||||
)
|
||||
|
||||
searchProvider := search.NewProvider(fieldsResolver).
|
||||
|
@ -73,7 +73,7 @@ func (api *recordApi) list(c echo.Context) error {
|
|||
searchProvider.CountCol("id")
|
||||
}
|
||||
|
||||
if requestData.Admin == nil && collection.ListRule != nil {
|
||||
if requestInfo.Admin == nil && collection.ListRule != nil {
|
||||
searchProvider.AddFilter(search.FilterData(*collection.ListRule))
|
||||
}
|
||||
|
||||
|
@ -110,16 +110,16 @@ func (api *recordApi) view(c echo.Context) error {
|
|||
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
|
||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||
}
|
||||
|
||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||
if requestData.Admin == nil && collection.ViewRule != nil && *collection.ViewRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
||||
if requestInfo.Admin == nil && collection.ViewRule != nil && *collection.ViewRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||
expr, err := search.FilterData(*collection.ViewRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -155,23 +155,23 @@ func (api *recordApi) create(c echo.Context) error {
|
|||
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
|
||||
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
|
||||
if requestData.Admin == nil && collection.CreateRule != nil {
|
||||
if requestInfo.Admin == nil && collection.CreateRule != nil {
|
||||
testRecord := models.NewRecord(collection)
|
||||
|
||||
// replace modifiers fields so that the resolved value is always
|
||||
// available when accessing requestData.Data using just the field name
|
||||
if requestData.HasModifierDataKeys() {
|
||||
requestData.Data = testRecord.ReplaceModifers(requestData.Data)
|
||||
// available when accessing requestInfo.Data using just the field name
|
||||
if requestInfo.HasModifierDataKeys() {
|
||||
requestInfo.Data = testRecord.ReplaceModifers(requestInfo.Data)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -200,7 +200,7 @@ func (api *recordApi) create(c echo.Context) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("DrySubmit create rule failure: %w", err)
|
||||
}
|
||||
hasFullManageAccess = hasAuthManageAccess(txDao, foundRecord, requestData)
|
||||
hasFullManageAccess = hasAuthManageAccess(txDao, foundRecord, requestInfo)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -259,26 +259,26 @@ func (api *recordApi) update(c echo.Context) error {
|
|||
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
|
||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||
}
|
||||
|
||||
// eager fetch the record so that the modifier field values are replaced
|
||||
// and available when accessing requestData.Data using just the field name
|
||||
if requestData.HasModifierDataKeys() {
|
||||
// and available when accessing requestInfo.Data using just the field name
|
||||
if requestInfo.HasModifierDataKeys() {
|
||||
record, err := api.app.Dao().FindRecordById(collection.Id, recordId)
|
||||
if err != nil || record == nil {
|
||||
return NewNotFoundError("", err)
|
||||
}
|
||||
requestData.Data = record.ReplaceModifers(requestData.Data)
|
||||
requestInfo.Data = record.ReplaceModifers(requestInfo.Data)
|
||||
}
|
||||
|
||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||
if requestData.Admin == nil && collection.UpdateRule != nil && *collection.UpdateRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
||||
if requestInfo.Admin == nil && collection.UpdateRule != nil && *collection.UpdateRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||
expr, err := search.FilterData(*collection.UpdateRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -296,7 +296,7 @@ func (api *recordApi) update(c echo.Context) error {
|
|||
}
|
||||
|
||||
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
|
||||
if err := form.LoadRequest(c.Request(), ""); err != nil {
|
||||
|
@ -344,16 +344,16 @@ func (api *recordApi) delete(c echo.Context) error {
|
|||
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
|
||||
return NewForbiddenError("Only admins can perform this action.", nil)
|
||||
}
|
||||
|
||||
ruleFunc := func(q *dbx.SelectQuery) error {
|
||||
if requestData.Admin == nil && collection.DeleteRule != nil && *collection.DeleteRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestData, true)
|
||||
if requestInfo.Admin == nil && collection.DeleteRule != nil && *collection.DeleteRule != "" {
|
||||
resolver := resolvers.NewRecordFieldResolver(api.app.Dao(), collection, requestInfo, true)
|
||||
expr, err := search.FilterData(*collection.DeleteRule).BuildExpr(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -17,14 +17,20 @@ import (
|
|||
"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.
|
||||
func RequestData(c echo.Context) *models.RequestData {
|
||||
func RequestInfo(c echo.Context) *models.RequestInfo {
|
||||
// return cached to avoid copying the body multiple times
|
||||
if v := c.Get(ContextRequestDataKey); v != nil {
|
||||
if data, ok := v.(*models.RequestData); ok {
|
||||
if v := c.Get(ContextRequestInfoKey); v != nil {
|
||||
if data, ok := v.(*models.RequestInfo); ok {
|
||||
// refresh auth state
|
||||
data.AuthRecord, _ = c.Get(ContextAuthRecordKey).(*models.Record)
|
||||
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,
|
||||
Query: map[string]any{},
|
||||
Data: map[string]any{},
|
||||
|
@ -52,7 +58,7 @@ func RequestData(c echo.Context) *models.RequestData {
|
|||
echo.BindQueryParams(c, &result.Query)
|
||||
rest.BindBody(c, &result.Data)
|
||||
|
||||
c.Set(ContextRequestDataKey, result)
|
||||
c.Set(ContextRequestInfoKey, result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
@ -86,13 +92,13 @@ func RecordAuthResponse(
|
|||
expands := strings.Split(c.QueryParam(expandQueryParam), ",")
|
||||
if len(expands) > 0 {
|
||||
// create a copy of the cached request data and adjust it to the current auth record
|
||||
requestData := *RequestData(e.HttpContext)
|
||||
requestData.Admin = nil
|
||||
requestData.AuthRecord = e.Record
|
||||
requestInfo := *RequestInfo(e.HttpContext)
|
||||
requestInfo.Admin = nil
|
||||
requestInfo.AuthRecord = e.Record
|
||||
failed := app.Dao().ExpandRecord(
|
||||
e.Record,
|
||||
expands,
|
||||
expandFetch(app.Dao(), &requestData),
|
||||
expandFetch(app.Dao(), &requestInfo),
|
||||
)
|
||||
if len(failed) > 0 && app.IsDebug() {
|
||||
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
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -145,7 +151,7 @@ func EnrichRecords(c echo.Context, dao *daos.Dao, records []*models.Record, defa
|
|||
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 {
|
||||
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.
|
||||
func expandFetch(
|
||||
dao *daos.Dao,
|
||||
requestData *models.RequestData,
|
||||
requestInfo *models.RequestInfo,
|
||||
) daos.ExpandFetchFunc {
|
||||
return func(relCollection *models.Collection, relIds []string) ([]*models.Record, 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
|
||||
}
|
||||
|
||||
|
@ -169,7 +175,7 @@ func expandFetch(
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -182,7 +188,7 @@ func expandFetch(
|
|||
})
|
||||
|
||||
if err == nil && len(records) > 0 {
|
||||
autoIgnoreAuthRecordsEmailVisibility(dao, records, requestData)
|
||||
autoIgnoreAuthRecordsEmailVisibility(dao, records, requestInfo)
|
||||
}
|
||||
|
||||
return records, err
|
||||
|
@ -196,13 +202,13 @@ func expandFetch(
|
|||
func autoIgnoreAuthRecordsEmailVisibility(
|
||||
dao *daos.Dao,
|
||||
records []*models.Record,
|
||||
requestData *models.RequestData,
|
||||
requestInfo *models.RequestInfo,
|
||||
) error {
|
||||
if len(records) == 0 || !records[0].Collection().IsAuth() {
|
||||
return nil // nothing to check
|
||||
}
|
||||
|
||||
if requestData.Admin != nil {
|
||||
if requestInfo.Admin != nil {
|
||||
for _, rec := range records {
|
||||
rec.IgnoreEmailVisibility(true)
|
||||
}
|
||||
|
@ -218,8 +224,8 @@ func autoIgnoreAuthRecordsEmailVisibility(
|
|||
recordIds[i] = rec.Id
|
||||
}
|
||||
|
||||
if requestData != nil && requestData.AuthRecord != nil && mappedRecords[requestData.AuthRecord.Id] != nil {
|
||||
mappedRecords[requestData.AuthRecord.Id].IgnoreEmailVisibility(true)
|
||||
if requestInfo != nil && requestInfo.AuthRecord != nil && mappedRecords[requestInfo.AuthRecord.Id] != nil {
|
||||
mappedRecords[requestInfo.AuthRecord.Id].IgnoreEmailVisibility(true)
|
||||
}
|
||||
|
||||
authOptions := collection.AuthOptions()
|
||||
|
@ -235,7 +241,7 @@ func autoIgnoreAuthRecordsEmailVisibility(
|
|||
Select(dao.DB().QuoteSimpleColumnName(collection.Name) + ".id").
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -264,7 +270,7 @@ func autoIgnoreAuthRecordsEmailVisibility(
|
|||
func hasAuthManageAccess(
|
||||
dao *daos.Dao,
|
||||
record *models.Record,
|
||||
requestData *models.RequestData,
|
||||
requestInfo *models.RequestInfo,
|
||||
) bool {
|
||||
if !record.Collection().IsAuth() {
|
||||
return false
|
||||
|
@ -276,12 +282,12 @@ func hasAuthManageAccess(
|
|||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/pocketbase/pocketbase/tests"
|
||||
)
|
||||
|
||||
func TestRequestData(t *testing.T) {
|
||||
func TestRequestInfo(t *testing.T) {
|
||||
e := echo.New()
|
||||
req := httptest.NewRequest(http.MethodPost, "/?test=123", strings.NewReader(`{"test":456}`))
|
||||
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
||||
|
@ -29,10 +29,10 @@ func TestRequestData(t *testing.T) {
|
|||
dummyAdmin.Id = "id2"
|
||||
c.Set(apis.ContextAdminKey, dummyAdmin)
|
||||
|
||||
result := apis.RequestData(c)
|
||||
result := apis.RequestInfo(c)
|
||||
|
||||
if result == nil {
|
||||
t.Fatal("Expected *models.RequestData instance, got nil")
|
||||
t.Fatal("Expected *models.RequestInfo instance, got nil")
|
||||
}
|
||||
|
||||
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
|
||||
// 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 check (eg. invalid rule or db error).
|
||||
|
@ -531,14 +531,14 @@ func (dao *Dao) SuggestUniqueAuthRecordUsername(
|
|||
//
|
||||
// Example:
|
||||
//
|
||||
// requestData := apis.RequestData(c /* echo.Context */)
|
||||
// requestInfo := apis.RequestInfo(c /* echo.Context */)
|
||||
// record, _ := dao.FindRecordById("example", "RECORD_ID")
|
||||
// rule := types.Pointer("@request.auth.id != '' || status = 'public'")
|
||||
// // ... or use one of the record collection's rule, eg. record.Collection().ViewRule
|
||||
//
|
||||
// if ok, _ := dao.CanAccessRecord(record, requestData, rule); ok { ... }
|
||||
func (dao *Dao) CanAccessRecord(record *models.Record, requestData *models.RequestData, accessRule *string) (bool, error) {
|
||||
if requestData.Admin != nil {
|
||||
// if ok, _ := dao.CanAccessRecord(record, requestInfo, rule); ok { ... }
|
||||
func (dao *Dao) CanAccessRecord(record *models.Record, requestInfo *models.RequestInfo, accessRule *string) (bool, error) {
|
||||
if requestInfo.Admin != nil {
|
||||
// admins can access everything
|
||||
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})
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -625,7 +625,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
scenarios := []struct {
|
||||
name string
|
||||
record *models.Record
|
||||
requestData *models.RequestData
|
||||
requestInfo *models.RequestInfo
|
||||
rule *string
|
||||
expected bool
|
||||
expectError bool
|
||||
|
@ -633,7 +633,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as admin with nil rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Admin: admin,
|
||||
},
|
||||
nil,
|
||||
|
@ -643,7 +643,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as admin with non-empty rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Admin: admin,
|
||||
},
|
||||
types.Pointer("id = ''"), // the filter rule should be ignored
|
||||
|
@ -653,7 +653,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as admin with invalid rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Admin: admin,
|
||||
},
|
||||
types.Pointer("id ?!@ 1"), // the filter rule should be ignored
|
||||
|
@ -663,7 +663,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as guest with nil rule",
|
||||
record,
|
||||
&models.RequestData{},
|
||||
&models.RequestInfo{},
|
||||
nil,
|
||||
false,
|
||||
false,
|
||||
|
@ -671,7 +671,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as guest with empty rule",
|
||||
record,
|
||||
&models.RequestData{},
|
||||
&models.RequestInfo{},
|
||||
types.Pointer(""),
|
||||
true,
|
||||
false,
|
||||
|
@ -679,7 +679,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as guest with invalid rule",
|
||||
record,
|
||||
&models.RequestData{},
|
||||
&models.RequestInfo{},
|
||||
types.Pointer("id ?!@ 1"),
|
||||
false,
|
||||
true,
|
||||
|
@ -687,7 +687,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as guest with mismatched rule",
|
||||
record,
|
||||
&models.RequestData{},
|
||||
&models.RequestInfo{},
|
||||
types.Pointer("@request.auth.id != ''"),
|
||||
false,
|
||||
false,
|
||||
|
@ -695,7 +695,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as guest with matched rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Data: map[string]any{"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",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
AuthRecord: authRecord,
|
||||
},
|
||||
nil,
|
||||
|
@ -715,7 +715,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as auth record with empty rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
AuthRecord: authRecord,
|
||||
},
|
||||
types.Pointer(""),
|
||||
|
@ -725,7 +725,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as auth record with invalid rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
AuthRecord: authRecord,
|
||||
},
|
||||
types.Pointer("id ?!@ 1"),
|
||||
|
@ -735,7 +735,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as auth record with mismatched rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
AuthRecord: authRecord,
|
||||
Data: map[string]any{"test": 1},
|
||||
},
|
||||
|
@ -746,7 +746,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
{
|
||||
"as auth record with matched rule",
|
||||
record,
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
AuthRecord: authRecord,
|
||||
Data: map[string]any{"test": 2},
|
||||
},
|
||||
|
@ -757,7 +757,7 @@ func TestCanAccessRecord(t *testing.T) {
|
|||
}
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
func (form *RecordUpsert) extractRequestData(
|
||||
func (form *RecordUpsert) extractRequestInfo(
|
||||
r *http.Request,
|
||||
keyPrefix string,
|
||||
) (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.
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := form.LoadData(requestData); err != nil {
|
||||
if err := form.LoadData(requestInfo); err != nil {
|
||||
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.
|
||||
func (form *RecordUpsert) LoadData(requestData map[string]any) error {
|
||||
func (form *RecordUpsert) LoadData(requestInfo map[string]any) error {
|
||||
// load base system fields
|
||||
if v, ok := requestData[schema.FieldNameId]; ok {
|
||||
if v, ok := requestInfo[schema.FieldNameId]; ok {
|
||||
form.Id = cast.ToString(v)
|
||||
}
|
||||
|
||||
// load auth system fields
|
||||
if form.record.Collection().IsAuth() {
|
||||
if v, ok := requestData[schema.FieldNameUsername]; ok {
|
||||
if v, ok := requestInfo[schema.FieldNameUsername]; ok {
|
||||
form.Username = cast.ToString(v)
|
||||
}
|
||||
if v, ok := requestData[schema.FieldNameEmail]; ok {
|
||||
if v, ok := requestInfo[schema.FieldNameEmail]; ok {
|
||||
form.Email = cast.ToString(v)
|
||||
}
|
||||
if v, ok := requestData[schema.FieldNameEmailVisibility]; ok {
|
||||
if v, ok := requestInfo[schema.FieldNameEmailVisibility]; ok {
|
||||
form.EmailVisibility = cast.ToBool(v)
|
||||
}
|
||||
if v, ok := requestData[schema.FieldNameVerified]; ok {
|
||||
if v, ok := requestInfo[schema.FieldNameVerified]; ok {
|
||||
form.Verified = cast.ToBool(v)
|
||||
}
|
||||
if v, ok := requestData["password"]; ok {
|
||||
if v, ok := requestInfo["password"]; ok {
|
||||
form.Password = cast.ToString(v)
|
||||
}
|
||||
if v, ok := requestData["passwordConfirm"]; ok {
|
||||
if v, ok := requestInfo["passwordConfirm"]; ok {
|
||||
form.PasswordConfirm = cast.ToString(v)
|
||||
}
|
||||
if v, ok := requestData["oldPassword"]; ok {
|
||||
if v, ok := requestInfo["oldPassword"]; ok {
|
||||
form.OldPassword = cast.ToString(v)
|
||||
}
|
||||
}
|
||||
|
||||
// replace modifiers (if any)
|
||||
requestData = form.record.ReplaceModifers(requestData)
|
||||
requestInfo = form.record.ReplaceModifers(requestInfo)
|
||||
|
||||
// create a shallow copy of 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
|
||||
rawData, err := json.Marshal(requestData)
|
||||
rawData, err := json.Marshal(requestInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,12 +6,11 @@ import (
|
|||
"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.
|
||||
type RequestData struct {
|
||||
Method string `json:"method"`
|
||||
Query map[string]any `json:"query"`
|
||||
// @todo consider changing to Body?
|
||||
type RequestInfo struct {
|
||||
Method string `json:"method"`
|
||||
Query map[string]any `json:"query"`
|
||||
Data map[string]any `json:"data"`
|
||||
Headers map[string]any `json:"headers"`
|
||||
AuthRecord *Record `json:"authRecord"`
|
||||
|
@ -19,7 +18,7 @@ type RequestData struct {
|
|||
}
|
||||
|
||||
// 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()
|
||||
|
||||
for key := range r.Data {
|
|
@ -6,20 +6,20 @@ import (
|
|||
"github.com/pocketbase/pocketbase/models"
|
||||
)
|
||||
|
||||
func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
||||
func TestRequestInfoHasModifierDataKeys(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
requestData *models.RequestData
|
||||
requestInfo *models.RequestInfo
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
"empty",
|
||||
&models.RequestData{},
|
||||
&models.RequestInfo{},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Data with regular fields",
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Query: map[string]any{"data+": "demo"}, // should be ignored
|
||||
Data: map[string]any{"a": 123, "b": "test", "c.d": false},
|
||||
},
|
||||
|
@ -27,21 +27,21 @@ func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
|||
},
|
||||
{
|
||||
"Data with +modifier fields",
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Data: map[string]any{"a+": 123, "b": "test", "c.d": false},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Data with -modifier fields",
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Data: map[string]any{"a": 123, "b-": "test", "c.d": false},
|
||||
},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Data with mixed modifier fields",
|
||||
&models.RequestData{
|
||||
&models.RequestInfo{
|
||||
Data: map[string]any{"a": 123, "b-": "test", "c.d+": false},
|
||||
},
|
||||
true,
|
||||
|
@ -49,7 +49,7 @@ func TestRequestDataHasModifierDataKeys(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
result := s.requestData.HasModifierDataKeys()
|
||||
result := s.requestInfo.HasModifierDataKeys()
|
||||
|
||||
if result != s.expected {
|
||||
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)
|
||||
})
|
||||
|
||||
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 {
|
||||
code, _ := call.Argument(0).Export().(string)
|
||||
message, _ := call.Argument(1).Export().(string)
|
||||
|
@ -462,7 +481,7 @@ func apisBinds(vm *goja.Runtime) {
|
|||
obj.Set("activityLogger", apis.ActivityLogger)
|
||||
|
||||
// record helpers
|
||||
obj.Set("requestData", apis.RequestData)
|
||||
obj.Set("requestInfo", apis.RequestInfo)
|
||||
obj.Set("recordAuthResponse", apis.RecordAuthResponse)
|
||||
obj.Set("enrichRecord", apis.EnrichRecord)
|
||||
obj.Set("enrichRecords", apis.EnrichRecords)
|
||||
|
|
|
@ -45,7 +45,7 @@ func TestBaseBindsCount(t *testing.T) {
|
|||
vm := goja.New()
|
||||
baseBinds(vm)
|
||||
|
||||
testBindsCount(vm, "this", 14, t)
|
||||
testBindsCount(vm, "this", 16, 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) {
|
||||
vm := goja.New()
|
||||
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.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ` + "```" + `js
|
||||
* const command = new Command({
|
||||
* use: "hello",
|
||||
|
@ -323,6 +325,51 @@ declare class Command implements 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
|
||||
/**
|
||||
* ValidationError defines a single formatted data validation error,
|
||||
|
@ -690,7 +737,7 @@ declare namespace $apis {
|
|||
let requireAdminOrRecordAuth: apis.requireAdminOrRecordAuth
|
||||
let requireAdminOrOwnerAuth: apis.requireAdminOrOwnerAuth
|
||||
let activityLogger: apis.activityLogger
|
||||
let requestData: apis.requestData
|
||||
let requestInfo: apis.requestInfo
|
||||
let recordAuthResponse: apis.recordAuthResponse
|
||||
let enrichRecord: apis.enrichRecord
|
||||
let enrichRecords: apis.enrichRecords
|
||||
|
|
|
@ -64,7 +64,7 @@ func (r *runner) run() (*search.ResolverResult, error) {
|
|||
}
|
||||
|
||||
if r.activeProps[0] == "@request" {
|
||||
if r.resolver.requestData == nil {
|
||||
if r.resolver.requestInfo == nil {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
}
|
||||
|
||||
|
@ -87,17 +87,17 @@ func (r *runner) run() (*search.ResolverResult, error) {
|
|||
|
||||
// check for data relation field
|
||||
if dataField.Type == schema.FieldTypeRelation && len(r.activeProps) > 3 {
|
||||
return r.processRequestDataRelationField(dataField)
|
||||
return r.processRequestInfoRelationField(dataField)
|
||||
}
|
||||
|
||||
// check for select:each field
|
||||
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
|
||||
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
|
||||
// ---
|
||||
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
|
||||
}
|
||||
|
||||
collection := r.resolver.requestData.AuthRecord.Collection()
|
||||
collection := r.resolver.requestInfo.AuthRecord.Collection()
|
||||
r.resolver.loadedCollections = append(r.resolver.loadedCollections, collection)
|
||||
|
||||
r.activeCollectionName = collection.Name
|
||||
|
@ -193,7 +193,7 @@ func (r *runner) processRequestAuthField() (*search.ResolverResult, error) {
|
|||
r.activeTableAlias,
|
||||
dbx.HashExp{
|
||||
// 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),
|
||||
tableAlias: r.multiMatchActiveTableAlias,
|
||||
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()
|
||||
}
|
||||
|
||||
func (r *runner) processRequestDataLengthModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||
dataItems := list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
||||
func (r *runner) processRequestInfoLengthModifier(dataField *schema.SchemaField) (*search.ResolverResult, error) {
|
||||
dataItems := list.ToUniqueStringSlice(r.resolver.requestInfo.Data[dataField.Name])
|
||||
|
||||
result := &search.ResolverResult{
|
||||
Identifier: fmt.Sprintf("%d", len(dataItems)),
|
||||
|
@ -227,13 +227,13 @@ func (r *runner) processRequestDataLengthModifier(dataField *schema.SchemaField)
|
|||
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)
|
||||
if !ok {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
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)
|
||||
if !ok {
|
||||
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
|
||||
if r.resolver.requestData != nil && len(r.resolver.requestData.Data) != 0 {
|
||||
dataRelIds = list.ToUniqueStringSlice(r.resolver.requestData.Data[dataField.Name])
|
||||
if r.resolver.requestInfo != nil && len(r.resolver.requestInfo.Data) != 0 {
|
||||
dataRelIds = list.ToUniqueStringSlice(r.resolver.requestInfo.Data[dataField.Name])
|
||||
}
|
||||
if len(dataRelIds) == 0 {
|
||||
return &search.ResolverResult{Identifier: "NULL"}, nil
|
||||
|
|
|
@ -56,7 +56,7 @@ type CollectionsFinder interface {
|
|||
// resolver := resolvers.NewRecordFieldResolver(
|
||||
// app.Dao(),
|
||||
// myCollection,
|
||||
// &models.RequestData{...},
|
||||
// &models.RequestInfo{...},
|
||||
// true,
|
||||
// )
|
||||
// provider := search.NewProvider(resolver)
|
||||
|
@ -68,21 +68,21 @@ type RecordFieldResolver struct {
|
|||
allowedFields []string
|
||||
loadedCollections []*models.Collection
|
||||
joins []*join // we cannot use a map because the insertion order is not preserved
|
||||
requestData *models.RequestData
|
||||
staticRequestData map[string]any
|
||||
requestInfo *models.RequestInfo
|
||||
staticRequestInfo map[string]any
|
||||
}
|
||||
|
||||
// NewRecordFieldResolver creates and initializes a new `RecordFieldResolver`.
|
||||
func NewRecordFieldResolver(
|
||||
dao CollectionsFinder,
|
||||
baseCollection *models.Collection,
|
||||
requestData *models.RequestData,
|
||||
requestInfo *models.RequestInfo,
|
||||
allowHiddenFields bool,
|
||||
) *RecordFieldResolver {
|
||||
r := &RecordFieldResolver{
|
||||
dao: dao,
|
||||
baseCollection: baseCollection,
|
||||
requestData: requestData,
|
||||
requestInfo: requestInfo,
|
||||
allowHiddenFields: allowHiddenFields,
|
||||
joins: []*join{},
|
||||
loadedCollections: []*models.Collection{baseCollection},
|
||||
|
@ -97,17 +97,17 @@ func NewRecordFieldResolver(
|
|||
},
|
||||
}
|
||||
|
||||
r.staticRequestData = map[string]any{}
|
||||
if r.requestData != nil {
|
||||
r.staticRequestData["method"] = r.requestData.Method
|
||||
r.staticRequestData["query"] = r.requestData.Query
|
||||
r.staticRequestData["headers"] = r.requestData.Headers
|
||||
r.staticRequestData["data"] = r.requestData.Data
|
||||
r.staticRequestData["auth"] = nil
|
||||
if r.requestData.AuthRecord != nil {
|
||||
r.requestData.AuthRecord.IgnoreEmailVisibility(true)
|
||||
r.staticRequestData["auth"] = r.requestData.AuthRecord.PublicExport()
|
||||
r.requestData.AuthRecord.IgnoreEmailVisibility(false)
|
||||
r.staticRequestInfo = map[string]any{}
|
||||
if r.requestInfo != nil {
|
||||
r.staticRequestInfo["method"] = r.requestInfo.Method
|
||||
r.staticRequestInfo["query"] = r.requestInfo.Query
|
||||
r.staticRequestInfo["headers"] = r.requestInfo.Headers
|
||||
r.staticRequestInfo["data"] = r.requestInfo.Data
|
||||
r.staticRequestInfo["auth"] = nil
|
||||
if r.requestInfo.AuthRecord != nil {
|
||||
r.requestInfo.AuthRecord.IgnoreEmailVisibility(true)
|
||||
r.staticRequestInfo["auth"] = r.requestInfo.AuthRecord.PublicExport()
|
||||
r.requestInfo.AuthRecord.IgnoreEmailVisibility(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (*search
|
|||
path[len(path)-1] = lastProp
|
||||
|
||||
// extract value
|
||||
resultVal, err := extractNestedMapVal(r.staticRequestData, path...)
|
||||
resultVal, err := extractNestedMapVal(r.staticRequestInfo, path...)
|
||||
|
||||
if modifier == issetModifier {
|
||||
if err != nil {
|
||||
|
@ -175,7 +175,7 @@ func (r *RecordFieldResolver) resolveStaticRequestField(path ...string) (*search
|
|||
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
|
||||
|
||||
switch v := resultVal.(type) {
|
||||
|
|
|
@ -22,7 +22,7 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
requestData := &models.RequestData{
|
||||
requestInfo := &models.RequestInfo{
|
||||
Headers: map[string]any{
|
||||
"a": "123",
|
||||
"b": "456",
|
||||
|
@ -313,7 +313,7 @@ func TestRecordFieldResolverUpdateQuery(t *testing.T) {
|
|||
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -353,11 +353,11 @@ func TestRecordFieldResolverResolveSchemaFields(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
requestData := &models.RequestData{
|
||||
requestInfo := &models.RequestInfo{
|
||||
AuthRecord: authRecord,
|
||||
}
|
||||
|
||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestData, true)
|
||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestInfo, true)
|
||||
|
||||
scenarios := []struct {
|
||||
fieldName string
|
||||
|
@ -424,7 +424,7 @@ func TestRecordFieldResolverResolveSchemaFields(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
|
||||
func TestRecordFieldResolverResolveStaticRequestInfoFields(t *testing.T) {
|
||||
app, _ := tests.NewTestApp()
|
||||
defer app.Cleanup()
|
||||
|
||||
|
@ -438,7 +438,7 @@ func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
requestData := &models.RequestData{
|
||||
requestInfo := &models.RequestInfo{
|
||||
Method: "get",
|
||||
Query: map[string]any{
|
||||
"a": 123,
|
||||
|
@ -455,7 +455,7 @@ func TestRecordFieldResolverResolveStaticRequestDataFields(t *testing.T) {
|
|||
AuthRecord: authRecord,
|
||||
}
|
||||
|
||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestData, true)
|
||||
r := resolvers.NewRecordFieldResolver(app.Dao(), collection, requestInfo, true)
|
||||
|
||||
scenarios := []struct {
|
||||
fieldName string
|
||||
|
|
Loading…
Reference in New Issue