View | Details | Raw Unified | Return to bug 32370
Collapse All | Expand All

(-)a/Koha/Object/JSONFields.pm (+120 lines)
Line 0 Link Here
1
package Koha::Object::JSONFields;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use JSON;
21
use Try::Tiny;
22
23
use Koha::Exceptions;
24
use Koha::Exceptions::Object;
25
26
=head1 NAME
27
28
Koha::Object::JSONFields - Class that adds JSON field manipulation helper methods
29
for Koha::Object-derived classes
30
31
=head1 SYNOPSIS
32
33
    use base qw(Koha::Object Koha::Object::JSONFields);
34
    my $object = Koha::Object->new({ property1 => $property1, property2 => $property2, etc... } );
35
    my $field_name_hashref = $object->decode_json_field({ field => 'field_name' });
36
    $object->encode_json_field({ field => 'field_name', data => $data });
37
38
=head1 API
39
40
=head2 Class methods
41
42
=head3 decode_json_field
43
44
    my $hashref = $object->decode_json_field({ field => 'field_name' });
45
46
Returns a data structure representing the JSON-decoded value of field I<field_name>.
47
48
=cut
49
50
sub decode_json_field {
51
    my ( $self, $params ) = @_;
52
53
    Koha::Exceptions::MissingParameter->throw( parameter => 'field' )
54
        unless defined $params->{field};
55
56
    my $field = $params->{field};
57
58
    return try {
59
        $self->$field ? $self->_json->decode( $self->$field ) : undef;
60
    } catch {
61
        Koha::Exceptions::Object::BadValue->throw("Error reading JSON data: $_");
62
    };
63
}
64
65
=head3 set_encoded_json_field
66
67
    $object->set_encoded_json_field(
68
        {   data  => $data,
69
            field => 'field_name',
70
        }
71
    );
72
73
Sets a JSON string encoded representation of I<$data> into the object's I<field_name>
74
attribute.
75
76
=cut
77
78
sub set_encoded_json_field {
79
    my ( $self, $params ) = @_;
80
81
    Koha::Exceptions::MissingParameter->throw( parameter => 'field' )
82
        unless defined $params->{field};
83
84
    Koha::Exceptions::MissingParameter->throw( parameter => 'data' )
85
        unless exists $params->{data};
86
87
    my $field = $params->{field};
88
    my $data  = $params->{data};
89
90
    return try {
91
        $self->$field( $data ? $self->_json->encode($data) : undef );
92
    } catch {
93
        Koha::Exceptions::Object::BadValue->throw("Error reading JSON data: $_");
94
    };
95
}
96
97
=head2 Internal methods
98
99
=head3 _json
100
101
    my $JSON_object = $self->_json;
102
103
Returns a JSON object with utf8 disabled. Encoding to UTF-8 should be
104
done later.
105
106
=cut
107
108
sub _json {
109
    my ($self) = @_;
110
    $self->{_json} //= JSON->new->utf8(0);
111
    return $self->{_json};
112
}
113
114
=head1 AUTHOR
115
116
Tomas Cohen Arazi, E<lt>tomascohen@theke.ioE<gt>
117
118
=cut
119
120
1;
(-)a/t/db_dependent/Koha/Object/JSONField.t (-29 / +35 lines)
Lines 37-102 subtest 'decode_json_field() and set_encoded_json_field() tests' => sub { Link Here
37
37
38
    $schema->storage->txn_begin;
38
    $schema->storage->txn_begin;
39
39
40
    my $idp = $builder->build_object({ class => 'Koha::Auth::Identity::Providers' });
40
    my $idp = $builder->build_object( { class => 'Koha::Auth::Identity::Providers' } );
41
41
42
    my $data = { some => 'data' };
42
    my $data = { some => 'data' };
43
43
44
    $idp->set_encoded_json_field({ data => $data, field => 'config' });
44
    $idp->set_encoded_json_field( { data => $data, field => 'config' } );
45
45
46
    is_deeply( $idp->_json->decode($idp->config), $data, 'decode what we sent' );
46
    is_deeply( $idp->_json->decode( $idp->config ),              $data, 'decode what we sent' );
47
    is_deeply( $idp->decode_json_field({ field => 'config' }), $data, 'check with decode_json_field' );
47
    is_deeply( $idp->decode_json_field( { field => 'config' } ), $data, 'check with decode_json_field' );
48
48
49
    # Let's get some Unicode stuff into the game
49
    # Let's get some Unicode stuff into the game
50
    $data = { favorite_Chinese => [ '葑', '癱' ], latin_dancing => [ '¢', '¥', 'á', 'û' ] };
50
    $data = { favorite_Chinese => [ '葑', '癱' ], latin_dancing => [ '¢', '¥', 'á', 'û' ] };
51
    $idp->set_encoded_json_field({ data => $data, field => 'config' })->store;
51
    $idp->set_encoded_json_field( { data => $data, field => 'config' } )->store;
52
53
    $idp->discard_changes;    # refresh
54
    is_deeply( $idp->decode_json_field( { field => 'config' } ), $data, 'Deep compare with Unicode data' );
52
55
53
    $idp->discard_changes; # refresh
54
    is_deeply( $idp->decode_json_field({ field => 'config' }), $data, 'Deep compare with Unicode data' );
55
    # To convince you even more
56
    # To convince you even more
56
    is( ord( $idp->decode_json_field({ field => 'config' })->{favorite_Chinese}->[0] ), 33873, 'We still found Unicode \x8451' );
57
    is(
57
    is( ord( $idp->decode_json_field({ field => 'config' })->{latin_dancing}->[0] ), 162, 'We still found the equivalent of Unicode \x00A2' );
58
        ord( $idp->decode_json_field( { field => 'config' } )->{favorite_Chinese}->[0] ), 33873,
59
        'We still found Unicode \x8451'
60
    );
61
    is(
62
        ord( $idp->decode_json_field( { field => 'config' } )->{latin_dancing}->[0] ), 162,
63
        'We still found the equivalent of Unicode \x00A2'
64
    );
58
65
59
    # Testing with sending encoded data (which we normally shouldn't do)
66
    # Testing with sending encoded data (which we normally shouldn't do)
60
    my $utf8_data;
67
    my $utf8_data;
61
    foreach my $k ( 'favorite_Chinese', 'latin_dancing' ) {
68
    foreach my $k ( 'favorite_Chinese', 'latin_dancing' ) {
62
        foreach my $c ( @{$data->{$k}} ) {
69
        foreach my $c ( @{ $data->{$k} } ) {
63
            push @{$utf8_data->{$k}}, Encode::encode('UTF-8', $c);
70
            push @{ $utf8_data->{$k} }, Encode::encode( 'UTF-8', $c );
64
        }
71
        }
65
    }
72
    }
66
    $idp->set_encoded_json_field({ data => $utf8_data, field => 'config' })->store;
73
    $idp->set_encoded_json_field( { data => $utf8_data, field => 'config' } )->store;
67
    $idp->discard_changes; # refresh
74
    $idp->discard_changes;    # refresh
68
    is_deeply( $idp->decode_json_field({ field => 'config' }), $utf8_data, 'Deep compare with utf8_data' );
75
    is_deeply( $idp->decode_json_field( { field => 'config' } ), $utf8_data, 'Deep compare with utf8_data' );
76
69
    # Need more evidence?
77
    # Need more evidence?
70
    is( ord( $idp->decode_json_field({ field => 'config' })->{favorite_Chinese}->[0] ), 232, 'We still found a UTF8 encoded byte' ); # ord does not need substr here
78
    is(
79
        ord( $idp->decode_json_field( { field => 'config' } )->{favorite_Chinese}->[0] ), 232,
80
        'We still found a UTF8 encoded byte'
81
    );                        # ord does not need substr here
71
82
72
    # pathological use cases
83
    # pathological use cases
73
84
74
    throws_ok
85
    throws_ok { $idp->set_encoded_json_field( { data => $data } ); }
75
        { $idp->set_encoded_json_field({ data => $data }); }
86
    'Koha::Exceptions::MissingParameter',
76
        'Koha::Exceptions::MissingParameter',
77
        'Exception thrown on missing parameter';
87
        'Exception thrown on missing parameter';
78
    is( $@->parameter, 'field', 'Correct parameter reported (field)' );
88
    is( $@->parameter, 'field', 'Correct parameter reported (field)' );
79
89
80
    throws_ok
90
    throws_ok { $idp->set_encoded_json_field( { data => $data, field => undef } ); }
81
        { $idp->set_encoded_json_field({ data => $data, field => undef }); }
91
    'Koha::Exceptions::MissingParameter',
82
        'Koha::Exceptions::MissingParameter',
83
        'Exception thrown on missing parameter';
92
        'Exception thrown on missing parameter';
84
    is( $@->parameter, 'field', 'Correct parameter reported (field)' );
93
    is( $@->parameter, 'field', 'Correct parameter reported (field)' );
85
94
86
    throws_ok
95
    throws_ok { $idp->set_encoded_json_field( { field => 'something' } ); }
87
        { $idp->set_encoded_json_field({ field => 'something' }); }
96
    'Koha::Exceptions::MissingParameter',
88
        'Koha::Exceptions::MissingParameter',
89
        'Exception thrown on missing parameter';
97
        'Exception thrown on missing parameter';
90
    is( $@->parameter, 'data', 'Correct parameter reported (data)' );
98
    is( $@->parameter, 'data', 'Correct parameter reported (data)' );
91
99
92
    $idp->set_encoded_json_field({ data => undef, field => 'config' });
100
    $idp->set_encoded_json_field( { data => undef, field => 'config' } );
93
    is( $idp->config, undef, 'undef is undef' );
101
    is( $idp->config, undef, 'undef is undef' );
94
102
95
    # set invalid data
103
    # set invalid data
96
    $idp->config('{');
104
    $idp->config('{');
97
    throws_ok
105
    throws_ok { $idp->decode_json_field( { field => 'config' } ) }
98
        { $idp->decode_json_field({ field => 'config' }) }
106
    'Koha::Exceptions::Object::BadValue',
99
        'Koha::Exceptions::Object::BadValue',
100
        'Exception thrown';
107
        'Exception thrown';
101
    like( "$@", qr/Error reading JSON data/ );
108
    like( "$@", qr/Error reading JSON data/ );
102
109
103
- 

Return to bug 32370