diff --git a/broker/go.mod b/broker/go.mod index 74a4ad89..4654b13d 100644 --- a/broker/go.mod +++ b/broker/go.mod @@ -23,7 +23,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/golang-migrate/migrate/v4 v4.19.1 github.com/google/uuid v1.6.0 - github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 + github.com/indexdata/cql-go v1.0.1-0.20260202173855-d9fe780396b2 github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 github.com/indexdata/xsd2goxsl v1.3.0 github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 diff --git a/broker/go.sum b/broker/go.sum index 35c9c567..d2a81891 100644 --- a/broker/go.sum +++ b/broker/go.sum @@ -74,6 +74,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DW github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030 h1:RP9TZZTGoBo2Jx04FEWaRmNhCX/Fk8VR04tX0FMRzik= github.com/indexdata/cql-go v1.0.1-0.20250722084932-84f3837d6030/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= +github.com/indexdata/cql-go v1.0.1-0.20260202173855-d9fe780396b2 h1:HYBpSNIr26sFXyOfq4pMGk13ygEezb1UA4gtrt9lsm0= +github.com/indexdata/cql-go v1.0.1-0.20260202173855-d9fe780396b2/go.mod h1:zmSHcE8JyK94EWZrV7VyjLr2QfRoj+EeEOttl9wm64U= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72 h1:/ssOlpKK1EOeWMCOUrxCOCZP+0Z1LD58Xg1nrU01TAQ= github.com/indexdata/go-utils v0.0.0-20250210100229-d30dbd51df72/go.mod h1:0sW6Szxv8GNU3LBtK6mgBKDEUnlovPfghiG9xi+i0R8= github.com/indexdata/xsd2goxsl v1.3.0 h1:LZGBORHnO6olHBtvc6hQEefymdRuiM77FgtW4pCek7g= diff --git a/broker/oapi/open-api.yaml b/broker/oapi/open-api.yaml index 77b288ba..e1f74b88 100644 --- a/broker/oapi/open-api.yaml +++ b/broker/oapi/open-api.yaml @@ -1084,4 +1084,3 @@ paths: application/json: schema: $ref: '#/components/schemas/SseResult' - diff --git a/broker/patron_request/api/api-handler.go b/broker/patron_request/api/api-handler.go index dc2730c9..aa638b00 100644 --- a/broker/patron_request/api/api-handler.go +++ b/broker/patron_request/api/api-handler.go @@ -10,6 +10,7 @@ import ( "time" "github.com/google/uuid" + "github.com/indexdata/cql-go/cqlbuilder" "github.com/indexdata/crosslink/broker/api" "github.com/indexdata/crosslink/broker/common" "github.com/indexdata/crosslink/broker/events" @@ -60,9 +61,38 @@ func (a *PatronRequestApiHandler) GetPatronRequests(w http.ResponseWriter, r *ht if params.Offset != nil { offset = *params.Offset } - - updatedCql := addAccessRestrictions(params.Cql, params.Side, symbol) - prs, count, err := a.prRepo.ListPatronRequests(ctx, pr_db.ListPatronRequestsParams{Limit: limit, Offset: offset}, &updatedCql) + var qb *cqlbuilder.QueryBuilder + if params.Cql == nil { + qb = cqlbuilder.NewQuery() + qb.Search("cql.allRecords").Term("1") + } else { + qb, err = cqlbuilder.NewQueryFromString(*params.Cql) + if err != nil { + addBadRequestError(ctx, w, err) + return + } + } + var side pr_db.PatronRequestSide + if isSideParamValid(params.Side) { + side = pr_db.PatronRequestSide(params.Side) + _, err = qb.And().Search("side").Term(params.Side).Build() + if err != nil { + addBadRequestError(ctx, w, err) + return + } + } + qb, err = addOwnerRestriction(qb, symbol, side) + if err != nil { + addBadRequestError(ctx, w, err) + return + } + cql, err := qb.Build() + if err != nil { + addBadRequestError(ctx, w, err) + return + } + cqlStr := cql.String() + prs, count, err := a.prRepo.ListPatronRequests(ctx, pr_db.ListPatronRequestsParams{Limit: limit, Offset: offset}, &cqlStr) if err != nil && !errors.Is(err, pgx.ErrNoRows) { //DB error addInternalError(ctx, w, err) return @@ -87,18 +117,32 @@ func (a *PatronRequestApiHandler) GetPatronRequests(w http.ResponseWriter, r *ht writeJsonResponse(w, resp) } -func addAccessRestrictions(cql *string, side string, symbol string) string { - var restrict string - if side == string(prservice.SideLending) { - restrict = "side = " + string(prservice.SideLending) + " AND supplier_symbol = " + symbol - } else { - restrict = "side = " + string(prservice.SideBorrowing) + " AND requester_symbol = " + symbol - } - if cql == nil { - return restrict - } else { - return *cql + " AND " + restrict - } +func addOwnerRestriction(queryBuilder *cqlbuilder.QueryBuilder, symbol string, side pr_db.PatronRequestSide) (*cqlbuilder.QueryBuilder, error) { + var err error + switch side { + case prservice.SideLending: + _, err = queryBuilder.And(). + Search("side").Term(string(prservice.SideLending)). + And().Search("supplier_symbol").Term(symbol). + Build() + case prservice.SideBorrowing: + _, err = queryBuilder.And(). + Search("side").Term(string(prservice.SideBorrowing)). + And().Search("requester_symbol").Term(symbol). + Build() + default: + _, err = queryBuilder.And(). + BeginClause(). + Search("side").Term(string(prservice.SideLending)). + And().Search("supplier_symbol").Term(symbol). + EndClause(). + Or(). + BeginClause().Search("side").Term(string(prservice.SideBorrowing)). + And().Search("requester_symbol").Term(symbol). + EndClause(). + Build() + } + return queryBuilder, err } func (a *PatronRequestApiHandler) PostPatronRequests(w http.ResponseWriter, r *http.Request, params proapi.PostPatronRequestsParams) { @@ -185,15 +229,19 @@ func (a *PatronRequestApiHandler) getPatronRequestById(ctx common.ExtendedContex } return nil, err } - if isOwner(pr, side, symbol) { + if isOwner(pr, symbol) && (!isSideParamValid(side) || string(pr.Side) == side) { return &pr, nil } return nil, nil } -func isOwner(pr pr_db.PatronRequest, side string, symbol string) bool { - return string(pr.Side) == side && ((side == string(prservice.SideBorrowing) && pr.RequesterSymbol.String == symbol) || - (side == string(prservice.SideLending) && pr.SupplierSymbol.String == symbol)) +func isSideParamValid(side string) bool { + return side == string(prservice.SideBorrowing) || side == string(prservice.SideLending) +} + +func isOwner(pr pr_db.PatronRequest, symbol string) bool { + return (string(pr.Side) == string(prservice.SideBorrowing) && pr.RequesterSymbol.String == symbol) || + (string(pr.Side) == string(prservice.SideLending) && pr.SupplierSymbol.String == symbol) } func (a *PatronRequestApiHandler) GetPatronRequestsId(w http.ResponseWriter, r *http.Request, id string, params proapi.GetPatronRequestsIdParams) { diff --git a/broker/patron_request/db/prcql.go b/broker/patron_request/db/prcql.go index d37a6070..3d7e194f 100644 --- a/broker/patron_request/db/prcql.go +++ b/broker/patron_request/db/prcql.go @@ -9,9 +9,21 @@ import ( "github.com/indexdata/cql-go/pgcql" ) +type FieldAllRecords struct{} + +func (f *FieldAllRecords) GetColumn() string { return "" } +func (f *FieldAllRecords) SetColumn(column string) {} +func (f *FieldAllRecords) Generate(sc cql.SearchClause, queryArgumentIndex int) (string, []any, error) { + // Accept standard cql.allRecords = 1 (ignore term/relation). + return "TRUE", nil, nil +} + func handlePatronRequestsQuery(cqlString string, noBaseArgs int) (pgcql.Query, error) { def := pgcql.NewPgDefinition() + fa := &FieldAllRecords{} + def.AddField("cql.allRecords", fa) + f := pgcql.NewFieldString().WithExact() def.AddField("state", f)