diff options
Diffstat (limited to 'extensions/Push/lib/Serialise.pm')
-rw-r--r-- | extensions/Push/lib/Serialise.pm | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/extensions/Push/lib/Serialise.pm b/extensions/Push/lib/Serialise.pm new file mode 100644 index 000000000..94f33c754 --- /dev/null +++ b/extensions/Push/lib/Serialise.pm @@ -0,0 +1,318 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +# This Source Code Form is "Incompatible With Secondary Licenses", as +# defined by the Mozilla Public License, v. 2.0. + +package Bugzilla::Extension::Push::Serialise; + +use strict; +use warnings; + +use Bugzilla::Constants; +use Bugzilla::Extension::Push::Util; +use Bugzilla::Version; + +use Scalar::Util 'blessed'; +use JSON (); + +my $_instance; +sub instance { + $_instance ||= Bugzilla::Extension::Push::Serialise->_new(); + return $_instance; +} + +sub _new { + my ($class) = @_; + my $self = {}; + bless($self, $class); + return $self; +} + +# given an object, serliase to a hash +sub object_to_hash { + my ($self, $object, $is_shallow) = @_; + + my $method = lc(blessed($object)); + $method =~ s/::/_/g; + $method =~ s/^bugzilla//; + return unless $self->can($method); + (my $name = $method) =~ s/^_//; + + # check for a cached hash + my $cache = Bugzilla->request_cache; + my $cache_id = "push." . ($is_shallow ? 'shallow.' : 'deep.') . $object; + if (exists($cache->{$cache_id})) { + return wantarray ? ($cache->{$cache_id}, $name) : $cache->{$cache_id}; + } + + # call the right method to serialise to a hash + my $rh = $self->$method($object, $is_shallow); + + # store in cache + if ($cache_id) { + $cache->{$cache_id} = $rh; + } + + return wantarray ? ($rh, $name) : $rh; +} + +# given a changes hash, return an event hash +sub changes_to_event { + my ($self, $changes) = @_; + + my $event = {}; + + # create common (created and modified) fields + $event->{'user'} = $self->object_to_hash(Bugzilla->user); + my $timestamp = + $changes->{'timestamp'} + || Bugzilla->dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)'); + $event->{'time'} = datetime_to_timestamp($timestamp); + + foreach my $change (@{$changes->{'changes'}}) { + if (exists $change->{'field'}) { + # map undef to emtpy + hash_undef_to_empty($change); + + # custom_fields change from undef to empty, ignore these changes + return if ($change->{'added'} || "") eq "" && + ($change->{'removed'} || "") eq ""; + + # use saner field serialisation + my $field = $change->{'field'}; + $change->{'field'} = $field; + + if ($field eq 'priority' || $field eq 'target_milestone') { + $change->{'added'} = _select($change->{'added'}); + $change->{'removed'} = _select($change->{'removed'}); + + } elsif ($field =~ /^cf_/) { + $change->{'added'} = _custom_field($field, $change->{'added'}); + $change->{'removed'} = _custom_field($field, $change->{'removed'}); + } + + $event->{'changes'} = [] unless exists $event->{'changes'}; + push @{$event->{'changes'}}, $change; + } + } + + return $event; +} + +# bugzilla returns '---' or '--' for single-select fields that have no value +# selected. it makes more sense to return an empty string. +sub _select { + my ($value) = @_; + return '' if $value eq '---' or $value eq '--'; + return $value; +} + +# return an object which serialises to a json boolean, but still acts as a perl +# boolean +sub _boolean { + my ($value) = @_; + return $value ? JSON::true : JSON::false; +} + +sub _string { + my ($value) = @_; + return defined($value) ? $value : ''; +} + +sub _time { + my ($value) = @_; + return defined($value) ? datetime_to_timestamp($value) : undef; +} + +sub _integer { + my ($value) = @_; + return defined($value) ? $value + 0 : undef; +} + +sub _array { + my ($value) = @_; + return defined($value) ? $value : []; +} + +sub _custom_field { + my ($field, $value) = @_; + $field = Bugzilla::Field->new({ name => $field }) unless blessed $field; + + if ($field->type == FIELD_TYPE_DATETIME) { + return _time($value); + + } elsif ($field->type == FIELD_TYPE_SINGLE_SELECT) { + return _select($value); + + } elsif ($field->type == FIELD_TYPE_MULTI_SELECT) { + return _array($value); + + } else { + return _string($value); + } +} + +# +# class mappings +# automatically derrived from the class name +# Bugzilla::Bug --> _bug, Bugzilla::User --> _user, etc +# + +sub _bug { + my ($self, $bug) = @_; + + my $version = $bug->can('version_obj') + ? $bug->version_obj + : Bugzilla::Version->new({ name => $bug->version, product => $bug->product_obj }); + + my $milestone; + if (_select($bug->target_milestone) ne '') { + $milestone = $bug->can('target_milestone_obj') + ? $bug->target_milestone_obj + : Bugzilla::Milestone->new({ name => $bug->target_milestone, product => $bug->product_obj }); + } + + my $status = $bug->can('status_obj') + ? $bug->status_obj + : Bugzilla::Status->new({ name => $bug->bug_status }); + + my $rh = { + id => _integer($bug->bug_id), + alias => _string($bug->alias), + assigned_to => $self->_user($bug->assigned_to), + classification => _string($bug->classification), + component => $self->_component($bug->component_obj), + creation_time => _time($bug->creation_ts || $bug->delta_ts), + flags => (mapr { $self->_flag($_) } $bug->flags), + is_private => _boolean(!is_public($bug)), + keywords => (mapr { _string($_->name) } $bug->keyword_objects), + last_change_time => _time($bug->delta_ts), + operating_system => _string($bug->op_sys), + platform => _string($bug->rep_platform), + priority => _select($bug->priority), + product => $self->_product($bug->product_obj), + qa_contact => $self->_user($bug->qa_contact), + reporter => $self->_user($bug->reporter), + resolution => _string($bug->resolution), + severity => _string($bug->bug_severity), + status => $self->_status($status), + summary => _string($bug->short_desc), + target_milestone => $self->_milestone($milestone), + url => _string($bug->bug_file_loc), + version => $self->_version($version), + whiteboard => _string($bug->status_whiteboard), + }; + + # add custom fields + my @custom_fields = Bugzilla->active_custom_fields( + { product => $bug->product_obj, component => $bug->component_obj }); + foreach my $field (@custom_fields) { + my $name = $field->name; + $rh->{$name} = _custom_field($field, $bug->$name); + } + + return $rh; +} + +sub _user { + my ($self, $user) = @_; + return undef unless $user; + return { + id => _integer($user->id), + login => _string($user->login), + real_name => _string($user->name), + }; +} + +sub _component { + my ($self, $component) = @_; + return { + id => _integer($component->id), + name => _string($component->name), + }; +} + +sub _attachment { + my ($self, $attachment, $is_shallow) = @_; + my $rh = { + id => _integer($attachment->id), + content_type => _string($attachment->contenttype), + creation_time => _time($attachment->attached), + description => _string($attachment->description), + file_name => _string($attachment->filename), + flags => (mapr { $self->_flag($_) } $attachment->flags), + is_obsolete => _boolean($attachment->isobsolete), + is_patch => _boolean($attachment->ispatch), + is_private => _boolean(!is_public($attachment)), + last_change_time => _time($attachment->modification_time), + }; + if (!$is_shallow) { + $rh->{bug} = $self->_bug($attachment->bug); + } + return $rh; +} + +sub _comment { + my ($self, $comment, $is_shallow) = @_; + my $rh = { + id => _integer($comment->bug_id), + body => _string($comment->body), + creation_time => _time($comment->creation_ts), + is_private => _boolean($comment->is_private), + number => _integer($comment->count), + }; + if (!$is_shallow) { + $rh->{bug} = $self->_bug($comment->bug); + } + return $rh; +} + +sub _product { + my ($self, $product) = @_; + return { + id => _integer($product->id), + name => _string($product->name), + }; +} + +sub _flag { + my ($self, $flag) = @_; + my $rh = { + id => _integer($flag->id), + name => _string($flag->type->name), + value => _string($flag->status), + }; + if ($flag->requestee) { + $rh->{'requestee'} = $self->_user($flag->requestee); + } + return $rh; +} + +sub _version { + my ($self, $version) = @_; + return { + id => _integer($version->id), + name => _string($version->name), + }; +} + +sub _milestone { + my ($self, $milestone) = @_; + return undef unless $milestone; + return { + id => _integer($milestone->id), + name => _string($milestone->name), + }; +} + +sub _status { + my ($self, $status) = @_; + return { + id => _integer($status->id), + name => _string($status->name), + }; +} + +1; |