diff --git a/broker/api/api-handler.go b/broker/api/api-handler.go index 39380672..85b49b34 100644 --- a/broker/api/api-handler.go +++ b/broker/api/api-handler.go @@ -6,7 +6,9 @@ import ( "errors" "fmt" "net/http" + "net/url" "reflect" + "strconv" "strings" "time" @@ -26,6 +28,7 @@ var EVENTS_PATH = "/events" var LOCATED_SUPPLIERS_PATH = "/located_suppliers" var PEERS_PATH = "/peers" var ILL_TRANSACTION_QUERY = "ill_transaction_id=" +var LIMIT_DEFAULT int32 = 10 type ApiHandler struct { eventRepo events.EventRepo @@ -45,6 +48,10 @@ func (a *ApiHandler) isTenantMode() bool { return a.tenantToSymbol != "" } +func (a *ApiHandler) getSymbolFromTenant(tenant string) string { + return strings.ReplaceAll(a.tenantToSymbol, "{tenant}", strings.ToUpper(tenant)) +} + func (a *ApiHandler) isOwner(trans *ill_db.IllTransaction, tenant *string, requesterSymbol *string) bool { if tenant == nil && requesterSymbol != nil { return trans.RequesterSymbol.String == *requesterSymbol @@ -55,22 +62,37 @@ func (a *ApiHandler) isOwner(trans *ill_db.IllTransaction, tenant *string, reque if tenant == nil { return false } - tenantSymbol := strings.ReplaceAll(a.tenantToSymbol, "{tenant}", strings.ToUpper(*tenant)) - return trans.RequesterSymbol.String == tenantSymbol + return trans.RequesterSymbol.String == a.getSymbolFromTenant(*tenant) } -func (a *ApiHandler) getIllTranFromParams(ctx extctx.ExtendedContext, - requesterReqId *oapi.RequesterRequestId, illTransactionId *oapi.IllTransactionId) (ill_db.IllTransaction, error) { +func (a *ApiHandler) getIllTranFromParams(ctx extctx.ExtendedContext, w http.ResponseWriter, + okapiTenant *string, requesterSymbol *string, requesterReqId *oapi.RequesterRequestId, + illTransactionId *oapi.IllTransactionId) (*ill_db.IllTransaction, error) { var tran ill_db.IllTransaction + var err error if requesterReqId != nil { - return a.illRepo.GetIllTransactionByRequesterRequestId(ctx, pgtype.Text{ + tran, err = a.illRepo.GetIllTransactionByRequesterRequestId(ctx, pgtype.Text{ String: *requesterReqId, Valid: true, }) } else if illTransactionId != nil { - return a.illRepo.GetIllTransactionById(ctx, *illTransactionId) + tran, err = a.illRepo.GetIllTransactionById(ctx, *illTransactionId) + } else { + err = fmt.Errorf("either requesterReqId or illTransactionId should be provided") + addBadRequestError(ctx, w, err) + return nil, err } - return tran, nil + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil + } + addInternalError(ctx, w, err) + return nil, err + } + if !a.isOwner(&tran, okapiTenant, requesterSymbol) { + return nil, nil + } + return &tran, nil } func (a *ApiHandler) GetEvents(w http.ResponseWriter, r *http.Request, params oapi.GetEventsParams) { @@ -81,32 +103,37 @@ func (a *ApiHandler) GetEvents(w http.ResponseWriter, r *http.Request, params oa ctx := extctx.CreateExtCtxWithArgs(context.Background(), &extctx.LoggerArgs{ Other: logParams, }) - tran, err := a.getIllTranFromParams(ctx, params.RequesterReqId, params.IllTransactionId) - if err != nil { //DB error - if errors.Is(err, pgx.ErrNoRows) { - writeEmpty(w) - return - } - addInternalError(ctx, w, err) + tran, err := a.getIllTranFromParams(ctx, w, params.XOkapiTenant, params.RequesterSymbol, + params.RequesterReqId, params.IllTransactionId) + if err != nil { return } - if !a.isOwner(&tran, params.XOkapiTenant, params.RequesterSymbol) { - writeEmpty(w) + var resp oapi.Events + if tran == nil { + writeJsonResponse(w, resp) return } - var eventList []events.Event - if tran.ID != "" { - eventList, err = a.eventRepo.GetIllTransactionEvents(ctx, tran.ID) - } else { - eventList, err = a.eventRepo.ListEvents(ctx) + dbparams := events.GetIllTransactionEventsParams{ + IllTransactionID: tran.ID, + Limit: LIMIT_DEFAULT, + Offset: 0, } + if params.Limit != nil { + dbparams.Limit = *params.Limit + } + if params.Offset != nil { + dbparams.Offset = *params.Offset + } + var fullCount int64 + var eventList []events.Event + eventList, fullCount, err = a.eventRepo.GetIllTransactionEvents(ctx, dbparams) if err != nil && !errors.Is(err, pgx.ErrNoRows) { addInternalError(ctx, w, err) return } - resp := []oapi.Event{} + resp.ResultInfo.Count = fullCount for _, event := range eventList { - resp = append(resp, toApiEvent(event)) + resp.Items = append(resp.Items, toApiEvent(event)) } writeJsonResponse(w, resp) } @@ -115,31 +142,89 @@ func (a *ApiHandler) GetIllTransactions(w http.ResponseWriter, r *http.Request, ctx := extctx.CreateExtCtxWithArgs(context.Background(), &extctx.LoggerArgs{ Other: map[string]string{"method": "GetIllTransactions"}, }) - tran, err := a.getIllTranFromParams(ctx, params.RequesterReqId, nil) - if err != nil { //DB error - if errors.Is(err, pgx.ErrNoRows) { - writeEmpty(w) + var resp oapi.IllTransactions + + var limit int32 = LIMIT_DEFAULT + if params.Limit != nil { + limit = *params.Limit + } + var offset int32 = 0 + if params.Offset != nil { + offset = *params.Offset + } + var fullCount int64 + if params.RequesterReqId != nil { + tran, err := a.getIllTranFromParams(ctx, w, params.XOkapiTenant, params.RequesterSymbol, + params.RequesterReqId, nil) + if err != nil { return } - addInternalError(ctx, w, err) - return - } - resp := []oapi.IllTransaction{} - if tran.ID != "" { - if a.isOwner(&tran, params.XOkapiTenant, params.RequesterSymbol) { - resp = append(resp, toApiIllTransaction(r, tran)) + if tran != nil { + fullCount = 1 + resp.Items = append(resp.Items, toApiIllTransaction(r, *tran)) + } + } else if a.isTenantMode() { + var tenantSymbol string + if params.XOkapiTenant != nil { + tenantSymbol = a.getSymbolFromTenant(*params.XOkapiTenant) + } else if params.RequesterSymbol != nil { + tenantSymbol = *params.RequesterSymbol + } + if tenantSymbol == "" { + writeJsonResponse(w, resp) + return + } + dbparams := ill_db.GetIllTransactionsByRequesterSymbolParams{ + Limit: limit, + Offset: offset, + RequesterSymbol: pgtype.Text{ + String: tenantSymbol, + Valid: true, + }, + } + var trans []ill_db.IllTransaction + var err error + trans, fullCount, err = a.illRepo.GetIllTransactionsByRequesterSymbol(ctx, dbparams) + if err != nil { //DB error + addInternalError(ctx, w, err) + return + } + for _, t := range trans { + resp.Items = append(resp.Items, toApiIllTransaction(r, t)) } } else { - trans, err := a.illRepo.ListIllTransactions(ctx) + dbparams := ill_db.ListIllTransactionsParams{ + Limit: limit, + Offset: offset, + } + var trans []ill_db.IllTransaction + var err error + trans, fullCount, err = a.illRepo.ListIllTransactions(ctx, dbparams) if err != nil { //DB error addInternalError(ctx, w, err) return } for _, t := range trans { - if a.isOwner(&t, params.XOkapiTenant, params.RequesterSymbol) { - resp = append(resp, toApiIllTransaction(r, t)) - } + resp.Items = append(resp.Items, toApiIllTransaction(r, t)) + } + } + resp.ResultInfo.Count = fullCount + if offset > 0 { + pOffset := offset - limit + if pOffset < 0 { + pOffset = 0 } + urlValues := r.URL.Query() + urlValues["offset"] = []string{strconv.Itoa(int(pOffset))} + link := toLinkUrlValues(r, urlValues) + resp.ResultInfo.PrevLink = &link + } + if fullCount > int64(limit+offset) { + noffset := offset + limit + urlValues := r.URL.Query() + urlValues["offset"] = []string{strconv.Itoa(int(noffset))} + link := toLinkUrlValues(r, urlValues) + resp.ResultInfo.NextLink = &link } writeJsonResponse(w, resp) } @@ -148,24 +233,16 @@ func (a *ApiHandler) GetIllTransactionsId(w http.ResponseWriter, r *http.Request ctx := extctx.CreateExtCtxWithArgs(context.Background(), &extctx.LoggerArgs{ Other: map[string]string{"method": "GetIllTransactionsId", "id": id}, }) - trans, err := a.illRepo.GetIllTransactionById(ctx, id) + tran, err := a.getIllTranFromParams(ctx, w, params.XOkapiTenant, params.RequesterSymbol, + nil, &id) if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - addNotFoundError(w) - return - } - addInternalError(ctx, w, err) - return - } - if !a.isOwner(&trans, params.XOkapiTenant, params.RequesterSymbol) { - addNotFoundError(w) return } - if trans.ID == "" { + if tran == nil { addNotFoundError(w) return } - writeJsonResponse(w, toApiIllTransaction(r, trans)) + writeJsonResponse(w, toApiIllTransaction(r, *tran)) } func (a *ApiHandler) DeleteIllTransactionsId(w http.ResponseWriter, r *http.Request, id string) { @@ -196,48 +273,64 @@ func (a *ApiHandler) GetPeers(w http.ResponseWriter, r *http.Request, params oap ctx := extctx.CreateExtCtxWithArgs(context.Background(), &extctx.LoggerArgs{ Other: map[string]string{"method": "GetPeers"}, }) - resp := []oapi.Peer{} - peers, err := a.illRepo.ListPeers(ctx) + dbparams := ill_db.ListPeersParams{ + Limit: LIMIT_DEFAULT, + Offset: 0, + } + if params.Cql != nil && *params.Cql != "" { + // paging does not work with CQL as the filter is applied after the paging + // the count is also number of peers before filtering + dbparams.Limit = 10_000 + } else { + if params.Limit != nil { + dbparams.Limit = *params.Limit + } + if params.Offset != nil { + dbparams.Offset = *params.Offset + } + } + peers, count, err := a.illRepo.ListPeers(ctx, dbparams) if err != nil { addInternalError(ctx, w, err) return } + var resp oapi.Peers + resp.ResultInfo.Count = count for _, p := range peers { symbols, e := a.illRepo.GetSymbolsByPeerId(ctx, p.ID) if e != nil { addInternalError(ctx, w, e) return } - resp = append(resp, toApiPeer(p, symbols)) + resp.Items = append(resp.Items, toApiPeer(p, symbols)) } - resp, err = filterPeers(params.Cql, resp) + resp.Items, err = filterPeers(params.Cql, resp.Items) if err != nil { - addInternalError(ctx, w, err) + addBadRequestError(ctx, w, err) return } writeJsonResponse(w, resp) } func filterPeers(cql *string, peers []oapi.Peer) ([]oapi.Peer, error) { + if cql == nil || *cql == "" { + return peers, nil + } var filtered []oapi.Peer - if cql != nil && *cql != "" { - var p icql.Parser - query, err := p.Parse(*cql) + var p icql.Parser + query, err := p.Parse(*cql) + if err != nil { + return peers, err + } + for _, entry := range peers { + match, err := matchQuery(query, entry.Symbols) if err != nil { return peers, err } - for _, entry := range peers { - match, err := matchQuery(query, entry.Symbols) - if err != nil { - return peers, err - } - if !match { - continue - } - filtered = append(filtered, entry) + if !match { + continue } - } else { - return peers, nil + filtered = append(filtered, entry) } return filtered, nil } @@ -509,42 +602,41 @@ func (a *ApiHandler) GetLocatedSuppliers(w http.ResponseWriter, r *http.Request, ctx := extctx.CreateExtCtxWithArgs(context.Background(), &extctx.LoggerArgs{ Other: logParams, }) - tran, err := a.getIllTranFromParams(ctx, params.RequesterReqId, params.IllTransactionId) - if err != nil { //DB error - if errors.Is(err, pgx.ErrNoRows) { - writeEmpty(w) - return - } - addInternalError(ctx, w, err) + tran, err := a.getIllTranFromParams(ctx, w, params.XOkapiTenant, params.RequesterSymbol, + params.RequesterReqId, params.IllTransactionId) + if err != nil { return } - if !a.isOwner(&tran, params.XOkapiTenant, params.RequesterSymbol) { - writeEmpty(w) + var resp oapi.LocatedSuppliers + if tran == nil { + writeJsonResponse(w, resp) return } var supList []ill_db.LocatedSupplier - if tran.ID != "" { - supList, err = a.illRepo.GetLocatedSupplierByIllTransition(ctx, tran.ID) - } else { - supList, err = a.illRepo.ListLocatedSuppliers(ctx) + dbparams := ill_db.GetLocatedSupplierByIllTransactionParams{ + IllTransactionID: tran.ID, + Limit: LIMIT_DEFAULT, + Offset: 0, + } + if params.Limit != nil { + dbparams.Limit = *params.Limit } + if params.Offset != nil { + dbparams.Offset = *params.Offset + } + var count int64 + supList, count, err = a.illRepo.GetLocatedSupplierByIllTransaction(ctx, dbparams) if err != nil && !errors.Is(err, pgx.ErrNoRows) { //DB error addInternalError(ctx, w, err) return } - resp := []oapi.LocatedSupplier{} + resp.ResultInfo.Count = count for _, supplier := range supList { - resp = append(resp, toApiLocatedSupplier(r, supplier)) + resp.Items = append(resp.Items, toApiLocatedSupplier(r, supplier)) } writeJsonResponse(w, resp) } -func writeEmpty(w http.ResponseWriter) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - w.Write([]byte("[]")) -} - func writeJsonResponse(w http.ResponseWriter, resp any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) @@ -749,7 +841,24 @@ func toString(text pgtype.Text) *string { } } +func toLinkUrlValues(r *http.Request, urlValues url.Values) string { + return toLinkPath(r, r.URL.Path, urlValues.Encode()) +} + func toLink(r *http.Request, path string, id string, query string) string { + if strings.Contains(r.RequestURI, "/broker/") { + path = "/broker" + path + } + if id != "" { + path = path + "/" + id + } + return toLinkPath(r, path, query) +} + +func toLinkPath(r *http.Request, path string, query string) string { + if query != "" { + path = path + "?" + query + } urlScheme := r.Header.Get("X-Forwarded-Proto") if len(urlScheme) == 0 { urlScheme = r.URL.Scheme @@ -767,14 +876,5 @@ func toLink(r *http.Request, path string, id string, query string) string { if strings.Contains(urlHost, "localhost") { urlScheme = "http" } - if strings.Contains(r.RequestURI, "/broker/") { - path = "/broker" + path - } - if id != "" { - path = path + "/" + id - } - if query != "" { - path = path + "?" + query - } return urlScheme + "://" + urlHost + path } diff --git a/broker/events/eventrepo.go b/broker/events/eventrepo.go index b7781a42..e5ed374f 100644 --- a/broker/events/eventrepo.go +++ b/broker/events/eventrepo.go @@ -14,8 +14,7 @@ type EventRepo interface { UpdateEventStatus(ctx extctx.ExtendedContext, params UpdateEventStatusParams) error GetEvent(ctx extctx.ExtendedContext, id string) (Event, error) Notify(ctx extctx.ExtendedContext, eventId string, signal Signal) error - GetIllTransactionEvents(ctx extctx.ExtendedContext, illTransactionId string) ([]Event, error) - ListEvents(ctx extctx.ExtendedContext) ([]Event, error) + GetIllTransactionEvents(ctx extctx.ExtendedContext, params GetIllTransactionEventsParams) ([]Event, int64, error) DeleteEventsByIllTransaction(ctx extctx.ExtendedContext, illTransId string) error } @@ -61,26 +60,17 @@ func (r *PgEventRepo) Notify(ctx extctx.ExtendedContext, eventId string, signal return err } -func (r *PgEventRepo) GetIllTransactionEvents(ctx extctx.ExtendedContext, illTransactionId string) ([]Event, error) { +func (r *PgEventRepo) GetIllTransactionEvents(ctx extctx.ExtendedContext, illTransactionId GetIllTransactionEventsParams) ([]Event, int64, error) { rows, err := r.queries.GetIllTransactionEvents(ctx, r.GetConnOrTx(), illTransactionId) var events []Event + var fullCount int64 if err == nil { for _, r := range rows { + fullCount = r.FullCount events = append(events, r.Event) } } - return events, err -} - -func (r *PgEventRepo) ListEvents(ctx extctx.ExtendedContext) ([]Event, error) { - rows, err := r.queries.ListEvents(ctx, r.GetConnOrTx()) - var events []Event - if err == nil { - for _, r := range rows { - events = append(events, r.Event) - } - } - return events, err + return events, fullCount, err } func (r *PgEventRepo) DeleteEventsByIllTransaction(ctx extctx.ExtendedContext, illTransId string) error { diff --git a/broker/ill_db/illrepo.go b/broker/ill_db/illrepo.go index 1f200d7c..efe81227 100644 --- a/broker/ill_db/illrepo.go +++ b/broker/ill_db/illrepo.go @@ -20,17 +20,17 @@ type IllRepo interface { GetIllTransactionByRequesterRequestIdForUpdate(ctx extctx.ExtendedContext, requesterRequestID pgtype.Text) (IllTransaction, error) GetIllTransactionById(ctx extctx.ExtendedContext, id string) (IllTransaction, error) GetIllTransactionByIdForUpdate(ctx extctx.ExtendedContext, id string) (IllTransaction, error) - ListIllTransactions(ctx extctx.ExtendedContext) ([]IllTransaction, error) + ListIllTransactions(ctx extctx.ExtendedContext, params ListIllTransactionsParams) ([]IllTransaction, int64, error) + GetIllTransactionsByRequesterSymbol(ctx extctx.ExtendedContext, params GetIllTransactionsByRequesterSymbolParams) ([]IllTransaction, int64, error) DeleteIllTransaction(ctx extctx.ExtendedContext, id string) error SavePeer(ctx extctx.ExtendedContext, params SavePeerParams) (Peer, error) GetPeerById(ctx extctx.ExtendedContext, id string) (Peer, error) GetPeerBySymbol(ctx extctx.ExtendedContext, symbol string) (Peer, error) - ListPeers(ctx extctx.ExtendedContext) ([]Peer, error) + ListPeers(ctx extctx.ExtendedContext, params ListPeersParams) ([]Peer, int64, error) DeletePeer(ctx extctx.ExtendedContext, id string) error SaveLocatedSupplier(ctx extctx.ExtendedContext, params SaveLocatedSupplierParams) (LocatedSupplier, error) GetLocatedSupplierByIllTransactionAndStatus(ctx extctx.ExtendedContext, params GetLocatedSupplierByIllTransactionAndStatusParams) ([]LocatedSupplier, error) - GetLocatedSupplierByIllTransition(ctx extctx.ExtendedContext, illTransactionID string) ([]LocatedSupplier, error) - ListLocatedSuppliers(ctx extctx.ExtendedContext) ([]LocatedSupplier, error) + GetLocatedSupplierByIllTransaction(ctx extctx.ExtendedContext, params GetLocatedSupplierByIllTransactionParams) ([]LocatedSupplier, int64, error) GetLocatedSupplierByIllTransactionAndStatusForUpdate(ctx extctx.ExtendedContext, params GetLocatedSupplierByIllTransactionAndStatusForUpdateParams) ([]LocatedSupplier, error) GetLocatedSupplierByIllTransactionAndSupplierForUpdate(ctx extctx.ExtendedContext, params GetLocatedSupplierByIllTransactionAndSupplierForUpdateParams) (LocatedSupplier, error) GetSelectedSupplierForIllTransaction(ctx extctx.ExtendedContext, illTransId string) (LocatedSupplier, error) @@ -86,15 +86,40 @@ func (r *PgIllRepo) GetIllTransactionByIdForUpdate(ctx extctx.ExtendedContext, i return row.IllTransaction, err } -func (r *PgIllRepo) ListIllTransactions(ctx extctx.ExtendedContext) ([]IllTransaction, error) { - rows, err := r.queries.ListIllTransactions(ctx, r.GetConnOrTx()) +func (r *PgIllRepo) ListIllTransactions(ctx extctx.ExtendedContext, params ListIllTransactionsParams) ([]IllTransaction, int64, error) { + rows, err := r.queries.ListIllTransactions(ctx, r.GetConnOrTx(), params) var transactions []IllTransaction + var fullCount int64 + if err == nil { + if len(rows) > 0 { + fullCount = rows[0].FullCount + for _, r := range rows { + fullCount = r.FullCount + transactions = append(transactions, r.IllTransaction) + } + } else { + params.Limit = 1 + params.Offset = 0 + rows, err = r.queries.ListIllTransactions(ctx, r.GetConnOrTx(), params) + if err == nil && len(rows) > 0 { + fullCount = rows[0].FullCount + } + } + } + return transactions, fullCount, err +} + +func (r *PgIllRepo) GetIllTransactionsByRequesterSymbol(ctx extctx.ExtendedContext, params GetIllTransactionsByRequesterSymbolParams) ([]IllTransaction, int64, error) { + rows, err := r.queries.GetIllTransactionsByRequesterSymbol(ctx, r.GetConnOrTx(), params) + var transactions []IllTransaction + var fullCount int64 if err == nil { for _, r := range rows { + fullCount = r.FullCount transactions = append(transactions, r.IllTransaction) } } - return transactions, err + return transactions, fullCount, err } func (r *PgIllRepo) DeleteIllTransaction(ctx extctx.ExtendedContext, id string) error { @@ -111,15 +136,17 @@ func (r *PgIllRepo) GetPeerBySymbol(ctx extctx.ExtendedContext, symbol string) ( return row.Peer, err } -func (r *PgIllRepo) ListPeers(ctx extctx.ExtendedContext) ([]Peer, error) { - rows, err := r.queries.ListPeers(ctx, r.GetConnOrTx()) +func (r *PgIllRepo) ListPeers(ctx extctx.ExtendedContext, params ListPeersParams) ([]Peer, int64, error) { + rows, err := r.queries.ListPeers(ctx, r.GetConnOrTx(), params) var peers []Peer + var fullCount int64 if err == nil { for _, r := range rows { + fullCount = r.FullCount peers = append(peers, r.Peer) } } - return peers, err + return peers, fullCount, err } func (r *PgIllRepo) GetLocatedSupplierByIllTransactionAndStatus(ctx extctx.ExtendedContext, params GetLocatedSupplierByIllTransactionAndStatusParams) ([]LocatedSupplier, error) { @@ -163,25 +190,17 @@ func (r *PgIllRepo) GetLocatedSupplierByIllTransactionAndSupplierForUpdate(ctx e return row.LocatedSupplier, err } -func (r *PgIllRepo) GetLocatedSupplierByIllTransition(ctx extctx.ExtendedContext, illTransactionID string) ([]LocatedSupplier, error) { - rows, err := r.queries.GetLocatedSupplierByIllTransition(ctx, r.GetConnOrTx(), illTransactionID) +func (r *PgIllRepo) GetLocatedSupplierByIllTransaction(ctx extctx.ExtendedContext, params GetLocatedSupplierByIllTransactionParams) ([]LocatedSupplier, int64, error) { + rows, err := r.queries.GetLocatedSupplierByIllTransaction(ctx, r.GetConnOrTx(), params) var suppliers []LocatedSupplier + var fullCount int64 if err == nil { for _, r := range rows { + fullCount = r.FullCount suppliers = append(suppliers, r.LocatedSupplier) } } - return suppliers, err -} -func (r *PgIllRepo) ListLocatedSuppliers(ctx extctx.ExtendedContext) ([]LocatedSupplier, error) { - rows, err := r.queries.ListLocatedSuppliers(ctx, r.GetConnOrTx()) - var suppliers []LocatedSupplier - if err == nil { - for _, r := range rows { - suppliers = append(suppliers, r.LocatedSupplier) - } - } - return suppliers, err + return suppliers, fullCount, err } func (r *PgIllRepo) GetSelectedSupplierForIllTransaction(ctx extctx.ExtendedContext, illTransId string) (LocatedSupplier, error) { diff --git a/broker/oapi/open-api.yaml b/broker/oapi/open-api.yaml index 2505d8da..2e30953f 100644 --- a/broker/oapi/open-api.yaml +++ b/broker/oapi/open-api.yaml @@ -29,10 +29,97 @@ components: RequesterSymbol: name: requester_symbol in: query + description: Filter by requester symbol schema: type: string - description: Filter by requester symbol + Offset: + name: offset + in: query + description: Offset for pagination (first item is 0) + schema: + type: integer + format: int32 + Limit: + name: limit + in: query + description: Limit for pagination + schema: + type: integer + format: int32 schemas: + Error: + type: object + properties: + error: + type: string + description: Error message + IllTransactions: + type: object + required: + - items + - resultInfo + properties: + resultInfo: + $ref: '#/components/schemas/ResultInfo' + items: + type: array + description: List of ILL transactions + items: + $ref: '#/components/schemas/IllTransaction' + Events: + type: object + required: + - items + - resultInfo + properties: + resultInfo: + $ref: '#/components/schemas/ResultInfo' + items: + type: array + description: List of events + items: + $ref: '#/components/schemas/Event' + Peers: + type: object + required: + - items + - resultInfo + properties: + resultInfo: + $ref: '#/components/schemas/ResultInfo' + items: + type: array + description: List of peers + items: + $ref: '#/components/schemas/Peer' + LocatedSuppliers: + type: object + required: + - items + - resultInfo + properties: + resultInfo: + $ref: '#/components/schemas/ResultInfo' + items: + type: array + description: List of peers + items: + $ref: '#/components/schemas/LocatedSupplier' + ResultInfo: + type: object + required: + - count + properties: + count: + type: integer + format: int64 + description: Total number of items in the result + nextLink: + type: string + description: Link to the next page of results + prevLink: + type: string + description: Link to the previous page of results Event: type: object properties: @@ -253,35 +340,27 @@ paths: - $ref: '#/components/parameters/RequesterRequestId' - $ref: '#/components/parameters/IllTransactionId' - $ref: '#/components/parameters/RequesterSymbol' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Offset' responses: '200': description: Successful retrieval of events content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Event' + $ref: '#/components/schemas/Events' '400': description: Bad Request. Invalid query parameters. content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' '500': description: Internal Server Error content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' /ill_transactions/{id}: get: summary: Get an ILL transaction by ID @@ -306,31 +385,19 @@ paths: content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' '404': description: Not Found. ILL transaction not found. content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' '500': description: Internal Server Error content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' delete: summary: Delete an ILL transaction by ID parameters: @@ -345,8 +412,16 @@ paths: description: ILL transaction deleted successfully (No Content) '404': description: ILL transaction not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /ill_transactions: get: summary: Get all ILL transactions @@ -354,45 +429,33 @@ paths: - $ref: '#/components/parameters/Tenant' - $ref: '#/components/parameters/RequesterRequestId' - $ref: '#/components/parameters/RequesterSymbol' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Offset' responses: '200': - description: Successful retrieval of the ILL transaction + description: Successful retrieval of the ILL transactions content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/IllTransaction' + $ref: '#/components/schemas/IllTransactions' '400': description: Bad Request. Invalid query parameters. content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' '403': description: Forbidden. Invalid tenant. content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' '500': description: Internal Server Error content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' /peers: get: summary: Get all peers @@ -402,15 +465,27 @@ paths: schema: type: string description: Filter peers by symbol + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Offset' responses: '200': description: Successful retrieval of peers content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/Peer' + $ref: '#/components/schemas/Peers' + '400': + description: Bad Request. Invalid query parameters. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' post: summary: Create a new peer requestBody: @@ -427,9 +502,16 @@ paths: $ref: '#/components/schemas/Peer' '400': description: Bad request (e.g., invalid data) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': description: Internal server error - + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /peers/{id}: get: summary: Get a peer by symbol @@ -449,6 +531,10 @@ paths: $ref: '#/components/schemas/Peer' '404': description: Peer not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' put: summary: Update a peer parameters: @@ -468,10 +554,22 @@ paths: description: Peer updated successfully '400': description: Bad request (e.g., invalid data) + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '404': description: Peer not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' delete: summary: Delete a peer parameters: @@ -486,8 +584,16 @@ paths: description: Peer deleted successfully (No Content) '404': description: Peer not found + content: + application/json: + schema: + $ref: '#/components/schemas/Error' '500': description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/Error' /located_suppliers: get: summary: Retrieve located suppliers @@ -496,32 +602,24 @@ paths: - $ref: '#/components/parameters/RequesterRequestId' - $ref: '#/components/parameters/IllTransactionId' - $ref: '#/components/parameters/RequesterSymbol' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Offset' responses: '200': description: Successful retrieval of located suppliers content: application/json: schema: - type: array - items: - $ref: '#/components/schemas/LocatedSupplier' + $ref: '#/components/schemas/LocatedSuppliers' '400': description: Bad Request. Invalid query parameters. content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' '500': description: Internal Server Error content: application/json: schema: - type: object - properties: - error: - type: string - description: Error message + $ref: '#/components/schemas/Error' diff --git a/broker/sqlc/event_query.sql b/broker/sqlc/event_query.sql index 62dceabe..6282916a 100644 --- a/broker/sqlc/event_query.sql +++ b/broker/sqlc/event_query.sql @@ -18,19 +18,16 @@ RETURNING sqlc.embed(event_config); DELETE FROM event_config WHERE event_name = $1; - -- name: GetEvent :one SELECT sqlc.embed(event) FROM event WHERE id = $1 LIMIT 1; --- name: ListEvents :many -SELECT sqlc.embed(event) FROM event -ORDER BY timestamp; - -- name: GetIllTransactionEvents :many -SELECT sqlc.embed(event) FROM event +SELECT sqlc.embed(event), COUNT(*) OVER () as full_count +FROM event WHERE ill_transaction_id = $1 -ORDER BY timestamp; +ORDER BY timestamp +LIMIT $2 OFFSET $3; -- name: SaveEvent :one INSERT INTO event ( diff --git a/broker/sqlc/ill_query.sql b/broker/sqlc/ill_query.sql index 7a35a80a..22bb81b1 100644 --- a/broker/sqlc/ill_query.sql +++ b/broker/sqlc/ill_query.sql @@ -12,9 +12,10 @@ WHERE symbol_value = $1 LIMIT 1; -- name: ListPeers :many -SELECT sqlc.embed(peer) +SELECT sqlc.embed(peer), COUNT(*) OVER () as full_count FROM peer -ORDER BY name; +ORDER BY name +LIMIT $1 OFFSET $2; -- name: SavePeer :one INSERT INTO peer (id, name, refresh_policy, refresh_time, url, loans_count, borrows_count, vendor, custom_data) @@ -82,9 +83,17 @@ WHERE requester_request_id = $1 LIMIT 1; -- name: ListIllTransactions :many -SELECT sqlc.embed(ill_transaction) +SELECT sqlc.embed(ill_transaction), COUNT(*) OVER () as full_count +FROM ill_transaction +ORDER BY timestamp +LIMIT $1 OFFSET $2; + +-- name: GetIllTransactionsByRequesterSymbol :many +SELECT sqlc.embed(ill_transaction), COUNT(*) OVER () as full_count FROM ill_transaction -ORDER BY timestamp; +WHERE requester_symbol = $1 +ORDER BY timestamp +LIMIT $2 OFFSET $3; -- name: SaveIllTransaction :one INSERT INTO ill_transaction (id, timestamp, requester_symbol, requester_id, last_requester_action, @@ -118,16 +127,12 @@ FROM located_supplier WHERE id = $1 LIMIT 1; --- name: GetLocatedSupplierByIllTransition :many -SELECT sqlc.embed(located_supplier) +-- name: GetLocatedSupplierByIllTransaction :many +SELECT sqlc.embed(located_supplier), COUNT(*) OVER () as full_count FROM located_supplier WHERE ill_transaction_id = $1 -ORDER BY ordinal; - --- name: ListLocatedSuppliers :many -SELECT sqlc.embed(located_supplier) -FROM located_supplier -ORDER BY ill_transaction_id, ordinal; +ORDER BY ordinal +LIMIT $2 OFFSET $3; -- name: GetLocatedSupplierByIllTransactionAndStatus :many SELECT sqlc.embed(located_supplier) diff --git a/broker/test/api/api-handler_test.go b/broker/test/api/api-handler_test.go index 52e09e84..4d337667 100644 --- a/broker/test/api/api-handler_test.go +++ b/broker/test/api/api-handler_test.go @@ -10,6 +10,7 @@ import ( "net/url" "os" "strconv" + "strings" "testing" "time" @@ -73,85 +74,134 @@ func TestMain(m *testing.M) { func TestGetEvents(t *testing.T) { illId := test.GetIllTransId(t, illRepo) eventId := test.GetEventId(t, eventRepo, illId, events.EventTypeNotice, events.EventStatusSuccess, events.EventNameMessageRequester) - body := getResponseBody(t, "/events") - var resp []oapi.Event + + httpGet(t, "/events", "", http.StatusBadRequest) + + body := getResponseBody(t, "/events?ill_transaction_id="+illId) + var resp oapi.Events err := json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } - if len(resp) == 0 { - t.Errorf("Did not find events") - } - if resp[0].ID != eventId { - t.Errorf("Did not find created event") - } + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(resp.Items), 1) + assert.GreaterOrEqual(t, resp.ResultInfo.Count, int64(1)) + assert.GreaterOrEqual(t, resp.ResultInfo.Count, int64(len(resp.Items))) + assert.Equal(t, eventId, resp.Items[0].ID) - body = getResponseBody(t, "/events?ill_transaction_id="+illId) + body = getResponseBody(t, "/events?ill_transaction_id=not-exists") err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if len(resp) == 0 { - t.Errorf("did not find events") - } - if resp[0].ID != eventId { - t.Errorf("did not find created event") - } + assert.NoError(t, err) + assert.Len(t, resp.Items, 0) - body = getResponseBody(t, "/events?ill_transaction_id=not-exists") + body = getResponseBody(t, "/events?ill_transaction_id="+url.QueryEscape(illId)+"&limit=1&offset=10") err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if len(resp) > 0 { - t.Errorf("should not find events") - } + assert.NoError(t, err) + assert.Len(t, resp.Items, 0) } func TestGetIllTransactions(t *testing.T) { id := test.GetIllTransId(t, illRepo) ctx := extctx.CreateExtCtxWithArgs(context.Background(), nil) trans, err := illRepo.GetIllTransactionById(ctx, id) - if err != nil { - t.Errorf("failed to read transaction from DB: %s", err) - } - reqReqId := "reqReqId1" + assert.NoError(t, err) + reqReqId := uuid.NewString() trans.RequesterRequestID = pgtype.Text{ String: reqReqId, Valid: true, } trans, err = illRepo.SaveIllTransaction(ctx, ill_db.SaveIllTransactionParams(trans)) - if err != nil { - t.Errorf("failed to save transaction in DB: %s", err) - } + assert.NoError(t, err) body := getResponseBody(t, "/ill_transactions") - var resp []oapi.IllTransaction + var resp oapi.IllTransactions err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if len(resp) == 0 { - t.Errorf("did not find ILL transaction") - } - + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(resp.Items), 1) + assert.Equal(t, resp.ResultInfo.Count, int64(len(resp.Items))) // Query - body = getResponseBody(t, "/ill_transactions?requester_req_id="+reqReqId) + body = getResponseBody(t, "/ill_transactions?requester_req_id="+url.QueryEscape(reqReqId)) err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if reqReqId != resp[0].RequesterRequestID { - t.Errorf("expected to find with same requester request id, got: %v, expected %v", resp[0].RequesterRequestID, reqReqId) - } + assert.NoError(t, err) + assert.Equal(t, reqReqId, resp.Items[0].RequesterRequestID) body = getResponseBody(t, "/ill_transactions?requester_req_id=not-exists") err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if len(resp) > 0 { - t.Errorf("should not find transactions") + assert.NoError(t, err) + assert.Len(t, resp.Items, 0) + + for i := range 2 * api.LIMIT_DEFAULT { + requester := "ISIL:DK-BIB1" + if i > api.LIMIT_DEFAULT+3 { + requester = "ISIL:DK-BIB2" + } + illId := uuid.NewString() + reqReqId := uuid.NewString() + _, err := illRepo.SaveIllTransaction(extctx.CreateExtCtxWithArgs(context.Background(), nil), ill_db.SaveIllTransactionParams{ + ID: illId, + RequesterSymbol: pgtype.Text{ + String: requester, + Valid: true, + }, + RequesterRequestID: pgtype.Text{ + String: reqReqId, + Valid: true, + }, + Timestamp: test.GetNow(), + }) + assert.NoError(t, err) } + body = getResponseBody(t, "/ill_transactions") + err = json.Unmarshal(body, &resp) + assert.NoError(t, err) + assert.Equal(t, int(api.LIMIT_DEFAULT), len(resp.Items)) + count := resp.ResultInfo.Count + assert.GreaterOrEqual(t, count, int64(1+2*api.LIMIT_DEFAULT)) + assert.LessOrEqual(t, count, int64(3*api.LIMIT_DEFAULT)) + assert.Nil(t, resp.ResultInfo.PrevLink) + assert.NotNil(t, resp.ResultInfo.NextLink) + assert.Equal(t, getLocalhostWithPort()+"/ill_transactions?offset=10", *resp.ResultInfo.NextLink) + + body = getResponseBody(t, "/ill_transactions?offset=1000") + err = json.Unmarshal(body, &resp) + assert.NoError(t, err) + assert.Equal(t, count, resp.ResultInfo.Count) + + body = getResponseBody(t, "/ill_transactions?limit=0") + err = json.Unmarshal(body, &resp) + assert.NoError(t, err) + assert.Equal(t, count, resp.ResultInfo.Count) + + body = getResponseBody(t, "/ill_transactions?offset=3&limit="+strconv.Itoa(int(api.LIMIT_DEFAULT))) + err = json.Unmarshal(body, &resp) + assert.NoError(t, err) + assert.GreaterOrEqual(t, resp.ResultInfo.Count, int64(1+2*api.LIMIT_DEFAULT)) + assert.LessOrEqual(t, resp.ResultInfo.Count, int64(3*api.LIMIT_DEFAULT)) + prevLink := *resp.ResultInfo.PrevLink + assert.Contains(t, prevLink, "offset=0") + + body = getResponseBody(t, "/broker/ill_transactions?requester_symbol="+url.QueryEscape("ISIL:DK-BIB1")) + resp.ResultInfo.NextLink = nil + resp.ResultInfo.PrevLink = nil + err = json.Unmarshal(body, &resp) + assert.NoError(t, err) + assert.Equal(t, int(api.LIMIT_DEFAULT), len(resp.Items)) + assert.GreaterOrEqual(t, resp.ResultInfo.Count, int64(3+api.LIMIT_DEFAULT)) + assert.LessOrEqual(t, resp.ResultInfo.Count, int64(2*api.LIMIT_DEFAULT)) + + assert.Nil(t, resp.ResultInfo.PrevLink) + assert.NotNil(t, resp.ResultInfo.NextLink) + nextLink := *resp.ResultInfo.NextLink + assert.True(t, strings.HasPrefix(nextLink, getLocalhostWithPort()+"/broker/ill_transactions?")) + assert.Contains(t, nextLink, "requester_symbol="+url.QueryEscape("ISIL:DK-BIB1")) + // we have estblished that the next link is correct, now we will check if it works + hres, err := http.Get(nextLink) // nolint:gosec + assert.NoError(t, err) + defer hres.Body.Close() + body, err = io.ReadAll(hres.Body) + assert.NoError(t, err) + err = json.Unmarshal(body, &resp) + assert.NoError(t, err) + assert.NotNil(t, resp.ResultInfo.PrevLink) + prevLink = *resp.ResultInfo.PrevLink + assert.True(t, strings.HasPrefix(prevLink, getLocalhostWithPort()+"/broker/ill_transactions?")) + assert.Contains(t, prevLink, "offset=0") } func TestGetIllTransactionsId(t *testing.T) { @@ -159,12 +209,8 @@ func TestGetIllTransactionsId(t *testing.T) { body := getResponseBody(t, "/ill_transactions/"+illId) var resp oapi.IllTransaction err := json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if resp.ID != illId { - t.Errorf("did not find the same ILL transaction") - } + assert.NoError(t, err) + assert.Equal(t, illId, resp.ID) assert.Equal(t, getLocalhostWithPort()+"/events?ill_transaction_id="+url.PathEscape(illId), resp.EventsLink) assert.Equal(t, getLocalhostWithPort()+"/located_suppliers?ill_transaction_id="+url.PathEscape(illId), resp.LocatedSuppliersLink) @@ -177,39 +223,26 @@ func TestGetLocatedSuppliers(t *testing.T) { illId := test.GetIllTransId(t, illRepo) peer := test.CreatePeer(t, illRepo, "ISIL:LOC_SUP", "") locSup := test.CreateLocatedSupplier(t, illRepo, illId, peer.ID, "ISIL:LOC_SUP", string(iso18626.TypeStatusLoaned)) - body := getResponseBody(t, "/located_suppliers") - var resp []oapi.LocatedSupplier + httpGet(t, "/located_suppliers", "", http.StatusBadRequest) + var resp oapi.LocatedSuppliers + body := getResponseBody(t, "/located_suppliers?ill_transaction_id="+illId) err := json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } - if len(resp) == 0 { - t.Errorf("Did not find located suppliers") - } - if resp[0].ID != locSup.ID { - t.Errorf("Did not find created located supplier") - } + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(resp.Items), 1) + assert.Equal(t, resp.Items[0].ID, locSup.ID) + assert.GreaterOrEqual(t, resp.ResultInfo.Count, int64(len(resp.Items))) - body = getResponseBody(t, "/located_suppliers?ill_transaction_id="+illId) + body = getResponseBody(t, "/located_suppliers?ill_transaction_id="+illId+"&limit=1&offset=0") err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if len(resp) == 0 { - t.Errorf("did not find located suppliers") - } - if resp[0].ID != locSup.ID { - t.Errorf("did not find created located supplier ") - } + assert.NoError(t, err) + assert.Len(t, resp.Items, 1) + assert.Equal(t, resp.Items[0].ID, locSup.ID) + assert.GreaterOrEqual(t, resp.ResultInfo.Count, int64(len(resp.Items))) body = getResponseBody(t, "/located_suppliers?ill_transaction_id=not-exists") err = json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("failed to unmarshal json: %s", err) - } - if len(resp) > 0 { - t.Errorf("should not find located suppliers") - } + assert.NoError(t, err) + assert.Len(t, resp.Items, 0) } func TestBrokerCRUD(t *testing.T) { @@ -252,57 +285,71 @@ func TestBrokerCRUD(t *testing.T) { assert.Equal(t, 0, len(httpGetTrans(t, "/broker/ill_transactions", "ruc", http.StatusOK))) + assert.Equal(t, 0, len(httpGetTrans(t, "/broker/ill_transactions", "", http.StatusOK))) + body = httpGet(t, "/broker/ill_transactions?requester_req_id="+url.QueryEscape(reqReqId), "diku", http.StatusOK) - var trans []oapi.IllTransaction - err = json.Unmarshal(body, &trans) + var resp oapi.IllTransactions + err = json.Unmarshal(body, &resp) assert.NoError(t, err) - assert.Len(t, trans, 1) - assert.Equal(t, illId, trans[0].ID) + assert.Len(t, resp.Items, 1) + assert.Equal(t, illId, resp.Items[0].ID) peer := test.CreatePeer(t, illRepo, "ISIL:LOC_OTHER", "") locSup := test.CreateLocatedSupplier(t, illRepo, illId, peer.ID, "ISIL:LOC_OTHER", string(iso18626.TypeStatusLoaned)) body = httpGet(t, "/broker/located_suppliers?requester_req_id="+url.QueryEscape(reqReqId), "diku", http.StatusOK) - var supps []oapi.LocatedSupplier + var supps oapi.LocatedSuppliers err = json.Unmarshal(body, &supps) assert.NoError(t, err) - assert.Len(t, supps, 1) - assert.Equal(t, locSup.ID, supps[0].ID) + assert.Len(t, supps.Items, 1) + assert.Equal(t, locSup.ID, supps.Items[0].ID) body = httpGet(t, "/broker/located_suppliers?ill_transaction_id="+url.QueryEscape(illId), "diku", http.StatusOK) err = json.Unmarshal(body, &supps) assert.NoError(t, err) - assert.Len(t, supps, 1) - assert.Equal(t, locSup.ID, supps[0].ID) + assert.Len(t, supps.Items, 1) + assert.Equal(t, locSup.ID, supps.Items[0].ID) - assert.Equal(t, []byte("[]"), httpGet(t, "/broker/located_suppliers?requester_req_id="+url.QueryEscape(reqReqId), "ruc", http.StatusOK)) + body = httpGet(t, "/broker/located_suppliers?requester_req_id="+url.QueryEscape(reqReqId), "ruc", http.StatusOK) + err = json.Unmarshal(body, &supps) + assert.NoError(t, err) + assert.Len(t, supps.Items, 0) - assert.Equal(t, []byte("[]"), httpGet(t, "/broker/located_suppliers?requester_req_id="+url.QueryEscape(uuid.NewString()), "diku", http.StatusOK)) + body = httpGet(t, "/broker/located_suppliers?requester_req_id="+url.QueryEscape(uuid.NewString()), "diku", http.StatusOK) + err = json.Unmarshal(body, &supps) + assert.NoError(t, err) + assert.Len(t, supps.Items, 0) eventId := test.GetEventId(t, eventRepo, illId, events.EventTypeNotice, events.EventStatusSuccess, events.EventNameMessageRequester) body = httpGet(t, "/broker/events?requester_req_id="+url.QueryEscape(reqReqId), "diku", http.StatusOK) - var events []oapi.Event + var events oapi.Events err = json.Unmarshal(body, &events) assert.NoError(t, err) - assert.Len(t, events, 1) - assert.Equal(t, eventId, events[0].ID) + assert.Len(t, events.Items, 1) + assert.Equal(t, eventId, events.Items[0].ID) body = httpGet(t, "/broker/events?requester_req_id="+url.QueryEscape(reqReqId)+"&requester_symbol="+url.QueryEscape("ISIL:DK-DIKU"), "", http.StatusOK) err = json.Unmarshal(body, &events) assert.NoError(t, err) - assert.Len(t, events, 1) - assert.Equal(t, eventId, events[0].ID) + assert.Len(t, events.Items, 1) + assert.Equal(t, eventId, events.Items[0].ID) body = httpGet(t, "/broker/events?ill_transaction_id="+url.QueryEscape(illId), "diku", http.StatusOK) err = json.Unmarshal(body, &events) assert.NoError(t, err) - assert.Len(t, events, 1) - assert.Equal(t, eventId, events[0].ID) + assert.Len(t, events.Items, 1) + assert.Equal(t, eventId, events.Items[0].ID) - assert.Equal(t, []byte("[]"), httpGet(t, "/broker/events?requester_req_id="+url.QueryEscape(reqReqId), "ruc", http.StatusOK)) + body = httpGet(t, "/broker/events?requester_req_id="+url.QueryEscape(reqReqId), "ruc", http.StatusOK) + err = json.Unmarshal(body, &events) + assert.NoError(t, err) + assert.Len(t, events.Items, 0) - assert.Equal(t, []byte("[]"), httpGet(t, "/broker/events?requester_req_id="+url.QueryEscape(uuid.NewString()), "diku", http.StatusOK)) + body = httpGet(t, "/broker/events?requester_req_id="+url.QueryEscape(uuid.NewString()), "diku", http.StatusOK) + err = json.Unmarshal(body, &events) + assert.NoError(t, err) + assert.Len(t, events.Items, 0) } func TestPeersCRUD(t *testing.T) { @@ -321,63 +368,52 @@ func TestPeersCRUD(t *testing.T) { body := httpRequest(t, "POST", "/peers", jsonBytes, "", http.StatusCreated) var respPeer oapi.Peer err = json.Unmarshal(body, &respPeer) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } - if toCreate.ID != respPeer.ID { - t.Errorf("expected same peer %s got %s", toCreate.ID, respPeer.ID) - } + assert.NoError(t, err) + assert.Equal(t, toCreate.ID, respPeer.ID) // Cannot post same again httpRequest(t, "POST", "/peers", jsonBytes, "", http.StatusBadRequest) // Update peer toCreate.Name = "Updated" jsonBytes, err = json.Marshal(toCreate) - if err != nil { - t.Errorf("Error marshaling JSON: %s", err) - } + assert.NoError(t, err) body = httpRequest(t, "PUT", "/peers/"+toCreate.ID, jsonBytes, "", http.StatusOK) err = json.Unmarshal(body, &respPeer) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } - if toCreate.ID != respPeer.ID { - t.Errorf("expected same peer %s got %s", toCreate.ID, respPeer.ID) - } - - if respPeer.Name != "Updated" { - t.Errorf("expected same peer name 'Updated' got %s", respPeer.Name) - } + assert.NoError(t, err) + assert.Equal(t, toCreate.ID, respPeer.ID) + assert.Equal(t, "Updated", respPeer.Name) // Get peer respPeer = getPeerById(t, toCreate.ID) - if toCreate.ID != respPeer.ID { - t.Errorf("expected same peer %s got %s", toCreate.ID, respPeer.ID) - } + assert.Equal(t, toCreate.ID, respPeer.ID) // Get peers respPeers := getPeers(t) - if len(respPeers) < 1 { - t.Errorf("Did not find peers") - } + assert.GreaterOrEqual(t, len(respPeers.Items), 1) + + body = getResponseBody(t, "/peers?offset=0&limit=1") + err = json.Unmarshal(body, &respPeers) + assert.NoError(t, err) + assert.GreaterOrEqual(t, respPeers.ResultInfo.Count, int64(1)) + + httpGet(t, "/peers?cql="+url.QueryEscape("badfield any ISIL:PEER"), "", http.StatusBadRequest) + + httpGet(t, "/peers?cql="+url.QueryEscape("("), "", http.StatusBadRequest) + // Query peers body = getResponseBody(t, "/peers?cql="+url.QueryEscape("symbol any ISIL:PEER")) err = json.Unmarshal(body, &respPeers) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } - if toCreate.ID != respPeers[0].ID { - t.Errorf("expected same peer %s got %s", toCreate.ID, respPeers[0].ID) - } + assert.NoError(t, err) + assert.GreaterOrEqual(t, len(respPeers.Items), 1) + assert.Equal(t, toCreate.ID, respPeers.Items[0].ID) + // Delete peer httpRequest(t, "DELETE", "/peers/"+toCreate.ID, nil, "", http.StatusNoContent) httpRequest(t, "DELETE", "/peers/"+toCreate.ID, nil, "", http.StatusNotFound) // Check no peers left respPeers = getPeers(t) - for _, p := range respPeers { - if p.ID == toCreate.ID { - t.Errorf("Expected this peer %s to be deleted", toCreate.ID) - } + for _, p := range respPeers.Items { + assert.NotEqual(t, toCreate.ID, p.ID) } } @@ -417,7 +453,8 @@ func TestNotFound(t *testing.T) { func TestGetEventsDbError(t *testing.T) { req, _ := http.NewRequest("GET", "/", nil) rr := httptest.NewRecorder() - handlerMock.GetEvents(rr, req, oapi.GetEventsParams{}) + reqId := uuid.New().String() + handlerMock.GetEvents(rr, req, oapi.GetEventsParams{RequesterReqId: &reqId}) if status := rr.Code; status != http.StatusInternalServerError { t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError) @@ -510,20 +547,19 @@ func TestPutPeersSymbolDbError(t *testing.T) { func TestGetLocatedSuppliersDbError(t *testing.T) { req, _ := http.NewRequest("GET", "/", nil) rr := httptest.NewRecorder() - handlerMock.GetLocatedSuppliers(rr, req, oapi.GetLocatedSuppliersParams{}) + reqReqId := uuid.New().String() + handlerMock.GetLocatedSuppliers(rr, req, oapi.GetLocatedSuppliersParams{RequesterReqId: &reqReqId}) if status := rr.Code; status != http.StatusInternalServerError { t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusInternalServerError) } } -func getPeers(t *testing.T) []oapi.Peer { +func getPeers(t *testing.T) oapi.Peers { body := getResponseBody(t, "/peers") - var respPeers []oapi.Peer + var respPeers oapi.Peers err := json.Unmarshal(body, &respPeers) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } + assert.NoError(t, err) return respPeers } @@ -531,9 +567,7 @@ func getPeerById(t *testing.T, symbol string) oapi.Peer { body := getResponseBody(t, "/peers/"+symbol) var resp oapi.Peer err := json.Unmarshal(body, &resp) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } + assert.NoError(t, err) return resp } @@ -554,20 +588,18 @@ func httpRequest(t *testing.T, method string, uriPath string, reqbytes []byte, t hres, err := client.Do(hreq) assert.NoError(t, err) defer hres.Body.Close() - assert.Equal(t, expectStatus, hres.StatusCode) body, err := io.ReadAll(hres.Body) + assert.Equal(t, expectStatus, hres.StatusCode, string(body)) assert.NoError(t, err) return body } func httpGetTrans(t *testing.T, uriPath string, tenant string, expectStatus int) []oapi.IllTransaction { body := httpRequest(t, "GET", uriPath, nil, tenant, expectStatus) - var res []oapi.IllTransaction + var res oapi.IllTransactions err := json.Unmarshal(body, &res) - if err != nil { - t.Errorf("Failed to unmarshal json: %s", err) - } - return res + assert.NoError(t, err) + return res.Items } func httpGet(t *testing.T, uriPath string, tenant string, expectStatus int) []byte { diff --git a/broker/test/mock_eventrepo.go b/broker/test/mock_eventrepo.go index 103b683d..de79f66f 100644 --- a/broker/test/mock_eventrepo.go +++ b/broker/test/mock_eventrepo.go @@ -2,6 +2,7 @@ package test import ( "errors" + "github.com/google/uuid" extctx "github.com/indexdata/crosslink/broker/common" "github.com/indexdata/crosslink/broker/events" @@ -60,15 +61,10 @@ func (r *MockEventRepositorySuccess) Notify(ctx extctx.ExtendedContext, eventId return nil } -func (r *MockEventRepositorySuccess) GetIllTransactionEvents(ctx extctx.ExtendedContext, illTransactionId string) ([]events.Event, error) { - return []events.Event{{ - ID: uuid.New().String(), - }}, nil -} -func (r *MockEventRepositorySuccess) ListEvents(ctx extctx.ExtendedContext) ([]events.Event, error) { +func (r *MockEventRepositorySuccess) GetIllTransactionEvents(ctx extctx.ExtendedContext, params events.GetIllTransactionEventsParams) ([]events.Event, int64, error) { return []events.Event{{ ID: uuid.New().String(), - }}, nil + }}, 0, nil } func (r *MockEventRepositorySuccess) DeleteEventsByIllTransaction(ctx extctx.ExtendedContext, illTransId string) error { @@ -99,12 +95,10 @@ func (r *MockEventRepositoryError) Notify(ctx extctx.ExtendedContext, eventId st return errors.New("DB error") } -func (r *MockEventRepositoryError) GetIllTransactionEvents(ctx extctx.ExtendedContext, illTransactionId string) ([]events.Event, error) { - return []events.Event{}, errors.New("DB error") -} -func (r *MockEventRepositoryError) ListEvents(ctx extctx.ExtendedContext) ([]events.Event, error) { - return []events.Event{}, errors.New("DB error") +func (r *MockEventRepositoryError) GetIllTransactionEvents(ctx extctx.ExtendedContext, params events.GetIllTransactionEventsParams) ([]events.Event, int64, error) { + return []events.Event{}, 0, errors.New("DB error") } + func (r *MockEventRepositoryError) DeleteEventsByIllTransaction(ctx extctx.ExtendedContext, illTransId string) error { return errors.New("DB error") } diff --git a/broker/test/mock_illrepo.go b/broker/test/mock_illrepo.go index 1baad647..462ffcb5 100644 --- a/broker/test/mock_illrepo.go +++ b/broker/test/mock_illrepo.go @@ -103,15 +103,22 @@ func (r *MockIllRepositorySuccess) GetIllTransactionByRequesterRequestIdForUpdat }, nil } -func (r *MockIllRepositorySuccess) ListIllTransactions(ctx extctx.ExtendedContext) ([]ill_db.IllTransaction, error) { +func (r *MockIllRepositorySuccess) ListIllTransactions(ctx extctx.ExtendedContext, params ill_db.ListIllTransactionsParams) ([]ill_db.IllTransaction, int64, error) { return []ill_db.IllTransaction{{ ID: "id", - }}, nil + }}, 0, nil } -func (r *MockIllRepositorySuccess) ListPeers(ctx extctx.ExtendedContext) ([]ill_db.Peer, error) { + +func (r *MockIllRepositorySuccess) GetIllTransactionsByRequesterSymbol(ctx extctx.ExtendedContext, params ill_db.GetIllTransactionsByRequesterSymbolParams) ([]ill_db.IllTransaction, int64, error) { + return []ill_db.IllTransaction{{ + ID: "id", + }}, 0, nil +} + +func (r *MockIllRepositorySuccess) ListPeers(ctx extctx.ExtendedContext, params ill_db.ListPeersParams) ([]ill_db.Peer, int64, error) { return []ill_db.Peer{{ ID: uuid.New().String(), - }}, nil + }}, 0, nil } func (r *MockIllRepositorySuccess) DeletePeer(ctx extctx.ExtendedContext, id string) error { return nil @@ -129,18 +136,18 @@ func (r *MockIllRepositorySuccess) GetCachedPeersBySymbols(ctx extctx.ExtendedCo return []ill_db.Peer{{ID: uuid.NewString()}}, "" } -func (r *MockIllRepositorySuccess) GetLocatedSupplierByIllTransition(ctx extctx.ExtendedContext, illTransactionID string) ([]ill_db.LocatedSupplier, error) { - return []ill_db.LocatedSupplier{{ID: uuid.NewString(), IllTransactionID: illTransactionID}}, nil -} -func (r *MockIllRepositorySuccess) ListLocatedSuppliers(ctx extctx.ExtendedContext) ([]ill_db.LocatedSupplier, error) { - return []ill_db.LocatedSupplier{{ID: uuid.NewString()}}, nil +func (r *MockIllRepositorySuccess) GetLocatedSupplierByIllTransaction(ctx extctx.ExtendedContext, params ill_db.GetLocatedSupplierByIllTransactionParams) ([]ill_db.LocatedSupplier, int64, error) { + return []ill_db.LocatedSupplier{{ID: uuid.NewString(), IllTransactionID: params.IllTransactionID}}, 0, nil } + func (r *MockIllRepositorySuccess) SaveSymbol(ctx extctx.ExtendedContext, params ill_db.SaveSymbolParams) (ill_db.Symbol, error) { return ill_db.Symbol(params), nil } + func (r *MockIllRepositorySuccess) DeleteSymbolByPeerId(ctx extctx.ExtendedContext, peerId string) error { return nil } + func (r *MockIllRepositorySuccess) GetSymbolsByPeerId(ctx extctx.ExtendedContext, peerId string) ([]ill_db.Symbol, error) { return []ill_db.Symbol{{ SymbolValue: "ISIL:SUP1", @@ -223,12 +230,18 @@ func (r *MockIllRepositoryError) GetIllTransactionByRequesterRequestIdForUpdate( return ill_db.IllTransaction{}, errors.New("DB error") } -func (r *MockIllRepositoryError) ListIllTransactions(ctx extctx.ExtendedContext) ([]ill_db.IllTransaction, error) { - return []ill_db.IllTransaction{}, errors.New("DB error") +func (r *MockIllRepositoryError) ListIllTransactions(ctx extctx.ExtendedContext, params ill_db.ListIllTransactionsParams) ([]ill_db.IllTransaction, int64, error) { + return []ill_db.IllTransaction{}, 0, errors.New("DB error") } -func (r *MockIllRepositoryError) ListPeers(ctx extctx.ExtendedContext) ([]ill_db.Peer, error) { - return []ill_db.Peer{{}}, errors.New("DB error") + +func (r *MockIllRepositoryError) GetIllTransactionsByRequesterSymbol(ctx extctx.ExtendedContext, params ill_db.GetIllTransactionsByRequesterSymbolParams) ([]ill_db.IllTransaction, int64, error) { + return []ill_db.IllTransaction{}, 0, errors.New("DB error") } + +func (r *MockIllRepositoryError) ListPeers(ctx extctx.ExtendedContext, params ill_db.ListPeersParams) ([]ill_db.Peer, int64, error) { + return []ill_db.Peer{{}}, 0, errors.New("DB error") +} + func (r *MockIllRepositoryError) DeletePeer(ctx extctx.ExtendedContext, id string) error { return errors.New("DB error") } @@ -245,18 +258,18 @@ func (r *MockIllRepositoryError) GetCachedPeersBySymbols(ctx extctx.ExtendedCont return []ill_db.Peer{}, "" } -func (r *MockIllRepositoryError) GetLocatedSupplierByIllTransition(ctx extctx.ExtendedContext, illTransactionID string) ([]ill_db.LocatedSupplier, error) { - return []ill_db.LocatedSupplier{}, errors.New("DB error") -} -func (r *MockIllRepositoryError) ListLocatedSuppliers(ctx extctx.ExtendedContext) ([]ill_db.LocatedSupplier, error) { - return []ill_db.LocatedSupplier{}, errors.New("DB error") +func (r *MockIllRepositoryError) GetLocatedSupplierByIllTransaction(ctx extctx.ExtendedContext, params ill_db.GetLocatedSupplierByIllTransactionParams) ([]ill_db.LocatedSupplier, int64, error) { + return []ill_db.LocatedSupplier{}, 0, errors.New("DB error") } + func (r *MockIllRepositoryError) SaveSymbol(ctx extctx.ExtendedContext, params ill_db.SaveSymbolParams) (ill_db.Symbol, error) { return ill_db.Symbol{}, errors.New("DB error") } + func (r *MockIllRepositoryError) DeleteSymbolByPeerId(ctx extctx.ExtendedContext, peerId string) error { return errors.New("DB error") } + func (r *MockIllRepositoryError) GetSymbolsByPeerId(ctx extctx.ExtendedContext, peerId string) ([]ill_db.Symbol, error) { return []ill_db.Symbol{}, errors.New("DB error") } @@ -264,12 +277,15 @@ func (r *MockIllRepositoryError) GetSymbolsByPeerId(ctx extctx.ExtendedContext, func (r *MockIllRepositoryError) DeleteLocatedSupplierByIllTransaction(ctx extctx.ExtendedContext, illTransId string) error { return errors.New("DB error") } + func (r *MockIllRepositoryError) DeleteIllTransaction(ctx extctx.ExtendedContext, id string) error { return errors.New("DB error") } + func (r *MockIllRepositoryError) GetIllTransactionByRequesterId(ctx extctx.ExtendedContext, peerId pgtype.Text) ([]ill_db.IllTransaction, error) { return []ill_db.IllTransaction{}, errors.New("DB error") } + func (r *MockIllRepositoryError) GetLocatedSupplierByPeerId(ctx extctx.ExtendedContext, peerId string) ([]ill_db.LocatedSupplier, error) { return []ill_db.LocatedSupplier{}, errors.New("DB error") } diff --git a/broker/test/utils.go b/broker/test/utils.go index aad78f08..95596af9 100644 --- a/broker/test/utils.go +++ b/broker/test/utils.go @@ -178,7 +178,12 @@ func EventsToCompareString(appCtx extctx.ExtendedContext, eventRepo events.Event var err error WaitForPredicateToBeTrue(func() bool { - eventList, err = eventRepo.GetIllTransactionEvents(appCtx, illId) + params := events.GetIllTransactionEventsParams{ + IllTransactionID: illId, + Limit: 100, + Offset: 0, + } + eventList, _, err = eventRepo.GetIllTransactionEvents(appCtx, params) if err != nil { t.Errorf("failed to find events for ill transaction id %v", illId) }