# -- # 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). 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. =cut