Files
scripts/Perl OTRS/Kernel/GenericInterface/Mapping/Simple.pm
2024-10-14 00:08:40 +02:00

476 lines
15 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::GenericInterface::Mapping::Simple;
use strict;
use warnings;
use Kernel::System::VariableCheck qw(IsHashRefWithData IsString IsStringWithData);
our $ObjectManagerDisabled = 1;
=head1 NAME
Kernel::GenericInterface::Mapping::Simple - GenericInterface simple data mapping backend
=head1 PUBLIC INTERFACE
=head2 new()
usually, you want to create an instance of this
by using Kernel::GenericInterface::Mapping->new();
=cut
sub new {
my ( $Type, %Param ) = @_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
# check needed params
for my $Needed (qw(DebuggerObject MappingConfig)) {
if ( !$Param{$Needed} ) {
return {
Success => 0,
ErrorMessage => "Got no $Needed!"
};
}
$Self->{$Needed} = $Param{$Needed};
}
# check mapping config
if ( !IsHashRefWithData( $Param{MappingConfig} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got no MappingConfig as hash ref with content!',
);
}
# check config - if we have a map config, it has to be a non-empty hash ref
if (
defined $Param{MappingConfig}->{Config}
&& !IsHashRefWithData( $Param{MappingConfig}->{Config} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => 'Got MappingConfig with Data, but Data is no hash ref with content!',
);
}
# check configuration
my $ConfigCheck = $Self->_ConfigCheck( Config => $Self->{MappingConfig}->{Config} );
return $ConfigCheck if !$ConfigCheck->{Success};
return $Self;
}
=head2 Map()
provides 1:1 and regex mapping for keys and values
also the use of a default for keys and values that were not mapped is possible
we need the config to be in the following format
$Self->{MappingConfig}->{Config} = {
KeyMapExact => { # optional. key/value pairs for direct replacement
'old_value' => 'new_value',
'another_old_value' => 'another_new_value',
'maps_to_same_value => 'another_new_value',
},
KeyMapRegEx => { # optional. replace keys with value if current key matches regex
'Stat(e|us)' => 'state',
'[pP]riority' => 'prio',
},
KeyMapDefault => { # required. replace keys if the have not been replaced before
MapType => 'Keep', # possible values are
# 'Keep' (leave unchanged)
# 'Ignore' (drop key/value pair)
# 'MapTo' (use provided value as default)
MapTo => 'new_value', # only used if 'MapType' is 'MapTo'. then required
},
ValueMap => { # optional.
'new_key_name' => { # optional. Replacement for a specific key
ValueMapExact => { # optional. key/value pairs for direct replacement
'old_value' => 'new_value',
'another_old_value' => 'another_new_value',
'maps_to_same_value => 'another_new_value',
},
ValueMapRegEx => { # optional. replace keys with value if current key matches regex
'Stat(e|us)' => 'state',
'[pP]riority' => 'prio',
},
},
},
ValueMapDefault => { # required. replace keys if the have not been replaced before
MapType => 'Keep', # possible values are
# 'Keep' (leave unchanged)
# 'Ignore' (drop key/value pair)
# 'MapTo' (use provided value as default)
MapTo => 'new_value', # only used if 'MapType' is 'MapTo'. then required
},
};
my $ReturnData = $MappingObject->Map(
Data => {
'original_key' => 'original_value',
'another_key' => 'next_value',
},
);
my $ReturnData = {
'changed_key' => 'changed_value',
'original_key' => 'another_changed_value',
'another_original_key' => 'default_value',
'default_key' => 'changed_value',
};
=cut
sub Map {
my ( $Self, %Param ) = @_;
# check data - only accept undef or hash ref
if ( defined $Param{Data} && ref $Param{Data} ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Got Data but it is not a hash ref in Mapping Simple backend!'
);
}
# return if data is empty
if ( !defined $Param{Data} || !%{ $Param{Data} } ) {
return {
Success => 1,
Data => {},
};
}
# prepare short config variable
my $Config = $Self->{MappingConfig}->{Config};
# no config means we just return input data
if ( !$Config ) {
return {
Success => 1,
Data => $Param{Data},
};
}
# go through keys for replacement
my %ReturnData;
CONFIGKEY:
for my $OldKey ( sort keys %{ $Param{Data} } ) {
# check if key is valid
if ( !IsStringWithData($OldKey) ) {
$Self->{DebuggerObject}->Notice( Summary => 'Got an original key that is not valid!' );
next CONFIGKEY;
}
# map key
my $NewKey;
# first check in exact (1:1) map
if ( $Config->{KeyMapExact} && $Config->{KeyMapExact}->{$OldKey} ) {
$NewKey = $Config->{KeyMapExact}->{$OldKey};
}
# if we have no match from exact map, try regex map
if ( !$NewKey && $Config->{KeyMapRegEx} ) {
KEYMAPREGEX:
for my $ConfigKey ( sort keys %{ $Config->{KeyMapRegEx} } ) {
next KEYMAPREGEX if $OldKey !~ m{ \A $ConfigKey \z }xms;
if ( $ReturnData{ $Config->{KeyMapRegEx}->{$ConfigKey} } ) {
$Self->{DebuggerObject}->Notice(
Summary =>
"The data key '$Config->{KeyMapRegEx}->{$ConfigKey}' already exists!",
);
next CONFIGKEY;
}
$NewKey = $Config->{KeyMapRegEx}->{$ConfigKey};
last KEYMAPREGEX;
}
}
# if we still have no match, apply default
if ( !$NewKey ) {
# check map type options
if ( $Config->{KeyMapDefault}->{MapType} eq 'Keep' ) {
$NewKey = $OldKey;
}
elsif ( $Config->{KeyMapDefault}->{MapType} eq 'Ignore' ) {
next CONFIGKEY;
}
elsif ( $Config->{KeyMapDefault}->{MapType} eq 'MapTo' ) {
# check if we already have a key with the same name
if ( $ReturnData{ $Config->{KeyMapDefault}->{MapTo} } ) {
$Self->{DebuggerObject}->Notice(
Summary => "The data key $Config->{KeyMapDefault}->{MapTo} already exists!",
);
next CONFIGKEY;
}
$NewKey = $Config->{KeyMapDefault}->{MapTo};
}
}
# sanity check - we should have a translated key now
if ( !$NewKey ) {
return $Self->{DebuggerObject}->Error( Summary => "Could not map data key $NewKey!" );
}
# map value
my $OldValue = $Param{Data}->{$OldKey};
# if value is no string, just pass through
if ( !IsString($OldValue) ) {
$ReturnData{$NewKey} = $OldValue;
next CONFIGKEY;
}
# check if we have a value mapping for the specific key
my $ValueMap;
if ( $Config->{ValueMap} && $Config->{ValueMap}->{$NewKey} ) {
$ValueMap = $Config->{ValueMap}->{$NewKey};
}
if ($ValueMap) {
# first check in exact (1:1) map
if ( $ValueMap->{ValueMapExact} && defined $ValueMap->{ValueMapExact}->{$OldValue} ) {
$ReturnData{$NewKey} = $ValueMap->{ValueMapExact}->{$OldValue};
next CONFIGKEY;
}
# if we have no match from exact map, try regex map
if ( $ValueMap->{ValueMapRegEx} ) {
VALUEMAPREGEX:
for my $ConfigKey ( sort keys %{ $ValueMap->{ValueMapRegEx} } ) {
next VALUEMAPREGEX if $OldValue !~ m{ \A $ConfigKey \z }xms;
$ReturnData{$NewKey} = $ValueMap->{ValueMapRegEx}->{$ConfigKey};
next CONFIGKEY;
}
}
}
# if we had no mapping, apply default
# keep current value
if ( $Config->{ValueMapDefault}->{MapType} eq 'Keep' ) {
$ReturnData{$NewKey} = $OldValue;
next CONFIGKEY;
}
# map to default value
if ( $Config->{ValueMapDefault}->{MapType} eq 'MapTo' ) {
$ReturnData{$NewKey} = $Config->{ValueMapDefault}->{MapTo};
next CONFIGKEY;
}
# implicit ignore
next CONFIGKEY;
}
return {
Success => 1,
Data => \%ReturnData,
};
}
=begin Internal:
=head2 _ConfigCheck()
does checks to make sure the config is sane
my $Return = $MappingObject->_ConfigCheck(
Config => { # config as defined for Map
...
},
);
in case of an error
$Return => {
Success => 0,
ErrorMessage => 'An error occurred',
};
in case of a success
$Return = {
Success => 1,
};
=cut
sub _ConfigCheck {
my ( $Self, %Param ) = @_;
# just return success if config is undefined or empty hashref
my $Config = $Param{Config};
if ( !defined $Config ) {
return {
Success => 1,
};
}
if ( ref $Config ne 'HASH' ) {
return $Self->{DebuggerObject}->Error(
Summary => 'Config is defined but not a hash reference!',
);
}
if ( !IsHashRefWithData($Config) ) {
return {
Success => 1,
};
}
# parse config options for validity
my %OnlyStringConfigTypes = (
KeyMapExact => 1,
KeyMapRegEx => 1,
KeyMapDefault => 1,
ValueMapDefault => 1,
);
my %RequiredConfigTypes = (
KeyMapDefault => 1,
ValueMapDefault => 1,
);
CONFIGTYPE:
for my $ConfigType (qw(KeyMapExact KeyMapRegEx KeyMapDefault ValueMap ValueMapDefault)) {
# require some types
if ( !defined $Config->{$ConfigType} ) {
next CONFIGTYPE if !$RequiredConfigTypes{$ConfigType};
return $Self->{DebuggerObject}->Error(
Summary => "Got no $ConfigType, but it is required!",
);
}
# check type definition
if ( !IsHashRefWithData( $Config->{$ConfigType} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got $ConfigType with Data, but Data is no hash ref with content!",
);
}
# check keys and values of these config types
next CONFIGTYPE if !$OnlyStringConfigTypes{$ConfigType};
for my $ConfigKey ( sort keys %{ $Config->{$ConfigType} } ) {
if ( !IsString($ConfigKey) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got key in $ConfigType which is not a string!",
);
}
if ( !IsString( $Config->{$ConfigType}->{$ConfigKey} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got value for $ConfigKey in $ConfigType which is not a string!",
);
}
}
}
# check default configuration in KeyMapDefault and ValueMapDefault
my %ValidMapTypes = (
Keep => 1,
Ignore => 1,
MapTo => 1,
);
CONFIGTYPE:
for my $ConfigType (qw(KeyMapDefault ValueMapDefault)) {
# require MapType as a string with a valid value
if (
!IsStringWithData( $Config->{$ConfigType}->{MapType} )
|| !$ValidMapTypes{ $Config->{$ConfigType}->{MapType} }
)
{
return $Self->{DebuggerObject}->Error(
Summary => "Got no valid MapType in $ConfigType!",
);
}
# check MapTo if MapType is set to 'MapTo'
if (
$Config->{$ConfigType}->{MapType} eq 'MapTo'
&& !IsStringWithData( $Config->{$ConfigType}->{MapTo} )
)
{
return $Self->{DebuggerObject}->Error(
Summary => "Got MapType 'MapTo', but MapTo value is not valid in $ConfigType!",
);
}
}
# check ValueMap
if ( IsHashRefWithData( $Config->{ValueMap} ) ) {
for my $KeyName ( sort keys %{ $Config->{ValueMap} } ) {
# require values to be hash ref
if ( !IsHashRefWithData( $Config->{ValueMap}->{$KeyName} ) ) {
return $Self->{DebuggerObject}->Error(
Summary => "Got $KeyName in ValueMap, but it is no hash ref with content!",
);
}
# possible sub-values are ValueMapExact or ValueMapRegEx and need to be hash ref if defined
SUBKEY:
for my $SubKeyName (qw(ValueMapExact ValueMapRegEx)) {
my $ValueMapType = $Config->{ValueMap}->{$KeyName}->{$SubKeyName};
next SUBKEY if !defined $ValueMapType;
if ( !IsHashRefWithData($ValueMapType) ) {
return $Self->{DebuggerObject}->Error(
Summary =>
"Got $SubKeyName in $KeyName in ValueMap,"
. ' but it is no hash ref with content!',
);
}
# key/value pairs of ValueMapExact and ValueMapRegEx must be strings
for my $ValueMapTypeKey ( sort keys %{$ValueMapType} ) {
if ( !IsString($ValueMapTypeKey) ) {
return $Self->{DebuggerObject}->Error(
Summary =>
"Got key in $SubKeyName in $KeyName in ValueMap which is not a string!",
);
}
if ( !IsString( $ValueMapType->{$ValueMapTypeKey} ) ) {
return $Self->{DebuggerObject}->Error(
Summary =>
"Got value for $ValueMapTypeKey in $SubKeyName in $KeyName in ValueMap"
. ' which is not a string!',
);
}
}
}
}
}
# if we arrive here, all checks were OK
return {
Success => 1,
};
}
1;
=end Internal:
=head1 TERMS AND CONDITIONS
This software is part of the OTRS project (L<https://otrs.org/>).
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 L<https://www.gnu.org/licenses/gpl-3.0.txt>.
=cut