476 lines
15 KiB
Perl
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
|