mirror of
https://github.com/anyproto/anytype-heart.git
synced 2025-06-08 05:47:07 +09:00
GO-5645: Add ensureFilters middleware to set filters context for listing endpoints
This commit is contained in:
parent
45a39daef2
commit
b12c66c712
4 changed files with 201 additions and 59 deletions
|
@ -14,7 +14,10 @@ import (
|
|||
"github.com/anyproto/anytype-heart/core/api/util"
|
||||
"github.com/anyproto/anytype-heart/core/event"
|
||||
"github.com/anyproto/anytype-heart/pb"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/bundle"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/logging"
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/anyproto/anytype-heart/util/pbtypes"
|
||||
)
|
||||
|
||||
const ApiVersion = "2025-05-20"
|
||||
|
@ -28,7 +31,7 @@ var (
|
|||
)
|
||||
|
||||
// ensureMetadataHeader is a middleware that ensures the metadata header is set.
|
||||
func (s *Server) ensureMetadataHeader() gin.HandlerFunc {
|
||||
func ensureMetadataHeader() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Anytype-Version", ApiVersion)
|
||||
c.Next()
|
||||
|
@ -84,7 +87,7 @@ func (s *Server) ensureAuthenticated(mw apicore.ClientCommands) gin.HandlerFunc
|
|||
}
|
||||
|
||||
// ensureAnalyticsEvent is a middleware that ensures broadcasting an analytics event after a successful request.
|
||||
func (s *Server) ensureAnalyticsEvent(code string, eventService apicore.EventService) gin.HandlerFunc {
|
||||
func ensureAnalyticsEvent(code string, eventService apicore.EventService) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
|
||||
|
@ -125,3 +128,29 @@ func ensureRateLimit(rate float64, burst int, isRateLimitDisabled bool) gin.Hand
|
|||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// ensureFilters is a middleware that ensures the filters are set in the context.
|
||||
func ensureFilters() gin.HandlerFunc {
|
||||
filterDefs := []struct {
|
||||
Param string
|
||||
RelationKey string
|
||||
Condition model.BlockContentDataviewFilterCondition
|
||||
}{
|
||||
{bundle.RelationKeyName.String(), bundle.RelationKeyName.String(), model.BlockContentDataviewFilter_Like},
|
||||
}
|
||||
|
||||
return func(c *gin.Context) {
|
||||
var filters []*model.BlockContentDataviewFilter
|
||||
for _, def := range filterDefs {
|
||||
if v := c.Query(def.Param); v != "" {
|
||||
filters = append(filters, &model.BlockContentDataviewFilter{
|
||||
RelationKey: def.RelationKey,
|
||||
Condition: def.Condition,
|
||||
Value: pbtypes.String(v),
|
||||
})
|
||||
}
|
||||
}
|
||||
c.Set("filters", filters)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ import (
|
|||
func TestEnsureMetadataHeader(t *testing.T) {
|
||||
t.Run("sets correct header", func(t *testing.T) {
|
||||
// given
|
||||
fx := newFixture(t)
|
||||
middleware := fx.ensureMetadataHeader()
|
||||
middleware := ensureMetadataHeader()
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
|
||||
|
@ -145,10 +144,9 @@ func TestEnsureAnalyticsEvent(t *testing.T) {
|
|||
fx := newFixture(t)
|
||||
code := "test-code"
|
||||
fx.eventMock.On("Broadcast", mock.AnythingOfType("*pb.Event")).Return()
|
||||
srv := &Server{}
|
||||
mw := srv.ensureAnalyticsEvent(code, fx.eventMock)
|
||||
middleware := ensureAnalyticsEvent(code, fx.eventMock)
|
||||
router := gin.New()
|
||||
router.Use(mw)
|
||||
router.Use(middleware)
|
||||
router.GET("/test", func(c *gin.Context) {
|
||||
c.String(http.StatusAccepted, "OK")
|
||||
})
|
||||
|
|
|
@ -22,7 +22,6 @@ const (
|
|||
)
|
||||
|
||||
// NewRouter builds and returns a *gin.Engine with all routes configured.
|
||||
|
||||
func (s *Server) NewRouter(mw apicore.ClientCommands, eventService apicore.EventService) *gin.Engine {
|
||||
isDebug := os.Getenv("ANYTYPE_API_DEBUG") == "1"
|
||||
if !isDebug {
|
||||
|
@ -31,7 +30,7 @@ func (s *Server) NewRouter(mw apicore.ClientCommands, eventService apicore.Event
|
|||
|
||||
router := gin.New()
|
||||
router.Use(gin.Recovery())
|
||||
router.Use(s.ensureMetadataHeader())
|
||||
router.Use(ensureMetadataHeader())
|
||||
|
||||
if isDebug {
|
||||
router.Use(gin.Logger())
|
||||
|
@ -44,7 +43,7 @@ func (s *Server) NewRouter(mw apicore.ClientCommands, eventService apicore.Event
|
|||
MaxPageSize: maxPageSize,
|
||||
})
|
||||
|
||||
// Shared ratelimiter with option to disable it through env var
|
||||
// Shared ratelimiter with the option to disable it through env var
|
||||
isRateLimitDisabled := os.Getenv("ANYTYPE_API_DISABLE_RATE_LIMIT") == "1"
|
||||
writeRateLimitMW := ensureRateLimit(maxWriteRequestsPerSecond, maxBurstRequests, isRateLimitDisabled)
|
||||
|
||||
|
@ -77,9 +76,8 @@ func (s *Server) NewRouter(mw apicore.ClientCommands, eventService apicore.Event
|
|||
{
|
||||
// TO BE DEPRECATED
|
||||
authGroup.POST("/auth/display_code", handler.DisplayCodeHandler(s.service))
|
||||
// TO BE DEPRECATED
|
||||
authGroup.POST("/auth/token", handler.TokenHandler(s.service))
|
||||
|
||||
// UPDATED ROUTES
|
||||
authGroup.POST("/auth/challenges", handler.CreateChallengeHandler(s.service))
|
||||
authGroup.POST("/auth/api_keys", handler.CreateApiKeyHandler(s.service))
|
||||
}
|
||||
|
@ -96,58 +94,187 @@ func (s *Server) NewRouter(mw apicore.ClientCommands, eventService apicore.Event
|
|||
// v1.DELETE("/spaces/:space_id/objects/:object_id/blocks/:block_id", writeRateLimitMW, object.DeleteBlockHandler(s.service))
|
||||
|
||||
// List
|
||||
v1.GET("/spaces/:space_id/lists/:list_id/views", s.ensureAnalyticsEvent("ListGetViews", eventService), handler.GetListViewsHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/lists/:list_id/views/:view_id/objects", s.ensureAnalyticsEvent("ListGetObjects", eventService), handler.GetObjectsInListHandler(s.service))
|
||||
v1.POST("/spaces/:space_id/lists/:list_id/objects", writeRateLimitMW, s.ensureAnalyticsEvent("ListAddObject", eventService), handler.AddObjectsToListHandler(s.service))
|
||||
v1.DELETE("/spaces/:space_id/lists/:list_id/objects/:object_id", writeRateLimitMW, s.ensureAnalyticsEvent("ListRemoveObject", eventService), handler.RemoveObjectFromListHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/lists/:list_id/views",
|
||||
ensureAnalyticsEvent("ListGetViews", eventService),
|
||||
handler.GetListViewsHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/lists/:list_id/views/:view_id/objects",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("ListGetObjects", eventService),
|
||||
handler.GetObjectsInListHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces/:space_id/lists/:list_id/objects",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("ListAddObject", eventService),
|
||||
handler.AddObjectsToListHandler(s.service),
|
||||
)
|
||||
v1.DELETE("/spaces/:space_id/lists/:list_id/objects/:object_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("ListRemoveObject", eventService),
|
||||
handler.RemoveObjectFromListHandler(s.service),
|
||||
)
|
||||
|
||||
// Member
|
||||
v1.GET("/spaces/:space_id/members", s.ensureAnalyticsEvent("MemberList", eventService), handler.ListMembersHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/members/:member_id", s.ensureAnalyticsEvent("MemberOpen", eventService), handler.GetMemberHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/members",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("MemberList", eventService),
|
||||
handler.ListMembersHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/members/:member_id",
|
||||
ensureAnalyticsEvent("MemberOpen", eventService),
|
||||
handler.GetMemberHandler(s.service),
|
||||
)
|
||||
// TODO: renable when granular permissions are implementeds
|
||||
// v1.PATCH("/spaces/:space_id/members/:member_id", writeRateLimitMW, space.UpdateMemberHandler(s.service))
|
||||
// v1.PATCH("/spaces/:space_id/members/:member_id",
|
||||
// writeRateLimitMW,
|
||||
// handler.UpdateMemberHandler(s.service),
|
||||
// )
|
||||
|
||||
// Object
|
||||
v1.GET("/spaces/:space_id/objects", s.ensureAnalyticsEvent("ObjectList", eventService), handler.ListObjectsHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/objects/:object_id", s.ensureAnalyticsEvent("ObjectOpen", eventService), handler.GetObjectHandler(s.service))
|
||||
v1.POST("/spaces/:space_id/objects", writeRateLimitMW, s.ensureAnalyticsEvent("ObjectCreate", eventService), handler.CreateObjectHandler(s.service))
|
||||
v1.PATCH("/spaces/:space_id/objects/:object_id", writeRateLimitMW, s.ensureAnalyticsEvent("ObjectUpdate", eventService), handler.UpdateObjectHandler(s.service))
|
||||
v1.DELETE("/spaces/:space_id/objects/:object_id", writeRateLimitMW, s.ensureAnalyticsEvent("ObjectDelete", eventService), handler.DeleteObjectHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/objects",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("ObjectList", eventService),
|
||||
handler.ListObjectsHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/objects/:object_id",
|
||||
ensureAnalyticsEvent("ObjectOpen", eventService),
|
||||
handler.GetObjectHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces/:space_id/objects",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("ObjectCreate", eventService),
|
||||
handler.CreateObjectHandler(s.service),
|
||||
)
|
||||
v1.PATCH("/spaces/:space_id/objects/:object_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("ObjectUpdate", eventService),
|
||||
handler.UpdateObjectHandler(s.service),
|
||||
)
|
||||
v1.DELETE("/spaces/:space_id/objects/:object_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("ObjectDelete", eventService),
|
||||
handler.DeleteObjectHandler(s.service),
|
||||
)
|
||||
|
||||
// Property
|
||||
v1.GET("/spaces/:space_id/properties", s.ensureAnalyticsEvent("PropertyList", eventService), handler.ListPropertiesHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/properties/:property_id", s.ensureAnalyticsEvent("PropertyOpen", eventService), handler.GetPropertyHandler(s.service))
|
||||
v1.POST("/spaces/:space_id/properties", writeRateLimitMW, s.ensureAnalyticsEvent("PropertyCreate", eventService), handler.CreatePropertyHandler(s.service))
|
||||
v1.PATCH("/spaces/:space_id/properties/:property_id", writeRateLimitMW, s.ensureAnalyticsEvent("PropertyUpdate", eventService), handler.UpdatePropertyHandler(s.service))
|
||||
v1.DELETE("/spaces/:space_id/properties/:property_id", writeRateLimitMW, s.ensureAnalyticsEvent("PropertyDelete", eventService), handler.DeletePropertyHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/properties",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("PropertyList", eventService),
|
||||
handler.ListPropertiesHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/properties/:property_id",
|
||||
ensureAnalyticsEvent("PropertyOpen", eventService),
|
||||
handler.GetPropertyHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces/:space_id/properties",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("PropertyCreate", eventService),
|
||||
handler.CreatePropertyHandler(s.service),
|
||||
)
|
||||
v1.PATCH("/spaces/:space_id/properties/:property_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("PropertyUpdate", eventService),
|
||||
handler.UpdatePropertyHandler(s.service),
|
||||
)
|
||||
v1.DELETE("/spaces/:space_id/properties/:property_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("PropertyDelete", eventService),
|
||||
handler.DeletePropertyHandler(s.service),
|
||||
)
|
||||
|
||||
// Search
|
||||
v1.POST("/search", s.ensureAnalyticsEvent("SearchGlobal", eventService), handler.GlobalSearchHandler(s.service))
|
||||
v1.POST("/spaces/:space_id/search", s.ensureAnalyticsEvent("SearchSpace", eventService), handler.SearchHandler(s.service))
|
||||
v1.POST("/search",
|
||||
ensureAnalyticsEvent("SearchGlobal", eventService),
|
||||
handler.GlobalSearchHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces/:space_id/search",
|
||||
ensureAnalyticsEvent("SearchSpace", eventService),
|
||||
handler.SearchHandler(s.service),
|
||||
)
|
||||
|
||||
// Space
|
||||
v1.GET("/spaces", s.ensureAnalyticsEvent("SpaceList", eventService), handler.ListSpacesHandler(s.service))
|
||||
v1.GET("/spaces/:space_id", s.ensureAnalyticsEvent("SpaceOpen", eventService), handler.GetSpaceHandler(s.service))
|
||||
v1.POST("/spaces", writeRateLimitMW, s.ensureAnalyticsEvent("SpaceCreate", eventService), handler.CreateSpaceHandler(s.service))
|
||||
v1.PATCH("/spaces/:space_id", writeRateLimitMW, s.ensureAnalyticsEvent("SpaceUpdate", eventService), handler.UpdateSpaceHandler(s.service))
|
||||
v1.GET("/spaces",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("SpaceList", eventService),
|
||||
handler.ListSpacesHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id",
|
||||
ensureAnalyticsEvent("SpaceOpen", eventService),
|
||||
handler.GetSpaceHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("SpaceCreate", eventService),
|
||||
handler.CreateSpaceHandler(s.service),
|
||||
)
|
||||
v1.PATCH("/spaces/:space_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("SpaceUpdate", eventService),
|
||||
handler.UpdateSpaceHandler(s.service),
|
||||
)
|
||||
|
||||
// Tag
|
||||
v1.GET("/spaces/:space_id/properties/:property_id/tags", s.ensureAnalyticsEvent("TagList", eventService), handler.ListTagsHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/properties/:property_id/tags/:tag_id", s.ensureAnalyticsEvent("TagOpen", eventService), handler.GetTagHandler(s.service))
|
||||
v1.POST("/spaces/:space_id/properties/:property_id/tags", writeRateLimitMW, s.ensureAnalyticsEvent("TagCreate", eventService), handler.CreateTagHandler(s.service))
|
||||
v1.PATCH("/spaces/:space_id/properties/:property_id/tags/:tag_id", writeRateLimitMW, s.ensureAnalyticsEvent("TagUpdate", eventService), handler.UpdateTagHandler(s.service))
|
||||
v1.DELETE("/spaces/:space_id/properties/:property_id/tags/:tag_id", writeRateLimitMW, s.ensureAnalyticsEvent("TagDelete", eventService), handler.DeleteTagHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/properties/:property_id/tags",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("TagList", eventService),
|
||||
handler.ListTagsHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/properties/:property_id/tags/:tag_id",
|
||||
ensureAnalyticsEvent("TagOpen", eventService),
|
||||
handler.GetTagHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces/:space_id/properties/:property_id/tags",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("TagCreate", eventService),
|
||||
handler.CreateTagHandler(s.service),
|
||||
)
|
||||
v1.PATCH("/spaces/:space_id/properties/:property_id/tags/:tag_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("TagUpdate", eventService),
|
||||
handler.UpdateTagHandler(s.service),
|
||||
)
|
||||
v1.DELETE("/spaces/:space_id/properties/:property_id/tags/:tag_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("TagDelete", eventService),
|
||||
handler.DeleteTagHandler(s.service),
|
||||
)
|
||||
|
||||
// Template
|
||||
v1.GET("/spaces/:space_id/types/:type_id/templates", s.ensureAnalyticsEvent("TemplateList", eventService), handler.ListTemplatesHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/types/:type_id/templates/:template_id", s.ensureAnalyticsEvent("TemplateOpen", eventService), handler.GetTemplateHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/types/:type_id/templates",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("TemplateList", eventService),
|
||||
handler.ListTemplatesHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/types/:type_id/templates/:template_id",
|
||||
ensureAnalyticsEvent("TemplateOpen", eventService),
|
||||
handler.GetTemplateHandler(s.service),
|
||||
)
|
||||
|
||||
// Type
|
||||
v1.GET("/spaces/:space_id/types", s.ensureAnalyticsEvent("TypeList", eventService), handler.ListTypesHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/types/:type_id", s.ensureAnalyticsEvent("TypeOpen", eventService), handler.GetTypeHandler(s.service))
|
||||
v1.POST("/spaces/:space_id/types", writeRateLimitMW, s.ensureAnalyticsEvent("TypeCreate", eventService), handler.CreateTypeHandler(s.service))
|
||||
v1.PATCH("/spaces/:space_id/types/:type_id", writeRateLimitMW, s.ensureAnalyticsEvent("TypeUpdate", eventService), handler.UpdateTypeHandler(s.service))
|
||||
v1.DELETE("/spaces/:space_id/types/:type_id", writeRateLimitMW, s.ensureAnalyticsEvent("TypeDelete", eventService), handler.DeleteTypeHandler(s.service))
|
||||
v1.GET("/spaces/:space_id/types",
|
||||
ensureFilters(),
|
||||
ensureAnalyticsEvent("TypeList", eventService),
|
||||
handler.ListTypesHandler(s.service),
|
||||
)
|
||||
v1.GET("/spaces/:space_id/types/:type_id",
|
||||
ensureAnalyticsEvent("TypeOpen", eventService),
|
||||
handler.GetTypeHandler(s.service),
|
||||
)
|
||||
v1.POST("/spaces/:space_id/types",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("TypeCreate", eventService),
|
||||
handler.CreateTypeHandler(s.service),
|
||||
)
|
||||
v1.PATCH("/spaces/:space_id/types/:type_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("TypeUpdate", eventService),
|
||||
handler.UpdateTypeHandler(s.service),
|
||||
)
|
||||
v1.DELETE("/spaces/:space_id/types/:type_id",
|
||||
writeRateLimitMW,
|
||||
ensureAnalyticsEvent("TypeDelete", eventService),
|
||||
handler.DeleteTypeHandler(s.service),
|
||||
)
|
||||
}
|
||||
|
||||
return router
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"github.com/anyproto/anytype-heart/pkg/lib/pb/model"
|
||||
"github.com/gogo/protobuf/types"
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
RelationKey string
|
||||
Condition model.BlockContentDataviewFilterCondition
|
||||
Value *types.Value
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue