aravind changes
This commit is contained in:
103
go-api/internal/service/owliver.go
Normal file
103
go-api/internal/service/owliver.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/krow/krow-backend/go-api/internal/authctx"
|
||||
"github.com/krow/krow-backend/go-api/internal/definition"
|
||||
"github.com/krow/krow-backend/go-api/internal/domain"
|
||||
"github.com/krow/krow-backend/go-api/internal/owliver"
|
||||
)
|
||||
|
||||
// allowedSuggestionParams names the accepted query parameters, on the pattern
|
||||
// of allowedDefinitionFilters: anything else is a caller mistake worth saying
|
||||
// out loud rather than a filter to ignore. It also keeps the endpoint from
|
||||
// quietly accepting a `role`, `org` or `user` parameter should one ever be
|
||||
// added by a client — who is asking is read from the session and nowhere else.
|
||||
var allowedSuggestionParams = map[string]bool{
|
||||
"page": true,
|
||||
"query": true,
|
||||
}
|
||||
|
||||
// maxEchoedPage bounds how much of a rejected page value is quoted back. Long
|
||||
// enough to name every real surface, short enough that the error cannot be used
|
||||
// to reflect a payload.
|
||||
const maxEchoedPage = 64
|
||||
|
||||
// SuggestionQuery is a validated suggestion request.
|
||||
//
|
||||
// Page is canonical: aliases are resolved here so nothing downstream has to
|
||||
// know that `hired` and `hired-history` are the same surface.
|
||||
type SuggestionQuery struct {
|
||||
Page string
|
||||
Query string
|
||||
}
|
||||
|
||||
// SuggestionsService answers "what could I usefully ask on this page?".
|
||||
//
|
||||
// It holds no pool, opens no transaction and reads no table. That is not an
|
||||
// omission — the panel calls it while the user types, and everything it needs
|
||||
// is the static catalogue in internal/owliver plus the caller's role. It is a
|
||||
// service rather than a function in the handler so that validation and
|
||||
// authorization sit where every other endpoint's do.
|
||||
type SuggestionsService struct{}
|
||||
|
||||
// NewSuggestions builds the suggestion service.
|
||||
func NewSuggestions() *SuggestionsService { return &SuggestionsService{} }
|
||||
|
||||
// ParseParams validates the query string.
|
||||
//
|
||||
// `page` is required and must name a real surface — the same closed vocabulary
|
||||
// internal/definition validates a definition's `pages:` against, so there is
|
||||
// one answer to "is that a page" in this process. `query` is optional: an
|
||||
// absent or too-short one is not an error, it is a request that has nothing to
|
||||
// rank yet, and Suggest answers it with an empty list.
|
||||
func (s *SuggestionsService) ParseParams(q url.Values) (SuggestionQuery, error) {
|
||||
var out SuggestionQuery
|
||||
|
||||
for name := range q {
|
||||
if !allowedSuggestionParams[name] {
|
||||
return out, domain.Invalid(fmt.Sprintf("unknown parameter %q", name))
|
||||
}
|
||||
}
|
||||
|
||||
raw := strings.TrimSpace(q.Get("page"))
|
||||
if raw == "" {
|
||||
return out, domain.Invalid("page is required")
|
||||
}
|
||||
page := definition.CanonicalPage(raw)
|
||||
if page == "" {
|
||||
echoed := raw
|
||||
if len(echoed) > maxEchoedPage {
|
||||
echoed = echoed[:maxEchoedPage]
|
||||
}
|
||||
return out, domain.Invalid(fmt.Sprintf(
|
||||
"Unsupported page: %s. Supported pages: %s.",
|
||||
echoed, strings.Join(definition.SupportedPages, ", ")))
|
||||
}
|
||||
|
||||
out.Page = page
|
||||
out.Query = q.Get("query")
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Suggest ranks the page's readings for this caller.
|
||||
//
|
||||
// The role comes off the session-resolved identity, exactly as Server.authorize
|
||||
// reads it, and an unrecognised role is offered nothing — the same deny-by-
|
||||
// default the policy table applies. Nothing else about the caller is consulted:
|
||||
// there is no branch here on organization, account type or anything a request
|
||||
// could set.
|
||||
//
|
||||
// No error case beyond parsing. A page with no readings for this caller, and a
|
||||
// query that matches none of them, both answer with an empty list — an empty
|
||||
// result is an answer, not a failure.
|
||||
func (s *SuggestionsService) Suggest(ident authctx.Identity, q SuggestionQuery) []owliver.Suggestion {
|
||||
role, known := domain.ParseRole(ident.Role)
|
||||
if !known {
|
||||
return []owliver.Suggestion{}
|
||||
}
|
||||
return owliver.Suggest(q.Page, q.Query, role)
|
||||
}
|
||||
Reference in New Issue
Block a user