view release on metacpan or search on metacpan
lib/GDPR/IAB/TCFv2/BitField.pm view on Meta::CPAN
sub TO_JSON {
my ($self, $filter_id) = @_;
if (defined $filter_id) {
my $val = $self->contains($filter_id);
if (!!$self->{options}->{json}->{compact}) {
return $val ? [$filter_id] : [];
}
my ($false, $true) = @{$self->{options}->{json}->{boolean_values}};
my $bool_val = $val ? $true : $false;
if (!!$self->{options}->{json}->{verbose}) {
return {$filter_id => $bool_val};
}
return $val ? {$filter_id => $true} : {};
}
if (!!$self->{options}->{json}->{compact}) {
return $self->all;
}
my $bitstring = unpack("B*", substr($self->{data}, $self->{offset} >> 3));
$bitstring = substr($bitstring, $self->{offset} & 7, $self->{max_id});
my ($false, $true) = @{$self->{options}->{json}->{boolean_values}};
if (!!$self->{options}->{json}->{verbose}) {
my %map = map { $_ => $false } 1 .. $self->{max_id};
while ($bitstring =~ /1/g) {
$map{pos($bitstring)} = $true;
}
return \%map;
}
my %map;
lib/GDPR/IAB/TCFv2/Parser.pm view on Meta::CPAN
croak 'missing gdpr consent string' unless defined $tc_string && length $tc_string;
my $segments = _decode_tc_string_segments($tc_string);
my $strict = !!$opts{strict};
my %options = (json => $opts{json} // {}, strict => $strict, reference_time => $opts{reference_time},);
$options{json}->{date_format} ||= DATE_FORMAT_ISO_8601;
$options{json}->{boolean_values} ||= [_json_false(), _json_true()];
if (exists $opts{prefetch}) {
my $prefetch = $opts{prefetch};
$prefetch = [$prefetch] if ref($prefetch) ne ref([]);
$options{prefetch} = $prefetch;
}
my $self = {
lib/GDPR/IAB/TCFv2/Parser.pm view on Meta::CPAN
return {map { @{$_} } grep { $verbose || $_->[1] } @data};
}
sub TO_JSON {
my $self = shift;
my %args = (@_ && ref $_[-1] eq 'HASH') ? %{pop @_} : @_;
my $filter_id = $args{vendor_id} // $self->{options}->{json}->{vendor_id};
my ($false, $true) = @{$self->{options}->{json}->{boolean_values}};
my $created = $self->_format_date($self->created);
my $last_updated = $self->_format_date($self->last_updated);
my $purpose_consents = $self->_format_json_subsection(
map { [
$_ => (
$filter_id
? $self->is_vendor_allowed_for_any_basis($filter_id, $_)
: $self->_safe_is_purpose_consent_allowed($_)
lib/GDPR/IAB/TCFv2/Parser.pm view on Meta::CPAN
or
use GDPR::IAB::TCFv2::Parser;
my $consent = GDPR::IAB::TCFv2::Parser->Parse(
'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA.argAC0gAAAAAAAAAAAA',
json => {
verbose => 0,
compact => 1,
use_epoch => 0,
boolean_values => [ 0, 1 ],
date_format => '%Y%m%d', # yyymmdd
},
strict => 1,
prefetch => 284,
);
Parse may receive an optional hash with the following parameters:
=over
lib/GDPR/IAB/TCFv2/Parser.pm view on Meta::CPAN
C<purpose/consents>, C<purpose/legitimate_interests>, C<vendor/consents> and C<vendor/legitimate_interests>. See L</TO_JSON> for more details.
=item *
C<use_epoch> changes the json encode. By default we format the C<created> and C<last_updated> are converted to string using
L<ISO_8601|https://en.wikipedia.org/wiki/ISO_8601>. With C<use_epoch> we will return the unix epoch in seconds.
See L</TO_JSON> for more details.
=item *
C<boolean_values> if present, expects an arrayref if two elements: the C<false> and the C<true> values to be used in json encoding.
If omit, we will try to use C<JSON::false> and C<JSON::true> if the package L<JSON> is available, else we will fallback to C<0> and C<1>.
=item *
C<date_format> if present accepts two kinds of value: an C<string> (to be used on C<POSIX::strftime>) or a code reference to a subroutine that
will be called with two arguments: epoch in seconds and nanoseconds. If omitted the format L<ISO_8601|https://en.wikipedia.org/wiki/ISO_8601> will be used
except if the option C<use_epoch> is true.
=item *
lib/GDPR/IAB/TCFv2/PublisherTC.pm view on Meta::CPAN
consents => $self->_format_json_subsection(\%custom_purpose_consents, $self->{num_custom_purposes}),
legitimate_interests =>
$self->_format_json_subsection(\%custom_purpose_legitimate_interests, $self->{num_custom_purposes}),
},
};
}
sub _format_json_subsection {
my ($self, $data, $max) = @_;
my ($false, $true) = @{$self->{options}->{json}->{boolean_values}};
if (!!$self->{options}->{json}->{compact}) {
return [grep { $data->{$_} } 1 .. $max,];
}
my $verbose = !!$self->{options}->{json}->{verbose};
return $data if $verbose;
return {map { $_ => $true } grep { $data->{$_} } keys %{$data}};
lib/GDPR/IAB/TCFv2/RangeSection.pm view on Meta::CPAN
sub TO_JSON {
my ($self, $filter_id) = @_;
if (defined $filter_id) {
my $val = $self->contains($filter_id);
if (!!$self->{options}->{json}->{compact}) {
return $val ? [$filter_id] : [];
}
my ($false, $true) = @{$self->{options}->{json}->{boolean_values}};
my $bool_val = $val ? $true : $false;
if (!!$self->{options}->{json}->{verbose}) {
return {$filter_id => $bool_val};
}
return $val ? {$filter_id => $true} : {};
}
return $self->all if !!$self->{options}->{json}->{compact};
my ($false, $true) = @{$self->{options}->{json}->{boolean_values}};
my %map;
if (!!$self->{options}->{json}->{verbose}) {
%map = map { $_ => $false } 1 .. $self->{max_id};
}
# Direct hash-element assignment per range; the previous form
# `%map = ( %map, map { $_ => $true } ... )` re-copied the running
# hash into a list every iteration, accidentally O(n^2) over the
# number of vendor IDs already accumulated.
t/02-json-bitfield.t view on Meta::CPAN
subtest "bitfield" => sub {
subtest "should convert data to json using compact flag and 0/1 as booleans" => sub {
subtest "should convert data to json using yyyymmdd as date format" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA',
json => {
verbose => 0,
compact => 1,
use_epoch => 0,
boolean_values => [0, 1],
date_format => '%Y%m%d', # yyymmdd
},
);
my $got = $consent->TO_JSON();
my $expected = _fixture_bitfield_compact(created => 20081207, last_updated => 20120110,);
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
subtest "should convert data to json using epoch date format" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA',
json => {verbose => 0, compact => 1, use_epoch => 1, boolean_values => [0, 1],},
);
my $got = $consent->TO_JSON();
my $expected = _fixture_bitfield_compact(created => 1228644257, last_updated => 1326215413,);
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
done_testing;
};
subtest "should convert data to json using default (non-compact) and 0/1 as booleans" => sub {
subtest "default non verbose, date as iso 8601" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA',
json => {verbose => 0, compact => 0, use_epoch => 0, boolean_values => [0, 1],},
);
ok $consent->vendor_consent(27);
my $got = $consent->TO_JSON();
my $expected = _fixture_bitfield_default();
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
subtest "default verbose, date as iso 8601" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA',
json => {verbose => 1, compact => 0, use_epoch => 0, boolean_values => [0, 1],},
);
ok $consent->vendor_consent(27);
my $got = $consent->TO_JSON();
my $expected = _fixture_bitfield_verbose();
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
done_testing;
};
subtest "publisher section" => sub {
subtest "publisher section without publisher_tc" => sub {
my $consent = GDPR::IAB::TCFv2->Parse('COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},);
my $got = $consent->TO_JSON;
my $expected = {"publisher" => {"restrictions" => {"7" => {"32" => 1}}}};
is_deeply $got->{publisher}, $expected->{publisher}, "must return the same publisher restriction section";
done_testing;
};
subtest "publisher section with publisher_tc" => sub {
subtest "without custom purposes" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA.argAC0gAAAAAAAAAAAA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},
);
my $got = $consent->TO_JSON;
my $expected = {
"publisher" => {
"consents" => [2, 4, 6, 8, 9, 10],
"legitimate_interests" => [2, 4, 5, 7, 10],
"custom_purposes" => {"consents" => [], "legitimate_interests" => [],},
"restrictions" => {"7" => {"32" => 1}}
}
};
is_deeply $got->{publisher}, $expected->{publisher}, "must return the same publisher restriction section";
done_testing;
};
subtest "with custom purposes" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA.YAAAAAAAAXA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},
);
my $got = $consent->TO_JSON;
my $expected = {
"publisher" => {
"consents" => [],
"legitimate_interests" => [],
"custom_purposes" => {"consents" => [1, 2], "legitimate_interests" => [1],},
"restrictions" => {"7" => {"32" => 1}}
}
t/02-json-bitfield.t view on Meta::CPAN
done_testing;
};
done_testing;
};
subtest "TO_JSON method should return the same hashref " => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'CLcVDxRMWfGmWAVAHCENAXCkAKDAADnAABRgA5mdfCKZuYJez-NQm0TBMYA4oCAAGQYIAAAAAAEAIAEgAA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},
);
my $got1 = $consent->TO_JSON();
my $got2 = $consent->TO_JSON();
is_deeply $got1, $got2, "must return the same hashref";
done_testing;
};
t/03-json-range.t view on Meta::CPAN
subtest "range" => sub {
subtest "should convert data to json using compact flag and 0/1 as booleans" => sub {
subtest "should convert data to json using yyyymmdd as date format" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COyfVVoOyfVVoADACHENAwCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAnSAMABgAFkAgQCDASeAmYBOgAA',
json => {
verbose => 0,
compact => 1,
use_epoch => 0,
boolean_values => [0, 1],
date_format => '%Y%m%d', # yyymmdd
},
);
my $got = $consent->TO_JSON();
my $expected = _fixture_range_compact(created => 20200427, last_updated => 20200427,);
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
subtest "should convert data to json using epoch date format" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COyfVVoOyfVVoADACHENAwCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAnSAMABgAFkAgQCDASeAmYBOgAA',
json => {verbose => 0, compact => 1, use_epoch => 1, boolean_values => [0, 1],},
);
my $got = $consent->TO_JSON();
my $expected = _fixture_range_compact(created => 1587946020, last_updated => 1587946020,);
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
done_testing;
};
subtest "should convert data to json using default (non-compact) and 0/1 as booleans" => sub {
subtest "default non verbose, date as iso 8601" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COyfVVoOyfVVoADACHENAwCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAnSAMABgAFkAgQCDASeAmYBOgAA',
json => {verbose => 0, compact => 0, use_epoch => 0, boolean_values => [0, 1],},
);
ok $consent->vendor_consent(23);
my $got = $consent->TO_JSON();
my $expected = _fixture_range_default();
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
subtest "default verbose, date as iso 8601" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COyfVVoOyfVVoADACHENAwCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAnSAMABgAFkAgQCDASeAmYBOgAA',
json => {verbose => 1, compact => 0, use_epoch => 0, boolean_values => [0, 1],},
);
ok $consent->vendor_consent(23);
my $got = $consent->TO_JSON();
my $expected = _fixture_range_verbose();
is_deeply $got, $expected, "must return the json hashref";
done_testing;
};
done_testing;
};
subtest "publisher section" => sub {
subtest "publisher section without publisher_tc" => sub {
my $consent = GDPR::IAB::TCFv2->Parse('COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},);
my $got = $consent->TO_JSON;
my $expected = {"publisher" => {"restrictions" => {"7" => {"32" => 1}}}};
is_deeply $got->{publisher}, $expected->{publisher}, "must return the same publisher restriction section";
done_testing;
};
subtest "publisher section with publisher_tc" => sub {
subtest "without custom purposes" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA.argAC0gAAAAAAAAAAAA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},
);
my $got = $consent->TO_JSON;
my $expected = {
"publisher" => {
"consents" => [2, 4, 6, 8, 9, 10],
"legitimate_interests" => [2, 4, 5, 7, 10],
"custom_purposes" => {"consents" => [], "legitimate_interests" => [],},
"restrictions" => {"7" => {"32" => 1}}
}
};
is_deeply $got->{publisher}, $expected->{publisher}, "must return the same publisher restriction section";
done_testing;
};
subtest "with custom purposes" => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COwAdDhOwAdDhN4ABAENAPCgAAQAAv___wAAAFP_AAp_4AI6ACACAA.YAAAAAAAAXA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},
);
my $got = $consent->TO_JSON;
my $expected = {
"publisher" => {
"consents" => [],
"legitimate_interests" => [],
"custom_purposes" => {"consents" => [1, 2], "legitimate_interests" => [1],},
"restrictions" => {"7" => {"32" => 1}}
}
t/03-json-range.t view on Meta::CPAN
done_testing;
};
done_testing;
};
subtest "TO_JSON method should return the same hashref " => sub {
my $consent = GDPR::IAB::TCFv2->Parse(
'COyfVVoOyfVVoADACHENAwCAAAAAAAAAAAAAE5QBgALgAqgD8AQACSwEygJyAnSAMABgAFkAgQCDASeAmYBOgAA',
json => {verbose => 0, compact => 1, use_epoch => 0, boolean_values => [0, 1],},
);
my $got1 = $consent->TO_JSON();
my $got2 = $consent->TO_JSON();
is_deeply $got1, $got2, "must return the same hashref";
done_testing;
};
t/07-golden.t view on Meta::CPAN
# Important for memory-tight smokers (e.g. OmniOS/Solaris on threaded perl).
subtest "String $count" => sub {
if ($entry->{expect_failure}) {
throws_ok { GDPR::IAB::TCFv2->Parse($tc_string); }
qr/\Q$entry->{error_match}\E/, "should fail as expected";
return;
}
my $consent;
lives_ok {
$consent = GDPR::IAB::TCFv2->Parse($tc_string, json => {boolean_values => [JSON::PP::false, JSON::PP::true]});
}
"parsed successfully";
return unless $consent;
my $tests = $entry->{tests};
is_deeply_with_diag($consent->TO_JSON, $tests->{to_json}, "TO_JSON match");
is($consent->version, $tests->{metadata}->{version}, "version match");
t/08-golden-validator.t view on Meta::CPAN
# Parse-failure entries don't carry a validator section.
next if $entry->{expect_failure};
my $expected = $entry->{tests}{validator};
next unless $expected; # forward compat with pre-Phase-2 corpora
$checked_count++;
my $consent = eval {
GDPR::IAB::TCFv2->Parse($entry->{tc_string}, json => {boolean_values => [JSON::PP::false, JSON::PP::true]});
};
if (my $err = $@) {
fail("String $count: parse failed unexpectedly: $err");
next;
}
for my $entry_v (@validators) {
my $name = $entry_v->{name};
my $expected_sc = $expected->{$name};
t/generate_golden.pl view on Meta::CPAN
open my $ifh, '<', $input_file or die "Could not open $input_file: $!";
my $ofh = IO::Compress::Gzip->new($output_file, Level => 9) or die "Could not open $output_file: $GzipError";
my $json = JSON::PP->new->canonical->utf8;
while (my $line = <$ifh>) {
chomp $line;
next unless $line;
eval {
my $consent = GDPR::IAB::TCFv2->Parse($line, json => {boolean_values => [JSON::PP::false, JSON::PP::true]});
my $data = {
tc_string => $line,
expect_failure => JSON::PP::false,
tests => {
to_json => $consent->TO_JSON,
metadata =>
{version => $consent->version, cmp_id => $consent->cmp_id, created_epoch => scalar($consent->created),},
sampling => {
purpose_1_consent => $consent->is_purpose_consent_allowed(1) ? JSON::PP::true : JSON::PP::false,