Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions broker/migrations/023_add_terminal_state.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
ALTER TABLE patron_request DROP COLUMN IF EXISTS terminal_state CASCADE;

DROP VIEW IF EXISTS patron_request_search_view;
CREATE OR REPLACE VIEW patron_request_search_view AS
SELECT
pr.*,
EXISTS (
SELECT 1
FROM notification n
WHERE n.pr_id = pr.id
) AS has_notification,
EXISTS (
SELECT 1
FROM notification n
WHERE n.pr_id = pr.id and cost is not null
) AS has_cost,
EXISTS (
SELECT 1
FROM notification n
WHERE n.pr_id = pr.id and acknowledged_at is null
) AS has_unread_notification,
pr.ill_request -> 'serviceInfo' ->> 'serviceType' AS service_type,
pr.ill_request -> 'serviceInfo' -> 'serviceLevel' ->> '#text' AS service_level,
immutable_to_timestamp(pr.ill_request -> 'serviceInfo' ->> 'needBeforeDate') AS needed_at
FROM patron_request pr;

31 changes: 31 additions & 0 deletions broker/migrations/023_add_terminal_state.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
ALTER TABLE patron_request ADD COLUMN terminal_state BOOLEAN NOT NULL DEFAULT false;

UPDATE patron_request
SET terminal_state = true
WHERE state IN ('COMPLETED', 'CANCELLED', 'UNFILLED');

CREATE INDEX idx_pr_terminal_state ON patron_request (terminal_state);

DROP VIEW IF EXISTS patron_request_search_view;
CREATE OR REPLACE VIEW patron_request_search_view AS
SELECT
pr.*,
EXISTS (
SELECT 1
FROM notification n
WHERE n.pr_id = pr.id
) AS has_notification,
EXISTS (
SELECT 1
FROM notification n
WHERE n.pr_id = pr.id and cost is not null
) AS has_cost,
EXISTS (
SELECT 1
FROM notification n
WHERE n.pr_id = pr.id and acknowledged_at is null
) AS has_unread_notification,
pr.ill_request -> 'serviceInfo' ->> 'serviceType' AS service_type,
pr.ill_request -> 'serviceInfo' -> 'serviceLevel' ->> '#text' AS service_level,
immutable_to_timestamp(pr.ill_request -> 'serviceInfo' ->> 'needBeforeDate') AS needed_at
FROM patron_request pr;
6 changes: 5 additions & 1 deletion broker/oapi/open-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -482,13 +482,17 @@ components:
description: List of patron request items
items:
$ref: '#/components/schemas/PrItem'
terminalState:
type: boolean
description: Indicates if the request is in terminal state
required:
- id
- timestamp
- state
- side
- illRequest
- needsAttention
- terminalState
PatronRequests:
type: object
required:
Expand Down Expand Up @@ -1169,7 +1173,7 @@ paths:
Query parameter cql can be used to filter the results.
With cql you can use these fields state, side, requester_symbol, supplier_symbol, needs_attention,
has_notification, has_cost, has_unread_notification, service_type, service_level, created_at, needed_at,
requester_req_id, title, patron, cql.serverChoice.
requester_req_id, title, patron, terminal_state, cql.serverChoice.
tags:
- patron-requests-api
parameters:
Expand Down
2 changes: 2 additions & 0 deletions broker/patron_request/api/api-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ func toApiPatronRequest(request pr_db.PatronRequest, illRequest iso18626.Request
LastActionOutcome: toString(request.LastActionOutcome),
LastActionResult: toString(request.LastActionResult),
Items: &items,
TerminalState: request.TerminalState,
}
}

