[Bps-public-commit] RT-Extension-NHD branch, master, created. e331ca2f9746ea7e771b93a63bde80fac86c8d06

Ruslan Zakirov ruz at bestpractical.com
Wed Sep 14 04:00:19 EDT 2011


The branch, master has been created
        at  e331ca2f9746ea7e771b93a63bde80fac86c8d06 (commit)

- Log -----------------------------------------------------------------
commit e331ca2f9746ea7e771b93a63bde80fac86c8d06
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Sep 14 12:00:04 2011 +0400

    initial commit

diff --git a/etc/schema.mysql b/etc/schema.mysql
new file mode 100644
index 0000000..cf772fc
--- /dev/null
+++ b/etc/schema.mysql
@@ -0,0 +1,13 @@
+CREATE TABLE NHDAgreements (
+  id INTEGER NOT NULL  AUTO_INCREMENT,
+  UUID varchar(48) CHARACTER SET ascii NOT NULL,
+  Name varchar(200) NOT NULL,
+  Status varchar(64) CHARACTER SET ascii NOT NULL,
+  Sender varchar(240) CHARACTER SET ascii NOT NULL,
+  Receiver varchar(240) CHARACTER SET ascii NOT NULL,
+  AccessKey varchar(40) CHARACTER SET ascii NOT NULL,
+
+  PRIMARY KEY (id)
+) ENGINE=InnoDB CHARACTER SET utf8;
+
+CREATE UNIQUE INDEX NHDAgreements1 ON NHDAgreements(UUID);
\ No newline at end of file
diff --git a/html/NoAuth/NHD/1.0/agreements/dhandler b/html/NoAuth/NHD/1.0/agreements/dhandler
new file mode 100644
index 0000000..29ace7b
--- /dev/null
+++ b/html/NoAuth/NHD/1.0/agreements/dhandler
@@ -0,0 +1,55 @@
+<%ARGS>
+$uuid => undef
+$name => undef
+$receiver_url => undef
+$sender_url => undef
+$access_key => undef
+$status => undef
+</%ARGS>
+<%INIT>
+my $duuid = $m->dhandler_arg;
+if ( !RT::Extension::NHD->CheckUUID( $duuid ) || ($uuid||'') ne $duuid ) {
+    return RT::Extension::NHD->BadWebRequest('Unprocessable Entity');
+}
+
+my $token = $r->headers_in->{'X-Ticket-Sharing-Token'};
+return RT::Extension::NHD->BadWebRequest('Authorization Required')
+    unless $token;
+
+my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+$agreement->Load( $uuid );
+return RT::Extension::NHD->BadWebRequest('Not Found')
+    if !$agreement->id && $action ne 'create';
+
+if ( $token ne join ':', ($agreement->UUID || $uuid), ($agreement->AccessKey || $access_key) ) {
+    return RT::Extension::NHD->BadWebRequest('Forbidden')
+}
+
+my $action = RT::Extension::NHD->WebRequestAction;
+return RT::Extension::NHD->BadWebRequest('Unprocessable Entity')
+    unless $action;
+
+if ( $agreement->id ) {
+    if ( $action eq 'show' ) {
+        return RT::Extension::NHD->WebSendJSON( $agreement->ForJSON );
+    }
+    elsif ( $action eq 'update' ) {
+        my ($status, $msg) = $agreement->UpdateBySender( %{ $agreement->FromJSON( \%ARGS ) } );
+        unless ( $status ) {
+            RT->Logger->error("Couldn't update NHD agreement: $msg");
+            return RT::Extension::NHD->BadWebRequest('Unprocessable Entity');
+        }
+        return RT::Extension::NHD->GoodWebRequest;
+    }
+    else {
+    }
+}
+else {
+    my ($status, $msg) = $agreement->Create( %{ $agreement->FromJSON( \%ARGS ) } );
+    unless ( $status ) {
+        RT->Logger->error("Couldn't create NHD agreement: $msg");
+        return RT::Extension::NHD->BadWebRequest('Unprocessable Entity');
+    }
+    return RT::Extension::NHD->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
new file mode 100644
index 0000000..12b603c
--- /dev/null
+++ b/html/NoAuth/NHD/1.0/autohandler
@@ -0,0 +1,18 @@
+<%INIT>
+my $version = $r->headers_in->{'X-Ticket-Sharing-Version'};
+if ( $version || $version ne '1' ) {
+    return RT::Extension::NHD->BadWebRequest('Precondition Failed');
+}
+
+my $ct = $r->content_type;
+my $method = uc $r->method;
+
+my $data;
+if ( $method ne 'GET' && (!$ct || $ct =~ m{^text/x-json}) ) {
+    $data = RT::Extension::NHD->FromJSON( $r->content );
+    return RT::Extension::NHD->BadWebRequest unless $data;
+}
+
+$m->call_next( %$data );
+
+</%INIT>
diff --git a/lib/RT/Extension/NHD.pm b/lib/RT/Extension/NHD.pm
new file mode 100644
index 0000000..12cad3d
--- /dev/null
+++ b/lib/RT/Extension/NHD.pm
@@ -0,0 +1,88 @@
+use 5.008003;
+use strict;
+use warnings;
+
+package RT::Extension::NHD;
+our $VERSION = '0.01';
+
+=head1 NAME
+
+RT::Extension::NHD - Networked Help Desk protocol for Request Tracker
+
+=head1 DESCRIPTION
+
+=cut
+
+use RT::NHD::Agreement;
+
+sub CheckUUID {
+    my $self = shift;
+    my $value = shift;
+
+    return 0 unless $value && $value =~ /^[0-9a-f]{40}$/i;
+    return 1;
+}
+
+#XXX
+my %HTTP_CODE = (
+    'OK' => 200,
+    'Created' => 201,
+
+    'Bad Request' => 400,
+    'Precondition Failed' => 400,
+    'Unprocessable Entity' => 400,
+    'Not Found' => 404,
+    'Forbidden' => 400,
+    'Authorization Required' => 400,
+
+);
+
+sub BadWebRequest {
+    my $self = shift;
+    my $info = shift || 'Bad Request';
+    return $self->StopWebRequest( $info );
+}
+
+sub GoodWebRequest {
+    my $self = shift;
+    my $info = shift || 'OK';
+    return $self->StopWebRequest( $info );
+}
+
+sub StopWebRequest {
+    my $self = shift;
+    my $info = shift;
+    my $code = $HTTP_CODE{ $info } or die "Bad status $info";
+    $HTML::Mason::Commands::r->headers_out->{'Status'} = "$code $info";
+    if ( $code =~ /^2..$/ ) {
+        $HTML::Mason::Commands::m->abort;
+    } else {
+        $HTML::Mason::Commands::m->clear_and_abort;
+    }
+}
+
+my %METHOD_TO_ACTION = ( GET => 'show', POST => 'create', PUT => 'update' );
+sub WebRequestAction {
+    return $METHOD_TO_ACTION{ uc $HTML::Mason::Commands::r->method };
+}
+
+sub WebSendJSON {
+    my $self = shift;
+    my $data = shift;
+    my $status = shift || 'OK';
+
+    $HTML::Mason::Commands::r->content_type( "text/x-json; charset=UTF-8" );
+    return $self->GoodWebRequest( $status );
+}
+
+=head1 AUTHOR
+
+Ruslan Zakirov E<lt>ruz at bestpractical.comE<gt>
+
+=head1 LICENSE
+
+GPL version 2.
+
+=cut
+
+1;
\ No newline at end of file
diff --git a/lib/RT/Extension/NHD/Test.pm b/lib/RT/Extension/NHD/Test.pm
new file mode 100644
index 0000000..4da47cd
--- /dev/null
+++ b/lib/RT/Extension/NHD/Test.pm
@@ -0,0 +1,25 @@
+use strict;
+use warnings;
+
+### after: use lib qw(@RT_LIB_PATH@);
+use lib qw(/opt/rt4/local/lib /opt/rt4/lib);
+
+package RT::Extension::NHD::Test;
+use base 'RT::Test';
+
+sub import {
+    my $class = shift;
+    my %args  = @_;
+
+    $args{'requires'} ||= [];
+    if ( $args{'testing'} ) {
+        unshift @{ $args{'requires'} }, 'RT::Extension::NHD';
+    } else {
+        $args{'testing'} = 'RT::Extension::NHD';
+    }
+
+    $class->SUPER::import( %args );
+    $class->export_to_level(1);
+}
+
+1;
diff --git a/lib/RT/NHD/Agreement.pm b/lib/RT/NHD/Agreement.pm
new file mode 100644
index 0000000..a9f2fde
--- /dev/null
+++ b/lib/RT/NHD/Agreement.pm
@@ -0,0 +1,91 @@
+use strict;
+use warnings;
+
+package RT::NHD::Agreement;
+use base 'RT::Record';
+
+use RT::NHD::Agreements;
+
+our @STATUSES = qw(pending accepted declined inactive);
+
+sub Table {'NHDAgreements'}
+
+sub Load {
+    my $self = shift;
+    my $value = shift;
+    if ( $value && $value =~ /^[0-9a-f]{40}$/i ) {
+        return $self->LoadByCols( @_, UUID => $value );
+    }
+    return $self->SUPER::Load( $value, @_ );
+}
+
+sub ValidateUUID { return RT::Extension::NHD->CheckUUID( $_[1] ) }
+sub ValidateAccessKey { return RT::Extension::NHD->CheckUUID( $_[1] ) }
+
+sub ValidateStatus {
+    my $self = shift;
+    my $value = shift;
+
+    return 0 unless $value;
+    return 0 unless grep $_ eq lc $value, @STATUSES;
+    return 1;
+}
+
+sub ValidateSender { return (shift)->_ValidateURI( @_ ) }
+sub ValidateReceiver { return (shift)->_ValidateURI( @_ ) }
+
+sub _ValidateURI {
+    my $self = shift;
+    my $value = shift;
+
+    return 0 unless $value;
+    return 0 unless URI->new( $value );
+    return 1;
+}
+
+sub FromJSON {
+    my $self = shift;
+    my $args = @_;
+
+    return {
+        UUID => $args->{'uuid'},
+        Name => $args->{'name'},
+        Status => $args->{'status'},
+        Sender => $args->{'sender_url'},
+        Receiver => $args->{'receiver_url'},
+        AccessKey => $args->{'access_key'},
+    };
+}
+
+sub ForJSON {
+    my $self = shift;
+    return {
+        uuid => $self->UUID,
+        name => $self->Name,
+        status => $self->Status,
+        sender_url => $self->Sender,
+        receiver_url => $self->Receiver,
+        access_key => $self->AccessKey,
+    };
+}
+
+sub _CoreAccessible { return {
+    id =>
+    { read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)' },
+    UUID =>
+    {read => 1, write => 0, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+    Name =>
+    {read => 1, write => 1, sql_type => 12, length => 200, is_blob => 0, is_numeric => 0, type => 'varchar(200)', default => ''},
+    Status =>
+    {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+    Sender =>
+    {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+    Receiver =>
+    {read => 1, write => 1, sql_type => 12, length => 240, is_blob => 0, is_numeric => 0, type => 'varchar(240)', default => ''},
+    AccessKey =>
+    {read => 1, write => 1, sql_type => 12, length => 40, is_blob => 0, is_numeric => 0, type => 'varchar(40)', default => ''},
+} }
+
+RT::Base->_ImportOverlays();
+
+1;
diff --git a/lib/RT/NHD/Agreements.pm b/lib/RT/NHD/Agreements.pm
new file mode 100644
index 0000000..268cc6c
--- /dev/null
+++ b/lib/RT/NHD/Agreements.pm
@@ -0,0 +1,13 @@
+use strict;
+use warnings;
+
+package RT::NHD::Agreements;
+use base 'RT::SearchBuilder';
+
+use RT::NHD::Agreement;
+
+sub Table { 'NHDAgreements' }
+
+RT::Base->_ImportOverlays();
+
+1;
diff --git a/spec_question.txt b/spec_question.txt
new file mode 100644
index 0000000..f85dce9
--- /dev/null
+++ b/spec_question.txt
@@ -0,0 +1,11 @@
+* should it /sharing only, can it be /xxx/sharing
+
+* how to define point of entry, eg reciever URL?
+
+* in section "Creating a Ticket Sharing Agreement" text
+  says "requester MUST include a X-Ticket-Sharing-Token",
+  but example has no such header.
+
+* Why content type of requests and responses is not 'text/x-json'?
+
+* is access_key of agreement mutable?
diff --git a/t/api/agreement.t b/t/api/agreement.t
new file mode 100644
index 0000000..4bb866d
--- /dev/null
+++ b/t/api/agreement.t
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use RT::Extension::NHD::Test tests => 25;
+use Digest::SHA1 qw(sha1_hex);
+
+{
+    my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+    isa_ok($agreement, 'RT::NHD::Agreement');
+    isa_ok($agreement, 'RT::Record');
+}
+
+my $i;
+
+{
+    my $uuid = sha1_hex( ''. ++$i );
+
+    my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+    my ($id, $msg) = $agreement->Create(
+        UUID => $uuid,
+        Name => 'Test Company',
+        Status => 'pending',
+        Sender => 'http://hoster.example.com/sharing',
+        Receiver => 'http://rt.example.com/sharing',
+        AccessKey => sha1_hex( ''. ++$i ),
+    );
+    ok($id, "Created an agreement $uuid");
+
+    $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+    $agreement->Load( $uuid );
+    ok( $agreement->id, 'loaded agreement' );
+
+    is( $agreement->UUID, $uuid, 'correct value' );
+    is( $agreement->Name, 'Test Company', 'correct value' );
+    is( $agreement->Status, 'pending', 'correct value' );
+    is( $agreement->Sender, 'http://hoster.example.com/sharing', 'correct value' );
+    is( $agreement->Receiver, 'http://rt.example.com/sharing', 'correct value' );
+    like( $agreement->AccessKey, qr{^[0-9a-f]{40}$}i, 'correct value' );
+}
+
+# bad status
+{
+    my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+    my $uuid = sha1_hex( ''. ++$i );
+    my ($id, $msg) = $agreement->Create(
+        UUID => $uuid,
+        Name => 'Test Company',
+        Status => 'booo',
+        Sender => 'http://hoster.example.com/sharing',
+        Receiver => 'http://rt.example.com/sharing',
+        AccessKey => sha1_hex( ''. ++$i ),
+    );
+    ok(!$id, "Couldn't create an agreement $uuid: $msg");
+}
+
+# can only be created with pending status
+{
+    my $agreement = RT::NHD::Agreement->new( RT->SystemUser );
+    my $uuid = sha1_hex( ''. ++$i );
+    my ($id, $msg) = $agreement->Create(
+        UUID => $uuid,
+        Name => 'Test Company',
+        Status => 'accepted',
+        Sender => 'http://hoster.example.com/sharing',
+        Receiver => 'http://rt.example.com/sharing',
+        AccessKey => sha1_hex( ''. ++$i ),
+    );
+    ok(!$id, "Couldn't create an agreement $uuid: $msg");
+}
+

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



More information about the Bps-public-commit mailing list