Files
scripts/Perl OTRS/Kernel/Modules/AdminGenericInterfaceErrorHandlingDefault.pm
2024-10-14 00:08:40 +02:00

746 lines
24 KiB
Perl

# --
# Copyright (C) 2001-2019 OTRS AG, https://otrs.com/
# --
# This software comes with ABSOLUTELY NO WARRANTY. For details, see
# the enclosed file COPYING for license information (GPL). If you
# did not receive this file, see https://www.gnu.org/licenses/gpl-3.0.txt.
# --
package Kernel::Modules::AdminGenericInterfaceErrorHandlingDefault;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(:all);
use Kernel::Language qw(Translatable);
our $ObjectManagerDisabled = 1;
sub new {
my ( $Type, %Param ) = @_;
my $Self = {%Param};
bless( $Self, $Type );
return $Self;
}
sub Run {
my ( $Self, %Param ) = @_;
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $WebserviceID = $ParamObject->GetParam( Param => 'WebserviceID' );
if ( !IsPositiveInteger($WebserviceID) ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Need WebserviceID!'),
);
}
my $WebserviceData = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceGet(
ID => $WebserviceID,
);
if ( !IsHashRefWithData($WebserviceData) ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}->Translate( 'Could not get data for WebserviceID %s', $WebserviceID ),
);
}
my $CommunicationType = $ParamObject->GetParam( Param => 'CommunicationType' );
if ( !IsStringWithData($CommunicationType) ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Need communication type!'),
);
}
if (
$CommunicationType ne 'Requester'
&& $CommunicationType ne 'Provider'
)
{
return $LayoutObject->ErrorScreen(
Message => Translatable("Communication type needs to be 'Requester' or 'Provider'!"),
);
}
if ( $Self->{Subaction} eq 'Add' ) {
return $Self->_Add(
WebserviceID => $WebserviceID,
WebserviceData => $WebserviceData,
CommunicationType => $CommunicationType,
);
}
elsif ( $Self->{Subaction} eq 'AddAction' ) {
# Challenge token check for write action.
$LayoutObject->ChallengeTokenCheck();
return $Self->_AddAction(
WebserviceID => $WebserviceID,
WebserviceData => $WebserviceData,
CommunicationType => $CommunicationType,
);
}
elsif ( $Self->{Subaction} eq 'Change' ) {
return $Self->_Change(
WebserviceID => $WebserviceID,
WebserviceData => $WebserviceData,
CommunicationType => $CommunicationType,
);
}
elsif ( $Self->{Subaction} eq 'ChangeAction' ) {
# Challenge token check for write action.
$LayoutObject->ChallengeTokenCheck();
return $Self->_ChangeAction(
WebserviceID => $WebserviceID,
WebserviceData => $WebserviceData,
CommunicationType => $CommunicationType,
);
}
elsif ( $Self->{Subaction} eq 'DeleteAction' ) {
# Challenge token check for write action.
$LayoutObject->ChallengeTokenCheck();
return $Self->_DeleteAction(
WebserviceID => $WebserviceID,
WebserviceData => $WebserviceData,
CommunicationType => $CommunicationType,
);
}
elsif ( $Self->{Subaction} eq 'PriorityAction' ) {
# Challenge token check for write action.
$LayoutObject->ChallengeTokenCheck();
return $Self->_PriorityAction(
WebserviceID => $WebserviceID,
WebserviceData => $WebserviceData,
CommunicationType => $CommunicationType,
);
}
# Fall-back if missing or other sub-action.
return $LayoutObject->ErrorScreen(
Message => Translatable('Invalid Subaction!'),
);
}
sub _Add {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ErrorHandlingType = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'ErrorHandlingType' );
if ( !IsStringWithData($ErrorHandlingType) ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Need ErrorHandlingType!'),
);
}
if ( !$Self->_ErrorHandlingTypeCheck( ErrorHandlingType => $ErrorHandlingType ) ) {
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}
->Translate( 'ErrorHandlingType %s is not registered', $ErrorHandlingType ),
);
}
return $Self->_ShowScreen(
%Param,
Action => 'Add',
ErrorHandlingConfig => {
Type => $ErrorHandlingType,
},
);
}
sub _AddAction {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $ErrorHandlingType = $ParamObject->GetParam( Param => 'ErrorHandlingType' );
if ( !IsStringWithData($ErrorHandlingType) ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Need ErrorHandlingType!'),
);
}
if ( !$Self->_ErrorHandlingTypeCheck( ErrorHandlingType => $ErrorHandlingType ) ) {
return $LayoutObject->ErrorScreen(
Message => $LayoutObject->{LanguageObject}
->Translate( 'ErrorHandlingType %s is not registered', $ErrorHandlingType ),
);
}
my %Errors;
my $RequestParams = $Self->_RequestParamsGet();
# Name already exists.
my $ErrorHandling = $ParamObject->GetParam( Param => 'ErrorHandling' );
if (
!IsStringWithData($ErrorHandling)
|| $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling}
)
{
$Errors{ErrorHandlingServerError} = 'ServerError';
}
my $ErrorHandlingConfig = {
%{$RequestParams},
Type => $ErrorHandlingType,
};
# Validation errors.
if (%Errors) {
return $Self->_ShowScreen(
%Param,
%Errors,
Action => 'Add',
ErrorHandling => $ErrorHandling,
ErrorHandlingConfig => {
%{$ErrorHandlingConfig},
ErrorHandling => $ErrorHandling,
},
);
}
# Add module to config.
$Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling}
= $ErrorHandlingConfig;
# Add module to priority list.
my $WebserviceErrorHandlingPriority
= $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority};
if ( IsArrayRefWithData($WebserviceErrorHandlingPriority) ) {
push @{$WebserviceErrorHandlingPriority}, $ErrorHandling;
}
else {
$Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority} = [$ErrorHandling];
}
my $UpdateSuccess = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceUpdate(
%{ $Param{WebserviceData} },
UserID => $Self->{UserID},
);
if ( !$UpdateSuccess ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Could not update web service'),
);
}
# Save button: stay in edit mode.
my $RedirectURL =
'Action='
. $Self->{Action}
. ';Subaction=Change;WebserviceID='
. $Param{WebserviceID}
. ';ErrorHandling='
. $LayoutObject->LinkEncode($ErrorHandling)
. ';ErrorHandlingType='
. $LayoutObject->LinkEncode($ErrorHandlingType)
. ';CommunicationType='
. $LayoutObject->LinkEncode( $Param{CommunicationType} )
. ';';
return $LayoutObject->Redirect(
OP => $RedirectURL,
);
}
sub _Change {
my ( $Self, %Param ) = @_;
my $ErrorHandling = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'ErrorHandling' );
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
if ( !IsStringWithData($ErrorHandling) ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Need ErrorHandling'),
);
}
my $ErrorHandlingConfig
= $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling};
if ( !IsHashRefWithData($ErrorHandlingConfig) ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}
->Translate( 'Could not determine config for error handler %s', $ErrorHandling ),
);
}
return $Self->_ShowScreen(
%Param,
Action => 'Change',
ErrorHandling => $ErrorHandling,
ErrorHandlingConfig => {
%{$ErrorHandlingConfig},
ErrorHandling => $ErrorHandling,
},
);
}
sub _ChangeAction {
my ( $Self, %Param ) = @_;
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $OldErrorHandling = $ParamObject->GetParam( Param => 'OldErrorHandling' );
if ( !IsStringWithData($OldErrorHandling) ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}->Translate( 'Need %s', 'OldErrorHandling' ),
);
}
my %Errors;
my $ErrorHandling = $ParamObject->GetParam( Param => 'ErrorHandling' );
if ( !IsStringWithData($ErrorHandling) ) {
$Errors{ErrorHandlingServerError} = 'ServerError';
}
my $ErrorHandlingConfig = $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}
->{$OldErrorHandling};
if ( !IsHashRefWithData($ErrorHandlingConfig) ) {
return $LayoutObject->ErrorScreen(
Message =>
$LayoutObject->{LanguageObject}
->Translate( 'Could not determine config for error handler %s', $OldErrorHandling ),
);
}
# Error handler was renamed, avoid conflicts.
if ( $OldErrorHandling ne $ErrorHandling ) {
# New name already exists, bail out.
if (
$Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling}
)
{
$Errors{ErrorHandlingServerError} = 'ServerError';
}
# OK, remove old error handler. New one will be added below.
if ( !%Errors ) {
delete $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}
->{$OldErrorHandling};
}
}
my $RequestParams = $Self->_RequestParamsGet();
$ErrorHandlingConfig = {
%{$RequestParams},
Type => $ErrorHandlingConfig->{Type},
};
# Validation errors.
if (%Errors) {
return $Self->_ShowScreen(
%Param,
%Errors,
Action => 'Change',
ErrorHandling => $OldErrorHandling,
ErrorHandlingConfig => {
%{$ErrorHandlingConfig},
ErrorHandling => $ErrorHandling,
},
);
}
# Update config.
$Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling}
= $ErrorHandlingConfig;
# Update priority array if necessary.
if ( $OldErrorHandling ne $ErrorHandling ) {
my $ErrorHandlingPriority
= $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority};
my ($OldErrorHandlingIndex) = grep { $ErrorHandlingPriority->[$_] eq $OldErrorHandling }
( 0 .. scalar @{$ErrorHandlingPriority} - 1 );
if ( IsStringWithData($OldErrorHandlingIndex) ) {
$ErrorHandlingPriority->[$OldErrorHandlingIndex] = $ErrorHandling;
}
}
my $UpdateSuccess = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceUpdate(
%{ $Param{WebserviceData} },
UserID => $Self->{UserID},
);
if ( !$UpdateSuccess ) {
return $LayoutObject->ErrorScreen(
Message => Translatable('Could not update web service'),
);
}
# if the user would like to continue editing the queue, just redirect to the edit screen
my $RedirectURL;
if (
defined $ParamObject->GetParam( Param => 'ContinueAfterSave' )
&& ( $ParamObject->GetParam( Param => 'ContinueAfterSave' ) eq '1' )
)
{
$RedirectURL =
'Action='
. $Self->{Action}
. ';Subaction=Change;WebserviceID='
. $Param{WebserviceID}
. ';ErrorHandling='
. $LayoutObject->LinkEncode($ErrorHandling)
. ';CommunicationType='
. $LayoutObject->LinkEncode( $Param{CommunicationType} )
. ';';
}
else {
# otherwise return to overview
$RedirectURL =
'Action=AdminGenericInterfaceWebservice;Subaction=Change;WebserviceID='
. $Param{WebserviceID}
. ';';
}
return $LayoutObject->Redirect(
OP => $RedirectURL,
);
}
sub _DeleteAction {
my ( $Self, %Param ) = @_;
my $ErrorHandling = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'ErrorHandling' );
if ( !IsStringWithData($ErrorHandling) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Need ErrorHandling!',
);
return $Self->_JSONResponse( Success => 0 );
}
if (
!IsHashRefWithData(
$Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling}
)
)
{
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => "Could not determine config for error handler $ErrorHandling",
);
return $Self->_JSONResponse( Success => 0 );
}
# Delete error handling module.
delete $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandling}->{$ErrorHandling};
# Remove entry from priority.
@{ $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority} }
= grep { $_ ne $ErrorHandling }
@{ $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority} };
my $Success = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceUpdate(
%{ $Param{WebserviceData} },
UserID => $Self->{UserID},
);
return $Self->_JSONResponse( Success => $Success );
}
sub _PriorityAction {
my ( $Self, %Param ) = @_;
my $PriorityJSON = $Kernel::OM->Get('Kernel::System::Web::Request')->GetParam( Param => 'Priority' );
if ( !$PriorityJSON ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Got no priority JSON array to save!',
);
return $Self->_JSONResponse( Success => 0 );
}
my $Priority = $Kernel::OM->Get('Kernel::System::JSON')->Decode(
Data => $PriorityJSON,
);
if ( !IsArrayRefWithData($Priority) ) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Got no priority array to save!',
);
return $Self->_JSONResponse( Success => 0 );
}
# Check if priority content matches original content (safety check - we should only change order, not elements).
my @PrioritySorted = sort @{$Priority};
my @OldPrioritySorted
= sort @{ $Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority} };
my $DataIsDifferent = DataIsDifferent(
Data1 => \@PrioritySorted,
Data2 => \@OldPrioritySorted,
);
if ($DataIsDifferent) {
$Kernel::OM->Get('Kernel::System::Log')->Log(
Priority => 'error',
Message => 'Priority array content could not be verified!',
);
return $Self->_JSONResponse( Success => 0 );
}
# Save the new priority.
$Param{WebserviceData}->{Config}->{ $Param{CommunicationType} }->{ErrorHandlingPriority} = $Priority;
my $Success = $Kernel::OM->Get('Kernel::System::GenericInterface::Webservice')->WebserviceUpdate(
%{ $Param{WebserviceData} },
UserID => $Self->{UserID},
);
return $Self->_JSONResponse( Success => $Success );
}
sub _ShowScreen {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
my $Output = $LayoutObject->Header();
$Output .= $LayoutObject->NavigationBar();
$LayoutObject->Block(
Name => 'Title' . $Param{Action},
Data => \%Param,
);
$LayoutObject->Block(
Name => 'Navigation' . $Param{Action},
Data => \%Param,
);
my %TemplateData = (
ErrorHandlingType => $Param{ErrorHandlingConfig}->{Type},
Description => $Param{ErrorHandlingConfig}->{Description},
ErrorCode => $Param{ErrorHandlingConfig}->{ErrorCode},
ErrorMessage => $Param{ErrorHandlingConfig}->{ErrorMessage},
ErrorHandling => $Param{ErrorHandlingConfig}->{ErrorHandling},
OldErrorHandling => $Param{ErrorHandling},
ErrorMessageContentFilter => $Param{ErrorHandlingConfig}->{ErrorMessageContentFilter},
);
if ( $Param{Action} eq 'Change' ) {
$LayoutObject->AddJSData(
Key => 'ErrorHandling',
Value => {
CommunicationType => $Param{CommunicationType},
ErrorHandlingModule => $Param{ErrorHandling},
WebserviceID => $Param{WebserviceID},
ErrorHandlingType => $Param{ErrorHandlingConfig}->{Type},
},
);
}
my @ErrorStageFilter;
if ( $Param{CommunicationType} eq 'Requester' ) {
@ErrorStageFilter = (
{
Key => 'RequesterRequestPrepare',
Value => Translatable('Invoker processing outgoing request data'),
},
{
Key => 'RequesterRequestMap',
Value => Translatable('Mapping outgoing request data'),
},
{
Key => 'RequesterRequestPerform',
Value => Translatable('Transport processing request into response'),
},
{
Key => 'RequesterResponseMap',
Value => Translatable('Mapping incoming response data'),
},
{
Key => 'RequesterResponseProcess',
Value => Translatable('Invoker processing incoming response data'),
},
);
}
else {
@ErrorStageFilter = (
{
Key => 'ProviderRequestReceive',
Value => Translatable('Transport receiving incoming request data'),
},
{
Key => 'ProviderRequestMap',
Value => Translatable('Mapping incoming request data'),
},
{
Key => 'ProviderRequestProcess',
Value => Translatable('Operation processing incoming request data'),
},
{
Key => 'ProviderResponseMap',
Value => Translatable('Mapping outgoing response data'),
},
{
Key => 'ProviderResponseTransmit',
Value => Translatable('Transport sending outgoing response data'),
},
);
}
$TemplateData{ErrorStageFilterStrg} = $LayoutObject->BuildSelection(
Data => \@ErrorStageFilter,
ID => 'ErrorStageFilter',
Name => 'ErrorStageFilter',
SelectedID => $Param{ErrorHandlingConfig}->{ErrorStageFilter},
PossibleNone => 1,
Multiple => 1,
Translate => 1,
Class => 'Modernize',
);
$TemplateData{StopAfterMatchStrg} = $LayoutObject->BuildSelection(
Data => {
'backend' => Translatable('skip same backend modules only'),
'all' => Translatable('skip all modules'),
},
ID => 'StopAfterMatch',
Name => 'StopAfterMatch',
SelectedID => $Param{ErrorHandlingConfig}->{StopAfterMatch},
PossibleNone => 1,
Translate => 1,
Class => 'Modernize',
);
if ( $Param{CommunicationType} eq 'Provider' ) {
my %Operations
= map { $_ => $_ } sort keys %{ $Param{WebserviceData}->{Config}->{Provider}->{Operation} || {} };
my $OperationDeletedText = $LayoutObject->{LanguageObject}->Translate('Operation deleted');
SELECTEDOPERATION:
for my $SelectedOperation ( @{ $Param{ErrorHandlingConfig}->{OperationFilter} || [] } ) {
next SELECTEDOPERATION if $Operations{$SelectedOperation};
$Operations{$SelectedOperation} = $SelectedOperation . " ($OperationDeletedText)";
}
my $OperationFilterStrg = $LayoutObject->BuildSelection(
Data => \%Operations,
ID => 'OperationFilter',
Name => 'OperationFilter',
SelectedID => $Param{ErrorHandlingConfig}->{OperationFilter},
Multiple => 1,
PossibleNone => 1,
Translate => 0,
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'OperationFilter',
Data => {
OperationFilterStrg => $OperationFilterStrg,
},
);
}
else {
my %Invokers = map { $_ => $_ } sort keys %{ $Param{WebserviceData}->{Config}->{Requester}->{Invoker} || {} };
my $InvokerDeletedText = $LayoutObject->{LanguageObject}->Translate('Invoker deleted');
SELECTEDINVOKER:
for my $SelectedInvoker ( @{ $Param{ErrorHandlingConfig}->{InvokerFilter} || [] } ) {
next SELECTEDINVOKER if $Invokers{$SelectedInvoker};
$Invokers{$SelectedInvoker} = $SelectedInvoker . " ($InvokerDeletedText)";
}
my $InvokerFilterStrg = $LayoutObject->BuildSelection(
Data => \%Invokers,
ID => 'InvokerFilter',
Name => 'InvokerFilter',
SelectedID => $Param{ErrorHandlingConfig}->{InvokerFilter},
Multiple => 1,
PossibleNone => 1,
Translate => 0,
Class => 'Modernize',
);
$LayoutObject->Block(
Name => 'InvokerFilter',
Data => {
InvokerFilterStrg => $InvokerFilterStrg,
},
);
}
$Output .= $LayoutObject->Output(
TemplateFile => $Self->{Action},
Data => {
%Param,
%TemplateData,
ErrorHandling => $Param{ErrorHandling},
WebserviceName => $Param{WebserviceData}->{Name},
},
);
$Output .= $LayoutObject->Footer();
return $Output;
}
sub _RequestParamsGet {
my ( $Self, %Param ) = @_;
my $ParamObject = $Kernel::OM->Get('Kernel::System::Web::Request');
my %GetParam;
for my $StringParam (
qw(
Description ErrorCode ErrorMessage
ErrorMessageContentFilter StopAfterMatch
)
)
{
$GetParam{$StringParam} = $ParamObject->GetParam( Param => $StringParam ) // '';
}
for my $ArrayParam (qw(InvokerFilter OperationFilter ErrorStageFilter)) {
$GetParam{$ArrayParam} = [ $ParamObject->GetArray( Param => $ArrayParam ) ];
}
return \%GetParam;
}
=head2 _ErrorHandlingTypeCheck()
checks if a given ErrorHandlingType is registered in the system.
=cut
sub _ErrorHandlingTypeCheck {
my ( $Self, %Param ) = @_;
return if !$Param{ErrorHandlingType};
my $ErrorHandlingConfig = $Kernel::OM->Get('Kernel::Config')->Get('GenericInterface::ErrorHandling::Module');
return if !IsHashRefWithData($ErrorHandlingConfig);
return if !IsHashRefWithData( $ErrorHandlingConfig->{ $Param{ErrorHandlingType} } );
return 1;
}
sub _JSONResponse {
my ( $Self, %Param ) = @_;
my $LayoutObject = $Kernel::OM->Get('Kernel::Output::HTML::Layout');
# Build JSON output.
my $JSON = $LayoutObject->JSONEncode(
Data => {
Success => $Param{Success} // 0,
},
);
# Send JSON response.
return $LayoutObject->Attachment(
ContentType => 'application/json; charset=' . $LayoutObject->{Charset},
Content => $JSON,
Type => 'inline',
NoCache => 1,
);
}
1;