# 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::PhabBugz::Project; use 5.10.1; use Moo; use Scalar::Util qw(blessed); use Types::Standard -all; use Type::Utils; use Type::Params qw( compile ); use Bugzilla::Error; use Bugzilla::Util qw(trim); use Bugzilla::Extension::PhabBugz::User; use Bugzilla::Extension::PhabBugz::Types qw(:types); use Bugzilla::Extension::PhabBugz::Util qw(request); ######################### # Initialization # ######################### has id => (is => 'ro', isa => Int); has phid => (is => 'ro', isa => Str); has type => (is => 'ro', isa => Str); has name => (is => 'ro', isa => Str); has description => (is => 'ro', isa => Maybe [Str]); has creation_ts => (is => 'ro', isa => Str); has modification_ts => (is => 'ro', isa => Str); has view_policy => (is => 'ro', isa => Str); has edit_policy => (is => 'ro', isa => Str); has join_policy => (is => 'ro', isa => Str); has members_raw => (is => 'ro', isa => ArrayRef [Dict [phid => Str]]); has members => (is => 'lazy', isa => ArrayRef [PhabUser]); my $Invocant = class_type {class => __PACKAGE__}; sub new_from_query { my ($class, $params) = @_; my $data = {queryKey => 'all', attachments => {members => 1}, constraints => $params}; my $result = request('project.search', $data); if (exists $result->{result}{data} && @{$result->{result}{data}}) { # If name is used as a query param, we need to loop through and look # for exact match as Conduit will tokenize the name instead of doing # exact string match :( If name is not used, then return first one. if (exists $params->{name}) { foreach my $item (@{$result->{result}{data}}) { next if $item->{fields}{name} ne $params->{name}; return $class->new($item); } } else { return $class->new($result->{result}{data}[0]); } } } sub BUILDARGS { my ($class, $params) = @_; $params->{name} = $params->{fields}->{name}; $params->{description} = $params->{fields}->{description}; $params->{creation_ts} = $params->{fields}->{dateCreated}; $params->{modification_ts} = $params->{fields}->{dateModified}; $params->{view_policy} = $params->{fields}->{policy}->{view}; $params->{edit_policy} = $params->{fields}->{policy}->{edit}; $params->{join_policy} = $params->{fields}->{policy}->{join}; $params->{members_raw} = $params->{attachments}->{members}->{members}; delete $params->{fields}; delete $params->{attachments}; return $params; } # { # "data": [ # { # "id": 1, # "type": "PROJ", # "phid": "PHID-PROJ-pfssn7lndryddv7hbx4i", # "fields": { # "name": "bmo-core-security", # "slug": "bmo-core-security", # "milestone": null, # "depth": 0, # "parent": null, # "icon": { # "key": "group", # "name": "Group", # "icon": "fa-users" # }, # "color": { # "key": "red", # "name": "Red" # }, # "dateCreated": 1500403964, # "dateModified": 1505248862, # "policy": { # "view": "secure-revision", # "edit": "secure-revision", # "join": "secure-revision" # }, # "description": "BMO Security Group for core-security" # }, # "attachments": { # "members": { # "members": [ # { # "phid": "PHID-USER-23ia7vewbjgcqahewncu" # }, # { # "phid": "PHID-USER-uif2miph2poiehjeqn5q" # } # ] # } # } # } # ], # "maps": { # "slugMap": {} # }, # "query": { # "queryKey": null # }, # "cursor": { # "limit": 100, # "after": null, # "before": null, # "order": null # } # } ######################### # Modification # ######################### sub create { state $check = compile( $Invocant | ClassName, Dict [ name => Str, description => Str, view_policy => Str, edit_policy => Str, join_policy => Str, ] ); my ($class, $params) = $check->(@_); my $name = trim($params->{name}); my $description = $params->{description}; my $view_policy = $params->{view_policy}; my $edit_policy = $params->{edit_policy}; my $join_policy = $params->{join_policy}; my $data = { transactions => [ {type => 'name', value => $name}, {type => 'description', value => $description}, {type => 'edit', value => $edit_policy}, {type => 'join', value => $join_policy}, {type => 'view', value => $view_policy}, {type => 'icon', value => 'group'}, {type => 'color', value => 'red'} ] }; my $result = request('project.edit', $data); return $class->new_from_query({phids => [$result->{result}{object}{phid}]}); } sub update { my ($self) = @_; my $data = {objectIdentifier => $self->phid, transactions => []}; if ($self->{set_name}) { push(@{$data->{transactions}}, {type => 'name', value => $self->{set_name}}); } if ($self->{set_description}) { push( @{$data->{transactions}}, {type => 'description', value => $self->{set_description}} ); } if ($self->{set_members}) { push( @{$data->{transactions}}, {type => 'members.set', value => $self->{set_members}} ); } else { if ($self->{add_members}) { push( @{$data->{transactions}}, {type => 'members.add', value => $self->{add_members}} ); } if ($self->{remove_members}) { push( @{$data->{transactions}}, {type => 'members.remove', value => $self->{remove_members}} ); } } if ($self->{set_policy}) { foreach my $name ("view", "edit") { next unless $self->{set_policy}->{$name}; push( @{$data->{transactions}}, {type => $name, value => $self->{set_policy}->{$name}} ); } } if ($self->{add_projects}) { push( @{$data->{transactions}}, {type => 'projects.add', value => $self->{add_projects}} ); } if ($self->{remove_projects}) { push( @{$data->{transactions}}, {type => 'projects.remove', value => $self->{remove_projects}} ); } my $result = request('project.edit', $data); return $result; } ######################### # Mutators # ######################### sub set_name { my ($self, $name) = @_; $name = trim($name); $self->{set_name} = $name; } sub set_description { my ($self, $description) = @_; $description = trim($description); $self->{set_description} = $description; } sub add_member { my ($self, $member) = @_; $self->{add_members} ||= []; my $member_phid = blessed $member ? $member->phid : $member; push(@{$self->{add_members}}, $member_phid); } sub remove_member { my ($self, $member) = @_; $self->{remove_members} ||= []; my $member_phid = blessed $member ? $member->phid : $member; push(@{$self->{remove_members}}, $member_phid); } sub set_members { my ($self, $members) = @_; $self->{set_members} = [map { blessed $_ ? $_->phid : $_ } @$members]; } sub set_policy { my ($self, $name, $policy) = @_; $self->{set_policy} ||= {}; $self->{set_policy}->{$name} = $policy; } ############ # Builders # ############ sub _build_members { my ($self) = @_; return [] unless $self->members_raw; my @phids; foreach my $member (@{$self->members_raw}) { push(@phids, $member->{phid}); } return [] if !@phids; return Bugzilla::Extension::PhabBugz::User->match({phids => \@phids}); } 1;