Expand Down Expand Up @@ -698,6 +699,7 @@ func buildDbPatronRequest(
RequesterReqID: getDbText(&requesterReqId),
Language: pr_db.LANGUAGE,
Items: []pr_db.PrItem{},
TerminalState: false,
// LastAction, LastActionOutcome and LastActionResult are not set on creation
// they will be updated when the first action is executed.
}
Expand Down
4 changes: 4 additions & 0 deletions broker/patron_request/db/prcql.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ func handlePatronRequestsQuery(cqlString string, noBaseArgs int) (pgcql.Query, e
fb = pgcql.NewFieldBool()
def.AddField("has_unread_notification", fb)

fb = pgcql.NewFieldBool()
def.AddField("terminal_state", fb)

f = pgcql.NewFieldString().WithExact()
def.AddField("service_type", f)

Expand Down Expand Up @@ -132,6 +135,7 @@ func (q *Queries) ListPatronRequestsCql(ctx context.Context, db DBTX, arg ListPa
&i.LastActionResult,
&i.Language,
&i.Items,
&i.TerminalState,
&i.FullCount,
); err != nil {
return nil, err
Expand Down
1 change: 1 addition & 0 deletions broker/patron_request/db/prrepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func (r *PgPrRepo) ListPatronRequests(ctx common.ExtendedContext, params ListPat
LastActionResult: r.LastActionResult,
Language: r.Language,
Items: r.Items,
TerminalState: r.TerminalState,
})
}
} else {
Expand Down
3 changes: 3 additions & 0 deletions broker/patron_request/service/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ func (a *PatronRequestActionService) finalizeActionExecution(ctx common.Extended
stateChanged := false
if transitionState, ok := actionMapping.GetActionTransition(currentPr, action, outcome); ok && transitionState != updatedPr.State {
updatedPr.State = transitionState
if config, configOk := actionMapping.getStateConfig(updatedPr); configOk && config.terminal {
updatedPr.TerminalState = true
}
toState := string(transitionState)
execResult.result.ActionResult.ToState = &toState
stateChanged = true
Expand Down
9 changes: 7 additions & 2 deletions broker/patron_request/service/action_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type stateConfig struct {
actions map[pr_db.PatronRequestAction]proapi.ModelAction
events map[string]proapi.ModelEvent
autoActions []pr_db.PatronRequestAction
terminal bool
}

// Constructor function to initialize the mappings for given StateModel
Expand All @@ -72,8 +73,12 @@ func NewActionMapping(stateModel *proapi.StateModel) *ActionMapping {
for _, state := range stateModel.States {
stateName := pr_db.PatronRequestState(state.Name)
currentStateConfig := stateConfig{
actions: make(map[pr_db.PatronRequestAction]proapi.ModelAction),
events: make(map[string]proapi.ModelEvent),
actions: make(map[pr_db.PatronRequestAction]proapi.ModelAction),
events: make(map[string]proapi.ModelEvent),
terminal: false,
}
if state.Terminal != nil && *state.Terminal {
currentStateConfig.terminal = true
}
actionEntries := make([]PatronRequestAction, 0)
if state.Actions != nil {
Expand Down
4 changes: 4 additions & 0 deletions broker/patron_request/service/message-handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ func (m *PatronRequestMessageHandler) applyEventTransition(pr pr_db.PatronReques
}
if hasTransition && transitionState != pr.State {
pr.State = transitionState
if config, configOk := actionMapping.getStateConfig(pr); configOk && config.terminal {
pr.TerminalState = true
}
return pr, true, true, nil
}
return pr, false, true, nil
Expand Down Expand Up @@ -385,6 +388,7 @@ func (m *PatronRequestMessageHandler) handleRequestMessage(ctx common.ExtendedCo
RequesterReqID: getDbText(raRequestId),
Language: pr_db.LANGUAGE,
Items: []pr_db.PrItem{},
TerminalState: false,
})
if err != nil {
status, response, handleErr := createRequestResponse(request, iso18626.TypeMessageStatusERROR, &iso18626.ErrorData{
Expand Down
9 changes: 5 additions & 4 deletions broker/sqlc/pr_query.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ WHERE id = $1
LIMIT 1;

-- name: ListPatronRequests :many
SELECT id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, language, items, COUNT(*) OVER () as full_count
SELECT id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, language, items, terminal_state, COUNT(*) OVER () as full_count
FROM patron_request_search_view
ORDER BY timestamp
LIMIT $1 OFFSET $2;
Expand All @@ -33,13 +33,14 @@ SET timestamp = $2,
last_action_outcome = $13,
last_action_result = $14,
items = $15,
language = $16
language = $16,
terminal_state = $17
WHERE id = $1
RETURNING sqlc.embed(patron_request);

-- name: CreatePatronRequest :one
INSERT INTO patron_request (id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, items, language)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
INSERT INTO patron_request (id, timestamp, ill_request, state, side, patron, requester_symbol, supplier_symbol, tenant, requester_req_id, needs_attention, last_action, last_action_outcome, last_action_result, items, language, terminal_state)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
RETURNING sqlc.embed(patron_request);

-- name: DeletePatronRequest :exec
Expand Down
3 changes: 2 additions & 1 deletion broker/sqlc/pr_schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ CREATE TABLE patron_request
last_action_outcome VARCHAR,
last_action_result VARCHAR,
items JSONB NOT NULL DEFAULT '[]'::jsonb,
language regconfig NOT NULL DEFAULT 'english'
language regconfig NOT NULL DEFAULT 'english',
terminal_state BOOLEAN NOT NULL DEFAULT false
);

CREATE OR REPLACE FUNCTION get_next_hrid(prefix VARCHAR) RETURNS VARCHAR AS $$
Expand Down
7 changes: 5 additions & 2 deletions broker/test/patron_request/api/api-handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func TestCrud(t *testing.T) {
"%20and%20requester_req_id%3D"+*foundPr.RequesterRequestId+"%20and%20needs_attention%3Dfalse%20and%20"+
"has_notification%3Dfalse%20and%20has_cost%3Dfalse%20and%20has_unread_notification%3Dfalse%20and%20"+
"service_type%3DCopy%20and%20service_level%3DCopy%20and%20created_at%3E2026-03-16%20and%20needed_at%3E2026-03-16"+
"%20and%20title%3D%22Typed%20request%20round%20trip%22%20and%20patron%3Dp1%20and%20cql.serverChoice%20all%20round", []byte{}, 200)
"%20and%20title%3D%22Typed%20request%20round%20trip%22%20and%20patron%3Dp1%20and%20cql.serverChoice%20all%20round%20and%20terminal_state%3Dfalse", []byte{}, 200)
err = json.Unmarshal(respBytes, &foundPrs)
assert.NoError(t, err, "failed to unmarshal patron request")

Expand Down Expand Up @@ -483,6 +483,7 @@ func TestActionsToCompleteState(t *testing.T) {
err = json.Unmarshal(respBytes, &foundPr)
assert.NoError(t, err, "failed to unmarshal patron request")
assert.Equal(t, string(prservice.BorrowerStateCompleted), foundPr.State)
assert.True(t, foundPr.TerminalState)

// Check requester patron request event count
respBytes = httpRequest(t, "GET", requesterPrPath+"/events"+queryParams, []byte{}, 200)
Expand Down Expand Up @@ -511,6 +512,7 @@ func TestActionsToCompleteState(t *testing.T) {
assert.NoError(t, err, "failed to unmarshal patron request")
assert.Equal(t, supPr.ID, foundPr.Id)
assert.Equal(t, string(prservice.LenderStateCompleted), foundPr.State)
assert.True(t, foundPr.TerminalState)

// Check supplier patron request event count
respBytes = httpRequest(t, "GET", supplierPrPath+"/events"+supQueryParams, []byte{}, 200)
Expand Down Expand Up @@ -608,7 +610,8 @@ func TestServerChoice(t *testing.T) {
PatronId: "PP-789",
},
},
Items: []pr_db.PrItem{},
Items: []pr_db.PrItem{},
TerminalState: false,
})
assert.NoError(t, err)
itemId := uuid.NewString()
Expand Down
10 changes: 6 additions & 4 deletions broker/test/patron_request/db/prrepo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ func TestItem(t *testing.T) {
Time: time.Now(),
Valid: true,
},
Language: "english",
Items: []pr_db.PrItem{},
Language: "english",
Items: []pr_db.PrItem{},
TerminalState: false,
})
assert.NoError(t, err)

Expand Down Expand Up @@ -165,8 +166,9 @@ func TestNotification(t *testing.T) {
Time: time.Now(),
Valid: true,
},
Language: "english",
Items: []pr_db.PrItem{},
Language: "english",
Items: []pr_db.PrItem{},
TerminalState: false,
})
assert.NoError(t, err)

Expand Down
Loading