diff --git a/roles/api/files/replace_metadata.json b/roles/api/files/replace_metadata.json index 071cd714d8..823cc465e2 100644 --- a/roles/api/files/replace_metadata.json +++ b/roles/api/files/replace_metadata.json @@ -36740,6 +36740,7 @@ "locked", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36766,6 +36767,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36793,6 +36795,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36820,6 +36823,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36846,6 +36850,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36872,6 +36877,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36898,6 +36904,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36924,6 +36931,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36951,6 +36959,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -36978,6 +36987,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -37004,6 +37014,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -37031,6 +37042,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -37058,6 +37070,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", @@ -37084,6 +37097,7 @@ "id", "assigned_group", "external_ticket_id", + "pre_workflow_ticket_reference", "requester_dn", "requester_group", "title", diff --git a/roles/common/files/fwo-api-calls/request/fragments/ticketDetails.graphql b/roles/common/files/fwo-api-calls/request/fragments/ticketDetails.graphql index a8c0805419..eed244d840 100644 --- a/roles/common/files/fwo-api-calls/request/fragments/ticketDetails.graphql +++ b/roles/common/files/fwo-api-calls/request/fragments/ticketDetails.graphql @@ -16,6 +16,7 @@ fragment ticketDetails on request_ticket { reason external_ticket_id external_ticket_source + pre_workflow_ticket_reference ticket_deadline ticket_priority locked diff --git a/roles/common/files/fwo-api-calls/request/fragments/ticketDetailsReqTaskOverview.graphql b/roles/common/files/fwo-api-calls/request/fragments/ticketDetailsReqTaskOverview.graphql index 7b73959ce0..2b65df888a 100644 --- a/roles/common/files/fwo-api-calls/request/fragments/ticketDetailsReqTaskOverview.graphql +++ b/roles/common/files/fwo-api-calls/request/fragments/ticketDetailsReqTaskOverview.graphql @@ -16,6 +16,7 @@ fragment ticketDetailsReqTaskOverview on request_ticket { reason external_ticket_id external_ticket_source + pre_workflow_ticket_reference ticket_deadline ticket_priority locked diff --git a/roles/common/files/fwo-api-calls/request/fragments/ticketOverview.graphql b/roles/common/files/fwo-api-calls/request/fragments/ticketOverview.graphql index 58d10c7822..d7fdecae60 100644 --- a/roles/common/files/fwo-api-calls/request/fragments/ticketOverview.graphql +++ b/roles/common/files/fwo-api-calls/request/fragments/ticketOverview.graphql @@ -15,6 +15,7 @@ fragment ticketOverview on request_ticket { reason external_ticket_id external_ticket_source + pre_workflow_ticket_reference ticket_deadline ticket_priority locked diff --git a/roles/common/files/fwo-api-calls/request/newTicket.graphql b/roles/common/files/fwo-api-calls/request/newTicket.graphql index b87e93e3a5..3dbae502c9 100644 --- a/roles/common/files/fwo-api-calls/request/newTicket.graphql +++ b/roles/common/files/fwo-api-calls/request/newTicket.graphql @@ -5,6 +5,7 @@ mutation newTicket( $requesterId: Int $deadline: timestamp $priority: Int + $preWorkflowTicketReference: String $locked: Boolean $requestTasks: request_reqtask_arr_rel_insert_input ) { @@ -15,6 +16,7 @@ mutation newTicket( requester_id: $requesterId ticket_deadline: $deadline ticket_priority: $priority + pre_workflow_ticket_reference: $preWorkflowTicketReference locked: $locked reqtasks: $requestTasks }) { diff --git a/roles/database/files/sql/creation/fworch-create-tables-request.sql b/roles/database/files/sql/creation/fworch-create-tables-request.sql index 6589fd0f05..cbfe53b64d 100644 --- a/roles/database/files/sql/creation/fworch-create-tables-request.sql +++ b/roles/database/files/sql/creation/fworch-create-tables-request.sql @@ -98,6 +98,7 @@ create table request.ticket reason text, external_ticket_id varchar, external_ticket_source int, + pre_workflow_ticket_reference varchar, ticket_deadline Timestamp, ticket_priority int, locked boolean NOT NULL DEFAULT FALSE diff --git a/roles/database/files/upgrade/9.5.2.sql b/roles/database/files/upgrade/9.5.2.sql index f0240df89b..789f9877c5 100644 --- a/roles/database/files/upgrade/9.5.2.sql +++ b/roles/database/files/upgrade/9.5.2.sql @@ -115,3 +115,5 @@ CREATE INDEX IF NOT EXISTS idx_change_history_audit_proof ON public.change_histo GRANT SELECT ON public.change_history TO fwo_ro; GRANT SELECT ON SEQUENCE public.change_history_id_seq TO fwo_ro; + +ALTER TABLE request.ticket ADD COLUMN IF NOT EXISTS pre_workflow_ticket_reference varchar; diff --git a/roles/lib/files/FWO.Data/Workflow/WfTicketBase.cs b/roles/lib/files/FWO.Data/Workflow/WfTicketBase.cs index 1a185b9918..f6da6e6ab2 100644 --- a/roles/lib/files/FWO.Data/Workflow/WfTicketBase.cs +++ b/roles/lib/files/FWO.Data/Workflow/WfTicketBase.cs @@ -39,6 +39,9 @@ public class WfTicketBase : WfStatefulObject [JsonProperty("external_ticket_source"), JsonPropertyName("external_ticket_source")] public int? ExternalTicketSource { get; set; } + [JsonProperty("pre_workflow_ticket_reference"), JsonPropertyName("pre_workflow_ticket_reference")] + public string? PreWorkflowTicketReference { get; set; } + [JsonProperty("ticket_deadline"), JsonPropertyName("ticket_deadline")] public DateTime? Deadline { get; set; } @@ -66,6 +69,7 @@ public WfTicketBase(WfTicketBase ticket) : base(ticket) Reason = ticket.Reason; ExternalTicketId = ticket.ExternalTicketId; ExternalTicketSource = ticket.ExternalTicketSource; + PreWorkflowTicketReference = ticket.PreWorkflowTicketReference; Deadline = ticket.Deadline; Priority = ticket.Priority; Locked = ticket.Locked; @@ -79,6 +83,7 @@ public override bool Sanitize() RequesterGroup = RequesterGroup.SanitizeLdapPathOpt(ref shortened); Reason = Reason.SanitizeOpt(ref shortened); ExternalTicketId = ExternalTicketId.SanitizeOpt(ref shortened); + PreWorkflowTicketReference = PreWorkflowTicketReference.SanitizeOpt(ref shortened); return shortened; } } diff --git a/roles/lib/files/FWO.Services/Workflow/WfDbAccessTickets.cs b/roles/lib/files/FWO.Services/Workflow/WfDbAccessTickets.cs index 5e886c9cde..78d0e96f77 100644 --- a/roles/lib/files/FWO.Services/Workflow/WfDbAccessTickets.cs +++ b/roles/lib/files/FWO.Services/Workflow/WfDbAccessTickets.cs @@ -23,6 +23,7 @@ public async Task AddTicketToDb(WfTicket ticket) ticket.UpdateCidrsInTaskElements(); ticket.UpdateIpStringsFromCidrInTaskElements(); var variables = BuildTicketVariables(ticket); + variables["preWorkflowTicketReference"] = ticket.PreWorkflowTicketReference; variables["requesterId"] = ticket.Requester?.DbId; variables["requestTasks"] = new WfTicketWriter(ticket); variables["locked"] = ticket.Locked; diff --git a/roles/middleware/files/FWO.Middleware.Server/OpenApi/ApiExampleServiceCollectionExtensions.cs b/roles/middleware/files/FWO.Middleware.Server/OpenApi/ApiExampleServiceCollectionExtensions.cs index 2be9ce8009..1d5b7a80f1 100644 --- a/roles/middleware/files/FWO.Middleware.Server/OpenApi/ApiExampleServiceCollectionExtensions.cs +++ b/roles/middleware/files/FWO.Middleware.Server/OpenApi/ApiExampleServiceCollectionExtensions.cs @@ -68,6 +68,7 @@ public sealed class CreateRequestRequestExample : ApiExampleProvider new() { RequestorName = "Alice Example", + PreWorkflowTicketReference = "Ticket-12345", RequestorId = "alice", RuleContactName = "Bob Approver", RuleContactId = "bob", diff --git a/roles/middleware/files/FWO.Middleware.Server/Requests/CreateRequestRequest.cs b/roles/middleware/files/FWO.Middleware.Server/Requests/CreateRequestRequest.cs index 8dca973032..af44fd3d1e 100644 --- a/roles/middleware/files/FWO.Middleware.Server/Requests/CreateRequestRequest.cs +++ b/roles/middleware/files/FWO.Middleware.Server/Requests/CreateRequestRequest.cs @@ -7,6 +7,12 @@ namespace FWO.Middleware.Server.Requests; /// public sealed class CreateRequestRequest { + /// + /// Gets the reference of the ticket in the workflow which preceded this workflow. + /// + [JsonPropertyName("preWorkflowTicketReference")] + public string? PreWorkflowTicketReference { get; set; } + /// /// Gets the RequestorName value. /// diff --git a/roles/middleware/files/FWO.Middleware.Server/Services/FlowRequestService.cs b/roles/middleware/files/FWO.Middleware.Server/Services/FlowRequestService.cs index f80728d15a..8471fc1118 100644 --- a/roles/middleware/files/FWO.Middleware.Server/Services/FlowRequestService.cs +++ b/roles/middleware/files/FWO.Middleware.Server/Services/FlowRequestService.cs @@ -236,6 +236,7 @@ private static WfTicket BuildTicket(CreateRequestRequest request, int ticketStat return new WfTicket { Title = request.Title, + PreWorkflowTicketReference = request.PreWorkflowTicketReference, StateId = ticketStateId, Requester = BuildRequester(request, requesterId), Reason = BuildRequestReason(request), diff --git a/roles/tests-unit/files/FWO.Test/FlowRequestContractTest.cs b/roles/tests-unit/files/FWO.Test/FlowRequestContractTest.cs index 07e2253824..890b90fccc 100644 --- a/roles/tests-unit/files/FWO.Test/FlowRequestContractTest.cs +++ b/roles/tests-unit/files/FWO.Test/FlowRequestContractTest.cs @@ -9,6 +9,16 @@ namespace FWO.Test; internal class FlowRequestContractTest { + [Test] + public void CreateRequestRequest_UsesExpectedPreWorkflowTicketReferenceJsonName() + { + CreateRequestRequest request = new() { PreWorkflowTicketReference = "Ticket-12345" }; + + string json = JsonSerializer.Serialize(request); + + Assert.That(json, Does.Contain("\"preWorkflowTicketReference\":\"Ticket-12345\"")); + } + [Test] public void GetRequestStatusRequest_RequiresTicketId() { diff --git a/roles/tests-unit/files/FWO.Test/FlowRequestServiceTest.cs b/roles/tests-unit/files/FWO.Test/FlowRequestServiceTest.cs index 7f604061e9..435db40a57 100644 --- a/roles/tests-unit/files/FWO.Test/FlowRequestServiceTest.cs +++ b/roles/tests-unit/files/FWO.Test/FlowRequestServiceTest.cs @@ -234,6 +234,7 @@ public async Task CreateRequest_ReturnsCreatedTicketAndResolvesTemporaryIds( RequestorId = "alice", RuleContactName = "Bob Approver", RuleContactId = "bob", + PreWorkflowTicketReference = "Ticket-12345", Title = "Allow HTTPS to app server", AddressObjects = [ @@ -292,6 +293,8 @@ public async Task CreateRequest_ReturnsCreatedTicketAndResolvesTemporaryIds( Assert.That(apiConnection.SentQueries, Contains.Item(RequestQueries.getTicketById)); Assert.That(apiConnection.LastTicketWriter, Is.Not.Null); Assert.That(apiConnection.LastTicketWriter!.Tasks, Has.Count.EqualTo(1)); + Assert.That(GetVariable(apiConnection.NewTicketVariables, "preWorkflowTicketReference"), Is.EqualTo("Ticket-12345")); + Assert.That(apiConnection.CreatedTicket!.PreWorkflowTicketReference, Is.EqualTo("Ticket-12345")); Assert.That(apiConnection.LastTicketWriter.Tasks[0].TaskType, Is.EqualTo(WfTaskType.access.ToString())); Assert.That(apiConnection.LastTicketWriter.Tasks[0].RuleAction, Is.EqualTo(1)); Assert.That(apiConnection.LastTicketWriter.Tasks[0].Elements.WfElementList, Has.Count.EqualTo(3)); @@ -2807,6 +2810,7 @@ private static WfTicket BuildCreatedTicket(object? variables, long ticketId) Title = Convert.ToString(GetVariable(variables, "title")) ?? "", StateId = Convert.ToInt32(GetVariable(variables, "state") ?? 0), Reason = Convert.ToString(GetVariable(variables, "reason")) ?? "", + PreWorkflowTicketReference = Convert.ToString(GetVariable(variables, "preWorkflowTicketReference")), Locked = Convert.ToBoolean(GetVariable(variables, "locked") ?? false), Priority = Convert.ToInt32(GetVariable(variables, "priority") ?? 0), Deadline = GetVariable(variables, "deadline") is DateTime deadline ? deadline : null, diff --git a/roles/tests-unit/files/FWO.Test/WfDbAccessTest.cs b/roles/tests-unit/files/FWO.Test/WfDbAccessTest.cs index cdb39f12fd..2cce6470ed 100644 --- a/roles/tests-unit/files/FWO.Test/WfDbAccessTest.cs +++ b/roles/tests-unit/files/FWO.Test/WfDbAccessTest.cs @@ -67,6 +67,7 @@ private sealed class WfDbAccessTestApiConn : SimulatedApiConnection public int AssignImplCommentCallCount { get; private set; } public int UpdateTicketStateCallCount { get; private set; } public int AddHistoryEntryCallCount { get; private set; } + public object? LastUpdateTicketVariables { get; private set; } public object? LastHistoryVariables { get; private set; } public string? LastHistoryQuery { get; private set; } public long NewApprovalId { get; set; } = 301; @@ -123,6 +124,7 @@ public override Task SendQueryAsync(string query, object? variables = null } if (query == RequestQueries.updateTicket) { + LastUpdateTicketVariables = variables; return Task.FromResult((T)(object)new ReturnId { UpdatedIdLong = UpdatedTicketId }); } if (query == RequestQueries.updateApproval) @@ -604,6 +606,34 @@ await actionHandler.Init(new List Assert.That(result, Is.SameAs(ticket)); } + [Test] + public async Task UpdateTicketInDb_DoesNotPersistCreateOnlyPreWorkflowTicketReference() + { + WfDbAccessTestApiConn apiConn = new() + { + UpdatedTicketId = 101 + }; + UserConfig userConfig = new(); + await userConfig.InitWithUserId(apiConn, 42, false); + WfHandler wfHandler = new(); + ActionHandler actionHandler = new(apiConn, wfHandler); + await actionHandler.Init([]); + WfDbAccess dbAccess = new(DefaultInit.DoNothing, userConfig, apiConn, actionHandler, false, WorkflowPhases.request); + + await dbAccess.UpdateTicketInDb(new WfTicket + { + Id = 101, + PreWorkflowTicketReference = "Changed reference" + }); + + Dictionary variables = (Dictionary)apiConn.LastUpdateTicketVariables!; + Assert.Multiple(() => + { + Assert.That(variables.ContainsKey("preWorkflowTicketReference"), Is.False); + Assert.That(RequestQueries.updateTicket, Does.Not.Contain("pre_workflow_ticket_reference")); + }); + } + [Test] public async Task UpdateTicketInDb_ReportsMismatchWhenReturnedIdDiffers() {