diff --git a/assets/js/initMOJFilterPage.js b/assets/js/initMOJFilterPage.js index 9900f834..99ed1657 100644 --- a/assets/js/initMOJFilterPage.js +++ b/assets/js/initMOJFilterPage.js @@ -19,3 +19,11 @@ new MOJFrontend.FilterToggleButton({ container: $('.moj-filter'), }, }) + +function moveFilterTagsToResults() { + var newContainer = $('.moj-action-bar__filterTagsContainer') + var tagsContainer = $('.moj-filter__selected') + tagsContainer.appendTo(newContainer) +} + +moveFilterTagsToResults() diff --git a/docs/architecture/workspace.json b/docs/architecture/workspace.json index d8b85f2e..ec96567a 100644 --- a/docs/architecture/workspace.json +++ b/docs/architecture/workspace.json @@ -1,944 +1,1151 @@ { - "configuration" : { }, - "description" : "Description", - "documentation" : { }, - "id" : 1, - "lastModifiedDate" : "2024-03-06T12:05:39Z", - "model" : { - "people" : [ { - "description" : "Someone who needs access to prisoner information to carry out their duties", - "id" : "1", - "location" : "Unspecified", - "name" : "Prison Staff User", - "properties" : { - "structurizr.dsl.identifier" : "prisonuser" - }, - "relationships" : [ { - "description" : "is a", - "destinationId" : "4", - "id" : "5", - "properties" : { - "structurizr.dsl.identifier" : "68705dae-837c-47e9-a9ca-488b263925e8" - }, - "sourceId" : "1", - "tags" : "Relationship" - }, { - "description" : "Logs in", - "destinationId" : "38", - "id" : "51", - "properties" : { - "structurizr.dsl.identifier" : "e0a4ae42-3511-45cd-afdd-590d38ae3ce5" + "configuration": {}, + "description": "Description", + "documentation": {}, + "id": 1, + "lastModifiedDate": "2024-03-06T12:05:39Z", + "model": { + "people": [ + { + "description": "Someone who needs access to prisoner information to carry out their duties", + "id": "1", + "location": "Unspecified", + "name": "Prison Staff User", + "properties": { + "structurizr.dsl.identifier": "prisonuser" }, - "sourceId" : "1", - "tags" : "Relationship" - }, { - "description" : "Logs in", - "destinationId" : "35", - "id" : "52", - "linkedRelationshipId" : "51", - "sourceId" : "1" - } ], - "tags" : "Element,Person" - }, { - "description" : "Someone who needs access to prisoner information to carry out their duties in probation", - "id" : "2", - "location" : "Unspecified", - "name" : "Probation Staff User", - "properties" : { - "structurizr.dsl.identifier" : "probationuser" + "relationships": [ + { + "description": "is a", + "destinationId": "4", + "id": "5", + "properties": { + "structurizr.dsl.identifier": "68705dae-837c-47e9-a9ca-488b263925e8" + }, + "sourceId": "1", + "tags": "Relationship" + }, + { + "description": "Logs in", + "destinationId": "38", + "id": "51", + "properties": { + "structurizr.dsl.identifier": "e0a4ae42-3511-45cd-afdd-590d38ae3ce5" + }, + "sourceId": "1", + "tags": "Relationship" + }, + { + "description": "Logs in", + "destinationId": "35", + "id": "52", + "linkedRelationshipId": "51", + "sourceId": "1" + } + ], + "tags": "Element,Person" }, - "relationships" : [ { - "description" : "is a", - "destinationId" : "4", - "id" : "6", - "properties" : { - "structurizr.dsl.identifier" : "81401891-a9e1-44b5-85fb-d1353e0d5df1" + { + "description": "Someone who needs access to prisoner information to carry out their duties in probation", + "id": "2", + "location": "Unspecified", + "name": "Probation Staff User", + "properties": { + "structurizr.dsl.identifier": "probationuser" }, - "sourceId" : "2", - "tags" : "Relationship" - }, { - "description" : "Logs in", - "destinationId" : "38", - "id" : "55", - "properties" : { - "structurizr.dsl.identifier" : "2f7da0a4-1bbb-4277-902a-19962d522315" - }, - "sourceId" : "2", - "tags" : "Relationship" - }, { - "description" : "Logs in", - "destinationId" : "35", - "id" : "56", - "linkedRelationshipId" : "55", - "sourceId" : "2" - } ], - "tags" : "Element,Person" - }, { - "description" : "Someone who needs access to prisoner information from an agency e.g. the police, NHS or other agency", - "id" : "3", - "location" : "Unspecified", - "name" : "External User", - "properties" : { - "structurizr.dsl.identifier" : "externaluser" + "relationships": [ + { + "description": "is a", + "destinationId": "4", + "id": "6", + "properties": { + "structurizr.dsl.identifier": "81401891-a9e1-44b5-85fb-d1353e0d5df1" + }, + "sourceId": "2", + "tags": "Relationship" + }, + { + "description": "Logs in", + "destinationId": "38", + "id": "55", + "properties": { + "structurizr.dsl.identifier": "2f7da0a4-1bbb-4277-902a-19962d522315" + }, + "sourceId": "2", + "tags": "Relationship" + }, + { + "description": "Logs in", + "destinationId": "35", + "id": "56", + "linkedRelationshipId": "55", + "sourceId": "2" + } + ], + "tags": "Element,Person" }, - "relationships" : [ { - "description" : "Logs in", - "destinationId" : "38", - "id" : "57", - "properties" : { - "structurizr.dsl.identifier" : "efa4d070-efa0-4111-9971-a9821c67b9fb" + { + "description": "Someone who needs access to prisoner information from an agency e.g. the police, NHS or other agency", + "id": "3", + "location": "Unspecified", + "name": "External User", + "properties": { + "structurizr.dsl.identifier": "externaluser" }, - "sourceId" : "3", - "tags" : "Relationship" - }, { - "description" : "Logs in", - "destinationId" : "35", - "id" : "58", - "linkedRelationshipId" : "57", - "sourceId" : "3" - } ], - "tags" : "Element,Person" - }, { - "id" : "4", - "location" : "Unspecified", - "name" : "DOM1 Login User", - "properties" : { - "structurizr.dsl.identifier" : "dom1" + "relationships": [ + { + "description": "Logs in", + "destinationId": "38", + "id": "57", + "properties": { + "structurizr.dsl.identifier": "efa4d070-efa0-4111-9971-a9821c67b9fb" + }, + "sourceId": "3", + "tags": "Relationship" + }, + { + "description": "Logs in", + "destinationId": "35", + "id": "58", + "linkedRelationshipId": "57", + "sourceId": "3" + } + ], + "tags": "Element,Person" }, - "relationships" : [ { - "description" : "Logs in", - "destinationId" : "38", - "id" : "53", - "properties" : { - "structurizr.dsl.identifier" : "4b554926-da3e-45b0-8cb8-b4c03258c254" + { + "id": "4", + "location": "Unspecified", + "name": "DOM1 Login User", + "properties": { + "structurizr.dsl.identifier": "dom1" }, - "sourceId" : "4", - "tags" : "Relationship" - }, { - "description" : "Logs in", - "destinationId" : "35", - "id" : "54", - "linkedRelationshipId" : "53", - "sourceId" : "4" - } ], - "tags" : "Element,Person,external system,Azure" - } ], - "softwareSystems" : [ { - "containers" : [ { - "documentation" : { }, - "id" : "8", - "name" : "External Users API", - "properties" : { - "structurizr.dsl.identifier" : "externalusersapi" + "relationships": [ + { + "description": "Logs in", + "destinationId": "38", + "id": "53", + "properties": { + "structurizr.dsl.identifier": "4b554926-da3e-45b0-8cb8-b4c03258c254" + }, + "sourceId": "4", + "tags": "Relationship" + }, + { + "description": "Logs in", + "destinationId": "35", + "id": "54", + "linkedRelationshipId": "53", + "sourceId": "4" + } + ], + "tags": "Element,Person,external system,Azure" + } + ], + "softwareSystems": [ + { + "containers": [ + { + "documentation": {}, + "id": "8", + "name": "External Users API", + "properties": { + "structurizr.dsl.identifier": "externalusersapi" + }, + "tags": "Element,Container" + } + ], + "documentation": {}, + "id": "7", + "location": "Unspecified", + "name": "External Users", + "properties": { + "structurizr.dsl.identifier": "externalusers" }, - "tags" : "Element,Container" - } ], - "documentation" : { }, - "id" : "7", - "location" : "Unspecified", - "name" : "External Users", - "properties" : { - "structurizr.dsl.identifier" : "externalusers" - }, - "tags" : "Element,Software System" - }, { - "documentation" : { }, - "id" : "9", - "location" : "Unspecified", - "name" : "Gov Notify", - "properties" : { - "structurizr.dsl.identifier" : "govnotify" + "tags": "Element,Software System" }, - "tags" : "Element,Software System,external system,Mail" - }, { - "containers" : [ { - "documentation" : { }, - "id" : "11", - "name" : "NOMIS User Roles API", - "properties" : { - "structurizr.dsl.identifier" : "nomisuserrolesapi" + { + "documentation": {}, + "id": "9", + "location": "Unspecified", + "name": "Gov Notify", + "properties": { + "structurizr.dsl.identifier": "govnotify" }, - "relationships" : [ { - "description" : "reads / writes", - "destinationId" : "12", - "id" : "13", - "properties" : { - "structurizr.dsl.identifier" : "0b658c21-f6df-4774-a8b0-c05632b9460b" - }, - "sourceId" : "11", - "tags" : "Relationship" - } ], - "tags" : "Element,Container" - }, { - "documentation" : { }, - "id" : "12", - "name" : "NOMIS Database", - "properties" : { - "structurizr.dsl.identifier" : "nomisdatabase" - }, - "tags" : "Element,Container" - } ], - "documentation" : { }, - "id" : "10", - "location" : "Unspecified", - "name" : "NOMIS Prison System", - "properties" : { - "structurizr.dsl.identifier" : "nomis" + "tags": "Element,Software System,external system,Mail" }, - "tags" : "Element,Software System,Legacy System" - }, { - "containers" : [ { - "documentation" : { }, - "id" : "15", - "name" : "HMPPS Manage User to delius API", - "properties" : { - "structurizr.dsl.identifier" : "hmppsmanageusersdeliusapi" - }, - "relationships" : [ { - "description" : "gets user info from", - "destinationId" : "16", - "id" : "17", - "properties" : { - "structurizr.dsl.identifier" : "62f97b4a-b43e-428a-8ff4-ab928448418f" - }, - "sourceId" : "15", - "tags" : "Relationship" - } ], - "tags" : "Element,Container" - }, { - "documentation" : { }, - "id" : "16", - "name" : "NDelius", - "properties" : { - "structurizr.dsl.identifier" : "ndelius" + { + "containers": [ + { + "documentation": {}, + "id": "11", + "name": "NOMIS User Roles API", + "properties": { + "structurizr.dsl.identifier": "nomisuserrolesapi" + }, + "relationships": [ + { + "description": "reads / writes", + "destinationId": "12", + "id": "13", + "properties": { + "structurizr.dsl.identifier": "0b658c21-f6df-4774-a8b0-c05632b9460b" + }, + "sourceId": "11", + "tags": "Relationship" + } + ], + "tags": "Element,Container" + }, + { + "documentation": {}, + "id": "12", + "name": "NOMIS Database", + "properties": { + "structurizr.dsl.identifier": "nomisdatabase" + }, + "tags": "Element,Container" + } + ], + "documentation": {}, + "id": "10", + "location": "Unspecified", + "name": "NOMIS Prison System", + "properties": { + "structurizr.dsl.identifier": "nomis" }, - "tags" : "Element,Container" - } ], - "documentation" : { }, - "id" : "14", - "location" : "Unspecified", - "name" : "Delius Systems", - "properties" : { - "structurizr.dsl.identifier" : "delius" + "tags": "Element,Software System,Legacy System" }, - "tags" : "Element,Software System,Legacy System" - }, { - "containers" : [ { - "documentation" : { }, - "id" : "19", - "name" : "Manage Users API", - "properties" : { - "structurizr.dsl.identifier" : "hmppsmanageusersapi" - }, - "relationships" : [ { - "description" : "gets NOMIS users and roles", - "destinationId" : "11", - "id" : "22", - "properties" : { - "structurizr.dsl.identifier" : "a9d58ddf-f9e6-4b09-b3fd-c72b0e4234aa" - }, - "sourceId" : "19", - "tags" : "Relationship" - }, { - "description" : "gets NOMIS users and roles", - "destinationId" : "10", - "id" : "23", - "linkedRelationshipId" : "22", - "sourceId" : "19" - }, { - "description" : "gets external users and roles", - "destinationId" : "8", - "id" : "26", - "properties" : { - "structurizr.dsl.identifier" : "4f105eb5-1729-4b72-857f-9edf8be85d56" - }, - "sourceId" : "19", - "tags" : "Relationship" - }, { - "description" : "gets external users and roles", - "destinationId" : "7", - "id" : "27", - "linkedRelationshipId" : "26", - "sourceId" : "19" - }, { - "description" : "gets Delius users and roles", - "destinationId" : "15", - "id" : "30", - "properties" : { - "structurizr.dsl.identifier" : "11a6ac07-0558-4cac-9957-4c9c120fc56a" - }, - "sourceId" : "19", - "tags" : "Relationship" - }, { - "description" : "gets Delius users and roles", - "destinationId" : "14", - "id" : "31", - "linkedRelationshipId" : "30", - "sourceId" : "19" - } ], - "tags" : "Element,Container" - }, { - "documentation" : { }, - "id" : "20", - "name" : "Manage Users UI", - "properties" : { - "structurizr.dsl.identifier" : "hmppsmanageusersui" + { + "containers": [ + { + "documentation": {}, + "id": "15", + "name": "HMPPS Manage User to delius API", + "properties": { + "structurizr.dsl.identifier": "hmppsmanageusersdeliusapi" + }, + "relationships": [ + { + "description": "gets user info from", + "destinationId": "16", + "id": "17", + "properties": { + "structurizr.dsl.identifier": "62f97b4a-b43e-428a-8ff4-ab928448418f" + }, + "sourceId": "15", + "tags": "Relationship" + } + ], + "tags": "Element,Container" + }, + { + "documentation": {}, + "id": "16", + "name": "NDelius", + "properties": { + "structurizr.dsl.identifier": "ndelius" + }, + "tags": "Element,Container" + } + ], + "documentation": {}, + "id": "14", + "location": "Unspecified", + "name": "Delius Systems", + "properties": { + "structurizr.dsl.identifier": "delius" }, - "relationships" : [ { - "description" : "connects to", - "destinationId" : "19", - "id" : "21", - "properties" : { - "structurizr.dsl.identifier" : "77a559c3-71f4-4f57-b427-34a21c281c2a" - }, - "sourceId" : "20", - "tags" : "Relationship" - } ], - "tags" : "Element,Container" - } ], - "documentation" : { }, - "id" : "18", - "location" : "Unspecified", - "name" : "HMPPS Manage Users", - "properties" : { - "structurizr.dsl.identifier" : "hmppsmanageusers" + "tags": "Element,Software System,Legacy System" }, - "relationships" : [ { - "description" : "gets NOMIS users and roles", - "destinationId" : "11", - "id" : "24", - "linkedRelationshipId" : "22", - "sourceId" : "18" - }, { - "description" : "gets NOMIS users and roles", - "destinationId" : "10", - "id" : "25", - "linkedRelationshipId" : "22", - "sourceId" : "18" - }, { - "description" : "gets external users and roles", - "destinationId" : "8", - "id" : "28", - "linkedRelationshipId" : "26", - "sourceId" : "18" - }, { - "description" : "gets external users and roles", - "destinationId" : "7", - "id" : "29", - "linkedRelationshipId" : "26", - "sourceId" : "18" - }, { - "description" : "gets Delius users and roles", - "destinationId" : "15", - "id" : "32", - "linkedRelationshipId" : "30", - "sourceId" : "18" - }, { - "description" : "gets Delius users and roles", - "destinationId" : "14", - "id" : "33", - "linkedRelationshipId" : "30", - "sourceId" : "18" - }, { - "destinationId" : "9", - "id" : "34", - "properties" : { - "structurizr.dsl.identifier" : "e90b8a91-ef79-4124-b6c5-ce1f8f655146" - }, - "sourceId" : "18", - "tags" : "Relationship" - } ], - "tags" : "Element,Software System,HMPPS Digital Service" - }, { - "containers" : [ { - "documentation" : { }, - "id" : "36", - "name" : "HMPPS Authorization Server", - "properties" : { - "structurizr.dsl.identifier" : "hmppsauthorizationserver" - }, - "relationships" : [ { - "description" : "gets user roles", - "destinationId" : "19", - "id" : "42", - "properties" : { - "structurizr.dsl.identifier" : "1434c670-02d2-4bfc-bf44-e76ff87bf0fc" - }, - "sourceId" : "36", - "tags" : "Relationship" - }, { - "description" : "gets user roles", - "destinationId" : "18", - "id" : "43", - "linkedRelationshipId" : "42", - "sourceId" : "36" - } ], - "tags" : "Element,Container,New" - }, { - "documentation" : { }, - "id" : "37", - "name" : "HMPPS Authorization UI", - "properties" : { - "structurizr.dsl.identifier" : "hmppsauthorizationui" - }, - "relationships" : [ { - "description" : "gets client credential info from", - "destinationId" : "36", - "id" : "41", - "properties" : { - "structurizr.dsl.identifier" : "3d7683ad-8c4e-45ef-8860-a6e1e00d47ff" - }, - "sourceId" : "37", - "tags" : "Relationship" - } ], - "tags" : "Element,Container,New" - }, { - "documentation" : { }, - "id" : "38", - "name" : "HMPPS Legacy auth service", - "properties" : { - "structurizr.dsl.identifier" : "hmppslegacyauth" - }, - "relationships" : [ { - "description" : "Proxies", - "destinationId" : "36", - "id" : "40", - "properties" : { - "structurizr.dsl.identifier" : "f1356642-1cf8-48d3-b1af-bbf96b809f9c" - }, - "sourceId" : "38", - "tags" : "Relationship" - }, { - "description" : "gets user roles", - "destinationId" : "19", - "id" : "46", - "properties" : { - "structurizr.dsl.identifier" : "bc31fbb7-cc54-444c-95ea-9950e1434f0b" - }, - "sourceId" : "38", - "tags" : "Relationship" - }, { - "description" : "gets user roles", - "destinationId" : "18", - "id" : "47", - "linkedRelationshipId" : "46", - "sourceId" : "38" - }, { - "description" : "Notifies users", - "destinationId" : "9", - "id" : "48", - "properties" : { - "structurizr.dsl.identifier" : "a317a7f5-1568-4f07-b2de-d79d0c2258c6" - }, - "sourceId" : "38", - "tags" : "Relationship" - }, { - "destinationId" : "39", - "id" : "50", - "properties" : { - "structurizr.dsl.identifier" : "0557ec44-bed5-4ba2-b974-6f5eacd16aec" - }, - "sourceId" : "38", - "tags" : "Relationship" - } ], - "tags" : "Element,Container" - }, { - "documentation" : { }, - "id" : "39", - "name" : "Token Verification API", - "properties" : { - "structurizr.dsl.identifier" : "tokenverificationapi" + { + "containers": [ + { + "documentation": {}, + "id": "19", + "name": "Manage Users API", + "properties": { + "structurizr.dsl.identifier": "hmppsmanageusersapi" + }, + "relationships": [ + { + "description": "gets NOMIS users and roles", + "destinationId": "11", + "id": "22", + "properties": { + "structurizr.dsl.identifier": "a9d58ddf-f9e6-4b09-b3fd-c72b0e4234aa" + }, + "sourceId": "19", + "tags": "Relationship" + }, + { + "description": "gets NOMIS users and roles", + "destinationId": "10", + "id": "23", + "linkedRelationshipId": "22", + "sourceId": "19" + }, + { + "description": "gets external users and roles", + "destinationId": "8", + "id": "26", + "properties": { + "structurizr.dsl.identifier": "4f105eb5-1729-4b72-857f-9edf8be85d56" + }, + "sourceId": "19", + "tags": "Relationship" + }, + { + "description": "gets external users and roles", + "destinationId": "7", + "id": "27", + "linkedRelationshipId": "26", + "sourceId": "19" + }, + { + "description": "gets Delius users and roles", + "destinationId": "15", + "id": "30", + "properties": { + "structurizr.dsl.identifier": "11a6ac07-0558-4cac-9957-4c9c120fc56a" + }, + "sourceId": "19", + "tags": "Relationship" + }, + { + "description": "gets Delius users and roles", + "destinationId": "14", + "id": "31", + "linkedRelationshipId": "30", + "sourceId": "19" + } + ], + "tags": "Element,Container" + }, + { + "documentation": {}, + "id": "20", + "name": "Manage Users UI", + "properties": { + "structurizr.dsl.identifier": "hmppsmanageusersui" + }, + "relationships": [ + { + "description": "connects to", + "destinationId": "19", + "id": "21", + "properties": { + "structurizr.dsl.identifier": "77a559c3-71f4-4f57-b427-34a21c281c2a" + }, + "sourceId": "20", + "tags": "Relationship" + } + ], + "tags": "Element,Container" + } + ], + "documentation": {}, + "id": "18", + "location": "Unspecified", + "name": "HMPPS Manage Users", + "properties": { + "structurizr.dsl.identifier": "hmppsmanageusers" }, - "tags" : "Element,Container" - } ], - "description" : "Authentication and Authorization server", - "documentation" : { }, - "id" : "35", - "location" : "Unspecified", - "name" : "HMPPS Auth", - "properties" : { - "structurizr.dsl.identifier" : "hmppsauth" + "relationships": [ + { + "description": "gets NOMIS users and roles", + "destinationId": "11", + "id": "24", + "linkedRelationshipId": "22", + "sourceId": "18" + }, + { + "description": "gets NOMIS users and roles", + "destinationId": "10", + "id": "25", + "linkedRelationshipId": "22", + "sourceId": "18" + }, + { + "description": "gets external users and roles", + "destinationId": "8", + "id": "28", + "linkedRelationshipId": "26", + "sourceId": "18" + }, + { + "description": "gets external users and roles", + "destinationId": "7", + "id": "29", + "linkedRelationshipId": "26", + "sourceId": "18" + }, + { + "description": "gets Delius users and roles", + "destinationId": "15", + "id": "32", + "linkedRelationshipId": "30", + "sourceId": "18" + }, + { + "description": "gets Delius users and roles", + "destinationId": "14", + "id": "33", + "linkedRelationshipId": "30", + "sourceId": "18" + }, + { + "destinationId": "9", + "id": "34", + "properties": { + "structurizr.dsl.identifier": "e90b8a91-ef79-4124-b6c5-ce1f8f655146" + }, + "sourceId": "18", + "tags": "Relationship" + } + ], + "tags": "Element,Software System,HMPPS Digital Service" }, - "relationships" : [ { - "description" : "gets user roles", - "destinationId" : "19", - "id" : "44", - "linkedRelationshipId" : "42", - "sourceId" : "35" - }, { - "description" : "gets user roles", - "destinationId" : "18", - "id" : "45", - "linkedRelationshipId" : "42", - "sourceId" : "35" - }, { - "description" : "Notifies users", - "destinationId" : "9", - "id" : "49", - "linkedRelationshipId" : "48", - "sourceId" : "35" - }, { - "description" : "Gets user and role information from", - "destinationId" : "18", - "id" : "65", - "properties" : { - "structurizr.dsl.identifier" : "88bd6cc1-05dd-42a3-91ee-309e98aa88ac" - }, - "sourceId" : "35", - "tags" : "Relationship" - } ], - "tags" : "Element,Software System,HMPPS Digital Service" - }, { - "containers" : [ { - "documentation" : { }, - "id" : "60", - "name" : "general HMPPS Digital Service", - "properties" : { - "structurizr.dsl.identifier" : "hmppsdigitalservice" + { + "containers": [ + { + "documentation": {}, + "id": "36", + "name": "HMPPS Authorization Server", + "properties": { + "structurizr.dsl.identifier": "hmppsauthorizationserver" + }, + "relationships": [ + { + "description": "gets user roles", + "destinationId": "19", + "id": "42", + "properties": { + "structurizr.dsl.identifier": "1434c670-02d2-4bfc-bf44-e76ff87bf0fc" + }, + "sourceId": "36", + "tags": "Relationship" + }, + { + "description": "gets user roles", + "destinationId": "18", + "id": "43", + "linkedRelationshipId": "42", + "sourceId": "36" + } + ], + "tags": "Element,Container,New" + }, + { + "documentation": {}, + "id": "37", + "name": "HMPPS Authorization UI", + "properties": { + "structurizr.dsl.identifier": "hmppsauthorizationui" + }, + "relationships": [ + { + "description": "gets client credential info from", + "destinationId": "36", + "id": "41", + "properties": { + "structurizr.dsl.identifier": "3d7683ad-8c4e-45ef-8860-a6e1e00d47ff" + }, + "sourceId": "37", + "tags": "Relationship" + } + ], + "tags": "Element,Container,New" + }, + { + "documentation": {}, + "id": "38", + "name": "HMPPS Legacy auth service", + "properties": { + "structurizr.dsl.identifier": "hmppslegacyauth" + }, + "relationships": [ + { + "description": "Proxies", + "destinationId": "36", + "id": "40", + "properties": { + "structurizr.dsl.identifier": "f1356642-1cf8-48d3-b1af-bbf96b809f9c" + }, + "sourceId": "38", + "tags": "Relationship" + }, + { + "description": "gets user roles", + "destinationId": "19", + "id": "46", + "properties": { + "structurizr.dsl.identifier": "bc31fbb7-cc54-444c-95ea-9950e1434f0b" + }, + "sourceId": "38", + "tags": "Relationship" + }, + { + "description": "gets user roles", + "destinationId": "18", + "id": "47", + "linkedRelationshipId": "46", + "sourceId": "38" + }, + { + "description": "Notifies users", + "destinationId": "9", + "id": "48", + "properties": { + "structurizr.dsl.identifier": "a317a7f5-1568-4f07-b2de-d79d0c2258c6" + }, + "sourceId": "38", + "tags": "Relationship" + }, + { + "destinationId": "39", + "id": "50", + "properties": { + "structurizr.dsl.identifier": "0557ec44-bed5-4ba2-b974-6f5eacd16aec" + }, + "sourceId": "38", + "tags": "Relationship" + } + ], + "tags": "Element,Container" + }, + { + "documentation": {}, + "id": "39", + "name": "Token Verification API", + "properties": { + "structurizr.dsl.identifier": "tokenverificationapi" + }, + "tags": "Element,Container" + } + ], + "description": "Authentication and Authorization server", + "documentation": {}, + "id": "35", + "location": "Unspecified", + "name": "HMPPS Auth", + "properties": { + "structurizr.dsl.identifier": "hmppsauth" }, - "relationships" : [ { - "description" : "verifies tokens", - "destinationId" : "39", - "id" : "61", - "properties" : { - "structurizr.dsl.identifier" : "28910f09-bd95-4be7-80c4-bce5babb654f" - }, - "sourceId" : "60", - "tags" : "Relationship" - }, { - "description" : "verifies tokens", - "destinationId" : "35", - "id" : "62", - "linkedRelationshipId" : "61", - "sourceId" : "60" - } ], - "tags" : "Element,Container" - } ], - "documentation" : { }, - "id" : "59", - "location" : "Unspecified", - "name" : "HMPPS Digital Services", - "properties" : { - "structurizr.dsl.identifier" : "hmppsdigitalservices" + "relationships": [ + { + "description": "gets user roles", + "destinationId": "19", + "id": "44", + "linkedRelationshipId": "42", + "sourceId": "35" + }, + { + "description": "gets user roles", + "destinationId": "18", + "id": "45", + "linkedRelationshipId": "42", + "sourceId": "35" + }, + { + "description": "Notifies users", + "destinationId": "9", + "id": "49", + "linkedRelationshipId": "48", + "sourceId": "35" + }, + { + "description": "Gets user and role information from", + "destinationId": "18", + "id": "65", + "properties": { + "structurizr.dsl.identifier": "88bd6cc1-05dd-42a3-91ee-309e98aa88ac" + }, + "sourceId": "35", + "tags": "Relationship" + } + ], + "tags": "Element,Software System,HMPPS Digital Service" }, - "relationships" : [ { - "description" : "verifies tokens", - "destinationId" : "39", - "id" : "63", - "linkedRelationshipId" : "61", - "sourceId" : "59" - }, { - "description" : "verifies tokens", - "destinationId" : "35", - "id" : "64", - "linkedRelationshipId" : "61", - "sourceId" : "59" - }, { - "description" : "checks authentication", - "destinationId" : "35", - "id" : "66", - "properties" : { - "structurizr.dsl.identifier" : "36d7bf3d-d1d0-46d2-a03e-99f5f2a7072d" - }, - "sourceId" : "59", - "tags" : "Relationship" - }, { - "description" : "checks user roles", - "destinationId" : "18", - "id" : "67", - "properties" : { - "structurizr.dsl.identifier" : "deeadfd8-3137-4e25-b8c3-c49729ed46c9" + { + "containers": [ + { + "documentation": {}, + "id": "60", + "name": "general HMPPS Digital Service", + "properties": { + "structurizr.dsl.identifier": "hmppsdigitalservice" + }, + "relationships": [ + { + "description": "verifies tokens", + "destinationId": "39", + "id": "61", + "properties": { + "structurizr.dsl.identifier": "28910f09-bd95-4be7-80c4-bce5babb654f" + }, + "sourceId": "60", + "tags": "Relationship" + }, + { + "description": "verifies tokens", + "destinationId": "35", + "id": "62", + "linkedRelationshipId": "61", + "sourceId": "60" + } + ], + "tags": "Element,Container" + } + ], + "documentation": {}, + "id": "59", + "location": "Unspecified", + "name": "HMPPS Digital Services", + "properties": { + "structurizr.dsl.identifier": "hmppsdigitalservices" }, - "sourceId" : "59", - "tags" : "Relationship" - } ], - "tags" : "Element,Software System,HMPPS Digital Service" - } ] + "relationships": [ + { + "description": "verifies tokens", + "destinationId": "39", + "id": "63", + "linkedRelationshipId": "61", + "sourceId": "59" + }, + { + "description": "verifies tokens", + "destinationId": "35", + "id": "64", + "linkedRelationshipId": "61", + "sourceId": "59" + }, + { + "description": "checks authentication", + "destinationId": "35", + "id": "66", + "properties": { + "structurizr.dsl.identifier": "36d7bf3d-d1d0-46d2-a03e-99f5f2a7072d" + }, + "sourceId": "59", + "tags": "Relationship" + }, + { + "description": "checks user roles", + "destinationId": "18", + "id": "67", + "properties": { + "structurizr.dsl.identifier": "deeadfd8-3137-4e25-b8c3-c49729ed46c9" + }, + "sourceId": "59", + "tags": "Relationship" + } + ], + "tags": "Element,Software System,HMPPS Digital Service" + } + ] }, - "name" : "Name", - "properties" : { - "structurizr.dsl" : "d29ya3NwYWNlIHsKCiAgICBtb2RlbCB7CiAgICAgICAgCiAgICAgICAgcHJpc29uVXNlciA9IHBlcnNvbiAiIFByaXNvbiBTdGFmZiBVc2VyIiAiU29tZW9uZSB3aG8gbmVlZHMgYWNjZXNzIHRvIHByaXNvbmVyIGluZm9ybWF0aW9uIHRvIGNhcnJ5IG91dCB0aGVpciBkdXRpZXMiCgogICAgICAgIHByb2JhdGlvblVzZXIgPSBwZXJzb24gIiBQcm9iYXRpb24gU3RhZmYgVXNlciIgIlNvbWVvbmUgd2hvIG5lZWRzIGFjY2VzcyB0byBwcmlzb25lciBpbmZvcm1hdGlvbiB0byBjYXJyeSBvdXQgdGhlaXIgZHV0aWVzIGluIHByb2JhdGlvbiIKCiAgICAgICAgZXh0ZXJuYWxVc2VyID0gcGVyc29uICIgRXh0ZXJuYWwgVXNlciIgIlNvbWVvbmUgd2hvIG5lZWRzIGFjY2VzcyB0byBwcmlzb25lciBpbmZvcm1hdGlvbiBmcm9tIGFuIGFnZW5jeSBlLmcuIHRoZSBwb2xpY2UsIE5IUyBvciBvdGhlciBhZ2VuY3kiCgogICAgICAgIERPTTEgPSBwZXJzb24gIkRPTTEgTG9naW4gVXNlciJ7CiAgICAgICAgICAgIHRhZ3MgImV4dGVybmFsIHN5c3RlbSIsICJBenVyZSIKICAgICAgICAgICAgcHJpc29uVXNlciAtPiBET00xICJpcyBhIgogICAgICAgICAgICBwcm9iYXRpb25Vc2VyIC0+IERPTTEgImlzIGEiCiAgICAgICAgfQoKICAgICAgICBFeHRlcm5hbFVzZXJzID0gc29mdHdhcmVTeXN0ZW0gIkV4dGVybmFsIFVzZXJzInsKICAgICAgICAgICAgRXh0ZXJuYWxVc2Vyc0FwaSA9IGNvbnRhaW5lciAiRXh0ZXJuYWwgVXNlcnMgQVBJIgogICAgICAgIH0KCiAgICAgICAgR292Tm90aWZ5ID0gc29mdHdhcmVTeXN0ZW0gIkdvdiBOb3RpZnkiIHsKICAgICAgICAgICAgdGFncyAiZXh0ZXJuYWwgc3lzdGVtIiwgIk1haWwiCiAgICAgICAgfQoKCiAgICAgICAgTk9NSVMgPSBzb2Z0d2FyZVN5c3RlbSAiTk9NSVMgUHJpc29uIFN5c3RlbSIgewogICAgICAgICAgICB0YWdzICJMZWdhY3kgU3lzdGVtIgogICAgICAgICAgICBOT01JU1VzZXJSb2xlc0FQSSA9IGNvbnRhaW5lciAiTk9NSVMgVXNlciBSb2xlcyBBUEkiCiAgICAgICAgICAgIE5PTUlTZGF0YWJhc2UgPSBjb250YWluZXIgIk5PTUlTIERhdGFiYXNlIgogICAgICAgICAgICBOT01JU1VzZXJSb2xlc0FQSSAtPiBOT01JU2RhdGFiYXNlICJyZWFkcyAvIHdyaXRlcyIKICAgICAgICB9CgoKICAgICAgICBEZWxpdXMgPSBzb2Z0d2FyZVN5c3RlbSAiRGVsaXVzIFN5c3RlbXMiIHsKICAgICAgICAgICAgdGFncyAiTGVnYWN5IFN5c3RlbSIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2Vyc0RlbGl1c0FwaSA9IGNvbnRhaW5lciAiSE1QUFMgTWFuYWdlIFVzZXIgdG8gZGVsaXVzIEFQSSIKICAgICAgICAgICAgTkRlbGl1cyA9IGNvbnRhaW5lciAiTkRlbGl1cyIKICAgICAgICAgICAgCiAgICAgICAgICAgIEhNUFBTTWFuYWdlVXNlcnNEZWxpdXNBcGkgLT4gTkRlbGl1cyAiZ2V0cyB1c2VyIGluZm8gZnJvbSIKICAgICAgICB9CgogICAgICAgIEhNUFBTTWFuYWdlVXNlcnMgPSBzb2Z0d2FyZVN5c3RlbSAiSE1QUFMgTWFuYWdlIFVzZXJzIiB7CiAgICAgICAgICAgIHRhZ3MgIkhNUFBTIERpZ2l0YWwgU2VydmljZSIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2Vyc0FwaSA9IGNvbnRhaW5lciAiTWFuYWdlIFVzZXJzIEFQSSIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2Vyc1VpID0gY29udGFpbmVyICJNYW5hZ2UgVXNlcnMgVUkiCiAgICAgICAgICAgIAogICAgICAgICAgICBITVBQU01hbmFnZVVzZXJzVWkgLT4gSE1QUFNNYW5hZ2VVc2Vyc0FwaSAiY29ubmVjdHMgdG8iCiAgICAgICAgICAgIEhNUFBTTWFuYWdlVXNlcnNBcGkgLT4gTk9NSVNVc2VyUm9sZXNBUEkgImdldHMgTk9NSVMgdXNlcnMgYW5kIHJvbGVzIgogICAgICAgICAgICBITVBQU01hbmFnZVVzZXJzQXBpIC0+IEV4dGVybmFsVXNlcnNBcGkgImdldHMgZXh0ZXJuYWwgdXNlcnMgYW5kIHJvbGVzIgogICAgICAgICAgICBITVBQU01hbmFnZVVzZXJzQXBpIC0+IEhNUFBTTWFuYWdlVXNlcnNEZWxpdXNBcGkgImdldHMgRGVsaXVzIHVzZXJzIGFuZCByb2xlcyIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2VycyAtPiBHb3ZOb3RpZnkKICAgICAgICB9CgoKICAgICAgICBITVBQU0F1dGggPSBzb2Z0d2FyZVN5c3RlbSAiSE1QUFMgQXV0aCIgIkF1dGhlbnRpY2F0aW9uIGFuZCBBdXRob3JpemF0aW9uIHNlcnZlciJ7CiAgICAgICAgICAgIHRhZ3MgIkhNUFBTIERpZ2l0YWwgU2VydmljZSIKCiAgICAgICAgICAgIEhNUFBTQXV0aG9yaXphdGlvblNlcnZlciA9IGNvbnRhaW5lciAiSE1QUFMgQXV0aG9yaXphdGlvbiBTZXJ2ZXIiIHsgCiAgICAgICAgICAgICAgICB0YWdzICJOZXciIAogICAgICAgICAgICB9CgogICAgICAgICAgICBITVBQU0F1dGhvcml6YXRpb25VSSA9IGNvbnRhaW5lciAiSE1QUFMgQXV0aG9yaXphdGlvbiBVSSJ7CiAgICAgICAgICAgICAgICB0YWdzICJOZXciCiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIEhNUFBTTGVnYWN5QXV0aCA9IGNvbnRhaW5lciAiSE1QUFMgTGVnYWN5IGF1dGggc2VydmljZSIKICAgICAgICAgICAgdG9rZW5WZXJpZmljYXRpb25BUEkgPSBjb250YWluZXIgIlRva2VuIFZlcmlmaWNhdGlvbiBBUEkiCgogICAgICAgICAgICBITVBQU0xlZ2FjeUF1dGggLT4gSE1QUFNBdXRob3JpemF0aW9uU2VydmVyICJQcm94aWVzIgogICAgICAgICAgICBITVBQU0F1dGhvcml6YXRpb25VSSAtPiBITVBQU0F1dGhvcml6YXRpb25TZXJ2ZXIgImdldHMgY2xpZW50IGNyZWRlbnRpYWwgaW5mbyBmcm9tIgogICAgICAgICAgICBITVBQU0F1dGhvcml6YXRpb25TZXJ2ZXIgLT4gSE1QUFNNYW5hZ2VVc2Vyc0FwaSAiZ2V0cyB1c2VyIHJvbGVzIgogICAgICAgICAgICBITVBQU0xlZ2FjeUF1dGggLT4gSE1QUFNNYW5hZ2VVc2Vyc0FwaSAiZ2V0cyB1c2VyIHJvbGVzIgogICAgICAgICAgICBITVBQU0xlZ2FjeUF1dGggLT4gR292Tm90aWZ5ICJOb3RpZmllcyB1c2VycyIKICAgICAgICAgICAgSE1QUFNMZWdhY3lBdXRoIC0+IHRva2VuVmVyaWZpY2F0aW9uQVBJCiAgICAgICAgICAgIHByaXNvblVzZXIgLT4gSE1QUFNMZWdhY3lBdXRoICJMb2dzIGluIgogICAgICAgICAgICBET00xIC0+IEhNUFBTTGVnYWN5QXV0aCAiTG9ncyBpbiIKICAgICAgICAgICAgcHJvYmF0aW9uVXNlciAtPiBITVBQU0xlZ2FjeUF1dGggIkxvZ3MgaW4iCiAgICAgICAgICAgIGV4dGVybmFsVXNlciAtPiBITVBQU0xlZ2FjeUF1dGggIkxvZ3MgaW4iCiAgICAgICAgfQoKICAgICAgICBITVBQU0RpZ2l0YWxTZXJ2aWNlcyA9IHNvZnR3YXJlU3lzdGVtICJITVBQUyBEaWdpdGFsIFNlcnZpY2VzInsKICAgICAgICAgICAgdGFncyAiSE1QUFMgRGlnaXRhbCBTZXJ2aWNlIiAKICAgICAgICAgICAgSE1QUFNEaWdpdGFsU2VydmljZSA9IGNvbnRhaW5lciAiZ2VuZXJhbCBITVBQUyBEaWdpdGFsIFNlcnZpY2UiCiAgICAgICAgICAgIEhNUFBTRGlnaXRhbFNlcnZpY2UgLT4gdG9rZW5WZXJpZmljYXRpb25BUEkgInZlcmlmaWVzIHRva2VucyIKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgSE1QUFNBdXRoIC0+IEhNUFBTTWFuYWdlVXNlcnMgIkdldHMgdXNlciBhbmQgcm9sZSBpbmZvcm1hdGlvbiBmcm9tIgogICAgICAgIEhNUFBTRGlnaXRhbFNlcnZpY2VzIC0+IEhNUFBTQXV0aCAiY2hlY2tzIGF1dGhlbnRpY2F0aW9uIgogICAgICAgIEhNUFBTRGlnaXRhbFNlcnZpY2VzIC0+IEhNUFBTTWFuYWdlVXNlcnMgImNoZWNrcyB1c2VyIHJvbGVzIgoKICAgIH0KCiAgICB2aWV3cyB7CgogICAgICAgIHN5c3RlbUxhbmRzY2FwZSAgIkhtcHBzQXV0aExhbmRzY2FwZSIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBITVBQU0F1dGggIkhNUFBTQXV0aCIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBITVBQU0RpZ2l0YWxTZXJ2aWNlcyAiRGlnaXRhbFNlcnZpY2VzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgIH0KCiAgICAgICAgY29udGFpbmVyIE5PTUlTICJOT01JUyJ7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBEZWxpdXMgIkRlbGl1cyJ7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgIH0KCiAgICAgICAgIGNvbnRhaW5lciBFeHRlcm5hbFVzZXJzICJFeHRlcm5hbFVzZXJzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICB9CgogICAgICAgICBjb250YWluZXIgSE1QUFNNYW5hZ2VVc2VycyAiTWFuYWdlVXNlcnMiewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgfQoKICAgICAgICBzdHlsZXMgewoKICAgICAgICAgICAgZWxlbWVudCAiU29mdHdhcmUgU3lzdGVtIiB7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kICMxMTY4YmQKICAgICAgICAgICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgICAgICAgfQoKICAgICAgICAgICAgZWxlbWVudCAiTGVnYWN5IFN5c3RlbSIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjY2NjY2NjCiAgICAgICAgICAgICAgICBjb2xvciAjMDAwMDAwCiAgICAgICAgICAgIH0gIAogICAgICAgICAgICBlbGVtZW50ICJFeHRlcm5hbCBTeXN0ZW0iIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzM1OThFRQogICAgICAgICAgICAgICAgY29sb3IgIzAwMDAwMAogICAgICAgICAgICB9ICAgICAgICAgICAgIAogICAgICAgICAgICBlbGVtZW50ICJQZXJzb24iIHsKICAgICAgICAgICAgICAgIHNoYXBlIHBlcnNvbgogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjMDg0MjdiCiAgICAgICAgICAgICAgICBjb2xvciAjZmZmZmZmCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGVsZW1lbnQgIkF6dXJlIiB7CiAgICAgICAgICAgICAgICBpY29uICJBenVyZUFELnBuZyIKICAgICAgICAgICAgICAgIG9wYWNpdHkgMTAwCiAgICAgICAgICAgICAgICBzdHJva2VXaWR0aCAzCiAgICAgICAgICAgICAgICBjb2xvciBibGFjawogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjYzJkOGVkCiAgICAgICAgICAgICAgICBzdHJva2UgYmxhY2sKICAgICAgICAgICAgfQoKICAgICAgICAgICBlbGVtZW50ICJNYWlsInsKICAgICAgICAgICAgICAgIGljb24gImVtYWlsLnBuZyIKICAgICAgICAgICAgICAgIG9wYWNpdHkgMTAwCiAgICAgICAgICAgICAgICBzdHJva2VXaWR0aCAzCiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kIHdoaXRlCiAgICAgICAgICAgICAgICBjb2xvciBibGFjawogICAgICAgICAgICAgICAgc3Ryb2tlIGJsYWNrCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGVsZW1lbnQgIk5ldyJ7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kIGxpZ2h0Z3JlZW4KICAgICAgICAgICAgICAgIGJvcmRlciBkYXNoZWQKICAgICAgICAgICAgICAgIHN0cm9rZVdpZHRoIDUKICAgICAgICAgICAgICAgIHN0cm9rZSBibGFjawogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQogICAgCn0K" + "name": "Name", + "properties": { + "structurizr.dsl": "d29ya3NwYWNlIHsKCiAgICBtb2RlbCB7CiAgICAgICAgCiAgICAgICAgcHJpc29uVXNlciA9IHBlcnNvbiAiIFByaXNvbiBTdGFmZiBVc2VyIiAiU29tZW9uZSB3aG8gbmVlZHMgYWNjZXNzIHRvIHByaXNvbmVyIGluZm9ybWF0aW9uIHRvIGNhcnJ5IG91dCB0aGVpciBkdXRpZXMiCgogICAgICAgIHByb2JhdGlvblVzZXIgPSBwZXJzb24gIiBQcm9iYXRpb24gU3RhZmYgVXNlciIgIlNvbWVvbmUgd2hvIG5lZWRzIGFjY2VzcyB0byBwcmlzb25lciBpbmZvcm1hdGlvbiB0byBjYXJyeSBvdXQgdGhlaXIgZHV0aWVzIGluIHByb2JhdGlvbiIKCiAgICAgICAgZXh0ZXJuYWxVc2VyID0gcGVyc29uICIgRXh0ZXJuYWwgVXNlciIgIlNvbWVvbmUgd2hvIG5lZWRzIGFjY2VzcyB0byBwcmlzb25lciBpbmZvcm1hdGlvbiBmcm9tIGFuIGFnZW5jeSBlLmcuIHRoZSBwb2xpY2UsIE5IUyBvciBvdGhlciBhZ2VuY3kiCgogICAgICAgIERPTTEgPSBwZXJzb24gIkRPTTEgTG9naW4gVXNlciJ7CiAgICAgICAgICAgIHRhZ3MgImV4dGVybmFsIHN5c3RlbSIsICJBenVyZSIKICAgICAgICAgICAgcHJpc29uVXNlciAtPiBET00xICJpcyBhIgogICAgICAgICAgICBwcm9iYXRpb25Vc2VyIC0+IERPTTEgImlzIGEiCiAgICAgICAgfQoKICAgICAgICBFeHRlcm5hbFVzZXJzID0gc29mdHdhcmVTeXN0ZW0gIkV4dGVybmFsIFVzZXJzInsKICAgICAgICAgICAgRXh0ZXJuYWxVc2Vyc0FwaSA9IGNvbnRhaW5lciAiRXh0ZXJuYWwgVXNlcnMgQVBJIgogICAgICAgIH0KCiAgICAgICAgR292Tm90aWZ5ID0gc29mdHdhcmVTeXN0ZW0gIkdvdiBOb3RpZnkiIHsKICAgICAgICAgICAgdGFncyAiZXh0ZXJuYWwgc3lzdGVtIiwgIk1haWwiCiAgICAgICAgfQoKCiAgICAgICAgTk9NSVMgPSBzb2Z0d2FyZVN5c3RlbSAiTk9NSVMgUHJpc29uIFN5c3RlbSIgewogICAgICAgICAgICB0YWdzICJMZWdhY3kgU3lzdGVtIgogICAgICAgICAgICBOT01JU1VzZXJSb2xlc0FQSSA9IGNvbnRhaW5lciAiTk9NSVMgVXNlciBSb2xlcyBBUEkiCiAgICAgICAgICAgIE5PTUlTZGF0YWJhc2UgPSBjb250YWluZXIgIk5PTUlTIERhdGFiYXNlIgogICAgICAgICAgICBOT01JU1VzZXJSb2xlc0FQSSAtPiBOT01JU2RhdGFiYXNlICJyZWFkcyAvIHdyaXRlcyIKICAgICAgICB9CgoKICAgICAgICBEZWxpdXMgPSBzb2Z0d2FyZVN5c3RlbSAiRGVsaXVzIFN5c3RlbXMiIHsKICAgICAgICAgICAgdGFncyAiTGVnYWN5IFN5c3RlbSIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2Vyc0RlbGl1c0FwaSA9IGNvbnRhaW5lciAiSE1QUFMgTWFuYWdlIFVzZXIgdG8gZGVsaXVzIEFQSSIKICAgICAgICAgICAgTkRlbGl1cyA9IGNvbnRhaW5lciAiTkRlbGl1cyIKICAgICAgICAgICAgCiAgICAgICAgICAgIEhNUFBTTWFuYWdlVXNlcnNEZWxpdXNBcGkgLT4gTkRlbGl1cyAiZ2V0cyB1c2VyIGluZm8gZnJvbSIKICAgICAgICB9CgogICAgICAgIEhNUFBTTWFuYWdlVXNlcnMgPSBzb2Z0d2FyZVN5c3RlbSAiSE1QUFMgTWFuYWdlIFVzZXJzIiB7CiAgICAgICAgICAgIHRhZ3MgIkhNUFBTIERpZ2l0YWwgU2VydmljZSIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2Vyc0FwaSA9IGNvbnRhaW5lciAiTWFuYWdlIFVzZXJzIEFQSSIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2Vyc1VpID0gY29udGFpbmVyICJNYW5hZ2UgVXNlcnMgVUkiCiAgICAgICAgICAgIAogICAgICAgICAgICBITVBQU01hbmFnZVVzZXJzVWkgLT4gSE1QUFNNYW5hZ2VVc2Vyc0FwaSAiY29ubmVjdHMgdG8iCiAgICAgICAgICAgIEhNUFBTTWFuYWdlVXNlcnNBcGkgLT4gTk9NSVNVc2VyUm9sZXNBUEkgImdldHMgTk9NSVMgdXNlcnMgYW5kIHJvbGVzIgogICAgICAgICAgICBITVBQU01hbmFnZVVzZXJzQXBpIC0+IEV4dGVybmFsVXNlcnNBcGkgImdldHMgZXh0ZXJuYWwgdXNlcnMgYW5kIHJvbGVzIgogICAgICAgICAgICBITVBQU01hbmFnZVVzZXJzQXBpIC0+IEhNUFBTTWFuYWdlVXNlcnNEZWxpdXNBcGkgImdldHMgRGVsaXVzIHVzZXJzIGFuZCByb2xlcyIKICAgICAgICAgICAgSE1QUFNNYW5hZ2VVc2VycyAtPiBHb3ZOb3RpZnkKICAgICAgICB9CgoKICAgICAgICBITVBQU0F1dGggPSBzb2Z0d2FyZVN5c3RlbSAiSE1QUFMgQXV0aCIgIkF1dGhlbnRpY2F0aW9uIGFuZCBBdXRob3JpemF0aW9uIHNlcnZlciJ7CiAgICAgICAgICAgIHRhZ3MgIkhNUFBTIERpZ2l0YWwgU2VydmljZSIKCiAgICAgICAgICAgIEhNUFBTQXV0aG9yaXphdGlvblNlcnZlciA9IGNvbnRhaW5lciAiSE1QUFMgQXV0aG9yaXphdGlvbiBTZXJ2ZXIiIHsgCiAgICAgICAgICAgICAgICB0YWdzICJOZXciIAogICAgICAgICAgICB9CgogICAgICAgICAgICBITVBQU0F1dGhvcml6YXRpb25VSSA9IGNvbnRhaW5lciAiSE1QUFMgQXV0aG9yaXphdGlvbiBVSSJ7CiAgICAgICAgICAgICAgICB0YWdzICJOZXciCiAgICAgICAgICAgIH0KICAgICAgICAgICAgCiAgICAgICAgICAgIEhNUFBTTGVnYWN5QXV0aCA9IGNvbnRhaW5lciAiSE1QUFMgTGVnYWN5IGF1dGggc2VydmljZSIKICAgICAgICAgICAgdG9rZW5WZXJpZmljYXRpb25BUEkgPSBjb250YWluZXIgIlRva2VuIFZlcmlmaWNhdGlvbiBBUEkiCgogICAgICAgICAgICBITVBQU0xlZ2FjeUF1dGggLT4gSE1QUFNBdXRob3JpemF0aW9uU2VydmVyICJQcm94aWVzIgogICAgICAgICAgICBITVBQU0F1dGhvcml6YXRpb25VSSAtPiBITVBQU0F1dGhvcml6YXRpb25TZXJ2ZXIgImdldHMgY2xpZW50IGNyZWRlbnRpYWwgaW5mbyBmcm9tIgogICAgICAgICAgICBITVBQU0F1dGhvcml6YXRpb25TZXJ2ZXIgLT4gSE1QUFNNYW5hZ2VVc2Vyc0FwaSAiZ2V0cyB1c2VyIHJvbGVzIgogICAgICAgICAgICBITVBQU0xlZ2FjeUF1dGggLT4gSE1QUFNNYW5hZ2VVc2Vyc0FwaSAiZ2V0cyB1c2VyIHJvbGVzIgogICAgICAgICAgICBITVBQU0xlZ2FjeUF1dGggLT4gR292Tm90aWZ5ICJOb3RpZmllcyB1c2VycyIKICAgICAgICAgICAgSE1QUFNMZWdhY3lBdXRoIC0+IHRva2VuVmVyaWZpY2F0aW9uQVBJCiAgICAgICAgICAgIHByaXNvblVzZXIgLT4gSE1QUFNMZWdhY3lBdXRoICJMb2dzIGluIgogICAgICAgICAgICBET00xIC0+IEhNUFBTTGVnYWN5QXV0aCAiTG9ncyBpbiIKICAgICAgICAgICAgcHJvYmF0aW9uVXNlciAtPiBITVBQU0xlZ2FjeUF1dGggIkxvZ3MgaW4iCiAgICAgICAgICAgIGV4dGVybmFsVXNlciAtPiBITVBQU0xlZ2FjeUF1dGggIkxvZ3MgaW4iCiAgICAgICAgfQoKICAgICAgICBITVBQU0RpZ2l0YWxTZXJ2aWNlcyA9IHNvZnR3YXJlU3lzdGVtICJITVBQUyBEaWdpdGFsIFNlcnZpY2VzInsKICAgICAgICAgICAgdGFncyAiSE1QUFMgRGlnaXRhbCBTZXJ2aWNlIiAKICAgICAgICAgICAgSE1QUFNEaWdpdGFsU2VydmljZSA9IGNvbnRhaW5lciAiZ2VuZXJhbCBITVBQUyBEaWdpdGFsIFNlcnZpY2UiCiAgICAgICAgICAgIEhNUFBTRGlnaXRhbFNlcnZpY2UgLT4gdG9rZW5WZXJpZmljYXRpb25BUEkgInZlcmlmaWVzIHRva2VucyIKICAgICAgICB9CiAgICAgICAgCiAgICAgICAgSE1QUFNBdXRoIC0+IEhNUFBTTWFuYWdlVXNlcnMgIkdldHMgdXNlciBhbmQgcm9sZSBpbmZvcm1hdGlvbiBmcm9tIgogICAgICAgIEhNUFBTRGlnaXRhbFNlcnZpY2VzIC0+IEhNUFBTQXV0aCAiY2hlY2tzIGF1dGhlbnRpY2F0aW9uIgogICAgICAgIEhNUFBTRGlnaXRhbFNlcnZpY2VzIC0+IEhNUFBTTWFuYWdlVXNlcnMgImNoZWNrcyB1c2VyIHJvbGVzIgoKICAgIH0KCiAgICB2aWV3cyB7CgogICAgICAgIHN5c3RlbUxhbmRzY2FwZSAgIkhtcHBzQXV0aExhbmRzY2FwZSIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBITVBQU0F1dGggIkhNUFBTQXV0aCIgewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBITVBQU0RpZ2l0YWxTZXJ2aWNlcyAiRGlnaXRhbFNlcnZpY2VzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgIH0KCiAgICAgICAgY29udGFpbmVyIE5PTUlTICJOT01JUyJ7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICB9CgogICAgICAgIGNvbnRhaW5lciBEZWxpdXMgIkRlbGl1cyJ7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgIH0KCiAgICAgICAgIGNvbnRhaW5lciBFeHRlcm5hbFVzZXJzICJFeHRlcm5hbFVzZXJzIiB7CiAgICAgICAgICAgIGluY2x1ZGUgKgogICAgICAgICB9CgogICAgICAgICBjb250YWluZXIgSE1QUFNNYW5hZ2VVc2VycyAiTWFuYWdlVXNlcnMiewogICAgICAgICAgICBpbmNsdWRlICoKICAgICAgICAgfQoKICAgICAgICBzdHlsZXMgewoKICAgICAgICAgICAgZWxlbWVudCAiU29mdHdhcmUgU3lzdGVtIiB7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kICMxMTY4YmQKICAgICAgICAgICAgICAgIGNvbG9yICNmZmZmZmYKICAgICAgICAgICAgfQoKICAgICAgICAgICAgZWxlbWVudCAiTGVnYWN5IFN5c3RlbSIgewogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjY2NjY2NjCiAgICAgICAgICAgICAgICBjb2xvciAjMDAwMDAwCiAgICAgICAgICAgIH0gIAogICAgICAgICAgICBlbGVtZW50ICJFeHRlcm5hbCBTeXN0ZW0iIHsKICAgICAgICAgICAgICAgIGJhY2tncm91bmQgIzM1OThFRQogICAgICAgICAgICAgICAgY29sb3IgIzAwMDAwMAogICAgICAgICAgICB9ICAgICAgICAgICAgIAogICAgICAgICAgICBlbGVtZW50ICJQZXJzb24iIHsKICAgICAgICAgICAgICAgIHNoYXBlIHBlcnNvbgogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjMDg0MjdiCiAgICAgICAgICAgICAgICBjb2xvciAjZmZmZmZmCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGVsZW1lbnQgIkF6dXJlIiB7CiAgICAgICAgICAgICAgICBpY29uICJBenVyZUFELnBuZyIKICAgICAgICAgICAgICAgIG9wYWNpdHkgMTAwCiAgICAgICAgICAgICAgICBzdHJva2VXaWR0aCAzCiAgICAgICAgICAgICAgICBjb2xvciBibGFjawogICAgICAgICAgICAgICAgYmFja2dyb3VuZCAjYzJkOGVkCiAgICAgICAgICAgICAgICBzdHJva2UgYmxhY2sKICAgICAgICAgICAgfQoKICAgICAgICAgICBlbGVtZW50ICJNYWlsInsKICAgICAgICAgICAgICAgIGljb24gImVtYWlsLnBuZyIKICAgICAgICAgICAgICAgIG9wYWNpdHkgMTAwCiAgICAgICAgICAgICAgICBzdHJva2VXaWR0aCAzCiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kIHdoaXRlCiAgICAgICAgICAgICAgICBjb2xvciBibGFjawogICAgICAgICAgICAgICAgc3Ryb2tlIGJsYWNrCiAgICAgICAgICAgIH0KCiAgICAgICAgICAgIGVsZW1lbnQgIk5ldyJ7CiAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kIGxpZ2h0Z3JlZW4KICAgICAgICAgICAgICAgIGJvcmRlciBkYXNoZWQKICAgICAgICAgICAgICAgIHN0cm9rZVdpZHRoIDUKICAgICAgICAgICAgICAgIHN0cm9rZSBibGFjawogICAgICAgICAgICB9CiAgICAgICAgfQogICAgfQogICAgCn0K" }, - "views" : { - "configuration" : { - "branding" : { }, - "lastSavedView" : "Delius", - "styles" : { - "elements" : [ { - "background" : "#1168bd", - "color" : "#ffffff", - "tag" : "Software System" - }, { - "background" : "#cccccc", - "color" : "#000000", - "tag" : "Legacy System" - }, { - "background" : "#3598ee", - "color" : "#000000", - "tag" : "External System" - }, { - "background" : "#08427b", - "color" : "#ffffff", - "shape" : "Person", - "tag" : "Person" - }, { - "background" : "#c2d8ed", - "color" : "#000000", - "icon" : "data:image/png;base64,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", - "opacity" : 100, - "stroke" : "#000000", - "strokeWidth" : 3, - "tag" : "Azure" - }, { - "background" : "#ffffff", - "color" : "#000000", - "icon" : "data:image/png;base64,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", - "opacity" : 100, - "stroke" : "#000000", - "strokeWidth" : 3, - "tag" : "Mail" - }, { - "background" : "#90ee90", - "border" : "Dashed", - "stroke" : "#000000", - "strokeWidth" : 5, - "tag" : "New" - } ] + "views": { + "configuration": { + "branding": {}, + "lastSavedView": "Delius", + "styles": { + "elements": [ + { + "background": "#1168bd", + "color": "#ffffff", + "tag": "Software System" + }, + { + "background": "#cccccc", + "color": "#000000", + "tag": "Legacy System" + }, + { + "background": "#3598ee", + "color": "#000000", + "tag": "External System" + }, + { + "background": "#08427b", + "color": "#ffffff", + "shape": "Person", + "tag": "Person" + }, + { + "background": "#c2d8ed", + "color": "#000000", + "icon": "data:image/png;base64,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", + "opacity": 100, + "stroke": "#000000", + "strokeWidth": 3, + "tag": "Azure" + }, + { + "background": "#ffffff", + "color": "#000000", + "icon": "data:image/png;base64,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", + "opacity": 100, + "stroke": "#000000", + "strokeWidth": 3, + "tag": "Mail" + }, + { + "background": "#90ee90", + "border": "Dashed", + "stroke": "#000000", + "strokeWidth": 5, + "tag": "New" + } + ] }, - "terminology" : { } + "terminology": {} }, - "containerViews" : [ { - "dimensions" : { - "height" : 3216, - "width" : 3391 - }, - "elements" : [ { - "id" : "1", - "x" : 2140, - "y" : 55 - }, { - "id" : "2", - "x" : 635, - "y" : 55 - }, { - "id" : "3", - "x" : 195, - "y" : 380 - }, { - "id" : "4", - "x" : 1295, - "y" : 60 - }, { - "id" : "9", - "x" : 1000, - "y" : 1910 - }, { - "id" : "18", - "x" : 1160, - "y" : 2850 - }, { - "id" : "36", - "x" : 380, - "y" : 1455 - }, { - "id" : "37", - "x" : 365, - "y" : 885 - }, { - "id" : "38", - "x" : 1445, - "y" : 1060 - }, { - "id" : "39", - "x" : 2245, - "y" : 1040 - }, { - "id" : "59", - "x" : 1835, - "y" : 1955 - } ], - "externalSoftwareSystemBoundariesVisible" : false, - "key" : "HMPPSAuth", - "order" : 2, - "paperSize" : "A3_Landscape", - "relationships" : [ { - "id" : "34" - }, { - "id" : "40" - }, { - "id" : "41" - }, { - "id" : "43" - }, { - "id" : "47" - }, { - "id" : "48" - }, { - "id" : "5" - }, { - "id" : "50" - }, { - "id" : "51" - }, { - "id" : "53" - }, { - "id" : "55" - }, { - "id" : "57" - }, { - "id" : "6" - }, { - "id" : "63" - }, { - "id" : "67" - } ], - "softwareSystemId" : "35" - }, { - "dimensions" : { - "height" : 1454, - "width" : 1108 - }, - "elements" : [ { - "id" : "35", - "x" : 330, - "y" : 930 - }, { - "id" : "60", - "x" : 330, - "y" : 330 - } ], - "externalSoftwareSystemBoundariesVisible" : false, - "key" : "DigitalServices", - "order" : 3, - "paperSize" : "A6_Portrait", - "relationships" : [ { - "id" : "62" - } ], - "softwareSystemId" : "59" - }, { - "dimensions" : { - "height" : 2020, - "width" : 1108 + "containerViews": [ + { + "dimensions": { + "height": 3216, + "width": 3391 + }, + "elements": [ + { + "id": "1", + "x": 2140, + "y": 55 + }, + { + "id": "2", + "x": 635, + "y": 55 + }, + { + "id": "3", + "x": 195, + "y": 380 + }, + { + "id": "4", + "x": 1295, + "y": 60 + }, + { + "id": "9", + "x": 1000, + "y": 1910 + }, + { + "id": "18", + "x": 1160, + "y": 2850 + }, + { + "id": "36", + "x": 380, + "y": 1455 + }, + { + "id": "37", + "x": 365, + "y": 885 + }, + { + "id": "38", + "x": 1445, + "y": 1060 + }, + { + "id": "39", + "x": 2245, + "y": 1040 + }, + { + "id": "59", + "x": 1835, + "y": 1955 + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "key": "HMPPSAuth", + "order": 2, + "paperSize": "A3_Landscape", + "relationships": [ + { + "id": "34" + }, + { + "id": "40" + }, + { + "id": "41" + }, + { + "id": "43" + }, + { + "id": "47" + }, + { + "id": "48" + }, + { + "id": "5" + }, + { + "id": "50" + }, + { + "id": "51" + }, + { + "id": "53" + }, + { + "id": "55" + }, + { + "id": "57" + }, + { + "id": "6" + }, + { + "id": "63" + }, + { + "id": "67" + } + ], + "softwareSystemId": "35" }, - "elements" : [ { - "id" : "11", - "x" : 329, - "y" : 808 - }, { - "id" : "12", - "x" : 329, - "y" : 1408 - }, { - "id" : "18", - "x" : 329, - "y" : 208 - } ], - "externalSoftwareSystemBoundariesVisible" : false, - "key" : "NOMIS", - "order" : 4, - "paperSize" : "A5_Portrait", - "relationships" : [ { - "id" : "13" - }, { - "id" : "24" - } ], - "softwareSystemId" : "10" - }, { - "dimensions" : { - "height" : 2020, - "width" : 1108 + { + "dimensions": { + "height": 1454, + "width": 1108 + }, + "elements": [ + { + "id": "35", + "x": 330, + "y": 930 + }, + { + "id": "60", + "x": 330, + "y": 330 + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "key": "DigitalServices", + "order": 3, + "paperSize": "A6_Portrait", + "relationships": [ + { + "id": "62" + } + ], + "softwareSystemId": "59" }, - "elements" : [ { - "id" : "15", - "x" : 329, - "y" : 808 - }, { - "id" : "16", - "x" : 329, - "y" : 1408 - }, { - "id" : "18", - "x" : 329, - "y" : 208 - } ], - "externalSoftwareSystemBoundariesVisible" : false, - "key" : "Delius", - "order" : 5, - "paperSize" : "A5_Portrait", - "relationships" : [ { - "id" : "17" - }, { - "id" : "32" - } ], - "softwareSystemId" : "14" - }, { - "dimensions" : { - "height" : 1420, - "width" : 1108 + { + "dimensions": { + "height": 2020, + "width": 1108 + }, + "elements": [ + { + "id": "11", + "x": 329, + "y": 808 + }, + { + "id": "12", + "x": 329, + "y": 1408 + }, + { + "id": "18", + "x": 329, + "y": 208 + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "key": "NOMIS", + "order": 4, + "paperSize": "A5_Portrait", + "relationships": [ + { + "id": "13" + }, + { + "id": "24" + } + ], + "softwareSystemId": "10" }, - "elements" : [ { - "id" : "8", - "x" : 329, - "y" : 808 - }, { - "id" : "18", - "x" : 329, - "y" : 208 - } ], - "externalSoftwareSystemBoundariesVisible" : false, - "key" : "ExternalUsers", - "order" : 6, - "paperSize" : "A6_Portrait", - "relationships" : [ { - "id" : "28" - } ], - "softwareSystemId" : "7" - }, { - "dimensions" : { - "height" : 2054, - "width" : 2366 + { + "dimensions": { + "height": 2020, + "width": 1108 + }, + "elements": [ + { + "id": "15", + "x": 329, + "y": 808 + }, + { + "id": "16", + "x": 329, + "y": 1408 + }, + { + "id": "18", + "x": 329, + "y": 208 + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "key": "Delius", + "order": 5, + "paperSize": "A5_Portrait", + "relationships": [ + { + "id": "17" + }, + { + "id": "32" + } + ], + "softwareSystemId": "14" }, - "elements" : [ { - "id" : "7", - "x" : 208, - "y" : 1529 - }, { - "id" : "10", - "x" : 958, - "y" : 1529 - }, { - "id" : "14", - "x" : 1710, - "y" : 1530 - }, { - "id" : "19", - "x" : 958, - "y" : 929 - }, { - "id" : "20", - "x" : 958, - "y" : 329 - }, { - "id" : "35", - "x" : 1708, - "y" : 329 - } ], - "externalSoftwareSystemBoundariesVisible" : false, - "key" : "ManageUsers", - "order" : 7, - "paperSize" : "A4_Landscape", - "relationships" : [ { - "id" : "21" - }, { - "id" : "23" - }, { - "id" : "27" - }, { - "id" : "31" - }, { - "id" : "44" - } ], - "softwareSystemId" : "18" - } ], - "systemLandscapeViews" : [ { - "dimensions" : { - "height" : 3316, - "width" : 4700 + { + "dimensions": { + "height": 1420, + "width": 1108 + }, + "elements": [ + { + "id": "8", + "x": 329, + "y": 808 + }, + { + "id": "18", + "x": 329, + "y": 208 + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "key": "ExternalUsers", + "order": 6, + "paperSize": "A6_Portrait", + "relationships": [ + { + "id": "28" + } + ], + "softwareSystemId": "7" }, - "elements" : [ { - "id" : "1", - "x" : 208, - "y" : 208 - }, { - "id" : "2", - "x" : 1900, - "y" : 225 - }, { - "id" : "3", - "x" : 2800, - "y" : 230 - }, { - "id" : "4", - "x" : 1100, - "y" : 235 - }, { - "id" : "7", - "x" : 2770, - "y" : 2835 - }, { - "id" : "9", - "x" : 1195, - "y" : 2130 - }, { - "id" : "10", - "x" : 3430, - "y" : 2840 - }, { - "id" : "14", - "x" : 2120, - "y" : 2835 - }, { - "id" : "18", - "x" : 2280, - "y" : 2120 - }, { - "id" : "35", - "x" : 1830, - "y" : 1440 - }, { - "id" : "59", - "x" : 3540, - "y" : 2150 - } ], - "enterpriseBoundaryVisible" : true, - "key" : "HmppsAuthLandscape", - "order" : 1, - "paperSize" : "A3_Landscape", - "relationships" : [ { - "id" : "25" - }, { - "id" : "29" - }, { - "id" : "33" - }, { - "id" : "34" - }, { - "id" : "45" - }, { - "id" : "49" - }, { - "id" : "5" - }, { - "id" : "52" - }, { - "id" : "54" - }, { - "id" : "56" - }, { - "id" : "58" - }, { - "id" : "6" - }, { - "id" : "64", - "vertices" : [ { - "x" : 2710, - "y" : 1505 - } ] - }, { - "id" : "65", - "vertices" : [ { - "x" : 2514, - "y" : 1944 - } ] - }, { - "id" : "66", - "vertices" : [ { - "x" : 2756, - "y" : 1802 - } ] - }, { - "id" : "67" - } ] - } ] + { + "dimensions": { + "height": 2054, + "width": 2366 + }, + "elements": [ + { + "id": "7", + "x": 208, + "y": 1529 + }, + { + "id": "10", + "x": 958, + "y": 1529 + }, + { + "id": "14", + "x": 1710, + "y": 1530 + }, + { + "id": "19", + "x": 958, + "y": 929 + }, + { + "id": "20", + "x": 958, + "y": 329 + }, + { + "id": "35", + "x": 1708, + "y": 329 + } + ], + "externalSoftwareSystemBoundariesVisible": false, + "key": "ManageUsers", + "order": 7, + "paperSize": "A4_Landscape", + "relationships": [ + { + "id": "21" + }, + { + "id": "23" + }, + { + "id": "27" + }, + { + "id": "31" + }, + { + "id": "44" + } + ], + "softwareSystemId": "18" + } + ], + "systemLandscapeViews": [ + { + "dimensions": { + "height": 3316, + "width": 4700 + }, + "elements": [ + { + "id": "1", + "x": 208, + "y": 208 + }, + { + "id": "2", + "x": 1900, + "y": 225 + }, + { + "id": "3", + "x": 2800, + "y": 230 + }, + { + "id": "4", + "x": 1100, + "y": 235 + }, + { + "id": "7", + "x": 2770, + "y": 2835 + }, + { + "id": "9", + "x": 1195, + "y": 2130 + }, + { + "id": "10", + "x": 3430, + "y": 2840 + }, + { + "id": "14", + "x": 2120, + "y": 2835 + }, + { + "id": "18", + "x": 2280, + "y": 2120 + }, + { + "id": "35", + "x": 1830, + "y": 1440 + }, + { + "id": "59", + "x": 3540, + "y": 2150 + } + ], + "enterpriseBoundaryVisible": true, + "key": "HmppsAuthLandscape", + "order": 1, + "paperSize": "A3_Landscape", + "relationships": [ + { + "id": "25" + }, + { + "id": "29" + }, + { + "id": "33" + }, + { + "id": "34" + }, + { + "id": "45" + }, + { + "id": "49" + }, + { + "id": "5" + }, + { + "id": "52" + }, + { + "id": "54" + }, + { + "id": "56" + }, + { + "id": "58" + }, + { + "id": "6" + }, + { + "id": "64", + "vertices": [ + { + "x": 2710, + "y": 1505 + } + ] + }, + { + "id": "65", + "vertices": [ + { + "x": 2514, + "y": 1944 + } + ] + }, + { + "id": "66", + "vertices": [ + { + "x": 2756, + "y": 1802 + } + ] + }, + { + "id": "67" + } + ] + } + ] } -} \ No newline at end of file +} diff --git a/helm_deploy/hmpps-authorization/Chart.yaml b/helm_deploy/hmpps-authorization/Chart.yaml index 1f361510..fa595583 100644 --- a/helm_deploy/hmpps-authorization/Chart.yaml +++ b/helm_deploy/hmpps-authorization/Chart.yaml @@ -8,5 +8,5 @@ dependencies: version: "2.9" repository: https://ministryofjustice.github.io/hmpps-helm-charts - name: generic-prometheus-alerts - version: "1.3" + version: "1.4" repository: https://ministryofjustice.github.io/hmpps-helm-charts diff --git a/integration_tests/e2e/edit-base-client-deployment.cy.ts b/integration_tests/e2e/edit-base-client-deployment.cy.ts index 5e5a882c..019c07bf 100644 --- a/integration_tests/e2e/edit-base-client-deployment.cy.ts +++ b/integration_tests/e2e/edit-base-client-deployment.cy.ts @@ -1,7 +1,7 @@ import Page from '../pages/page' import ViewBaseClientPage from '../pages/viewBaseClient' import EditBaseClientDeploymentDetailsPage from '../pages/editBaseClientDeploymentDetails' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' import AuthSignInPage from '../pages/authSignIn' import AuthErrorPage from '../pages/authError' @@ -16,7 +16,7 @@ context('Edit base client deployment: Auth', () => { cy.task('stubSignIn') cy.task('stubManageUser') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubGetListClientInstancesList') }) @@ -41,7 +41,7 @@ context('Edit base client deployment details page', () => { cy.task('stubSignIn') cy.task('stubManageUser') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubGetListClientInstancesList') editBaseClientDeploymentDetailsPage = visitEditBaseClientDeploymentDetailsPage() }) diff --git a/integration_tests/e2e/edit-base-client-details.cy.ts b/integration_tests/e2e/edit-base-client-details.cy.ts index 4b731e5b..999a736b 100644 --- a/integration_tests/e2e/edit-base-client-details.cy.ts +++ b/integration_tests/e2e/edit-base-client-details.cy.ts @@ -1,7 +1,7 @@ import Page from '../pages/page' import EditBaseClientDetailsPage from '../pages/editBaseClientDetails' import ViewBaseClientPage from '../pages/viewBaseClient' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' import AuthSignInPage from '../pages/authSignIn' import AuthErrorPage from '../pages/authError' @@ -16,7 +16,7 @@ context('Edit base client details: Auth', () => { cy.task('stubSignIn') cy.task('stubManageUser') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubGetListClientInstancesList') }) @@ -41,7 +41,7 @@ context('Edit base client details page - client-credentials flow', () => { cy.task('stubSignIn') cy.task('stubManageUser') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubGetListClientInstancesList') cy.task('stubAuthManageDetails') editBaseClientDetailsPage = visitEditBaseClientDetailsPage() @@ -126,7 +126,7 @@ context('Edit base client details page - authorization-code flow', () => { cy.task('stubSignIn') cy.task('stubManageUser') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.AuthorizationCode }) + cy.task('stubGetBaseClient', { grantType: GrantType.AuthorizationCode }) cy.task('stubGetListClientInstancesList') editBaseClientDetailsPage = visitEditBaseClientDetailsPage() }) diff --git a/integration_tests/e2e/edit-client-instances.cy.ts b/integration_tests/e2e/edit-client-instances.cy.ts index 189d60cc..1995ee76 100644 --- a/integration_tests/e2e/edit-client-instances.cy.ts +++ b/integration_tests/e2e/edit-client-instances.cy.ts @@ -2,7 +2,7 @@ import Page from '../pages/page' import ViewBaseClientPage from '../pages/viewBaseClient' import ViewClientSecretsPage from '../pages/viewClientSecrets' import ConfirmDeleteClientPage from '../pages/confirmDeleteClient' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' const visitBaseClientPage = (): ViewBaseClientPage => { cy.signIn({ failOnStatusCode: true, redirectPath: '/base-clients/base_client_id_1' }) @@ -21,7 +21,7 @@ context('Base client page - client instances', () => { beforeEach(() => { cy.task('reset') cy.task('stubSignIn') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubManageUser') cy.task('stubGetListClientInstancesList') }) diff --git a/integration_tests/e2e/login.cy.ts b/integration_tests/e2e/login.cy.ts index 6d62a3fd..3c309d4e 100644 --- a/integration_tests/e2e/login.cy.ts +++ b/integration_tests/e2e/login.cy.ts @@ -2,7 +2,7 @@ import IndexPage from '../pages/index' import AuthSignInPage from '../pages/authSignIn' import Page from '../pages/page' import AuthManageDetailsPage from '../pages/authManageDetails' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' import AuthErrorPage from '../pages/authError' context('SignIn', () => { @@ -10,7 +10,7 @@ context('SignIn', () => { cy.task('reset') cy.task('stubSignIn', ['ROLE_OAUTH_ADMIN']) cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubManageUser') cy.task('stubAuthManageDetails') }) diff --git a/integration_tests/e2e/view-base-client-list.cy.ts b/integration_tests/e2e/view-base-client-list.cy.ts index e0438a04..8df7db90 100644 --- a/integration_tests/e2e/view-base-client-list.cy.ts +++ b/integration_tests/e2e/view-base-client-list.cy.ts @@ -2,7 +2,7 @@ import Page from '../pages/page' import ViewBaseClientListPage from '../pages/viewBaseClientList' import ViewBaseClientPage from '../pages/viewBaseClient' import NewBaseClientGrantPage from '../pages/newBaseClientGrant' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' import AuthSignInPage from '../pages/authSignIn' import AuthErrorPage from '../pages/authError' @@ -17,7 +17,7 @@ context('Homepage - Auth', () => { cy.task('reset') cy.task('stubSignIn') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubManageUser') cy.task('stubGetListClientInstancesList') }) @@ -42,7 +42,7 @@ context('Homepage - list base-clients', () => { cy.task('reset') cy.task('stubSignIn') cy.task('stubListBaseClients') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubManageUser') cy.task('stubGetListClientInstancesList') diff --git a/integration_tests/e2e/view-base-client.cy.ts b/integration_tests/e2e/view-base-client.cy.ts index 98e7304f..d78788d3 100644 --- a/integration_tests/e2e/view-base-client.cy.ts +++ b/integration_tests/e2e/view-base-client.cy.ts @@ -4,7 +4,7 @@ import ViewClientSecretsPage from '../pages/viewClientSecrets' import ConfirmDeleteClientPage from '../pages/confirmDeleteClient' import EditBaseClientDetailsPage from '../pages/editBaseClientDetails' import EditBaseClientDeploymentDetailsPage from '../pages/editBaseClientDeploymentDetails' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' import AuthSignInPage from '../pages/authSignIn' import AuthErrorPage from '../pages/authError' @@ -17,7 +17,7 @@ context('Base client page - Auth', () => { beforeEach(() => { cy.task('reset') cy.task('stubSignIn') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubManageUser') cy.task('stubGetListClientInstancesList') cy.task('stubAddClientInstance') @@ -42,7 +42,7 @@ context('Base client page - client credentials flow', () => { beforeEach(() => { cy.task('reset') cy.task('stubSignIn') - cy.task('stubGetBaseClient', { grantType: GrantTypes.ClientCredentials }) + cy.task('stubGetBaseClient', { grantType: GrantType.ClientCredentials }) cy.task('stubManageUser') cy.task('stubGetListClientInstancesList') cy.task('stubAddClientInstance') @@ -115,7 +115,7 @@ context('Base client page - authorization-code flow', () => { beforeEach(() => { cy.task('reset') cy.task('stubSignIn') - cy.task('stubGetBaseClient', { grantType: GrantTypes.AuthorizationCode }) + cy.task('stubGetBaseClient', { grantType: GrantType.AuthorizationCode }) cy.task('stubManageUser') cy.task('stubGetListClientInstancesList') cy.task('stubAddClientInstance') diff --git a/integration_tests/mockApis/baseClientsApi.ts b/integration_tests/mockApis/baseClientsApi.ts index e7b682d2..99061c05 100644 --- a/integration_tests/mockApis/baseClientsApi.ts +++ b/integration_tests/mockApis/baseClientsApi.ts @@ -5,7 +5,7 @@ import { getListClientInstancesResponseMock, getSecretsResponseMock, } from '../../server/data/localMockData/baseClientsResponseMock' -import { GrantTypes } from '../../server/data/enums/grantTypes' +import { GrantType } from '../../server/data/enums/grantType' export default { stubListBaseClients: () => { @@ -24,7 +24,7 @@ export default { }) }, - stubGetBaseClient: (config: { grantType: GrantTypes }) => { + stubGetBaseClient: (config: { grantType: GrantType }) => { return stubFor({ request: { method: 'GET', diff --git a/package-lock.json b/package-lock.json index 827fb1a7..66686d4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "connect-flash": "^0.1.1", "connect-redis": "^7.1.1", "csurf": "^1.11.0", - "date-fns": "^2.30.0", + "date-fns": "^3.3.1", "express": "^4.18.2", "express-prom-bundle": "^7.0.0", "express-session": "^1.18.0", @@ -62,8 +62,8 @@ "@types/superagent": "^8.1.4", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", "audit-ci": "^6.6.1", "concurrently": "^8.2.2", "cookie-session": "^2.1.0", @@ -262,7 +262,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sso": { + "node_modules/@aws-sdk/client-sso": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.525.0.tgz", "integrity": "sha512-6KwGQWFoNLH1UupdWPFdKPfTgjSz1kN8/r8aCzuvvXBe4Pz+iDUZ6FEJzGWNc9AapjvZDNO1hs23slomM9rTaA==", @@ -310,7 +310,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sso-oidc": { + "node_modules/@aws-sdk/client-sso-oidc": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.525.0.tgz", "integrity": "sha512-zz13k/6RkjPSLmReSeGxd8wzGiiZa4Odr2Tv3wTcxClM4wOjD+zOgGv4Fe32b9AMqaueiCdjbvdu7AKcYxFA4A==", @@ -362,7 +362,7 @@ "@aws-sdk/credential-provider-node": "^3.525.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sts": { + "node_modules/@aws-sdk/client-sts": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.525.0.tgz", "integrity": "sha512-a8NUGRvO6rkfTZCbMaCsjDjLbERCwIUU9dIywFYcRgbFhkupJ7fSaZz3Het98U51M9ZbTEpaTa3fz0HaJv8VJw==", @@ -414,7 +414,7 @@ "@aws-sdk/credential-provider-node": "^3.525.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/core": { + "node_modules/@aws-sdk/core": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.525.0.tgz", "integrity": "sha512-E3LtEtMWCriQOFZpVKpLYzbdw/v2PAOEAMhn2VRRZ1g0/g1TXzQrfhEU2yd8l/vQEJaCJ82ooGGg7YECviBUxA==", @@ -430,7 +430,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-env": { + "node_modules/@aws-sdk/credential-provider-env": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.523.0.tgz", "integrity": "sha512-Y6DWdH6/OuMDoNKVzZlNeBc6f1Yjk1lYMjANKpIhMbkRCvLJw/PYZKOZa8WpXbTYdgg9XLjKybnLIb3ww3uuzA==", @@ -444,7 +444,26 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-ini": { + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.525.0.tgz", + "integrity": "sha512-RNWQGuSBQZhl3iqklOslUEfQ4br1V3DCPboMpeqFtddUWJV3m2u2extFur9/4Uy+1EHVF120IwZUKtd8dF+ibw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/fetch-http-handler": "^2.4.3", + "@smithy/node-http-handler": "^2.4.1", + "@smithy/property-provider": "^2.1.3", + "@smithy/protocol-http": "^3.2.1", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-stream": "^2.1.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.525.0.tgz", "integrity": "sha512-JDnccfK5JRb9jcgpc9lirL9PyCwGIqY0nKdw3LlX5WL5vTpTG4E1q7rLAlpNh7/tFD1n66Itarfv2tsyHMIqCw==", @@ -465,7 +484,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-node": { + "node_modules/@aws-sdk/credential-provider-node": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.525.0.tgz", "integrity": "sha512-RJXlO8goGXpnoHQAyrCcJ0QtWEOFa34LSbfdqBIjQX/fwnjUuEmiGdXTV3AZmwYQ7juk49tfBneHbtOP3AGqsQ==", @@ -487,7 +506,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-process": { + "node_modules/@aws-sdk/credential-provider-process": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.523.0.tgz", "integrity": "sha512-f0LP9KlFmMvPWdKeUKYlZ6FkQAECUeZMmISsv6NKtvPCI9e4O4cLTeR09telwDK8P0HrgcRuZfXM7E30m8re0Q==", @@ -502,7 +521,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-sso": { + "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.525.0.tgz", "integrity": "sha512-7V7ybtufxdD3plxeIeB6aqHZeFIUlAyPphXIUgXrGY10iNcosL970rQPBeggsohe4gCM6UvY2TfMeEcr+ZE8FA==", @@ -519,7 +538,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-web-identity": { + "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.525.0.tgz", "integrity": "sha512-sAukOjR1oKb2JXG4nPpuBFpSwGUhrrY17PG/xbTy8NAoLLhrqRwnErcLfdTfmj6tH+3094k6ws/Sh8a35ae7fA==", @@ -534,7 +553,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-host-header": { + "node_modules/@aws-sdk/middleware-host-header": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.523.0.tgz", "integrity": "sha512-4g3q7Ta9sdD9TMUuohBAkbx/e3I/juTqfKi7TPgP+8jxcYX72MOsgemAMHuP6CX27eyj4dpvjH+w4SIVDiDSmg==", @@ -548,7 +567,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-logger": { + "node_modules/@aws-sdk/middleware-logger": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.523.0.tgz", "integrity": "sha512-PeDNJNhfiaZx54LBaLTXzUaJ9LXFwDFFIksipjqjvxMafnoVcQwKbkoPUWLe5ytT4nnL1LogD3s55mERFUsnwg==", @@ -561,7 +580,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-recursion-detection": { + "node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.523.0.tgz", "integrity": "sha512-nZ3Vt7ehfSDYnrcg/aAfjjvpdE+61B3Zk68i6/hSUIegT3IH9H1vSW67NDKVp+50hcEfzWwM2HMPXxlzuyFyrw==", @@ -575,7 +594,23 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-user-agent": { + "node_modules/@aws-sdk/middleware-sdk-sqs": { + "version": "3.525.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.525.0.tgz", + "integrity": "sha512-w+H1VOajANjo5gxe2/rQjO7HEuIiEyuFNZzNsztH1E9JBZ01Z2EvEYAfZTkCOV40Or4I2lTHnyt9voXIxW+bzw==", + "dependencies": { + "@aws-sdk/types": "3.523.0", + "@smithy/smithy-client": "^2.4.2", + "@smithy/types": "^2.10.1", + "@smithy/util-hex-encoding": "^2.1.1", + "@smithy/util-utf8": "^2.1.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.525.0.tgz", "integrity": "sha512-4al/6uO+t/QIYXK2OgqzDKQzzLAYJza1vWFS+S0lJ3jLNGyLB5BMU5KqWjDzevYZ4eCnz2Nn7z0FveUTNz8YdQ==", @@ -590,7 +625,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/region-config-resolver": { + "node_modules/@aws-sdk/region-config-resolver": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.525.0.tgz", "integrity": "sha512-8kFqXk6UyKgTMi7N7QlhA6qM4pGPWbiUXqEY2RgUWngtxqNFGeM9JTexZeuavQI+qLLe09VPShPNX71fEDcM6w==", @@ -606,7 +641,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/token-providers": { + "node_modules/@aws-sdk/token-providers": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.525.0.tgz", "integrity": "sha512-puVjbxuK0Dq7PTQ2HdddHy2eQjOH8GZbump74yWJa6JVpRW84LlOcNmP+79x4Kscvz2ldWB8XDFw/pcCiSDe5A==", @@ -622,7 +657,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/types": { + "node_modules/@aws-sdk/types": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", @@ -634,7 +669,7 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-endpoints": { + "node_modules/@aws-sdk/util-endpoints": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.525.0.tgz", "integrity": "sha512-DIW7WWU5tIGkeeKX6NJUyrEIdWMiqjLQG3XBzaUj+ufIENwNjdAHhlD8l2vX7Yr3JZRT6yN/84wBCj7Tw1xd1g==", @@ -648,7 +683,18 @@ "node": ">=14.0.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-user-agent-browser": { + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.495.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.495.0.tgz", + "integrity": "sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.523.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.523.0.tgz", "integrity": "sha512-6ZRNdGHX6+HQFqTbIA5+i8RWzxFyxsZv8D3soRfpdyWIKkzhSz8IyRKXRciwKBJDaC7OX2jzGE90wxRQft27nA==", @@ -659,7 +705,7 @@ "tslib": "^2.5.0" } }, - "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-user-agent-node": { + "node_modules/@aws-sdk/util-user-agent-node": { "version": "3.525.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.525.0.tgz", "integrity": "sha512-88Wjt4efyUSBGcyIuh1dvoMqY1k15jpJc5A/3yi67clBQEFsu9QCodQCQPqmRjV3VRcMtBOk+jeCTiUzTY5dRQ==", @@ -681,88 +727,6 @@ } } }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.525.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.525.0.tgz", - "integrity": "sha512-RNWQGuSBQZhl3iqklOslUEfQ4br1V3DCPboMpeqFtddUWJV3m2u2extFur9/4Uy+1EHVF120IwZUKtd8dF+ibw==", - "dependencies": { - "@aws-sdk/types": "3.523.0", - "@smithy/fetch-http-handler": "^2.4.3", - "@smithy/node-http-handler": "^2.4.1", - "@smithy/property-provider": "^2.1.3", - "@smithy/protocol-http": "^3.2.1", - "@smithy/smithy-client": "^2.4.2", - "@smithy/types": "^2.10.1", - "@smithy/util-stream": "^2.1.3", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/@aws-sdk/types": { - "version": "3.523.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", - "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", - "dependencies": { - "@smithy/types": "^2.10.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-sqs": { - "version": "3.525.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.525.0.tgz", - "integrity": "sha512-w+H1VOajANjo5gxe2/rQjO7HEuIiEyuFNZzNsztH1E9JBZ01Z2EvEYAfZTkCOV40Or4I2lTHnyt9voXIxW+bzw==", - "dependencies": { - "@aws-sdk/types": "3.523.0", - "@smithy/smithy-client": "^2.4.2", - "@smithy/types": "^2.10.1", - "@smithy/util-hex-encoding": "^2.1.1", - "@smithy/util-utf8": "^2.1.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@aws-sdk/types": { - "version": "3.523.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.523.0.tgz", - "integrity": "sha512-AqGIu4u+SxPiUuNBp2acCVcq80KDUFjxe6e3cMTvKWTzCbrVk1AXv0dAaJnCmdkWIha6zJDWxpIk/aL4EGhZ9A==", - "dependencies": { - "@smithy/types": "^2.10.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.489.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.489.0.tgz", - "integrity": "sha512-kcDtLfKog/p0tC4gAeqJqWxAiEzfe2LRPnKamvSG2Mjbthx4R/alE2dxyIq/wW+nvRv0fqR3OD5kD1+eVfdr/w==", - "dependencies": { - "@smithy/types": "^2.8.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.465.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.465.0.tgz", - "integrity": "sha512-f+QNcWGswredzC1ExNAB/QzODlxwaTdXkNT5cvke2RLX8SFU5pYk6h4uCtWC0vWPELzOfMfloBrJefBzlarhsw==", - "dependencies": { - "tslib": "^2.5.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@aws-sdk/util-utf8-browser": { "version": "3.259.0", "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", @@ -1416,10 +1380,12 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.6", - "license": "MIT", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -1462,9 +1428,8 @@ }, "node_modules/@babel/traverse/node_modules/globals": { "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -3186,9 +3151,9 @@ "license": "MIT" }, "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/send": { @@ -3277,16 +3242,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.0.tgz", + "integrity": "sha512-j6vT/kCulhG5wBmGtstKeiVr1rdXE4nk+DT1k6trYkwlrvW9eOF5ZbgKnd/YR6PcM4uTEXa0h6Fcvf6X7Dxl0w==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/type-utils": "7.1.0", + "@typescript-eslint/utils": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -3302,8 +3267,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3312,15 +3277,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.1.0.tgz", + "integrity": "sha512-V1EknKUubZ1gWFjiOZhDSNToOjs63/9O0puCgGS8aDOgpZY326fzFu15QAUjwaXzRZjf/qdsdBrckYdv9YxB8w==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4" }, "engines": { @@ -3331,7 +3296,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3340,13 +3305,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.1.0.tgz", + "integrity": "sha512-6TmN4OJiohHfoOdGZ3huuLhpiUgOGTpgXNUPJgeZOZR3DnIpdSgtt83RS35OYNNXxM4TScVlpVKC9jyQSETR1A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3357,13 +3322,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.1.0.tgz", + "integrity": "sha512-UZIhv8G+5b5skkcuhgvxYWHjk7FW7/JP5lPASMEUoliAPwIH/rxoUSQPia2cuOj9AmDZmwUl1usKm85t5VUMew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/typescript-estree": "7.1.0", + "@typescript-eslint/utils": "7.1.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -3375,7 +3340,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -3384,9 +3349,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", + "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -3397,13 +3362,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", + "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/visitor-keys": "7.1.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -3449,17 +3414,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-WUFba6PZC5OCGEmbweGpnNJytJiLG7ZvDBJJoUcX4qZYf1mGZ97mO2Mps6O2efxJcJdRNpqweCistDbZMwIVHw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/scope-manager": "7.1.0", + "@typescript-eslint/types": "7.1.0", + "@typescript-eslint/typescript-estree": "7.1.0", "semver": "^7.5.4" }, "engines": { @@ -3470,16 +3435,16 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", + "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/types": "7.1.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -3733,9 +3698,9 @@ } }, "node_modules/applicationinsights/node_modules/@opentelemetry/api": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.8.0.tgz", - "integrity": "sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==", "engines": { "node": ">=8.0.0" } @@ -4777,6 +4742,22 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, + "node_modules/concurrently/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/confusing-browser-globals": { "version": "1.0.11", "dev": true, @@ -5112,17 +5093,12 @@ } }, "node_modules/date-fns": { - "version": "2.30.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz", + "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/dateformat": { @@ -8440,9 +8416,8 @@ }, "node_modules/lint-staged/node_modules/chalk": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -8600,9 +8575,9 @@ } }, "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", "dev": true, "dependencies": { "path-key": "^4.0.0" @@ -9474,18 +9449,16 @@ }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -10277,9 +10250,6 @@ "version": "4.6.13", "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.13.tgz", "integrity": "sha512-MHgkS4B+sPjCXpf+HfdetBwbRz6vCtsceTmw1pHNYJAsYxrfpOP6dz+piJWGos8wqG7qb3vj/Rrc5qOlmInUuA==", - "workspaces": [ - "./packages/*" - ], "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.5.14", @@ -10290,8 +10260,10 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "license": "MIT" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", @@ -11190,11 +11162,12 @@ } }, "node_modules/ts-api-utils": { - "version": "1.0.3", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", "dev": true, - "license": "MIT", "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" diff --git a/package.json b/package.json index 9153d5ee..553e627c 100644 --- a/package.json +++ b/package.json @@ -103,7 +103,7 @@ "bunyan-format": "^0.2.1", "compression": "^1.7.4", "connect-flash": "^0.1.1", - "date-fns": "^2.30.0", + "date-fns": "^3.3.1", "express": "^4.18.2", "express-prom-bundle": "^7.0.0", "fishery": "^2.2.2", @@ -150,8 +150,8 @@ "@types/superagent": "^8.1.4", "@types/supertest": "^6.0.2", "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/parser": "^7.1.0", "audit-ci": "^6.6.1", "concurrently": "^8.2.2", "cookie-session": "^2.1.0", diff --git a/server/controllers/baseClientController.ts b/server/controllers/baseClientController.ts index 97cea587..47e53fe1 100644 --- a/server/controllers/baseClientController.ts +++ b/server/controllers/baseClientController.ts @@ -7,7 +7,7 @@ import { mapCreateBaseClientForm, mapEditBaseClientDeploymentForm, mapEditBaseCl import { BaseClient, BaseClientListFilter, ClientSecrets } from '../interfaces/baseClientApi/baseClient' import editBaseClientPresenter from '../views/presenters/editBaseClientPresenter' import mapFilterForm from '../mappers/forms/mapFilterForm' -import { GrantTypes } from '../data/enums/grantTypes' +import { GrantType } from '../data/enums/grantType' import { kebab } from '../utils/utils' import baseClientAudit, { BaseClientAuditFunction } from '../audit/baseClientAudit' import { BaseClientEvent } from '../audit/baseClientEvent' @@ -71,7 +71,7 @@ export default class BaseClientController { public displayNewBaseClient(): RequestHandler { return async (req, res) => { const { grant } = req.query - if (!(grant === kebab(GrantTypes.ClientCredentials) || grant === kebab(GrantTypes.AuthorizationCode))) { + if (!(grant === kebab(GrantType.ClientCredentials) || grant === kebab(GrantType.AuthorizationCode))) { res.render('pages/new-base-client-grant.njk', { enableAuthorizationCode }) return } diff --git a/server/data/enums/clientTypes.ts b/server/data/enums/clientTypes.ts index d49ead67..86fcc998 100644 --- a/server/data/enums/clientTypes.ts +++ b/server/data/enums/clientTypes.ts @@ -1,5 +1,22 @@ +import { snake } from '../../utils/utils' + // eslint-disable-next-line no-shadow,import/prefer-default-export export enum ClientType { Personal = 'personal', Service = 'service', + Blank = 'blank', +} + +export const toClientType = (type: string): ClientType => { + const value = snake(type) + if (value === 'personal') { + return ClientType.Personal + } + if (value === 'service') { + return ClientType.Service + } + if (value === '' || value === 'blank') { + return ClientType.Blank + } + throw new Error(`Invalid client type: ${value}`) } diff --git a/server/data/enums/grantType.ts b/server/data/enums/grantType.ts new file mode 100644 index 00000000..afaa531f --- /dev/null +++ b/server/data/enums/grantType.ts @@ -0,0 +1,18 @@ +import { snake } from '../../utils/utils' + +// eslint-disable-next-line no-shadow,import/prefer-default-export +export enum GrantType { + ClientCredentials = 'client_credentials', + AuthorizationCode = 'authorization_code', +} + +export const toGrantType = (type: string): GrantType => { + const value = snake(type) + if (value === 'client_credentials') { + return GrantType.ClientCredentials + } + if (value === 'authorization_code') { + return GrantType.AuthorizationCode + } + throw new Error(`Invalid grant type: ${value}`) +} diff --git a/server/data/enums/grantTypes.ts b/server/data/enums/grantTypes.ts deleted file mode 100644 index 68c49d21..00000000 --- a/server/data/enums/grantTypes.ts +++ /dev/null @@ -1,5 +0,0 @@ -// eslint-disable-next-line no-shadow,import/prefer-default-export -export enum GrantTypes { - ClientCredentials = 'client_credentials', - AuthorizationCode = 'authorization_code', -} diff --git a/server/data/localMockData/baseClientsResponseMock.ts b/server/data/localMockData/baseClientsResponseMock.ts index 1bde937d..8c57c985 100644 --- a/server/data/localMockData/baseClientsResponseMock.ts +++ b/server/data/localMockData/baseClientsResponseMock.ts @@ -4,7 +4,7 @@ import { ListBaseClientsResponse, ListClientInstancesResponse, } from '../../interfaces/baseClientApi/baseClientResponse' -import { GrantTypes } from '../enums/grantTypes' +import { GrantType } from '../enums/grantType' import { MfaType } from '../enums/mfaTypes' import { snake } from '../../utils/utils' @@ -36,7 +36,7 @@ export const listBaseClientsResponseMock: ListBaseClientsResponse = { ], } -export const getBaseClientResponseMock: (grantType: GrantTypes) => GetBaseClientResponse = (grantType: GrantTypes) => { +export const getBaseClientResponseMock: (grantType: GrantType) => GetBaseClientResponse = (grantType: GrantType) => { const response: GetBaseClientResponse = { grantType: 'CLIENT_CREDENTIALS', clientId: 'base_client_id_1', @@ -59,7 +59,7 @@ export const getBaseClientResponseMock: (grantType: GrantTypes) => GetBaseClient deploymentInfo: 'deployment deployment info', }, } - if (snake(grantType) === GrantTypes.ClientCredentials) { + if (snake(grantType) === GrantType.ClientCredentials) { return { ...response, grantType: 'CLIENT_CREDENTIALS', diff --git a/server/interfaces/baseClientApi/baseClient.ts b/server/interfaces/baseClientApi/baseClient.ts index 67798f59..77b493cd 100644 --- a/server/interfaces/baseClientApi/baseClient.ts +++ b/server/interfaces/baseClientApi/baseClient.ts @@ -1,4 +1,6 @@ import { MfaType } from '../../data/enums/mfaTypes' +import { GrantType } from '../../data/enums/grantType' +import { ClientType } from '../../data/enums/clientTypes' export interface BaseClient { baseClientId: string @@ -37,6 +39,7 @@ interface ServiceDetails { contact: string status: string } + export interface DeploymentDetails { clientType: string team: string @@ -64,10 +67,7 @@ export interface ClientSecrets { } export interface BaseClientListFilter { - roleSearch: string - clientCredentials: boolean - authorisationCode: boolean - serviceClientType: boolean - personalClientType: boolean - blankClientType: boolean + roleSearch?: string + grantType?: GrantType + clientType?: ClientType[] } diff --git a/server/mappers/baseClientApi/listBaseClients.test.ts b/server/mappers/baseClientApi/listBaseClients.test.ts index a9197c5e..b61861eb 100644 --- a/server/mappers/baseClientApi/listBaseClients.test.ts +++ b/server/mappers/baseClientApi/listBaseClients.test.ts @@ -1,6 +1,9 @@ import { createMock } from '@golevelup/ts-jest' import { Request } from 'express' import { mapFilterToUrlQuery, mapListBaseClientRequest } from './listBaseClients' +import { filterFactory } from '../../testutils/factories' +import { GrantType } from '../../data/enums/grantType' +import { ClientType } from '../../data/enums/clientTypes' describe('Mappers for filtering base client list', () => { describe('mapListBaseClientRequest', () => { @@ -14,14 +17,7 @@ describe('Mappers for filtering base client list', () => { const filter = mapListBaseClientRequest(request) // THEN the filter defaults to include all values - const expected = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const expected = {} expect(filter).toEqual(expected) }) @@ -37,14 +33,8 @@ describe('Mappers for filtering base client list', () => { const filter = mapListBaseClientRequest(request) // THEN the filter defaults to include the roleSearch value - const expected = { - roleSearch: 'test role', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const expected = filterFactory.build({ roleSearch: 'test role' }) + expect(filter).toEqual(expected) }) @@ -60,37 +50,7 @@ describe('Mappers for filtering base client list', () => { const filter = mapListBaseClientRequest(request) // THEN the filter includes specified grantType only - const expected = { - roleSearch: '', - clientCredentials: true, - authorisationCode: false, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } - expect(filter).toEqual(expected) - }) - - it('maps multiple grantType parameters', async () => { - // GIVEN a request with a grantType parameter - const request = createMock({ - query: { - grantType: ['client-credentials', 'authorization-code'], - }, - }) - - // WHEN we map the request to a filter - const filter = mapListBaseClientRequest(request) - - // THEN the filter includes all specified grantTypes - const expected = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const expected = { grantType: GrantType.ClientCredentials } expect(filter).toEqual(expected) }) @@ -106,14 +66,9 @@ describe('Mappers for filtering base client list', () => { const filter = mapListBaseClientRequest(request) // THEN the filter includes specified grantType only - const expected = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: false, - personalClientType: true, - blankClientType: false, - } + const expected = filterFactory.build({ + clientType: [ClientType.Personal], + }) expect(filter).toEqual(expected) }) @@ -129,14 +84,9 @@ describe('Mappers for filtering base client list', () => { const filter = mapListBaseClientRequest(request) // THEN the filter includes all specified clientTypes - const expected = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: false, - } + const expected = filterFactory.build({ + clientType: [ClientType.Personal, ClientType.Service], + }) expect(filter).toEqual(expected) }) }) @@ -144,14 +94,7 @@ describe('Mappers for filtering base client list', () => { describe('mapFilterToUrlQuery', () => { it('maps to an empty string when the filter is empty', async () => { // GIVEN an empty filter - const filter = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const filter = {} // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) @@ -162,14 +105,7 @@ describe('Mappers for filtering base client list', () => { it('maps roleSearch to role', async () => { // GIVEN a filter with a roleSearch - const filter = { - roleSearch: 'test', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const filter = filterFactory.build({ roleSearch: 'test' }) // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) @@ -180,14 +116,7 @@ describe('Mappers for filtering base client list', () => { it('maps roleSearch to role with encoded characters', async () => { // GIVEN a filter with a roleSearch - const filter = { - roleSearch: 'test role', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const filter = filterFactory.build({ roleSearch: 'test role' }) // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) @@ -198,14 +127,9 @@ describe('Mappers for filtering base client list', () => { it('maps grantType parameters', async () => { // GIVEN a filter with a grantType parameter - const filter = { - roleSearch: '', - clientCredentials: true, - authorisationCode: false, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - } + const filter = filterFactory.build({ + grantType: GrantType.ClientCredentials, + }) // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) @@ -216,14 +140,9 @@ describe('Mappers for filtering base client list', () => { it('maps clientType parameters', async () => { // GIVEN a filter with a client parameter - const filter = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: false, - personalClientType: true, - blankClientType: false, - } + const filter = filterFactory.build({ + clientType: [ClientType.Personal], + }) // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) @@ -234,38 +153,30 @@ describe('Mappers for filtering base client list', () => { it('maps multiple clientType parameters', async () => { // GIVEN a filter with a client parameter - const filter = { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: false, - } + const filter = filterFactory.build({ + clientType: [ClientType.Service, ClientType.Personal], + }) // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) // THEN the query includes all specified clientTypes - expect(query).toEqual('clientType=personal&clientType=service') + expect(query).toEqual('clientType=service&clientType=personal') }) it('maps all parameters', async () => { // GIVEN a filter with a client parameter - const filter = { + const filter = filterFactory.build({ roleSearch: 'test role', - clientCredentials: true, - authorisationCode: false, - serviceClientType: true, - personalClientType: true, - blankClientType: false, - } + grantType: GrantType.ClientCredentials, + clientType: [ClientType.Service, ClientType.Personal], + }) // WHEN we map the filter to a query string const query = mapFilterToUrlQuery(filter) // THEN the query includes all specified clientTypes - expect(query).toEqual('role=test%20role&grantType=client-credentials&clientType=personal&clientType=service') + expect(query).toEqual('role=test%20role&grantType=client-credentials&clientType=service&clientType=personal') }) }) }) diff --git a/server/mappers/baseClientApi/listBaseClients.ts b/server/mappers/baseClientApi/listBaseClients.ts index b66dbc5a..d1d6c603 100644 --- a/server/mappers/baseClientApi/listBaseClients.ts +++ b/server/mappers/baseClientApi/listBaseClients.ts @@ -1,27 +1,29 @@ import { Request } from 'express' import { ListBaseClientsResponse } from '../../interfaces/baseClientApi/baseClientResponse' import { BaseClient, BaseClientListFilter } from '../../interfaces/baseClientApi/baseClient' +import { toClientType } from '../../data/enums/clientTypes' +import { toGrantType } from '../../data/enums/grantType' import { kebab, multiSeparatorSplit, snake, toBaseClientId } from '../../utils/utils' -import { GrantTypes } from '../../data/enums/grantTypes' -import { ClientType } from '../../data/enums/clientTypes' export const mapListBaseClientRequest = (request: Request): BaseClientListFilter => { const asJson = JSON.stringify(request.query) const data = asJson ? JSON.parse(asJson) : {} - const grantTypes = mapQueryList(data.grantType, [GrantTypes.ClientCredentials, GrantTypes.AuthorizationCode]).map( - snake, - ) - const clientTypes = mapQueryList(data.clientType, [ClientType.Personal, ClientType.Service, 'blank']).map(snake) - - return { - roleSearch: data.role ? data.role.trim() : '', - clientCredentials: grantTypes.includes(GrantTypes.ClientCredentials), - authorisationCode: grantTypes.includes(GrantTypes.AuthorizationCode), - serviceClientType: clientTypes.includes(ClientType.Service), - personalClientType: clientTypes.includes(ClientType.Personal), - blankClientType: clientTypes.includes('blank'), + const filter: BaseClientListFilter = {} + if (data.role) { + filter.roleSearch = data.role.trim() + } + if (data.grantType) { + filter.grantType = toGrantType(data.grantType) + } + if (data.clientType) { + if (Array.isArray(data.clientType)) { + filter.clientType = data.clientType.map(toClientType) + } else { + filter.clientType = [toClientType(data.clientType)] + } } + return filter } export const mapFilterToUrlQuery = (filter: BaseClientListFilter): string => { @@ -29,36 +31,20 @@ export const mapFilterToUrlQuery = (filter: BaseClientListFilter): string => { if (filter.roleSearch) { urlQuery += `role=${encodeURIComponent(filter.roleSearch)}&` } - if (!(filter.clientCredentials && filter.authorisationCode)) { - if (filter.clientCredentials) { - urlQuery += `grantType=${kebab(GrantTypes.ClientCredentials)}&` - } - if (filter.authorisationCode) { - urlQuery += `grantType=${kebab(GrantTypes.AuthorizationCode)}&` - } + if (filter.grantType) { + urlQuery += `grantType=${kebab(filter.grantType)}&` } - if (!(filter.personalClientType && filter.serviceClientType && filter.blankClientType)) { - if (filter.personalClientType) { - urlQuery += `clientType=${kebab(ClientType.Personal)}&` - } - if (filter.serviceClientType) { - urlQuery += `clientType=${kebab(ClientType.Service)}&` - } - if (filter.blankClientType) { - urlQuery += `clientType=blank&` - } + if (filter.clientType) { + filter.clientType.forEach(clientType => { + urlQuery += `clientType=${kebab(clientType)}&` + }) } + if (urlQuery.endsWith('&')) { urlQuery = urlQuery.slice(0, -1) } - return urlQuery -} -const mapQueryList = (value: string | string[], defaults: string[]): string[] => { - if (Array.isArray(value)) { - return value - } - return value ? [value] : defaults + return urlQuery } export default (response: ListBaseClientsResponse): BaseClient[] => { diff --git a/server/mappers/forms/mapEditBaseClientDetailsForm.test.ts b/server/mappers/forms/mapEditBaseClientDetailsForm.test.ts index 9d4f514e..7c168026 100644 --- a/server/mappers/forms/mapEditBaseClientDetailsForm.test.ts +++ b/server/mappers/forms/mapEditBaseClientDetailsForm.test.ts @@ -3,7 +3,7 @@ import { createMock } from '@golevelup/ts-jest' import { baseClientFactory } from '../../testutils/factories' import { dateISOString, offsetNow } from '../../utils/utils' import { mapEditBaseClientDetailsForm } from '../index' -import { GrantTypes } from '../../data/enums/grantTypes' +import { GrantType } from '../../data/enums/grantType' import { MfaType } from '../../data/enums/mfaTypes' const formRequest = (form: Record) => { @@ -99,7 +99,7 @@ describe('mapEditBaseClientDetailsForm', () => { baseClientId: detailedBaseClient.baseClientId, approvedScopes: 'requestscope1,requestscope2', audit: 'request audit', - grantType: GrantTypes.ClientCredentials, + grantType: GrantType.ClientCredentials, authorities: 'requestauthority1\r\nrequestauthority2', databaseUsername: 'request databaseUsername', allowedIPs: 'requestallowedIP1\r\nrequestallowedIP2', @@ -114,7 +114,7 @@ describe('mapEditBaseClientDetailsForm', () => { expect(update.baseClientId).toEqual(detailedBaseClient.baseClientId) expect(update.scopes).toEqual(['requestscope1', 'requestscope2']) expect(update.audit).toEqual('request audit') - expect(update.grantType).toEqual(GrantTypes.ClientCredentials) + expect(update.grantType).toEqual(GrantType.ClientCredentials) expect(update.clientCredentials.authorities).toEqual(['requestauthority1', 'requestauthority2']) expect(update.clientCredentials.databaseUserName).toEqual('request databaseUsername') expect(update.config.allowedIPs).toEqual(['requestallowedIP1', 'requestallowedIP2']) @@ -141,7 +141,7 @@ describe('mapEditBaseClientDetailsForm', () => { baseClientId: detailedBaseClient.baseClientId, approvedScopes: 'requestscope1,requestscope2', audit: 'request audit', - grantType: GrantTypes.AuthorizationCode, + grantType: GrantType.AuthorizationCode, redirectUris: 'requestredirectUri1\r\nrequestredirectUri2', jwtFields: 'request jwtFields', azureAdLoginFlow: 'redirect', @@ -186,7 +186,7 @@ describe('mapEditBaseClientDetailsForm', () => { baseClientId: detailedBaseClient.baseClientId, approvedScopes: 'requestscope1,requestscope2', audit: 'request audit', - grantType: GrantTypes.AuthorizationCode, + grantType: GrantType.AuthorizationCode, redirectUris: 'requestredirectUri1\r\nrequestredirectUri2', jwtFields: 'request jwtFields', azureAdLoginFlow: 'redirect', diff --git a/server/mappers/forms/mapFilterForm.test.ts b/server/mappers/forms/mapFilterForm.test.ts index 9698cf88..e436f997 100644 --- a/server/mappers/forms/mapFilterForm.test.ts +++ b/server/mappers/forms/mapFilterForm.test.ts @@ -2,6 +2,8 @@ import type { Request } from 'express' import { createMock } from '@golevelup/ts-jest' import mapFilterForm from './mapFilterForm' import { BaseClientListFilter } from '../../interfaces/baseClientApi/baseClient' +import { GrantType } from '../../data/enums/grantType' +import { ClientType } from '../../data/enums/clientTypes' const formRequest = (form: Record) => { return createMock({ body: form }) @@ -20,15 +22,7 @@ describe('mapFilterForm', () => { }) }) - describe('grant checkbox', () => { - it('processes undefined', () => { - const request = formRequest({}) - - const result: BaseClientListFilter = mapFilterForm(request) - - expect(result.authorisationCode).toBeFalsy() - expect(result.clientCredentials).toBeFalsy() - }) + describe('grant radios', () => { it('processes single string', () => { const request = formRequest({ grantType: 'client_credentials', @@ -36,52 +30,50 @@ describe('mapFilterForm', () => { const result: BaseClientListFilter = mapFilterForm(request) - expect(result.authorisationCode).toBeFalsy() - expect(result.clientCredentials).toBeTruthy() + expect(result.grantType).toEqual(GrantType.ClientCredentials) }) - it('processes array', () => { + }) + + describe('client type radios', () => { + it('parses filter on but no selection as select all', () => { const request = formRequest({ - grantType: ['client_credentials', 'authorization_code'], + filterClientType: 'clientFilter', }) const result: BaseClientListFilter = mapFilterForm(request) - expect(result.authorisationCode).toBeTruthy() - expect(result.clientCredentials).toBeTruthy() + expect(result.clientType).toBeUndefined() }) - }) - - describe('client type checkbox', () => { - it('processes undefined', () => { - const request = formRequest({}) + it('parses filter on with all selected as select all', () => { + const request = formRequest({ + filterClientType: 'clientFilter', + clientType: ['personal', 'service', 'blank'], + }) const result: BaseClientListFilter = mapFilterForm(request) - expect(result.personalClientType).toBeFalsy() - expect(result.serviceClientType).toBeFalsy() - expect(result.blankClientType).toBeFalsy() + expect(result.clientType).toBeUndefined() }) + it('processes single string', () => { const request = formRequest({ + filterClientType: 'clientFilter', clientType: 'personal', }) const result: BaseClientListFilter = mapFilterForm(request) - expect(result.personalClientType).toBeTruthy() - expect(result.serviceClientType).toBeFalsy() - expect(result.blankClientType).toBeFalsy() + expect(result.clientType).toEqual([ClientType.Personal]) }) it('processes array', () => { const request = formRequest({ + filterClientType: 'clientFilter', clientType: ['personal', 'service'], }) const result: BaseClientListFilter = mapFilterForm(request) - expect(result.personalClientType).toBeTruthy() - expect(result.serviceClientType).toBeTruthy() - expect(result.blankClientType).toBeFalsy() + expect(result.clientType).toEqual([ClientType.Personal, ClientType.Service]) }) }) }) diff --git a/server/mappers/forms/mapFilterForm.ts b/server/mappers/forms/mapFilterForm.ts index 1e643d8c..ea09d3c3 100644 --- a/server/mappers/forms/mapFilterForm.ts +++ b/server/mappers/forms/mapFilterForm.ts @@ -1,24 +1,32 @@ import { Request } from 'express' import { BaseClientListFilter } from '../../interfaces/baseClientApi/baseClient' -import { GrantTypes } from '../../data/enums/grantTypes' -import { ClientType } from '../../data/enums/clientTypes' +import { toGrantType } from '../../data/enums/grantType' +import { toClientType } from '../../data/enums/clientTypes' import { snake } from '../../utils/utils' export default (request: Request): BaseClientListFilter => { // valid days is calculated from expiry date const data = request.body - const grantTypes = mapCheckboxes(data.grantType).map(snake) - const clientTypes = mapCheckboxes(data.clientType).map(snake) + const filter: BaseClientListFilter = {} + if (data.role) { + filter.roleSearch = data.role.trim() + } - return { - roleSearch: data.role ? data.role.trim() : '', - clientCredentials: grantTypes.includes(GrantTypes.ClientCredentials), - authorisationCode: grantTypes.includes(GrantTypes.AuthorizationCode), - serviceClientType: clientTypes.includes(ClientType.Service), - personalClientType: clientTypes.includes(ClientType.Personal), - blankClientType: clientTypes.includes('blank'), + if (data.grantType && data.grantType !== 'all') { + filter.grantType = toGrantType(snake(data.grantType)) } + + if (data.filterClientType === 'clientFilter') { + const clientTypes = mapCheckboxes(data.clientType).map(snake).map(toClientType) + + // if no or all client types are selected, we don't want to filter by client type + if (clientTypes.length > 0 && clientTypes.length < 3) { + filter.clientType = clientTypes + } + } + + return filter } const mapCheckboxes = (value: string | string[]): string[] => { diff --git a/server/routes/baseClientRouter.ts b/server/routes/baseClientRouter.ts index 228e7173..dd740f14 100644 --- a/server/routes/baseClientRouter.ts +++ b/server/routes/baseClientRouter.ts @@ -18,9 +18,15 @@ export default function baseClientRouter(services: Services): Router { handlers.map(handler => asyncMiddleware(handler)), ) + const redirect = (path: string, redirectPath: string) => + router.get(path, (req, res) => { + res.redirect(redirectPath) + }) + const baseClientController = new BaseClientController(services.baseClientService) get('/', baseClientController.displayBaseClients()) + redirect('/base-clients', '/') get('/base-clients/new', baseClientController.displayNewBaseClient()) get('/base-clients/:baseClientId/deployment', baseClientController.displayEditBaseClientDeployment()) get('/base-clients/:baseClientId/edit', baseClientController.displayEditBaseClient()) diff --git a/server/testutils/factories/filter.ts b/server/testutils/factories/filter.ts index 15af5294..e45930ca 100644 --- a/server/testutils/factories/filter.ts +++ b/server/testutils/factories/filter.ts @@ -1,11 +1,4 @@ import { Factory } from 'fishery' import { BaseClientListFilter } from '../../interfaces/baseClientApi/baseClient' -export default Factory.define(() => ({ - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, -})) +export default Factory.define(() => ({})) diff --git a/server/views/pages/base-clients.njk b/server/views/pages/base-clients.njk index 822bfb87..7d9556c5 100644 --- a/server/views/pages/base-clients.njk +++ b/server/views/pages/base-clients.njk @@ -22,6 +22,7 @@ {%- from "govuk/components/table/macro.njk" import govukTable -%} {%- from "moj/components/filter/macro.njk" import mojFilter -%} {%- from "moj/components/button-menu/macro.njk" import mojButtonMenu -%} +{% from "govuk/components/radios/macro.njk" import govukRadios %} {% set filterOptionsHtml %} @@ -39,76 +40,118 @@ value: presenter.filter.roleSearch }) }} - {{ govukCheckboxes({ - idPrefix: 'grant-type', - name: 'grantType', - classes: "govuk-checkboxes--small", + {{ govukRadios({ + name: "grantType", + classes: "govuk-radios--small", fieldset: { legend: { - text: 'Grant type', - classes: 'govuk-fieldset__legend--m' + text: "Grant type", + isPageHeading: false, + classes: "govuk-fieldset__legend--m" } }, items: [ { - value: 'client-credentials', - text: 'Client credentials', - checked: presenter.filter.clientCredentials + value: "all", + text: "All", + checked: 'grantType' not in presenter.filter }, { - value: 'authorization-code', - text: 'Authorization code', - checked: presenter.filter.authorisationCode + value: "client-credentials", + text: "Client credentials", + checked: presenter.filter.grantType === "client_credentials" + }, + { + value: "authorization-code", + text: "Authorization code", + checked: presenter.filter.grantType === "authorization_code" } ] }) }} - {{ govukCheckboxes({ - idPrefix: 'client-type', - name: 'clientType', - classes: "govuk-checkboxes--small", + {{ govukRadios({ + name: "filterClientType", + classes: "govuk-radios--small", fieldset: { legend: { - text: 'Client type', - classes: 'govuk-fieldset__legend--m' + text: "Client type", + isPageHeading: false, + classes: "govuk-fieldset__legend--m" } }, items: [ { - value: 'service', - text: 'Service', - checked: presenter.filter.serviceClientType - }, - { - value: 'personal', - text: 'Personal', - checked: presenter.filter.personalClientType + value: "all", + text: "All", + checked: 'clientType' not in presenter.filter }, { - value: 'blank', - text: '[blank]', - checked: presenter.filter.blankClientType + value: "clientFilter", + text: "Filter", + checked: 'clientType' in presenter.filter, + conditional: { + html: govukCheckboxes({ + idPrefix: 'client-type', + name: 'clientType', + classes: "govuk-checkboxes--small", + items: [ + { + value: 'service', + text: 'Service', + checked: 'clientType' in presenter.filter and 'service' in presenter.filter.clientType + }, + { + value: 'personal', + text: 'Personal', + checked: 'clientType' in presenter.filter and 'personal' in presenter.filter.clientType + }, + { + value: 'blank', + text: '[blank]', + checked: 'clientType' in presenter.filter and 'blank' in presenter.filter.clientType + } + ] + }) + } } ] }) }} {% endset %} - - {% block content %} -
+
+ {% if presenter.showSelectedFilters %} + {{ mojFilter({ + heading: { + text: 'Filter' + }, + selectedFilters: { + heading: { + text: 'Selected filters' + }, + + clearLink: { + text: 'Clear filters', + href: '/' + }, + + categories: presenter.selectedFilterCategories + }, + optionsHtml: filterOptionsHtml + }) }} + {% else %} {{ mojFilter({ heading: { text: 'Filter' }, - optionsHtml: filterOptionsHtml }) }} + {% endif %}
@@ -136,6 +179,7 @@ } }] }) }} +
diff --git a/server/views/partials/list_filter.njk b/server/views/partials/list_filter.njk deleted file mode 100644 index 3e1a8f23..00000000 --- a/server/views/partials/list_filter.njk +++ /dev/null @@ -1,83 +0,0 @@ -{% call govukFieldset({ - legend: { - text: "Filter clients", - classes: "govuk-fieldset__legend--l", - isPageHeading: false - } -}) %} - -
-
- {{ govukInput({ - label: { - text: "Role" - }, - id: "filter-role", - name: "filterRole" - }) }} -
- -
- {{ govukSelect({ - id: "filter-grant-type", - name: "filterGrantType", - label: { - text: "Grant type" - }, - items: [ - { - value: "all", - text: "All", - selected: "true" - }, - { - value: "client-credentials", - text: "Client creds" - }, - { - value: "authorization-code", - text: "Auth code" - } - ] - }) }} - -
- -
- {{ govukSelect({ - id: "filter-client-type", - name: "filterClientType", - label: { - text: "Client type" - }, - items: [ - { - value: "all", - text: "All", - selected: "true" - }, - { - value: "service", - text: "Service" - }, - { - value: "personal", - text: "Personal" - } - ] - }) }} -
- - -
- {{ govukLabel({ - text: "." - }) }} - {{ govukButton({ - text: "Search" - }) }} -
- -
- -{% endcall %} \ No newline at end of file diff --git a/server/views/presenters/listBaseClientsPresenter.test.ts b/server/views/presenters/listBaseClientsPresenter.test.ts index 23793d85..43b7de14 100644 --- a/server/views/presenters/listBaseClientsPresenter.test.ts +++ b/server/views/presenters/listBaseClientsPresenter.test.ts @@ -1,6 +1,8 @@ import { BaseClient } from '../../interfaces/baseClientApi/baseClient' import { baseClientFactory, filterFactory } from '../../testutils/factories' import listBaseClientsPresenter, { filterBaseClient } from './listBaseClientsPresenter' +import { GrantType } from '../../data/enums/grantType' +import { ClientType } from '../../data/enums/clientTypes' let baseClients: BaseClient[] @@ -105,5 +107,232 @@ describe('listBaseClientsPresenter', () => { expect(passesFilter).toBeFalsy() }) }) + + describe('by grant type', () => { + const clientCredentialsBaseClient = baseClientFactory.build({ grantType: GrantType.ClientCredentials }) + const authCodeBaseClient = baseClientFactory.build({ grantType: GrantType.AuthorizationCode }) + + it('defaults to matching all types', () => { + const filter = filterFactory.build({}) + + expect(filterBaseClient(clientCredentialsBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(authCodeBaseClient, filter)).toBeTruthy() + }) + + it('can filter to client credentials only', () => { + const filter = filterFactory.build({ + grantType: GrantType.ClientCredentials, + }) + + expect(filterBaseClient(clientCredentialsBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(authCodeBaseClient, filter)).toBeFalsy() + }) + + it('can filter to auth code only', () => { + const filter = filterFactory.build({ + grantType: GrantType.AuthorizationCode, + }) + + expect(filterBaseClient(clientCredentialsBaseClient, filter)).toBeFalsy() + expect(filterBaseClient(authCodeBaseClient, filter)).toBeTruthy() + }) + }) + + describe('by client type', () => { + const serviceBaseClient = baseClientFactory.build({ deployment: { clientType: ClientType.Service } }) + const personalBaseClient = baseClientFactory.build({ deployment: { clientType: ClientType.Personal } }) + const blankBaseClient = baseClientFactory.build({ deployment: { clientType: '' } }) + + it('defaults to matching all types', () => { + const filter = filterFactory.build() + + expect(filterBaseClient(serviceBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(personalBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(blankBaseClient, filter)).toBeTruthy() + }) + + it('can filter to only service clients', () => { + const filter = filterFactory.build({ clientType: [ClientType.Service] }) + + expect(filterBaseClient(serviceBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(personalBaseClient, filter)).toBeFalsy() + expect(filterBaseClient(blankBaseClient, filter)).toBeFalsy() + }) + + it('can filter to only personal clients', () => { + const filter = filterFactory.build({ clientType: [ClientType.Personal] }) + + expect(filterBaseClient(serviceBaseClient, filter)).toBeFalsy() + expect(filterBaseClient(personalBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(blankBaseClient, filter)).toBeFalsy() + }) + + it('can filter to only blank clients', () => { + const filter = filterFactory.build({ clientType: [ClientType.Blank] }) + + expect(filterBaseClient(serviceBaseClient, filter)).toBeFalsy() + expect(filterBaseClient(personalBaseClient, filter)).toBeFalsy() + expect(filterBaseClient(blankBaseClient, filter)).toBeTruthy() + }) + + it('can filter to multiple client types', () => { + const filter = filterFactory.build({ clientType: [ClientType.Personal, ClientType.Blank] }) + + expect(filterBaseClient(serviceBaseClient, filter)).toBeFalsy() + expect(filterBaseClient(personalBaseClient, filter)).toBeTruthy() + expect(filterBaseClient(blankBaseClient, filter)).toBeTruthy() + }) + }) + }) + + describe('filter presenter values', () => { + describe('showSelectedFilters', () => { + it('returns false if no filter is given', () => { + const presenter = listBaseClientsPresenter(baseClients) + + expect(presenter.showSelectedFilters).toBeFalsy() + }) + + it('returns false if no filters are selected', () => { + const filter = filterFactory.build() + const presenter = listBaseClientsPresenter(baseClients, filter) + + expect(presenter.showSelectedFilters).toBeFalsy() + }) + + it('returns true if any filters are selected', () => { + const filter = filterFactory.build({ roleSearch: 'ONE' }) + const presenter = listBaseClientsPresenter(baseClients, filter) + + expect(presenter.showSelectedFilters).toBeTruthy() + }) + }) + + describe('selectedFilterCategories', () => { + it('is empty by default', () => { + const filter = filterFactory.build() + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(0) + }) + + it('has a role category if a role search string is applied', () => { + const filter = filterFactory.build({ roleSearch: 'ONE' }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(1) + expect(selectedFilterCategories[0].heading.text).toEqual('Role') + expect(selectedFilterCategories[0].items[0].text).toEqual('ONE') + }) + + it('has visible grant type category if non-all grantType category selected', () => { + const filter = filterFactory.build({ grantType: GrantType.AuthorizationCode }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(1) + expect(selectedFilterCategories[0].heading.text).toEqual('Grant type') + expect(selectedFilterCategories[0].items[0].text).toEqual('Authorisation code') + }) + + it('has visible client type category if single client type is selected', () => { + const filter = filterFactory.build({ clientType: [ClientType.Personal] }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(1) + expect(selectedFilterCategories[0].heading.text).toEqual('Client type') + expect(selectedFilterCategories[0].items[0].text).toEqual('Personal') + }) + + it('has two client type categories if single client type is selected', () => { + const filter = filterFactory.build({ clientType: [ClientType.Personal, ClientType.Service] }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(1) + expect(selectedFilterCategories[0].heading.text).toEqual('Client type') + expect(selectedFilterCategories[0].items[0].text).toEqual('Personal') + expect(selectedFilterCategories[0].items[1].text).toEqual('Service') + }) + + it('has no client type category if no client type is selected', () => { + const filter = filterFactory.build({ + clientType: [], + }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(0) + }) + + it('has no client type category if all client types are selected', () => { + const filter = filterFactory.build({ + clientType: [ClientType.Personal, ClientType.Service, ClientType.Blank], + }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(0) + }) + + it('can have complex filters with multiple categories', () => { + const filter = filterFactory.build({ + roleSearch: 'ONE', + grantType: GrantType.AuthorizationCode, + clientType: [ClientType.Personal, ClientType.Blank], + }) + const { selectedFilterCategories } = listBaseClientsPresenter(baseClients, filter) + + expect(selectedFilterCategories).toHaveLength(3) + expect(selectedFilterCategories[0].heading.text).toEqual('Role') + expect(selectedFilterCategories[0].items[0].text).toEqual('ONE') + expect(selectedFilterCategories[1].heading.text).toEqual('Grant type') + expect(selectedFilterCategories[1].items[0].text).toEqual('Authorisation code') + expect(selectedFilterCategories[2].heading.text).toEqual('Client type') + expect(selectedFilterCategories[2].items[0].text).toEqual('Personal') + expect(selectedFilterCategories[2].items[1].text).toEqual('Blank') + }) + }) + + describe('removeFilterLink', () => { + // Given some base clients + const baseClientA = baseClientFactory.build({ + baseClientId: 'baseClientIdA', + count: 1, + clientCredentials: { authorities: ['ONE', 'TWO'] }, + }) + const baseClientB = baseClientFactory.build({ + baseClientId: 'baseClientIdB', + count: 2, + clientCredentials: { authorities: ['ALPHA'] }, + }) + baseClients = [baseClientA, baseClientB] + + // current URL is /?role=ONE&grantType=authorization-code&clientType=personal&clientType=blank + const compoundFilter = filterFactory.build({ + roleSearch: 'ONE', + grantType: GrantType.AuthorizationCode, + clientType: [ClientType.Personal, ClientType.Blank], + }) + + const presenter = listBaseClientsPresenter(baseClients, compoundFilter) + + it('removes the role search', () => { + const roleItem = presenter.selectedFilterCategories[0].items[0] + const expected = '/?grantType=authorization-code&clientType=personal&clientType=blank' + const actual = roleItem.href + expect(actual).toEqual(expected) + }) + + it('removes the grant category completely if all grant type items removed', () => { + const grantTypeItem = presenter.selectedFilterCategories[1].items[0] + const expected = '/?role=ONE&clientType=personal&clientType=blank' + const actual = grantTypeItem.href + expect(actual).toEqual(expected) + }) + + it('retains the blank clientType item if personal clientType removed', () => { + const clientTypeItem = presenter.selectedFilterCategories[2].items[0] + const expected = '/?role=ONE&grantType=authorization-code&clientType=blank' + const actual = clientTypeItem.href + expect(actual).toEqual(expected) + }) + }) }) }) diff --git a/server/views/presenters/listBaseClientsPresenter.ts b/server/views/presenters/listBaseClientsPresenter.ts index f9a0dba7..acbe4453 100644 --- a/server/views/presenters/listBaseClientsPresenter.ts +++ b/server/views/presenters/listBaseClientsPresenter.ts @@ -1,7 +1,8 @@ import { BaseClient, BaseClientListFilter } from '../../interfaces/baseClientApi/baseClient' -import { convertToTitleCase, dateFormatFromString, snake } from '../../utils/utils' -import { GrantTypes } from '../../data/enums/grantTypes' -import { ClientType } from '../../data/enums/clientTypes' +import { convertToTitleCase, dateFormatFromString } from '../../utils/utils' +import { GrantType, toGrantType } from '../../data/enums/grantType' +import { ClientType, toClientType } from '../../data/enums/clientTypes' +import { mapFilterToUrlQuery } from '../../mappers/baseClientApi/listBaseClients' const indexTableHead = () => { return [ @@ -102,52 +103,157 @@ const filterItems = (data: BaseClient[], filter?: BaseClientListFilter) => { return filter ? data.filter(item => filterBaseClient(item, filter)) : data } -export const filterBaseClient = (baseClient: BaseClient, filter: BaseClientListFilter) => { - if (filter.roleSearch) { +const filterByRoleSearch = (baseClient: BaseClient, roleSearch: string): boolean => { + if (roleSearch) { const roles = baseClient.clientCredentials.authorities.join(' ').toLowerCase() - const roleSearch = filter.roleSearch.toLowerCase().trim() - if (roles.includes(roleSearch) === false) { - return false - } + const roleSearchLower = roleSearch.toLowerCase().trim() + return roles.includes(roleSearchLower) } + return true +} - const grantType = baseClient.grantType ? snake(baseClient.grantType) : '' +const filterByGrantType = (baseClient: BaseClient, grantTypeFilter: GrantType | undefined): boolean => { + const grantType = baseClient.grantType ? toGrantType(baseClient.grantType) : null + return grantTypeFilter ? grantType === grantTypeFilter : true +} + +const filterByClientType = (baseClient: BaseClient, clientTypeFilter: ClientType[] | undefined): boolean => { const clientType = - baseClient.deployment && baseClient.deployment.clientType ? snake(baseClient.deployment.clientType) : '' + baseClient.deployment && baseClient.deployment.clientType + ? toClientType(baseClient.deployment.clientType) + : ClientType.Blank + return clientTypeFilter ? clientTypeFilter.includes(clientType) : true +} + +export const filterBaseClient = (baseClient: BaseClient, filter: BaseClientListFilter): boolean => { + return ( + filterByRoleSearch(baseClient, filter.roleSearch) && + filterByGrantType(baseClient, filter.grantType) && + filterByClientType(baseClient, filter.clientType) + ) +} + +type SelectedFilterCategory = { + heading: { text: string } + items: { href: string; text: string }[] +} + +const createRoleSearchCategory = (filter: BaseClientListFilter): SelectedFilterCategory[] => { + return filter.roleSearch + ? [ + { + heading: { text: 'Role' }, + items: [{ href: removeFilterLink(filter, 'roleSearch'), text: filter.roleSearch }], + }, + ] + : [] +} - if (grantType === GrantTypes.ClientCredentials && !filter.clientCredentials) { - return false +const createGrantTypeCategory = (filter: BaseClientListFilter): SelectedFilterCategory[] => { + if (!filter.grantType) return [] + + const grantTypesCategory: SelectedFilterCategory = { + heading: { text: 'Grant type' }, + items: [], } - if (grantType === GrantTypes.AuthorizationCode && !filter.authorisationCode) { - return false + if (filter.grantType === GrantType.ClientCredentials) { + grantTypesCategory.items.push({ href: removeFilterLink(filter, 'clientCredentials'), text: 'Client credentials' }) } - if (clientType === ClientType.Personal && !filter.personalClientType) { - return false + if (filter.grantType === GrantType.AuthorizationCode) { + grantTypesCategory.items.push({ href: removeFilterLink(filter, 'authorisationCode'), text: 'Authorisation code' }) } - if (clientType === ClientType.Service && !filter.serviceClientType) { - return false + + return [grantTypesCategory] +} + +const createClientTypeCategory = (filter: BaseClientListFilter): SelectedFilterCategory[] => { + if (!filter.clientType) return [] + + const clientTypeCategory: SelectedFilterCategory = { + heading: { text: 'Client type' }, + items: [], } - if (clientType === '' && !filter.blankClientType) { - return false + if (filter.clientType.includes(ClientType.Personal)) { + clientTypeCategory.items.push({ href: removeFilterLink(filter, 'personalClientType'), text: 'Personal' }) } - return true + if (filter.clientType.includes(ClientType.Service)) { + clientTypeCategory.items.push({ href: removeFilterLink(filter, 'serviceClientType'), text: 'Service' }) + } + + if (filter.clientType.includes(ClientType.Blank)) { + clientTypeCategory.items.push({ href: removeFilterLink(filter, 'blankClientType'), text: 'Blank' }) + } + + if (clientTypeCategory.items.length > 0 && clientTypeCategory.items.length < 3) { + return [clientTypeCategory] + } + return [] +} + +const getSelectedFilterCategories = (filter?: BaseClientListFilter): SelectedFilterCategory[] => { + if (!filter) return [] + return [...createRoleSearchCategory(filter), ...createGrantTypeCategory(filter), ...createClientTypeCategory(filter)] +} + +const removeRoleSearchFilter = (filter: BaseClientListFilter, filterToRemove: string): BaseClientListFilter => { + if (filter.roleSearch && filterToRemove !== 'roleSearch') { + return { roleSearch: filter.roleSearch } + } + return {} +} + +const removeGrantTypeFilter = (filter: BaseClientListFilter, filterToRemove: string): BaseClientListFilter => { + if (filter.grantType && !['clientCredentials', 'authorisationCode'].includes(filterToRemove)) { + return { grantType: filter.grantType } + } + return {} +} + +const removeClientTypeFilter = (filter: BaseClientListFilter, filterToRemove: string): BaseClientListFilter => { + if (filter.clientType) { + let clients = filter.clientType + if (filterToRemove === 'personalClientType') { + clients = clients.filter(client => client !== ClientType.Personal) + } + if (filterToRemove === 'serviceClientType') { + clients = clients.filter(client => client !== ClientType.Service) + } + if (filterToRemove === 'blankClientType') { + clients = clients.filter(client => client !== ClientType.Blank) + } + if (clients.length > 0 && clients.length < 3) { + return { clientType: clients } + } + } + return {} +} + +const removeFilterLink = (filter: BaseClientListFilter, filterToRemove: string): string => { + const newFilter: BaseClientListFilter = { + ...removeRoleSearchFilter(filter, filterToRemove), + ...removeGrantTypeFilter(filter, filterToRemove), + ...removeClientTypeFilter(filter, filterToRemove), + } + + const query = mapFilterToUrlQuery(newFilter) + return query ? `/?${query}` : '/' +} + +const showSelectedFilters = (filter?: BaseClientListFilter) => { + if (!filter) return false + return !(!filter.roleSearch && !filter.grantType && !filter.clientType) } export default (data: BaseClient[], filter?: BaseClientListFilter) => { return { tableHead: indexTableHead(), tableRows: indexTableRows(data, filter), - filter: filter || { - roleSearch: '', - clientCredentials: true, - authorisationCode: true, - serviceClientType: true, - personalClientType: true, - blankClientType: true, - }, + filter: filter || {}, + showSelectedFilters: showSelectedFilters(filter), + selectedFilterCategories: getSelectedFilterCategories(filter), } }