Skip to content

Commit

Permalink
Special case access
Browse files Browse the repository at this point in the history
  • Loading branch information
Lee Wright authored and Lee Wright committed Jul 2, 2024
1 parent 370551b commit b61ec4c
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ namespace Pidp.Features.DigitalEvidenceCaseManagement.Commands;
using Newtonsoft.Json;
using NodaTime;
using Pidp.Data;
using Pidp.Exceptions;
using Pidp.Features.AccessRequests;
using Pidp.Infrastructure.HttpClients.Edt;
using Pidp.Kafka.Interfaces;
using Pidp.Models;
using Pidp.Models.Lookups;
Expand All @@ -21,6 +23,7 @@ public class Command : ICommand<IDomainResult>
public string AgencyFileNumber { get; set; } = string.Empty;
public int RequestId { get; set; }
public int CaseId { get; set; }
public bool ToolsCaseRequest { get; set; }
public string? Key { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string CaseGroup { get; set; } = string.Empty;
Expand All @@ -30,9 +33,9 @@ public class CommandValidator : AbstractValidator<Command>
{
public CommandValidator()
{
this.RuleFor(x => x.AgencyFileNumber).NotEmpty();
this.RuleFor(x => x.AgencyFileNumber).NotEmpty().When(x => !x.ToolsCaseRequest);
this.RuleFor(x => x.PartyId).GreaterThan(0);
this.RuleFor(x => x.CaseId).GreaterThan(0);
this.RuleFor(x => x.CaseId).GreaterThan(0).When(x => !x.ToolsCaseRequest);
// BCPSDEMS-1655 - case key not necessary
// this.RuleFor(command => command.Key).NotEmpty();

Expand All @@ -43,18 +46,25 @@ public class CommandHandler : ICommandHandler<Command, IDomainResult>
private readonly IClock clock;
private readonly ILogger logger;
private readonly PidpConfiguration config;
private readonly IEdtCaseManagementClient caseMgmtClient;

private readonly PidpDbContext context;
private readonly IKafkaProducer<string, SubAgencyDomainEvent> kafkaProducer;
private static readonly Histogram CaseQueueRequestDuration = Metrics
.CreateHistogram("pipd_case_request_duration", "Histogram of case request call durations.");

public CommandHandler(IClock clock, ILogger<CommandHandler> logger, PidpConfiguration config, PidpDbContext context, IKafkaProducer<string, SubAgencyDomainEvent> kafkaProducer)
public CommandHandler(IClock clock, ILogger<CommandHandler> logger,
PidpConfiguration config, PidpDbContext context,
IKafkaProducer<string, SubAgencyDomainEvent> kafkaProducer,
IEdtCaseManagementClient caseMgmtClient
)
{
this.clock = clock;
this.logger = logger;
this.config = config;
this.context = context;
this.kafkaProducer = kafkaProducer;
this.caseMgmtClient = caseMgmtClient;
}

public async Task<IDomainResult> HandleAsync(Command command)
Expand All @@ -75,13 +85,30 @@ public async Task<IDomainResult> HandleAsync(Command command)

try
{

if (string.IsNullOrEmpty(command.Key))
// not a tools request and no key provided
if (!command.ToolsCaseRequest && string.IsNullOrEmpty(command.Key))
{
// case has no RCC number - we'll record and move on
this.logger.LogCaseMissingKey(command.CaseId, dto.Jpdid);
}

if (command.ToolsCaseRequest)
{
var toolsCase = await this.caseMgmtClient.GetCase(this.config.AUFToolsCaseId);
if (toolsCase == null)
{
throw new AccessRequestException("Tools case not found");
}
else
{
this.logger.LogRequestToolsCase(toolsCase.Id, dto.Jpdid);
command.Key = toolsCase.Key;
command.CaseId = toolsCase.Id;
command.Name = toolsCase.Name;
command.AgencyFileNumber = "AUF Tools Case";
}
}

var subAgencyRequest = await this.SubmitAgencyCaseRequest(command); //save all trx at once for production(remove this and handle using idempotent)

// var exportedEvent = this.AddOutbox(command, subAgencyRequest, dto);
Expand Down Expand Up @@ -128,7 +155,7 @@ private async Task PublishSubAgencyAccessRequest(PartyDto dto, SubmittingAgencyR
{
var msgKey = Guid.NewGuid().ToString();
Serilog.Log.Logger.Information("Publishing Sub Agency Domain Event to topic {0} {1} {2}", this.config.KafkaCluster.CaseAccessRequestTopicName, msgKey, subAgencyRequest.RequestId);
await this.kafkaProducer.ProduceAsync(this.config.KafkaCluster.CaseAccessRequestTopicName, msgKey, new SubAgencyDomainEvent
var publishResponse = await this.kafkaProducer.ProduceAsync(this.config.KafkaCluster.CaseAccessRequestTopicName, msgKey, new SubAgencyDomainEvent
{
RequestId = subAgencyRequest.RequestId,
CaseId = subAgencyRequest.CaseId,
Expand All @@ -139,6 +166,16 @@ private async Task PublishSubAgencyAccessRequest(PartyDto dto, SubmittingAgencyR
UserId = dto.UserId,
RequestedOn = subAgencyRequest.RequestedOn,
});

if (publishResponse.Status == Confluent.Kafka.PersistenceStatus.Persisted)
{
Serilog.Log.Logger.Information($"Published response to {publishResponse.TopicPartition} for {subAgencyRequest.RequestId}");
}
else
{
Serilog.Log.Logger.Error($"Failed to publish to {this.config.KafkaCluster.CaseAccessRequestTopicName} for {subAgencyRequest.RequestId}");
throw new AccessRequestException($"Failed to publish to {this.config.KafkaCluster.CaseAccessRequestTopicName} for {subAgencyRequest.RequestId}");
}
}

private async Task<SubmittingAgencyRequest> SubmitAgencyCaseRequest(Command command)
Expand All @@ -153,6 +190,7 @@ private async Task<SubmittingAgencyRequest> SubmitAgencyCaseRequest(Command comm
PartyId = command.PartyId,
RequestedOn = this.clock.GetCurrentInstant()
};

this.context.SubmittingAgencyRequests.Add(subAgencyAccessRequest);

await this.context.SaveChangesAsync();
Expand Down Expand Up @@ -188,4 +226,6 @@ public static partial class SubmittingAgencyLoggingExtensions
public static partial void LogUserNotEnroled(this ILogger logger, string? username);
[LoggerMessage(3, LogLevel.Warning, "Case request ID {caseId} - for user {username} does not have a valid key (RCCNumber).")]
public static partial void LogCaseMissingKey(this ILogger logger, int caseId, string? username);
[LoggerMessage(4, LogLevel.Information, "Tools case {caseId} request - for user {username}.")]
public static partial void LogRequestToolsCase(this ILogger logger, int caseId, string? username);
}
2 changes: 1 addition & 1 deletion backend/webapi/PidpConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class PidpConfiguration
private static readonly string? EnvironmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

public string ApplicationUrl { get; set; } = string.Empty;

public int AUFToolsCaseId { get; set; }
public AddressAutocompleteClientConfiguration AddressAutocompleteClient { get; set; } = new();
public ConnectionStringConfiguration ConnectionStrings { get; set; } = new();
public ChesClientConfiguration ChesClient { get; set; } = new();
Expand Down
1 change: 1 addition & 0 deletions backend/webapi/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"Url": "localhost",
"Port": 1025
},
"AUFToolsCaseId": 214,
"PlrClient": {
"Url": "http://localhost:5060/api"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,28 @@
</ui-page-section>
<ui-page-section>
<ui-page-section-subheader icon="table_chart" heading="Case Listing">
<div *ngIf="showAUFLink">
<button
class="mat-elevation-z6"
mat-stroked-button
type="button"
color="primary"
(click)="launchAUF()">
{{launchDEMSLabel}}
</button>
<div class="row align-items-start">
<div *ngIf="showAUFLink" class="col">
<button
class="mat-elevation-z6"
mat-stroked-button
type="button"
color="primary"
(click)="launchAUF()">
{{launchDEMSLabel}}
</button>
</div>
<div *ngIf="showCaseToolsLink" class="col">
<button
class="mat-elevation-z6"
mat-stroked-button
type="button"
color="primary"
[disabled]="hasToolsCaseAccess()"
(click)="requestToolsCaseAccess()">
{{accessPoliceToolsCaseLabel}}
</button>
</div>
</div>
</ui-page-section-subheader>
<ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export class DigitalEvidenceCaseManagementPage
public isCaseSearchInProgress: boolean;
public showAUFLink: boolean;
public showCaseImportLink: boolean;
public showCaseToolsLink: boolean;

public isCaseFound: boolean;
public accessRequestFailed: boolean;
Expand All @@ -100,6 +101,8 @@ export class DigitalEvidenceCaseManagementPage
public checkingQueuedCaseInProgress: boolean;

public launchDEMSLabel: string;
public accessPoliceToolsCaseLabel: string;

public hasCaseListingResults: boolean;
public caseTooltip: string;
public refreshCount: number;
Expand Down Expand Up @@ -151,7 +154,11 @@ export class DigitalEvidenceCaseManagementPage
.pipe(map((token) => token?.identity_provider ?? ''));
this.showAUFLink = this.config.caseManagement.showAUFLink;
this.showCaseImportLink = this.config.caseManagement.showCaseImportLink;
this.showCaseToolsLink = this.config.caseManagement.showCaseToolsLink;
this.launchDEMSLabel = this.config.launch.subAgencyAufPortalLabel;
this.accessPoliceToolsCaseLabel =
this.config.launch.policeToolsCaseAccessLabel;

accessTokenService.decodeToken().subscribe((n) => {
if (n !== null) {
this.result = n.identity_provider;
Expand Down Expand Up @@ -339,6 +346,50 @@ export class DigitalEvidenceCaseManagementPage
this.openPopUp(this.config.demsImportURL);
}

public hasToolsCaseAccess(): boolean {
return this.caseListing.some(
(c) => c.agencyFileNumber === 'AUF Tools Case'
);
}

public requestToolsCaseAccess(): void {
this.refreshEnabled = true;

const accessRequest: DigitalEvidenceCaseAccessRequest = {
partyId: this.partyService.partyId,
toolsCaseRequest: true,
caseId: -1,
agencyFileNumber: '',
key: '',
name: '',
};

this.resource
.requestAccess(accessRequest)
.pipe(
tap(() => this.getPartyRequests()),
catchError((error: HttpErrorResponse) => {
if (error.status === HttpStatusCode.NotFound) {
this.navigateToRoot();
}
this.toastService.openErrorToast(
'Failed to add case request :' + error.message
);
this.accessRequestFailed = true;
return of(noop());
})
)
.subscribe(() => {
this.formState.caseName.patchValue('');
this.formState.agencyCode.patchValue(
this.organizationType.submittingAgencyCode
);
this.requestedCase = null;
this.refreshCount = 0;
this.refreshTable();
});
}

public findCase(): void {
if (this.isCaseSearchInProgress) {
return;
Expand Down Expand Up @@ -521,6 +572,7 @@ export class DigitalEvidenceCaseManagementPage

const accessRequest: DigitalEvidenceCaseAccessRequest = {
partyId: this.partyService.partyId,
toolsCaseRequest: false,
agencyFileNumber: agencyFileNumber,
caseId: this.requestedCase.id,
name: this.requestedCase.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export interface Field {
display: boolean;
}


export interface DigitalEvidenceCase {
name: string;
description: string;
Expand All @@ -36,6 +35,7 @@ export interface DigitalEvidenceCaseAccessRequest {
partyId: number;
caseId: number;
key: string;
toolsCaseRequest: boolean;
agencyFileNumber: string;
name: string;
}
Expand Down
2 changes: 2 additions & 0 deletions workspace/apps/pidp/src/environments/environment.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface AppEnvironment extends EnvironmentConfig {
caseManagement: {
showAUFLink: boolean;
showCaseImportLink: boolean;
showCaseToolsLink: boolean;
stickyAgencyCodes: string[];
};
emails: {
Expand All @@ -35,6 +36,7 @@ export interface AppEnvironment extends EnvironmentConfig {
subAgencyAufPortalLabel: string;
bcpsDemsPortalLabel: string;
outOfCustodyPortalLabel: string;
policeToolsCaseAccessLabel: string;
};
urls: {
bcscSupport: string;
Expand Down
2 changes: 2 additions & 0 deletions workspace/apps/pidp/src/environments/environment.prod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const environment: AppEnvironment = {
caseManagement: {
showAUFLink: true,
showCaseImportLink: false,
showCaseToolsLink: true,
stickyAgencyCodes: ['FAKE'],
},
environmentName: EnvironmentName.LOCAL,
Expand Down Expand Up @@ -68,6 +69,7 @@ export const environment: AppEnvironment = {
bcpsDemsPortalLabel: 'Launch DEMS',
outOfCustodyPortalLabel: 'Launch BCPS Disclosure Portal',
publicDisclosurePortalLabel: 'Access your disclosure material',
policeToolsCaseAccessLabel: 'Access the AUF tools case',
},
keycloakConfig: {
config: {
Expand Down

0 comments on commit b61ec4c

Please sign in to comment.