mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-09-12 21:58:58 -04:00
feat(graph): accept sortProperties on search requests
Validated against the sortable-field whitelist (name, size, lastModifiedDateTime, photo.takenDateTime) and forwarded to the search service as order_by. Also fixes the stub search service's IndexSpace signature (streaming response) so the suite builds again. (cherry picked from commit 9aa6a3492de3468da7a8480a3b9ef5748cee1f5d) (cherry picked from commit 35a6cad8ea6a7890bdc0bc98c7ac97bdcbf477cc)
This commit is contained in:
1 parent
43f6efe403
commit
da6809a2cb
2 files changed
+87
No files matched your search
@@ -43,6 +43,10 @@ func (g Graph) SearchQuery(w http.ResponseWriter, r *http.Request) {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
if err := validateSortProperties(sr.SortProperties); err != nil {
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
th := r.Header.Get(revaCtx.TokenHeader)
|
||||
@@ -80,6 +84,7 @@ func (g Graph) runSingleSearch(ctx context.Context, sr libregraph.SearchRequest,
|
||||
PageSize: pageSize,
|
||||
Aggregations: libregraphAggregationsToSearch(sr.Aggregations),
|
||||
AggregationFilters: sr.AggregationFilters,
|
||||
OrderBy: libregraphSortToSearch(sr.SortProperties),
|
||||
})
|
||||
if err != nil {
|
||||
return libregraph.SearchResponse{}, err
|
||||
@@ -170,6 +175,43 @@ func validateAggregations(aggs []libregraph.AggregationOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// sortableFields is the set of fields accepted in sortProperties (mirrored in
|
||||
// the openapi spec). A field qualifies only if it is indexed as a sortable
|
||||
// type in both search backends AND carried on the Match entity: the service
|
||||
// layer re-sorts matches when merging the per-space result streams and needs
|
||||
// the sort key on the match itself.
|
||||
var sortableFields = map[string]struct{}{
|
||||
"name": {},
|
||||
"size": {},
|
||||
"lastModifiedDateTime": {},
|
||||
"photo.takenDateTime": {},
|
||||
}
|
||||
|
||||
// validateSortProperties rejects sorting by fields outside sortableFields.
|
||||
func validateSortProperties(sortProperties []libregraph.SortProperty) error {
|
||||
for _, sp := range sortProperties {
|
||||
if _, ok := sortableFields[sp.Name]; !ok {
|
||||
return fmt.Errorf("field %q is not sortable; sortable fields: name, size, lastModifiedDateTime, photo.takenDateTime", sp.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func libregraphSortToSearch(in []libregraph.SortProperty) []*searchsvc.SortProperty {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]*searchsvc.SortProperty, 0, len(in))
|
||||
for _, sp := range in {
|
||||
p := &searchsvc.SortProperty{Name: sp.Name}
|
||||
if sp.IsDescending != nil {
|
||||
p.IsDescending = *sp.IsDescending
|
||||
}
|
||||
out = append(out, p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func libregraphAggregationsToSearch(in []libregraph.AggregationOption) []*searchsvc.AggregationOption {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
|
||||
@@ -198,6 +198,51 @@ var _ = ginkgo.Describe("SearchQuery", func() {
|
||||
ginkgo.Entry("from+size overflow collapses", int32Ptr(1<<31-1), int32Ptr(500), int32(1<<31-1-500), int32(500)),
|
||||
)
|
||||
|
||||
ginkgo.It("forwards sortProperties to the search service as order_by", func() {
|
||||
var captured *searchsvc.SearchRequest
|
||||
g := graphWithSearch(stubSearchService{
|
||||
search: func(req *searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) {
|
||||
captured = req
|
||||
return &searchsvc.SearchResponse{}, nil
|
||||
},
|
||||
})
|
||||
rr := postSearchQuery(g, `{
|
||||
"requests": [{
|
||||
"entityTypes": ["driveItem"],
|
||||
"query": {"queryString": "mediatype:image"},
|
||||
"sortProperties": [
|
||||
{"name": "photo.takenDateTime", "isDescending": true},
|
||||
{"name": "name"}
|
||||
]
|
||||
}]
|
||||
}`)
|
||||
Expect(rr.Code).To(Equal(http.StatusOK), rr.Body.String())
|
||||
Expect(captured).ToNot(BeNil())
|
||||
Expect(captured.OrderBy).To(HaveLen(2))
|
||||
Expect(captured.OrderBy[0].Name).To(Equal("photo.takenDateTime"))
|
||||
Expect(captured.OrderBy[0].IsDescending).To(BeTrue())
|
||||
Expect(captured.OrderBy[1].Name).To(Equal("name"))
|
||||
Expect(captured.OrderBy[1].IsDescending).To(BeFalse())
|
||||
})
|
||||
|
||||
ginkgo.It("rejects sorting by an unsupported field with 400", func() {
|
||||
g := graphWithSearch(stubSearchService{
|
||||
search: func(*searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) {
|
||||
ginkgo.Fail("search service must not be called when validation fails")
|
||||
return nil, nil
|
||||
},
|
||||
})
|
||||
rr := postSearchQuery(g, `{
|
||||
"requests": [{
|
||||
"entityTypes": ["driveItem"],
|
||||
"query": {"queryString": "mediatype:image"},
|
||||
"sortProperties": [{"name": "photo.iso"}]
|
||||
}]
|
||||
}`)
|
||||
Expect(rr.Code).To(Equal(http.StatusBadRequest), rr.Body.String())
|
||||
Expect(rr.Body.String()).To(ContainSubstring("photo.iso"))
|
||||
})
|
||||
|
||||
ginkgo.It("rejects a terms aggregation on a numeric field with 400", func() {
|
||||
g := graphWithSearch(stubSearchService{
|
||||
search: func(*searchsvc.SearchRequest) (*searchsvc.SearchResponse, error) {
|
||||
|
||||
Reference in new issue
Block a user