[Bps-public-commit] RT-Extension-NHD branch, master, updated. 30c8a6128c733fb54dccbdea2f576ded80e2e136

Ruslan Zakirov ruz at bestpractical.com
Tue Nov 8 16:21:57 EST 2011


The branch, master has been updated
       via  30c8a6128c733fb54dccbdea2f576ded80e2e136 (commit)
       via  501ee7ff3bdee4d104b52ce394918dfe232f215c (commit)
       via  d2fecbe1ff31228c3c409abde7f94b6d20a1c067 (commit)
       via  5f48aff6302916345369ddaee5fe655ed89b7699 (commit)
       via  6afb2fb2b2c96f8c8eeb757b76d42df5447011b9 (commit)
       via  358d77dd25a3cd657875f57764361e49d940e242 (commit)
       via  746f9bdf8804757a51be6b34a6a8ab94e4a585c1 (commit)
       via  fe51dcc2bdc16c907c3e27ad63a42effc9fab9d9 (commit)
      from  fce47f20858fff5b0ffa8c66f54333e84a90608d (commit)

Summary of changes:
 META.yml                                           |    2 +-
 Makefile.PL                                        |    6 +-
 README                                             |   11 +
 TODO                                               |    7 +
 etc/schema.mysql                                   |    1 +
 html/Admin/Tools/NHD/Create.html                   |   14 +-
 html/Admin/Tools/NHD/Modify.html                   |   24 ++
 html/NoAuth/NHD/1.0/agreements/dhandler            |   54 ++----
 html/NoAuth/NHD/1.0/autohandler                    |    6 +-
 html/NoAuth/NHD/1.0/tickets/dhandler               |   71 ++++++
 inc/Module/Install/ReadmeFromPod.pm                |   48 ++++
 lib/RT/Extension/{NHD.pm => NetworkedHelpDesk.pm}  |   53 ++++-
 .../Extension/{NHD => NetworkedHelpDesk}/Test.pm   |   22 +-
 .../{NHD => NetworkedHelpDesk}/Test/Web.pm         |    6 +-
 lib/RT/NHD/Agreement.pm                            |   14 +-
 lib/RT/NHD/Ticket.pm                               |  227 ++++++++++++++++++++
 t/api/agreement.t                                  |   10 +-
 t/rest/agreement.t                                 |    8 +-
 t/rest/ticket.t                                    |   91 ++++++++
 t/web/agreement.t                                  |    6 +-
 20 files changed, 594 insertions(+), 87 deletions(-)
 create mode 100644 README
 create mode 100644 html/NoAuth/NHD/1.0/tickets/dhandler
 create mode 100644 inc/Module/Install/ReadmeFromPod.pm
 rename lib/RT/Extension/{NHD.pm => NetworkedHelpDesk.pm} (60%)
 rename lib/RT/Extension/{NHD => NetworkedHelpDesk}/Test.pm (69%)
 rename lib/RT/Extension/{NHD => NetworkedHelpDesk}/Test/Web.pm (67%)
 create mode 100644 lib/RT/NHD/Ticket.pm
 create mode 100644 t/rest/ticket.t

- Log -----------------------------------------------------------------
commit fe51dcc2bdc16c907c3e27ad63a42effc9fab9d9
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Nov 4 14:36:08 2011 +0400

    rename to ::Extension::NetworkedHelpDesk

diff --git a/META.yml b/META.yml
index e093b57..5b76b1c 100644
--- a/META.yml
+++ b/META.yml
@@ -13,7 +13,7 @@ license: gpl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
   version: 1.4
-name: RT-Extension-NHD
+name: RT-Extension-NetworkedHelpDesk
 no_index:
   directory:
     - etc
diff --git a/Makefile.PL b/Makefile.PL
index eff40a1..58039aa 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,7 +1,7 @@
 use inc::Module::Install;
 
-RTx('RT-Extension-NHD');
-all_from('lib/RT/Extension/NHD.pm');
-#readme_from('lib/RT/Extension/NHD.pm');
+RTx('RT-Extension-NetworkedHelpDesk');
+all_from('lib/RT/Extension/NetworkedHelpDesk.pm');
+readme_from('lib/RT/Extension/NetworkedHelpDesk.pm');
 
 WriteAll;
\ No newline at end of file
diff --git a/html/NoAuth/NHD/1.0/agreements/dhandler b/html/NoAuth/NHD/1.0/agreements/dhandler
index e03c736..8c57062 100644
--- a/html/NoAuth/NHD/1.0/agreements/dhandler
+++ b/html/NoAuth/NHD/1.0/agreements/dhandler
@@ -8,52 +8,52 @@ $status => undef
 </%ARGS>
 <%INIT>
 my $duuid = $m->dhandler_arg;
-unless ( RT::Extension::NHD->CheckUUID( $duuid ) ) {
-    return RT::Extension::NHD->BadWebRequest(
+unless ( RT::Extension::NetworkedHelpDesk->CheckUUID( $duuid ) ) {
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
         'Unprocessable Entity',
         "Value in the URI isn't a UUID"
     );
 }
 
-my $action = RT::Extension::NHD->WebRequestAction;
-return RT::Extension::NHD->BadWebRequest('Method Not Allowed')
+my $action = RT::Extension::NetworkedHelpDesk->WebRequestAction;
+return RT::Extension::NetworkedHelpDesk->BadWebRequest('Method Not Allowed')
     unless $action;
 
 if ( $action ne 'create' ) {
     $ARGS{'uuid'} = $uuid ||= $duuid;
 } elsif ( ($uuid||'') ne $duuid ) {
-    return RT::Extension::NHD->BadWebRequest(
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
         'Unprocessable Entity',
         "UUIDs in the URI and body don't match"
     );
 }
 
 my $token = $r->headers_in->{'X-Ticket-Sharing-Token'};
-return RT::Extension::NHD->BadWebRequest('Unauthorized')
+return RT::Extension::NetworkedHelpDesk->BadWebRequest('Unauthorized')
     unless $token;
 
 my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
 $agreement->Load( $uuid );
 
 if ( $action eq 'create' && $agreement->id ) {
-    return RT::Extension::NHD->BadWebRequest(
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
         'Unprocessable Entity',
         "Couldn't create an agreement: record already exists"
     );
 }
 elsif ( $action ne 'create' ) {
-    return RT::Extension::NHD->BadWebRequest('Not Found')
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest('Not Found')
         unless $agreement->id;
 
     if ( $token ne join ':', $agreement->UUID, $agreement->AccessKey ) {
-        return RT::Extension::NHD->BadWebRequest('Forbidden')
+        return RT::Extension::NetworkedHelpDesk->BadWebRequest('Forbidden')
     }
 }
 
 my %data = %{ $agreement->FromJSON( \%ARGS ) };
 
 my ($user, $msg) = $agreement->LoadOrCreateUser( %data );
-return RT::Extension::NHD->BadWebRequest(
+return RT::Extension::NetworkedHelpDesk->BadWebRequest(
     'Unprocessable Entity',
     "Couldn't create user: $msg"
 ) unless $user;
@@ -63,26 +63,26 @@ $agreement->CurrentUser( RT::CurrentUser->new( $user ) );
 if ( $agreement->id ) {
 
     if ( $action eq 'show' ) {
-        return RT::Extension::NHD->WebSendJSON( $agreement->ForJSON );
+        return RT::Extension::NetworkedHelpDesk->WebSendJSON( $agreement->ForJSON );
     }
     elsif ( $action eq 'update' ) {
         my ($status, $msg) = $agreement->Update( %data );
         unless ( $status ) {
             RT->Logger->error("Couldn't update NHD agreement: $msg");
-            return RT::Extension::NHD->BadWebRequest('Unprocessable Entity');
+            return RT::Extension::NetworkedHelpDesk->BadWebRequest('Unprocessable Entity');
         }
-        return RT::Extension::NHD->GoodWebRequest;
+        return RT::Extension::NetworkedHelpDesk->GoodWebRequest;
     }
     else {
-        return RT::Extension::NHD->BadWebRequest;
+        return RT::Extension::NetworkedHelpDesk->BadWebRequest;
     }
 }
 else {
     my ($status, $msg) = $agreement->Create( %data );
     unless ( $status ) {
         RT->Logger->error("Couldn't create NHD agreement: $msg");
-        return RT::Extension::NHD->BadWebRequest('Unprocessable Entity');
+        return RT::Extension::NetworkedHelpDesk->BadWebRequest('Unprocessable Entity');
     }
-    return RT::Extension::NHD->GoodWebRequest('Created');
+    return RT::Extension::NetworkedHelpDesk->GoodWebRequest('Created');
 }
 </%INIT>
\ No newline at end of file
diff --git a/html/NoAuth/NHD/1.0/autohandler b/html/NoAuth/NHD/1.0/autohandler
index 6cae99d..8a47806 100644
--- a/html/NoAuth/NHD/1.0/autohandler
+++ b/html/NoAuth/NHD/1.0/autohandler
@@ -1,7 +1,7 @@
 <%INIT>
 my $version = $r->headers_in->{'X-Ticket-Sharing-Version'};
 if ( ($version||'') ne '1' ) {
-    return RT::Extension::NHD->BadWebRequest('Precondition Failed');
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest('Precondition Failed');
 }
 
 my $ct = $r->headers_in->{'Content-Type'};
@@ -15,8 +15,8 @@ if ( $method ne 'GET' && (!$ct || $ct =~ m{^text/x-json}) ) {
     seek $input, 0, 0;
     my $content = do { local $/; <$input> };
 
-    $data = RT::Extension::NHD->FromJSON( $content );
-    return RT::Extension::NHD->BadWebRequest unless $data;
+    $data = RT::Extension::NetworkedHelpDesk->FromJSON( $content );
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest unless $data;
 }
 
 $m->call_next( %$data );
diff --git a/lib/RT/Extension/NHD.pm b/lib/RT/Extension/NetworkedHelpDesk.pm
similarity index 92%
rename from lib/RT/Extension/NHD.pm
rename to lib/RT/Extension/NetworkedHelpDesk.pm
index 515e238..df26232 100644
--- a/lib/RT/Extension/NHD.pm
+++ b/lib/RT/Extension/NetworkedHelpDesk.pm
@@ -2,12 +2,12 @@ use 5.008003;
 use strict;
 use warnings;
 
-package RT::Extension::NHD;
+package RT::Extension::NetworkedHelpDesk;
 our $VERSION = '0.01';
 
 =head1 NAME
 
-RT::Extension::NHD - Networked Help Desk protocol for Request Tracker
+RT::Extension::NetworkedHelpDesk - Networked Help Desk protocol for Request Tracker
 
 =head1 DESCRIPTION
 
@@ -39,7 +39,7 @@ sub JSONRequest {
     my ($method, $uri, %args) = @_;
 
     my $data;
-    $data = RT::Extension::NHD->ToJSON( delete $args{'Data'} )
+    $data = RT::Extension::NetworkedHelpDesk->ToJSON( delete $args{'Data'} )
         unless uc($method) eq 'GET';
     my %headers = %{ delete $args{'Headers'} || {} };
     %headers = (
diff --git a/lib/RT/Extension/NHD/Test.pm b/lib/RT/Extension/NetworkedHelpDesk/Test.pm
similarity index 69%
rename from lib/RT/Extension/NHD/Test.pm
rename to lib/RT/Extension/NetworkedHelpDesk/Test.pm
index de2d34a..e435e39 100644
--- a/lib/RT/Extension/NHD/Test.pm
+++ b/lib/RT/Extension/NetworkedHelpDesk/Test.pm
@@ -4,7 +4,7 @@ use warnings;
 ### after: use lib qw(@RT_LIB_PATH@);
 use lib qw(/opt/rt4/local/lib /opt/rt4/lib);
 
-package RT::Extension::NHD::Test;
+package RT::Extension::NetworkedHelpDesk::Test;
 use base 'RT::Test';
 
 sub import {
@@ -13,17 +13,17 @@ sub import {
 
     $args{'requires'} ||= [];
     if ( $args{'testing'} ) {
-        unshift @{ $args{'requires'} }, 'RT::Extension::NHD';
+        unshift @{ $args{'requires'} }, 'RT::Extension::NetworkedHelpDesk';
     } else {
-        $args{'testing'} = 'RT::Extension::NHD';
+        $args{'testing'} = 'RT::Extension::NetworkedHelpDesk';
     }
 
     $class->SUPER::import( %args );
     $class->export_to_level(1);
 
     no strict 'subs';
-    my $orig = \&RT::Extension::NHD::SendRequest;
-    *RT::Extension::NHD::SendRequest = sub {
+    my $orig = \&RT::Extension::NetworkedHelpDesk::SendRequest;
+    *RT::Extension::NetworkedHelpDesk::SendRequest = sub {
         return $orig->(@_) if $_[1] && $_[1]->uri =~ RT->Config->Get('WebDomain');
 
         my $self = shift;
@@ -34,18 +34,18 @@ sub import {
 
 sub new_agent {
     my $self = shift;
-    require RT::Extension::NHD::Test::Web;
-    return  RT::Extension::NHD::Test::Web->new_agent( @_ );
+    require RT::Extension::NetworkedHelpDesk::Test::Web;
+    return  RT::Extension::NetworkedHelpDesk::Test::Web->new_agent( @_ );
 }
 
-sub remote_requests { return RT::Extension::NHD::Test->get_objects_from_file('requests') }
+sub remote_requests { return RT::Extension::NetworkedHelpDesk::Test->get_objects_from_file('requests') }
 
 sub set_next_remote_response {
     my $self = shift;
     my $code = shift;
     my %args = @_;
 
-    my $msg = $args{'Message'} || $RT::Extension::NHD::HTTP_MESSAGE{ $code }
+    my $msg = $args{'Message'} || $RT::Extension::NetworkedHelpDesk::HTTP_MESSAGE{ $code }
         || die "no message for code $code";
 
     my %headers = %{ $args{'Headers'} || {} };
@@ -54,9 +54,9 @@ sub set_next_remote_response {
         'X-Ticket-Sharing-Version' => '1',
     );
     my $content = $args{'Data'};
-    $content = RT::Extension::NHD->ToJSON( $content )
+    $content = RT::Extension::NetworkedHelpDesk->ToJSON( $content )
         if ref $content;
-    RT::Extension::NHD::Test->push_object_into_file( responses => HTTP::Response->new(
+    RT::Extension::NetworkedHelpDesk::Test->push_object_into_file( responses => HTTP::Response->new(
         $code, $msg, [%headers], $content,
     ) );
     return;
diff --git a/lib/RT/Extension/NHD/Test/Web.pm b/lib/RT/Extension/NetworkedHelpDesk/Test/Web.pm
similarity index 67%
rename from lib/RT/Extension/NHD/Test/Web.pm
rename to lib/RT/Extension/NetworkedHelpDesk/Test/Web.pm
index 280890f..1c1c514 100644
--- a/lib/RT/Extension/NHD/Test/Web.pm
+++ b/lib/RT/Extension/NetworkedHelpDesk/Test/Web.pm
@@ -1,10 +1,10 @@
 use strict;
 use warnings;
 
-package RT::Extension::NHD::Test::Web;
+package RT::Extension::NetworkedHelpDesk::Test::Web;
 use base qw(RT::Test::Web);
 
-require RT::Extension::NHD::Test;
+require RT::Extension::NetworkedHelpDesk::Test;
 require Test::More;
 
 sub new_agent {
@@ -20,7 +20,7 @@ sub json_request {
     my $self = shift;
     my ($method, $uri, %args) = @_;
     $uri = $self->rt_base_url .'NoAuth/NHD/1.0'. $uri;
-    RT::Extension::NHD->JSONRequest( $method, $uri, %args );
+    RT::Extension::NetworkedHelpDesk->JSONRequest( $method, $uri, %args );
 }
 
 1;
\ No newline at end of file
diff --git a/lib/RT/NHD/Agreement.pm b/lib/RT/NHD/Agreement.pm
index ec4b7cd..23d93a2 100644
--- a/lib/RT/NHD/Agreement.pm
+++ b/lib/RT/NHD/Agreement.pm
@@ -14,7 +14,7 @@ sub Table {'NHDAgreements'}
 sub Load {
     my $self = shift;
     my $value = shift;
-    if ( RT::Extension::NHD->CheckUUID($value) ) {
+    if ( RT::Extension::NetworkedHelpDesk->CheckUUID($value) ) {
         return $self->LoadByCols( @_, UUID => $value );
     }
     return $self->SUPER::Load( $value, @_ );
@@ -157,11 +157,11 @@ sub Send {
     return (0, 'We are neither sender nor receiver')
         unless $recipient;
 
-    my $method = RT::Extension::NHD->ActionToWebMethod( $action );
+    my $method = RT::Extension::NetworkedHelpDesk->ActionToWebMethod( $action );
     return (0, "Unknown action '$action'")
         unless $method;
 
-    my $response = RT::Extension::NHD->JSONRequest(
+    my $response = RT::Extension::NetworkedHelpDesk->JSONRequest(
         $method => $self->$recipient() .'/agreements/'. $self->UUID,
         Headers => {
             'X-Ticket-Sharing-Token' => $self->UUID .':'. $self->AccessKey,
@@ -243,8 +243,8 @@ sub LoadOrCreateUser {
     return ($user, 'Created');
 }
 
-sub ValidateUUID { return RT::Extension::NHD->CheckUUID( $_[1] ) }
-sub ValidateAccessKey { return RT::Extension::NHD->CheckUUID( $_[1] ) }
+sub ValidateUUID { return RT::Extension::NetworkedHelpDesk->CheckUUID( $_[1] ) }
+sub ValidateAccessKey { return RT::Extension::NetworkedHelpDesk->CheckUUID( $_[1] ) }
 
 sub ValidateStatus {
     my $self = shift;
diff --git a/t/api/agreement.t b/t/api/agreement.t
index 9135de7..ee7163d 100644
--- a/t/api/agreement.t
+++ b/t/api/agreement.t
@@ -3,12 +3,12 @@
 use strict;
 use warnings;
 
-use RT::Extension::NHD::Test tests => 58;
-my $test = 'RT::Extension::NHD::Test';
+use RT::Extension::NetworkedHelpDesk::Test tests => 58;
+my $test = 'RT::Extension::NetworkedHelpDesk::Test';
 
 use Digest::SHA1 qw(sha1_hex);
 
-use_ok 'RT::Extension::NHD';
+use_ok 'RT::Extension::NetworkedHelpDesk';
 
 {
     my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
@@ -196,7 +196,7 @@ my $i = 0;
         $agreement->UUID .':'. $agreement->AccessKey;
     is lc $requests[0]->header('Content-Type'), 'text/x-json; charset="utf-8"';
     is_deeply(
-        RT::Extension::NHD->FromJSON( $requests[0]->content ),
+        RT::Extension::NetworkedHelpDesk->FromJSON( $requests[0]->content ),
         $agreement->ForJSON,
     );
 
@@ -222,7 +222,7 @@ my $i = 0;
     };
     is lc $requests[0]->header('Content-Type'), 'text/x-json; charset="utf-8"';
     is_deeply(
-        RT::Extension::NHD->FromJSON( $requests[0]->content ),
+        RT::Extension::NetworkedHelpDesk->FromJSON( $requests[0]->content ),
         $agreement->ForJSON( Fields => ['Name', 'AccessKey'] ),
     );
 }
diff --git a/t/rest/agreement.t b/t/rest/agreement.t
index cd39a90..f5c2331 100644
--- a/t/rest/agreement.t
+++ b/t/rest/agreement.t
@@ -3,12 +3,12 @@
 use strict;
 use warnings;
 
-use RT::Extension::NHD::Test tests => 18;
+use RT::Extension::NetworkedHelpDesk::Test tests => 18;
 use Digest::SHA1 qw(sha1_hex);
 
-RT::Extension::NHD::Test->started_ok;
+RT::Extension::NetworkedHelpDesk::Test->started_ok;
 
-my $m = RT::Extension::NHD::Test->new_agent;
+my $m = RT::Extension::NetworkedHelpDesk::Test->new_agent;
 
 my $i = 0;
 {
@@ -59,7 +59,7 @@ my $i = 0;
     );
     is( $response->code, 200, 'got agreement' );
     is_deeply(
-        RT::Extension::NHD->FromJSON( $response->content ),
+        RT::Extension::NetworkedHelpDesk->FromJSON( $response->content ),
         {
             uuid => $uuid,
             name => 'Test Company',
diff --git a/t/web/agreement.t b/t/web/agreement.t
index f6ca09a..2ce5e6a 100644
--- a/t/web/agreement.t
+++ b/t/web/agreement.t
@@ -3,8 +3,8 @@
 use strict;
 use warnings;
 
-use RT::Extension::NHD::Test tests => 18;
-my $test = 'RT::Extension::NHD::Test';
+use RT::Extension::NetworkedHelpDesk::Test tests => 18;
+my $test = 'RT::Extension::NetworkedHelpDesk::Test';
 use Digest::SHA1 qw(sha1_hex);
 
 my $org = 'Cool Company Sharing Tickets';
@@ -40,7 +40,7 @@ my $remote_url = 'http://hoster.example.com/sharing';
         $agreement->UUID .':'. $agreement->AccessKey;
     is lc $requests[0]->header('Content-Type'), 'text/x-json; charset="utf-8"';
     is_deeply(
-        RT::Extension::NHD->FromJSON( $requests[0]->content ),
+        RT::Extension::NetworkedHelpDesk->FromJSON( $requests[0]->content ),
         $agreement->ForJSON,
     );
 }

commit 746f9bdf8804757a51be6b34a6a8ab94e4a585c1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Nov 4 14:36:44 2011 +0400

    generate README from POD

diff --git a/README b/README
new file mode 100644
index 0000000..3fa22f5
--- /dev/null
+++ b/README
@@ -0,0 +1,11 @@
+NAME
+    RT::Extension::NetworkedHelpDesk - Networked Help Desk protocol for
+    Request Tracker
+
+DESCRIPTION
+AUTHOR
+    Ruslan Zakirov <ruz at bestpractical.com>
+
+LICENSE
+    GPL version 2.
+
diff --git a/inc/Module/Install/ReadmeFromPod.pm b/inc/Module/Install/ReadmeFromPod.pm
new file mode 100644
index 0000000..348531e
--- /dev/null
+++ b/inc/Module/Install/ReadmeFromPod.pm
@@ -0,0 +1,48 @@
+#line 1
+package Module::Install::ReadmeFromPod;
+
+use 5.006;
+use strict;
+use warnings;
+use base qw(Module::Install::Base);
+use vars qw($VERSION);
+
+$VERSION = '0.12';
+
+sub readme_from {
+  my $self = shift;
+  return unless $self->is_admin;
+
+  my $file = shift || $self->_all_from
+    or die "Can't determine file to make readme_from";
+  my $clean = shift;
+
+  print "Writing README from $file\n";
+
+  require Pod::Text;
+  my $parser = Pod::Text->new();
+  open README, '> README' or die "$!\n";
+  $parser->output_fh( *README );
+  $parser->parse_file( $file );
+  if ($clean) {
+    $self->clean_files('README');
+  }
+  return 1;
+}
+
+sub _all_from {
+  my $self = shift;
+  return unless $self->admin->{extensions};
+  my ($metadata) = grep {
+    ref($_) eq 'Module::Install::Metadata';
+  } @{$self->admin->{extensions}};
+  return unless $metadata;
+  return $metadata->{values}{all_from} || '';
+}
+
+'Readme!';
+
+__END__
+
+#line 112
+

commit 358d77dd25a3cd657875f57764361e49d940e242
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Nov 5 02:05:03 2011 +0400

    extract request processing bits for reuse

diff --git a/html/NoAuth/NHD/1.0/agreements/dhandler b/html/NoAuth/NHD/1.0/agreements/dhandler
index 8c57062..5f2c8cc 100644
--- a/html/NoAuth/NHD/1.0/agreements/dhandler
+++ b/html/NoAuth/NHD/1.0/agreements/dhandler
@@ -50,9 +50,9 @@ elsif ( $action ne 'create' ) {
     }
 }
 
-my %data = %{ $agreement->FromJSON( \%ARGS ) };
+my $data = $agreement->FromJSON( \%ARGS );
 
-my ($user, $msg) = $agreement->LoadOrCreateUser( %data );
+my ($user, $msg) = $agreement->LoadOrCreateUser( %$data );
 return RT::Extension::NetworkedHelpDesk->BadWebRequest(
     'Unprocessable Entity',
     "Couldn't create user: $msg"
@@ -60,29 +60,9 @@ return RT::Extension::NetworkedHelpDesk->BadWebRequest(
 
 $agreement->CurrentUser( RT::CurrentUser->new( $user ) );
 
-if ( $agreement->id ) {
-
-    if ( $action eq 'show' ) {
-        return RT::Extension::NetworkedHelpDesk->WebSendJSON( $agreement->ForJSON );
-    }
-    elsif ( $action eq 'update' ) {
-        my ($status, $msg) = $agreement->Update( %data );
-        unless ( $status ) {
-            RT->Logger->error("Couldn't update NHD agreement: $msg");
-            return RT::Extension::NetworkedHelpDesk->BadWebRequest('Unprocessable Entity');
-        }
-        return RT::Extension::NetworkedHelpDesk->GoodWebRequest;
-    }
-    else {
-        return RT::Extension::NetworkedHelpDesk->BadWebRequest;
-    }
-}
-else {
-    my ($status, $msg) = $agreement->Create( %data );
-    unless ( $status ) {
-        RT->Logger->error("Couldn't create NHD agreement: $msg");
-        return RT::Extension::NetworkedHelpDesk->BadWebRequest('Unprocessable Entity');
-    }
-    return RT::Extension::NetworkedHelpDesk->GoodWebRequest('Created');
-}
+return RT::Extension::NetworkedHelpDesk->ProcessRequest(
+    Object => $agreement,
+    Action => $action,
+    Data   => $data,
+);
 </%INIT>
\ No newline at end of file
diff --git a/lib/RT/Extension/NetworkedHelpDesk.pm b/lib/RT/Extension/NetworkedHelpDesk.pm
index df26232..602d84c 100644
--- a/lib/RT/Extension/NetworkedHelpDesk.pm
+++ b/lib/RT/Extension/NetworkedHelpDesk.pm
@@ -18,6 +18,35 @@ use JSON::Any;
 use LWP::UserAgent;
 use HTTP::Request;
 
+sub ProcessRequest {
+    my $self = shift;
+    my %args = @_;
+
+    my ($object, $action, $data) = @args{qw(Object Action Data)};
+    if ( $object->id ) {
+        if ( $action eq 'show' ) {
+            return $self->WebSendJSON( $object->ForJSON );
+        }
+        elsif ( $action eq 'update' ) {
+            my ($status, $msg) = $object->Update( %$data );
+            unless ( $status ) {
+                RT->Logger->error("Couldn't update ". ref($object) .": $msg");
+                return $self->BadWebRequest('Unprocessable Entity');
+            }
+            return $self->GoodWebRequest;
+        }
+    }
+    else {
+        my ($status, $msg) = $object->Create( %$data );
+        unless ( $status ) {
+            RT->Logger->error("Couldn't create ". ref($object) .": $msg");
+            return $self->BadWebRequest('Unprocessable Entity');
+        }
+        return $self->GoodWebRequest('Created');
+    }
+    return $self->BadWebRequest;
+}
+
 sub FromJSON {
     return JSON::Any->new->from_json( $_[1] );
 }

commit 6afb2fb2b2c96f8c8eeb757b76d42df5447011b9
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Nov 5 02:18:58 2011 +0400

    update TODO

diff --git a/TODO b/TODO
index 8084931..96722ab 100644
--- a/TODO
+++ b/TODO
@@ -8,3 +8,7 @@
 * script that updates remote agreements when Organization or
   NHD_WebURL changes.
 
+* implement agreements' status change check
+
+* add Queue to agreements where we receive
+** don't allow admin to accept until it's filled

commit 5f48aff6302916345369ddaee5fe655ed89b7699
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Nov 8 15:06:44 2011 +0400

    add queue to agreements

diff --git a/TODO b/TODO
index 96722ab..bf98dcd 100644
--- a/TODO
+++ b/TODO
@@ -12,3 +12,4 @@
 
 * add Queue to agreements where we receive
 ** don't allow admin to accept until it's filled
+*** done, test it
diff --git a/etc/schema.mysql b/etc/schema.mysql
index cff4c36..6218130 100644
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -7,6 +7,7 @@ CREATE TABLE NHDAgreements (
   Receiver varchar(240) CHARACTER SET ascii NOT NULL,
   AccessKey varchar(40) CHARACTER SET ascii NOT NULL,
   DeactivatedBy varchar(10) CHARACTER SET ascii NULL DEFAULT NULL,
+  Queue INTEGER NOT NULL DEFAULT 0,
 
   PRIMARY KEY (id)
 ) ENGINE=InnoDB CHARACTER SET utf8;
diff --git a/html/Admin/Tools/NHD/Create.html b/html/Admin/Tools/NHD/Create.html
index 088d2c2..34beb18 100644
--- a/html/Admin/Tools/NHD/Create.html
+++ b/html/Admin/Tools/NHD/Create.html
@@ -23,6 +23,14 @@
   <td class="label"><&|/l&>Status</&>:</td>
   <td class="value"><&|/l&>pending</&></td>
 </tr>
+<tr>
+  <td class="label"><&|/l&>Queue</&>:</td>
+  <td class="value"><& /Elements/SelectQueue, ShowAllQueues => 1, Default => $Queue &></td>
+</tr>
+<tr>
+  <td> </td>
+  <td class="comment"><% loc("Limit queue tickets can be shared from.") %></td>
+</tr>
 
 </table>
 
@@ -33,6 +41,7 @@
 $Create   => undef
 $Name     => ''
 $Receiver => ''
+$Queue    => 0
 </%ARGS>
 <%INIT>
 my @results;
@@ -40,9 +49,10 @@ my @results;
 if ( $Create ) {
     my $agreement = RT::NHD::Agreement->new( $session{'CurrentUser'} );
     my ($status, $msg) = $agreement->Create(
-        Name => $Name,
+        Name     => $Name,
         Receiver => $Receiver,
-        Sender => RT->Config->Get('NHD_WebURL'),
+        Sender   => RT->Config->Get('NHD_WebURL'),
+        Queue    => $Queue,
     );
     MaybeRedirectForResults(
         Path      => '/Admin/Tools/NHD/Modify.html',
diff --git a/html/Admin/Tools/NHD/Modify.html b/html/Admin/Tools/NHD/Modify.html
index eb019cd..9ba4dba 100644
--- a/html/Admin/Tools/NHD/Modify.html
+++ b/html/Admin/Tools/NHD/Modify.html
@@ -26,6 +26,22 @@
   &></td>
 </tr>
 
+<tr>
+  <td class="label"><&|/l&>Queue</&>:</td>
+  <td class="value"><& /Elements/SelectQueue,
+      ShowAllQueues => 1,
+      Default       => $Queue || $agreement->Queue,
+  &></td>
+</tr>
+<tr>
+  <td> </td>
+% if ( $who_we_are eq 'Receiver' ) {
+  <td class="comment"><% loc("Queue where shared tickets end up.") %></td>
+% } else {
+  <td class="comment"><% loc("Limit queue tickets can be shared from.") %></td>
+% }
+</tr>
+
 </table>
 
 <& /Elements/Submit, Name => 'Update', Label => loc('Create') &>
@@ -55,7 +71,15 @@ unless ( $who_we_are ) {
     ));
 }
 
+my $skip_update = 0;
 if ( $Update ) {
+    if ( $status eq 'accepted' && $who_we_are eq 'Receiver' && !$Queue ) {
+        $skip_update = 1;
+        push @results, loc("Queue required for receiving tickets");
+    }
+}
+
+if ( $Update && !$skip_update ) {
     my %args = (Name => $Name, Status => $Status);
     delete $args{$_} foreach grep !defined $args{$_} || !length $args{$_}, keys %args;
 
diff --git a/lib/RT/NHD/Agreement.pm b/lib/RT/NHD/Agreement.pm
index 23d93a2..b567343 100644
--- a/lib/RT/NHD/Agreement.pm
+++ b/lib/RT/NHD/Agreement.pm
@@ -306,7 +306,7 @@ sub ForJSON {
     my @fields = $args{'Fields'} ? @{$args{'Fields'}} : keys %FIELDS_MAP;
 
     my %res;
-    $res{ $FIELDS_MAP{$_} } = $self->$_() foreach @fields;
+    $res{ $FIELDS_MAP{$_} } = $self->$_() foreach grep exists $FIELDS_MAP{$_}, @fields;
     if ( exists $res{'name'} && $self->WhoWeAre eq 'Sender' ) {
         $res{'name'} = RT->Config->Get('NHD_Name');
     }
@@ -334,6 +334,8 @@ sub RollbackTransaction {
 sub _CoreAccessible { return {
     id =>
     { read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)' },
+    Queue =>
+    {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => 0},
     UUID =>
     {read => 1, write => 0, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
     Name =>

commit d2fecbe1ff31228c3c409abde7f94b6d20a1c067
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Nov 8 20:00:26 2011 +0400

    render message if any provided as text

diff --git a/lib/RT/Extension/NetworkedHelpDesk.pm b/lib/RT/Extension/NetworkedHelpDesk.pm
index 602d84c..e783fe9 100644
--- a/lib/RT/Extension/NetworkedHelpDesk.pm
+++ b/lib/RT/Extension/NetworkedHelpDesk.pm
@@ -102,18 +102,19 @@ our %HTTP_MESSAGE = reverse %HTTP_CODE;
 sub BadWebRequest {
     my $self = shift;
     my $info = shift || 'Bad Request';
-    return $self->StopWebRequest( $info );
+    return $self->StopWebRequest( $info, @_ );
 }
 
 sub GoodWebRequest {
     my $self = shift;
     my $info = shift || 'OK';
-    return $self->StopWebRequest( $info );
+    return $self->StopWebRequest( $info, @_ );
 }
 
 sub StopWebRequest {
     my $self = shift;
     my $info = shift;
+    my $content = shift;
 
     my $code = $HTTP_CODE{ $info } or die "Bad status $info";
 
@@ -123,11 +124,14 @@ sub StopWebRequest {
         $r->headers_out->{'WWW-Authenticate'} = 'X-Ticket-Sharing';
     }
 
-    if ( $code =~ /^2..$/ ) {
-        $HTML::Mason::Commands::m->abort( $code );
-    } else {
-        $HTML::Mason::Commands::m->clear_and_abort( $code );
+    $HTML::Mason::Commands::m->clear_buffer if $content || $code !~ /^2..$/;
+
+    if ( $content ) {
+        $HTML::Mason::Commands::r->headers_out->{'Content-Type'}
+            = 'text/plain; charset="UTF-8"';
+        $HTML::Mason::Commands::m->out($content);
     }
+    $HTML::Mason::Commands::m->abort( $code );
 }
 
 my %METHOD_TO_ACTION = ( GET => 'show', POST => 'create', PUT => 'update' );

commit 501ee7ff3bdee4d104b52ce394918dfe232f215c
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Nov 8 20:01:12 2011 +0400

    first step towards sharing tickets

diff --git a/html/NoAuth/NHD/1.0/tickets/dhandler b/html/NoAuth/NHD/1.0/tickets/dhandler
new file mode 100644
index 0000000..531fa2a
--- /dev/null
+++ b/html/NoAuth/NHD/1.0/tickets/dhandler
@@ -0,0 +1,71 @@
+<%ARGS>
+$uuid => undef
+</%ARGS>
+<%INIT>
+my $tuuid = $m->dhandler_arg;
+unless ( RT::Extension::NetworkedHelpDesk->CheckUUID( $tuuid ) ) {
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+        'Unprocessable Entity',
+        "Value in the URI isn't a UUID"
+    );
+}
+
+my $action = RT::Extension::NetworkedHelpDesk->WebRequestAction;
+return RT::Extension::NetworkedHelpDesk->BadWebRequest('Method Not Allowed')
+    unless $action;
+
+my $token = $r->headers_in->{'X-Ticket-Sharing-Token'};
+return RT::Extension::NetworkedHelpDesk->BadWebRequest('Unauthorized')
+    unless $token;
+
+my ($auuid, $access_key) = split /:/, $token, 2;
+unless ( RT::Extension::NetworkedHelpDesk->CheckUUID( $auuid ) ) {
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+        'Unprocessable Entity',
+        "Value in the token isn't a UUID"
+    );
+}
+
+my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+$agreement->Load( $auuid );
+return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+    'Forbidden', "No agreement $auuid"
+) unless $agreement->id;
+return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+    'Forbidden', "Access key mismatch"
+) unless $access_key eq $agreement->AccessKey;
+return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+    'Forbidden', "Agreement is ". $agreement->Status
+) unless $agreement->Status eq 'accepted';
+
+if ( $action ne 'create' ) {
+    $ARGS{'uuid'} = $uuid ||= $tuuid;
+} elsif ( ($uuid||'') ne $tuuid ) {
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+        'Unprocessable Entity',
+        "UUIDs in the URI and body don't match"
+    );
+}
+
+my $ticket = RT::NHD::Ticket->new( RT->SystemUser );
+$ticket->Load( $uuid );
+if ( $action eq 'create' && $ticket->id ) {
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest(
+        'Unprocessable Entity',
+        "Couldn't create an agreement: record already exists"
+    );
+}
+elsif ( $action ne 'create' ) {
+    return RT::Extension::NetworkedHelpDesk->BadWebRequest('Not Found')
+        unless $ticket->id;
+}
+
+my $data = $ticket->FromJSON( \%ARGS );
+$data->{'Agreement'} = $agreement;
+
+return RT::Extension::NetworkedHelpDesk->ProcessRequest(
+    Object => $ticket,
+    Action => $action,
+    Data   => $data,
+);
+</%INIT>
\ No newline at end of file
diff --git a/lib/RT/Extension/NetworkedHelpDesk.pm b/lib/RT/Extension/NetworkedHelpDesk.pm
index e783fe9..b002d43 100644
--- a/lib/RT/Extension/NetworkedHelpDesk.pm
+++ b/lib/RT/Extension/NetworkedHelpDesk.pm
@@ -14,6 +14,8 @@ RT::Extension::NetworkedHelpDesk - Networked Help Desk protocol for Request Trac
 =cut
 
 use RT::NHD::Agreements;
+use RT::NHD::Ticket;
+
 use JSON::Any;
 use LWP::UserAgent;
 use HTTP::Request;
diff --git a/lib/RT/NHD/Ticket.pm b/lib/RT/NHD/Ticket.pm
new file mode 100644
index 0000000..90d9c13
--- /dev/null
+++ b/lib/RT/NHD/Ticket.pm
@@ -0,0 +1,169 @@
+use strict;
+use warnings;
+
+package RT::NHD::Ticket;
+use base 'RT::Base';
+
+use Digest::SHA1 qw(sha1_hex);
+
+sub new {
+    my $proto = shift;
+    my $self = bless {}, ref($proto) || $proto;
+    $self->_Init( @_ );
+    return $self;
+}
+
+sub _Init { return (shift)->CurrentUser(@_) }
+
+sub Ticket {
+    my $self = shift;
+    $self->{ticket} = shift if @_;
+    return $self->{'ticket'} || RT::Ticket->new( $self->CurrentUser );
+}
+
+sub Load {
+    my $self = shift;
+    my $value = shift;
+
+    unless ( RT::Extension::NetworkedHelpDesk->CheckUUID($value) ) {
+        $RT::Logger->error("Doesn't look like UUID");
+        return;
+    }
+
+    my $tickets = RT::Tickets->new( $self->CurrentUser );
+    my $alias = $tickets->Join(
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => 'Attributes',
+        FIELD2 => 'ObjectId',
+    );
+    $tickets->_SQLLimit(
+        ALIAS           => $alias,
+        FIELD           => 'ObjectType',
+        VALUE           => 'RT::Ticket',
+    );
+    $tickets->_SQLLimit(
+        ALIAS           => $alias,
+        FIELD           => 'Name',
+        VALUE           => 'NHDUUID',
+    );
+    $tickets->_SQLLimit(
+        ALIAS           => $alias,
+        FIELD           => 'Content',
+        VALUE           => $value,
+    );
+    my $ticket = $self->Ticket( $tickets->First );
+    return $ticket->id;
+}
+
+sub Create {
+    my $self = shift;
+    my %args = @_;
+
+    my $agreement = $args{'Agreement'}
+        or return (0, "No agreement");
+    unless ( $agreement->Status eq 'accepted' ) {
+        return (0, "Agreement is not accepted");
+    }
+
+    my $queue = RT::Queue->new( $self->CurrentUser );
+    $queue->Load( $agreement->Queue );
+    unless ( $queue->id ) {
+        return (0, "Agreement has no queue defined");
+    }
+
+    if ( ref $args{'Requestor'} eq 'HASH') {
+        my ($user, $msg) = $self->LoadOrCreateUser(
+            %{ $args{'Requestor'} },
+            Agreement => $agreement,
+        );
+        return (0, "Couldn't create a user: $msg")
+            unless $user;
+
+        $args{'Requestor'} = $user->id;
+    }
+
+    my $corresponds = delete $args{'Corresponds'};
+
+    my $ticket = RT::Ticket->new( $self->CurrentUser );
+    my ($id, $txn, $msg) = $ticket->Create(
+        %args,
+        Queue => $queue,
+    );
+    $self->Ticket( $ticket );
+
+    return ($id, $msg);
+}
+
+sub id { (shift)->Ticket->id }
+
+our %FIELDS_MAP = (
+    Subject     => 'subject',
+    Status      => 'status',
+    UUID        => 'uuid',
+    Created     => 'requested_at',
+    Requestor   => 'requester',
+    Creator     => 'author',
+    Content     => 'body',
+    Created     => 'authored_at',
+    Corresponds => 'comments',
+    Name        => 'name',
+);
+
+sub ForJSON {
+    my $self = shift;
+
+    my %res;
+}
+
+sub FromJSON {
+    my $self = shift;
+    my $args = shift;
+
+    my $turn_hash = sub {
+        my ($args) = @_;
+        my $res = {};
+        while ( my ($k, $v) = each %FIELDS_MAP ) {
+            next unless exists $args->{ $v };
+            $res->{ $k } = $args->{ $v };
+            if ( $k eq 'Created' ) {
+                my $date = RT::Date->new( RT->SystemUser );
+                $date->Set( Format => 'unknown', Value => $res->{ $k } );
+                $res->{ $k } = $date;
+            }
+        };
+        return $res;
+    };
+
+    my $res = $turn_hash->( $args );
+
+    foreach my $e ( @{ $res->{'Corresponds'} || [] } ) {
+        $e = $turn_hash->( $e );
+    }
+    foreach my $e ( $res->{'Requestor'} ) {
+        $e = $turn_hash->( $e );
+    }
+
+    return $res;
+}
+
+sub LoadOrCreateUser {
+    my $self = shift;
+    my %args = @_;
+
+    my $user = RT::User->new( RT->SystemUser );
+    $user->Load( $args{'UUID'} );
+    return ($user, 'Loaded') if $user->id;
+
+    my ($status, $msg) = $user->Create(
+        Name => $args{'UUID'},
+        RealName => $args{'Name'},
+        Privileged => 0,
+        Disabled => 0,
+        Comments => "Auto created by Networked Help Desk",
+    );
+    return ($status, $msg) unless $status;
+    return ($user, 'Created');
+}
+
+1;
diff --git a/t/rest/ticket.t b/t/rest/ticket.t
new file mode 100644
index 0000000..7e9c510
--- /dev/null
+++ b/t/rest/ticket.t
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Extension::NetworkedHelpDesk::Test tests => 13;
+my $test = 'RT::Extension::NetworkedHelpDesk::Test';
+use Digest::SHA1 qw(sha1_hex);
+
+$test->started_ok;
+
+my $m = $test->new_agent;
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+
+my $i = 0;
+my $auuid = sha1_hex( ''. ++$i );
+my $access_key = sha1_hex( ''. ++$i );
+
+{
+    my $response = $m->json_request(
+        POST => '/agreements/'. $auuid,
+        Headers => {
+            'X-Ticket-Sharing-Token' => "$auuid:$access_key",
+        },
+        Data => {
+            uuid => $auuid,
+            name => 'Test Company',
+            status => 'pending',
+            sender_url => 'http://hoster.example.com/sharing',
+            receiver_url => RT->Config->Get('NHD_WebURL'),
+            access_key => $access_key,
+        },
+    );
+    is( $response->code, 201, 'created' );
+
+    DBIx::SearchBuilder::Record::Cachable::FlushCache();
+    my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+    $agreement->Load( $auuid );
+    ok( $agreement->id, 'loaded agreement' );
+
+    $test->set_next_remote_response(200);
+
+    my ($status, $msg) = $agreement->Update( Queue => $queue->id, Status => 'accepted' );
+    ok $status, "accepted agreement" or diag "error: $msg";
+}
+
+{
+    my $uuid = sha1_hex( ''. ++$i );
+    my $response = $m->json_request(
+        POST => '/tickets/'. $uuid,
+        Headers => {
+            'X-Ticket-Sharing-Token' => "$auuid:$access_key",
+        },
+        Data => {
+            uuid => $uuid,
+            subject => 'test ticket',
+            requested_at => "2010-11-24 14:13:54 -0800",
+            status => 'open',
+            requester => {
+                uuid => sha1_hex( ''. ++$i ),
+                name => 'John Doe',
+            },
+        },
+    );
+    is( $response->code, 201, 'created' );
+
+    my $ticket = $test->last_ticket;
+    ok $ticket && $ticket->id, 'created a ticket';
+    is $ticket->Subject, 'test ticket';
+    is $ticket->Status, 'open';
+}
+

commit 30c8a6128c733fb54dccbdea2f576ded80e2e136
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Nov 9 01:21:37 2011 +0400

    simple Update for tickets

diff --git a/TODO b/TODO
index bf98dcd..50021be 100644
--- a/TODO
+++ b/TODO
@@ -13,3 +13,5 @@
 * add Queue to agreements where we receive
 ** don't allow admin to accept until it's filled
 *** done, test it
+
+* deal with users for tickets
diff --git a/lib/RT/NHD/Ticket.pm b/lib/RT/NHD/Ticket.pm
index 90d9c13..090ab30 100644
--- a/lib/RT/NHD/Ticket.pm
+++ b/lib/RT/NHD/Ticket.pm
@@ -6,6 +6,9 @@ use base 'RT::Base';
 
 use Digest::SHA1 qw(sha1_hex);
 
+our @STATUSES = qw(open pending solved);
+our %STATUS_NHD2RT = ( open => 'open', pending => 'stalled', solved => 'resolved' );
+
 sub new {
     my $proto = shift;
     my $self = bless {}, ref($proto) || $proto;
@@ -92,9 +95,52 @@ sub Create {
     );
     $self->Ticket( $ticket );
 
+    $ticket->AddAttribute( Name => 'NHDUUID', Value => $args{'UUID'} );
+
     return ($id, $msg);
 }
 
+sub Update {
+    my $self = shift;
+    my %args = @_;
+
+    my $corresponds = delete $args{'Corresponds'};
+    my $actor = delete $args{'UpdatedBy'};
+
+    my $ticket = $self->Ticket;
+
+    if ( exists $args{'Status'} ) {
+        return $self->RollbackTransaction("Invalid status value")
+            unless grep $_ eq lc($args{'Status'}), @STATUSES;
+        $args{'Status'} = $STATUS_NHD2RT{ lc $args{'Status'} };
+        delete $args{'Status'} if $ticket->Status eq $args{'Status'};
+    }
+    foreach my $field ( qw(Status Subject) ) {
+        next unless exists $args{ $field };
+
+        my $cur = $ticket->$field();
+        my $new = $args{ $field };
+        next if (defined $new && !defined $cur)
+            || (!defined $new && defined $cur)
+            || $new ne $cur;
+
+        delete $args{ $field };
+    }
+
+    $RT::Handle->BeginTransaction;
+    foreach my $field ( qw(Status Subject) ) {
+        next unless exists $args{ $field };
+
+        my $method = "Set$field";
+        my ($status, $msg) = $ticket->$method( $args{ $field } );
+        return $self->RollbackTransaction( "Couldn't update $field: $msg" )
+            unless $status;
+    }
+    $RT::Handle->Commit;
+
+    return (1, 'Updated');
+}
+
 sub id { (shift)->Ticket->id }
 
 our %FIELDS_MAP = (
@@ -108,6 +154,7 @@ our %FIELDS_MAP = (
     Created     => 'authored_at',
     Corresponds => 'comments',
     Name        => 'name',
+    UpdatedBy   => 'current_actor',
 );
 
 sub ForJSON {
@@ -166,4 +213,15 @@ sub LoadOrCreateUser {
     return ($user, 'Created');
 }
 
+sub RollbackTransaction {
+    my $self = shift;
+    my $msg = shift;
+
+    $RT::Handle->Rollback if $self->_Handle->TransactionDepth;
+
+    $self->Ticket->Load( $self->id );
+
+    return (0, $msg);
+}
+
 1;
diff --git a/t/rest/ticket.t b/t/rest/ticket.t
index 7e9c510..de4b361 100644
--- a/t/rest/ticket.t
+++ b/t/rest/ticket.t
@@ -69,5 +69,23 @@ my $access_key = sha1_hex( ''. ++$i );
     ok $ticket && $ticket->id, 'created a ticket';
     is $ticket->Subject, 'test ticket';
     is $ticket->Status, 'open';
+
+    my $response = $m->json_request(
+        PUT => '/tickets/'. $uuid,
+        Headers => {
+            'X-Ticket-Sharing-Token' => "$auuid:$access_key",
+        },
+        Data => {
+            uuid => $uuid,
+            subject => 'another test ticket',
+            status => 'pending',
+        },
+    );
+    is( $response->code, 200, 'updated' );
+
+    my $ticket = $test->last_ticket;
+    ok $ticket && $ticket->id, 'created a ticket';
+    is $ticket->Subject, 'another test ticket';
+    is $ticket->Status, 'stalled';
 }
 

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list