[Bps-public-commit] rt-extension-rest2 branch, master, updated. 1.07-34-g6b9d3a8
? sunnavy
sunnavy at bestpractical.com
Fri May 1 13:20:27 EDT 2020
The branch, master has been updated
via 6b9d3a89e96d6a98750d3775cf1a84f727793853 (commit)
via cb41cb698e9dfc505840d7732aa786cdbc754873 (commit)
via c00d83344fe6d15ac29e0d5af048e8194be037f0 (commit)
via aff286f50f2d20a8815eeac3988c9ed598a1fea5 (commit)
via b39d88e316f3f0a313a39f5f63b1d2a599d76dbd (commit)
from 77e6be3eca589a1d7bcef2adec78854b7a56eae7 (commit)
Summary of changes:
README | 156 +++++++++++++++++
lib/RT/Extension/REST2.pm | 171 +++++++++++++++++++
lib/RT/Extension/REST2/Resource/Message.pm | 63 ++++++-
xt/attachments.t | 259 +++++++++++++++++++++++++++++
xt/data/image.png | Bin 0 -> 3929 bytes
5 files changed, 645 insertions(+), 4 deletions(-)
create mode 100644 xt/attachments.t
create mode 100644 xt/data/image.png
- Log -----------------------------------------------------------------
commit b39d88e316f3f0a313a39f5f63b1d2a599d76dbd
Author: gibus <gibus at easter-eggs.com>
Date: Thu Oct 18 13:56:49 2018 +0200
Allow attachments as JSON Array with Base64 encoded content
diff --git a/README b/README
index 801bbe0..df683e1 100644
--- a/README
+++ b/README
@@ -193,6 +193,133 @@ USAGE
You may of course choose to ignore the ETag header and not provide
If-Match in your requests; RT doesn't require its use.
+ Replying/Commenting Tickets
+ You can reply to or comment a ticket by POSTing to _url from the
+ correspond or comment hyperlinks that were returned when fetching the
+ ticket.
+
+ curl -X POST
+ -H "Content-Type: application/json"
+ -d '{
+ "Subject" : "response",
+ "Content" : "What is your <em>issue</em>?",
+ "ContentType": "text/html",
+ "TimeTaken" : "1"
+ }'
+ -H 'Authorization: token XX_TOKEN_XX'
+ 'XX_TICKET_URL_XX'/correspond
+
+ Replying or commenting a ticket is quite similar to a ticket creation:
+ you send a POST request, with data encoded in JSON. The difference lies
+ in the properties of the JSON data object you can pass:
+
+ Subject
+ The subject of your response/comment, optional
+
+ Content
+ The content of your response/comment, mandatory unless there is a
+ non empty Attachments property to add at least one attachment to the
+ ticket (see "Add Attachments" section below).
+
+ ContentType
+ The MIME content type of your response/comment, typically text/plain
+ or /text/html, mandatory unless there is a non empty Attachments
+ property to add at least one attachment to the ticket (see "Add
+ Attachments" section below).
+
+ TimeTaken
+ The time, in minutes, you've taken to work on your response/comment,
+ optional.
+
+ Add Attachments
+ You can attach any binary or text file to your response or comment by
+ specifying Attachements property in the JSON object, which should be a
+ JSON array where each item represents a file you want to attach. Each
+ item is a JSON object with the following properties:
+
+ FileName
+ The name of the file to attach to your response/comment, mandatory.
+
+ FileType
+ The MIME type of the file to attach to your response/comment,
+ mandatory.
+
+ FileContent
+ The content, *encoded in MIME Base64* of the file to attach to your
+ response/comment, mandatory.
+
+ The reason why you should encode the content of any file to MIME Base64
+ is that a JSON string value should be a sequence of zero or more Unicode
+ characters. MIME Base64 is a binary-to-text encoding scheme widely used
+ (for eg. by web browser) to send binary data when text data is required.
+ Most popular language have MIME Base64 libraries that you can use to
+ encode the content of your attached files (see MIME::Base64 for Perl).
+ Note that even text files should be MIME Base64 encoded to be passed in
+ the FileContent property.
+
+ Here's a Perl example to send an image and a plain text file attached to
+ a comment:
+
+ #!/usr/bin/perl
+ use strict;
+ use warnings;
+
+ use LWP::UserAgent;
+ use JSON;
+ use MIME::Base64;
+ use Data::Dumper;
+
+ my $url = 'http://rt.local/REST/2.0/ticket/1/comment';
+
+ my $img_path = '/tmp/my_image.png';
+ my $img_content;
+ open my $img_fh, '<', $img_path or die "Cannot read $img_path: $!\n";
+ {
+ local $/;
+ $img_content = <$img_fh>;
+ }
+ close $img_fh;
+ $img_content = MIME::Base64::encode_base64($img_content);
+
+ my $txt_path = '~/.bashrc';
+ my $txt_content;
+ open my $txt_fh, '<', glob($txt_path) or die "Cannot read $txt_path: $!\n";
+ {
+ local $/;
+ $txt_content = <$txt_fh>;
+ }
+ close $txt_fh;
+ $txt_content = MIME::Base64::encode_base64($txt_content);
+
+ my $json = JSON->new->utf8;
+ my $payload = {
+ Content => '<p>I want <b>two</b> <em>attachments</em></p>',
+ ContentType => 'text/html',
+ Subject => 'Attachments in JSON Array',
+ Attachments => [
+ {
+ FileName => 'my_image.png',
+ FileType => 'image/png',
+ FileContent => $img_content,
+ },
+ {
+ FileName => '.bashrc',
+ FileType => 'text/plain',
+ FileContent => $txt_content,
+ },
+ ],
+ };
+
+ my $req = HTTP::Request->new(POST => $url);
+ $req->header('Authorization' => 'token 6-66-66666666666666666666666666666666');
+ $req->header('Content-Type' => 'application/json' );
+ $req->header('Accept' => 'application/json' );
+ $req->content($json->encode($payload));
+
+ my $ua = LWP::UserAgent->new;
+ my $res = $ua->request($req);
+ print Dumper($json->decode($res->content)) . "\n";
+
Summary
RT's REST2 API provides the tools you need to build robust and dynamic
integrations. Tools like ETag/If-Match allow you to avoid conflicts such
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 8a61c1f..998bdf5 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -224,6 +224,148 @@ adding time worked can be automatically be recalculated).
You may of course choose to ignore the C<ETag> header and not provide
C<If-Match> in your requests; RT doesn't require its use.
+=head3 Replying/Commenting Tickets
+
+You can reply to or comment a ticket by C<POST>ing to C<_url> from the
+C<correspond> or C<comment> hyperlinks that were returned when fetching the
+ticket.
+
+ curl -X POST
+ -H "Content-Type: application/json"
+ -d '{
+ "Subject" : "response",
+ "Content" : "What is your <em>issue</em>?",
+ "ContentType": "text/html",
+ "TimeTaken" : "1"
+ }'
+ -H 'Authorization: token XX_TOKEN_XX'
+ 'XX_TICKET_URL_XX'/correspond
+
+Replying or commenting a ticket is quite similar to a ticket creation: you
+send a C<POST> request, with data encoded in C<JSON>. The difference lies in
+the properties of the JSON data object you can pass:
+
+=over 4
+
+=item C<Subject>
+
+The subject of your response/comment, optional
+
+=item C<Content>
+
+The content of your response/comment, mandatory unless there is a non empty
+C<Attachments> property to add at least one attachment to the ticket (see
+L<Add Attachments> section below).
+
+=item C<ContentType>
+
+The MIME content type of your response/comment, typically C<text/plain> or
+C</text/html>, mandatory unless there is a non empty C<Attachments> property
+to add at least one attachment to the ticket (see L<Add Attachments> section
+below).
+
+=item C<TimeTaken>
+
+The time, in minutes, you've taken to work on your response/comment, optional.
+
+=back
+
+=head3 Add Attachments
+
+You can attach any binary or text file to your response or comment by
+specifying C<Attachements> property in the JSON object, which should be a
+JSON array where each item represents a file you want to attach. Each item
+is a JSON object with the following properties:
+
+=over 4
+
+=item C<FileName>
+
+The name of the file to attach to your response/comment, mandatory.
+
+=item C<FileType>
+
+The MIME type of the file to attach to your response/comment, mandatory.
+
+=item C<FileContent>
+
+The content, I<encoded in C<MIME Base64>> of the file to attach to your
+response/comment, mandatory.
+
+=back
+
+The reason why you should encode the content of any file to C<MIME Base64>
+is that a JSON string value should be a sequence of zero or more Unicode
+characters. C<MIME Base64> is a binary-to-text encoding scheme widely used
+(for eg. by web browser) to send binary data when text data is required.
+Most popular language have C<MIME Base64> libraries that you can use to
+encode the content of your attached files (see L<MIME::Base64> for C<Perl>).
+Note that even text files should be C<MIME Base64> encoded to be passed in
+the C<FileContent> property.
+
+Here's a Perl example to send an image and a plain text file attached to a
+comment:
+
+ #!/usr/bin/perl
+ use strict;
+ use warnings;
+
+ use LWP::UserAgent;
+ use JSON;
+ use MIME::Base64;
+ use Data::Dumper;
+
+ my $url = 'http://rt.local/REST/2.0/ticket/1/comment';
+
+ my $img_path = '/tmp/my_image.png';
+ my $img_content;
+ open my $img_fh, '<', $img_path or die "Cannot read $img_path: $!\n";
+ {
+ local $/;
+ $img_content = <$img_fh>;
+ }
+ close $img_fh;
+ $img_content = MIME::Base64::encode_base64($img_content);
+
+ my $txt_path = '~/.bashrc';
+ my $txt_content;
+ open my $txt_fh, '<', glob($txt_path) or die "Cannot read $txt_path: $!\n";
+ {
+ local $/;
+ $txt_content = <$txt_fh>;
+ }
+ close $txt_fh;
+ $txt_content = MIME::Base64::encode_base64($txt_content);
+
+ my $json = JSON->new->utf8;
+ my $payload = {
+ Content => '<p>I want <b>two</b> <em>attachments</em></p>',
+ ContentType => 'text/html',
+ Subject => 'Attachments in JSON Array',
+ Attachments => [
+ {
+ FileName => 'my_image.png',
+ FileType => 'image/png',
+ FileContent => $img_content,
+ },
+ {
+ FileName => '.bashrc',
+ FileType => 'text/plain',
+ FileContent => $txt_content,
+ },
+ ],
+ };
+
+ my $req = HTTP::Request->new(POST => $url);
+ $req->header('Authorization' => 'token 6-66-66666666666666666666666666666666');
+ $req->header('Content-Type' => 'application/json' );
+ $req->header('Accept' => 'application/json' );
+ $req->content($json->encode($payload));
+
+ my $ua = LWP::UserAgent->new;
+ my $res = $ua->request($req);
+ print Dumper($json->decode($res->content)) . "\n";
+
=head3 Summary
RT's REST2 API provides the tools you need to build robust and dynamic
diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index 720791c..a50c5c8 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -4,6 +4,7 @@ use warnings;
use Moose;
use namespace::autoclean;
+use MIME::Base64;
extends 'RT::Extension::REST2::Resource';
use RT::Extension::REST2::Util qw( error_as_json update_custom_fields );
@@ -49,7 +50,20 @@ sub from_json {
my $self = shift;
my $body = JSON::decode_json( $self->request->content );
- if (!$body->{ContentType}) {
+ if ($body->{Attachments}) {
+ foreach my $attachment (@{$body->{Attachments}}) {
+ foreach my $field ('FileName', 'FileType', 'FileContent') {
+ return error_as_json(
+ $self->response,
+ \400, "$field is a required field for each attachment in Attachments")
+ unless $attachment->{$field};
+ }
+ }
+
+ $body->{NoContent} = 1 unless $body->{Content};
+ }
+
+ if (!$body->{NoContent} && !$body->{ContentType}) {
return error_as_json(
$self->response,
\400, "ContentType is a required field for application/json");
@@ -65,11 +79,20 @@ sub add_message {
my $MIME = HTML::Mason::Commands::MakeMIMEEntity(
Interface => 'REST',
- Body => $args{Content} || $self->request->content,
+ $args{NoContent} ? () : (Body => $args{Content} || $self->request->content),
Type => $args{ContentType} || $self->request->content_type,
Subject => $args{Subject},
);
+ # Process attachments
+ foreach my $attachment (@{$args{Attachments}}) {
+ $MIME->attach(
+ Type => $attachment->{FileType},
+ Filename => $attachment->{FileName},
+ Data => MIME::Base64::decode_base64($attachment->{FileContent}),
+ );
+ }
+
my ( $Trans, $msg, $TransObj );
if ($self->type eq 'correspond') {
( $Trans, $msg, $TransObj ) = $self->record->Correspond(
commit aff286f50f2d20a8815eeac3988c9ed598a1fea5
Author: gibus <gibus at easter-eggs.com>
Date: Thu Oct 18 13:57:39 2018 +0200
Add test for attachments as JSON Array with Base64 encoded content
diff --git a/xt/attachments.t b/xt/attachments.t
new file mode 100644
index 0000000..966be4d
--- /dev/null
+++ b/xt/attachments.t
@@ -0,0 +1,150 @@
+use strict;
+use warnings;
+use lib 't/lib';
+use RT::Extension::REST2::Test tests => undef;
+use Test::Deep;
+use MIME::Base64;
+
+my $mech = RT::Extension::REST2::Test->mech;
+my $auth = RT::Extension::REST2::Test->authorization_header;
+my $rest_base_path = '/REST/2.0';
+my $user = RT::Extension::REST2::Test->user;
+
+$user->PrincipalObj->GrantRight(Right => 'CreateTicket');
+$user->PrincipalObj->GrantRight(Right => 'ReplyToTicket');
+$user->PrincipalObj->GrantRight(Right => 'CommentOnTicket');
+$user->PrincipalObj->GrantRight(Right => 'ShowTicket');
+$user->PrincipalObj->GrantRight(Right => 'ShowTicketComments');
+
+my $ticket = RT::Ticket->new($user);
+$ticket->Create(Queue => 'General', Subject => 'hello world');
+my $ticket_id = $ticket->id;
+
+my $image_name = 'image.png';
+my $image_path = RT::Test::get_relocatable_file($image_name, 'data');
+my $image_content;
+open my $fh, '<', $image_path or die "Cannot read $image_path: $!\n";
+{
+ local $/;
+ $image_content = <$fh>;
+}
+close $fh;
+
+$image_content = MIME::Base64::encode_base64($image_content);
+
+# Comment ticket with image and text attachments through JSON Base64
+{
+ my $payload = {
+ Content => 'Have you seen this <b>image</b>',
+ ContentType => 'text/html',
+ Subject => 'HTML comment with PNG image and text file',
+ Attachments => [
+ {
+ FileName => $image_name,
+ FileType => 'image/png',
+ FileContent => $image_content,
+ },
+ {
+ FileName => 'password',
+ FileType => 'text/plain',
+ FileContent => MIME::Base64::encode_base64('Hey this is secret!'),
+ },
+ ],
+ };
+ my $res = $mech->post_json("$rest_base_path/ticket/$ticket_id/comment",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 201);
+ cmp_deeply($mech->json_response, [re(qr/Comments added|Message recorded/)]);
+
+ my $transaction_id = $ticket->Transactions->Last->id;
+ my $attachments = $ticket->Attachments->ItemsArrayRef;
+
+ # 3 attachments + 1 wrapper
+ is(scalar(@$attachments), 4);
+
+ # 1st attachment is wrapper
+ is($attachments->[0]->TransactionId, $transaction_id);
+ is($attachments->[0]->Parent, 0);
+ is($attachments->[0]->Subject, 'HTML comment with PNG image and text file');
+ ok(!$attachments->[0]->Filename);
+ is($attachments->[0]->ContentType, 'multipart/mixed');
+
+ # 2nd attachment is comment's content
+ is($attachments->[1]->Parent, $attachments->[0]->id);
+ is($attachments->[1]->TransactionId, $transaction_id);
+ is($attachments->[1]->ContentType, 'text/html');
+ is($attachments->[1]->ContentEncoding, 'none');
+ is($attachments->[1]->Content, 'Have you seen this <b>image</b>');
+ ok(!$attachments->[1]->Subject);
+
+ # 3rd attachment is image
+ my $expected_encoding = $RT::Handle->BinarySafeBLOBs ? 'none' : 'base64';
+ is($attachments->[2]->Parent, $attachments->[0]->id);
+ is($attachments->[2]->TransactionId, $transaction_id);
+ is($attachments->[2]->ContentType, 'image/png');
+ is($attachments->[2]->ContentEncoding, $expected_encoding);
+ is($attachments->[2]->Filename, $image_name);
+ ok(!$attachments->[2]->Subject);
+
+ # 4th attachment is text file
+ is($attachments->[3]->Parent, $attachments->[0]->id);
+ is($attachments->[3]->TransactionId, $transaction_id);
+ is($attachments->[3]->ContentType, 'text/plain');
+ is($attachments->[3]->ContentEncoding, 'none');
+ is($attachments->[3]->Filename, 'password');
+ is($attachments->[3]->Content, 'Hey this is secret!');
+ ok(!$attachments->[3]->Subject);
+}
+
+#Comment ticket with image attachment and no content
+{
+ my $payload = {
+ Subject => 'No content, just an image',
+ Attachments => [
+ {
+ FileName => $image_name,
+ FileType => 'image/png',
+ FileContent => $image_content,
+ },
+ ],
+ };
+ my $res = $mech->post_json("$rest_base_path/ticket/$ticket_id/comment",
+ $payload,
+ 'Authorization' => $auth,
+ );
+ is($res->code, 201);
+ cmp_deeply($mech->json_response, [re(qr/Comments added|Message recorded/)]);
+
+ my $transaction_id = $ticket->Transactions->Last->id;
+ my @attachments = grep { $_->TransactionId == $transaction_id } @{$ticket->Attachments->ItemsArrayRef};
+
+ # 2 attachments + 1 wrapper
+ is(scalar(@attachments), 3);
+
+ # 1st attachment is wrapper
+ is($attachments[0]->Parent, 0);
+ is($attachments[0]->Subject, 'No content, just an image');
+ ok(!$attachments[0]->Filename);
+ is($attachments[0]->ContentType, 'multipart/mixed');
+
+ # 2nd attachment is empty comment's content
+ is($attachments[1]->Parent, $attachments[0]->id);
+ is($attachments[1]->TransactionId, $transaction_id);
+ is($attachments[1]->ContentType, 'application/octet-stream');
+ ok(!$attachments[1]->ContentEncoding);
+ ok(!$attachments[1]->Content);
+ ok(!$attachments[1]->Subject);
+
+ # 3rd attachment is image
+ my $expected_encoding = $RT::Handle->BinarySafeBLOBs ? 'none' : 'base64';
+ is($attachments[2]->Parent, $attachments[0]->id);
+ is($attachments[2]->TransactionId, $transaction_id);
+ is($attachments[2]->ContentType, 'image/png');
+ is($attachments[2]->ContentEncoding, $expected_encoding);
+ is($attachments[2]->Filename, $image_name);
+ ok(!$attachments[2]->Subject);
+}
+
+done_testing;
diff --git a/xt/data/image.png b/xt/data/image.png
new file mode 100644
index 0000000..8a87374
Binary files /dev/null and b/xt/data/image.png differ
commit c00d83344fe6d15ac29e0d5af048e8194be037f0
Author: gibus <gibus at easter-eggs.com>
Date: Sat Oct 20 11:57:52 2018 +0200
Allow attachments with multipart/form-data
diff --git a/README b/README
index df683e1..9b49e36 100644
--- a/README
+++ b/README
@@ -320,6 +320,35 @@ USAGE
my $res = $ua->request($req);
print Dumper($json->decode($res->content)) . "\n";
+ Encoding the content of attachments file in MIME Base64 has the drawback
+ of adding some processing overhead and to increase the sent data size by
+ around 33%. RT's REST2 API provides another way to attach any binary or
+ text file to your response or comment by POSTing, instead of a JSON
+ request, a multipart/form-data request. This kind of request is similar
+ to what the browser sends when you add attachments in RT's reply or
+ comment form. As its name suggests, a multipart/form-data request
+ message contains a series of parts, each representing a form field. To
+ reply to or comment a ticket, the request has to include a field named
+ JSON, which, as previously, is a JSON object with Subject, Content,
+ ContentType, TimeTaken properties. Files can then be attached by
+ specifying a field named Attachments for each of them, with the content
+ of the file as value and the appropriate MIME type.
+
+ The curl invocation is quite straightforward:
+
+ curl -X POST
+ -H "Content-Type: multipart/form-data"
+ -F 'JSON={
+ "Subject" : "Attachments in multipart/form-data",
+ "Content" : "<p>I want <b>two</b> <em>attachments</em></p>",
+ "ContentType": "text/html",
+ "TimeTaken" : "1"
+ };type=application/json'
+ -F 'Attachments=@/tmp/my_image.png;type=image/png'
+ -F 'Attachments=@/tmp/.bashrc;type=text/plain'
+ -H 'Authorization: token XX_TOKEN_XX'
+ 'XX_TICKET_URL_XX'/comment
+
Summary
RT's REST2 API provides the tools you need to build robust and dynamic
integrations. Tools like ETag/If-Match allow you to avoid conflicts such
diff --git a/lib/RT/Extension/REST2.pm b/lib/RT/Extension/REST2.pm
index 998bdf5..e91273b 100644
--- a/lib/RT/Extension/REST2.pm
+++ b/lib/RT/Extension/REST2.pm
@@ -366,6 +366,35 @@ comment:
my $res = $ua->request($req);
print Dumper($json->decode($res->content)) . "\n";
+Encoding the content of attachments file in C<MIME Base64> has the drawback
+of adding some processing overhead and to increase the sent data size by
+around 33%. RT's REST2 API provides another way to attach any binary or text
+file to your response or comment by C<POST>ing, instead of a JSON request, a
+C<multipart/form-data> request. This kind of request is similar to what the
+browser sends when you add attachments in RT's reply or comment form. As its
+name suggests, a C<multipart/form-data> request message contains a series of
+parts, each representing a form field. To reply to or comment a ticket, the
+request has to include a field named C<JSON>, which, as previously, is a
+JSON object with C<Subject>, C<Content>, C<ContentType>, C<TimeTaken>
+properties. Files can then be attached by specifying a field named
+C<Attachments> for each of them, with the content of the file as value and
+the appropriate MIME type.
+
+The curl invocation is quite straightforward:
+
+ curl -X POST
+ -H "Content-Type: multipart/form-data"
+ -F 'JSON={
+ "Subject" : "Attachments in multipart/form-data",
+ "Content" : "<p>I want <b>two</b> <em>attachments</em></p>",
+ "ContentType": "text/html",
+ "TimeTaken" : "1"
+ };type=application/json'
+ -F 'Attachments=@/tmp/my_image.png;type=image/png'
+ -F 'Attachments=@/tmp/.bashrc;type=text/plain'
+ -H 'Authorization: token XX_TOKEN_XX'
+ 'XX_TICKET_URL_XX'/comment
+
=head3 Summary
RT's REST2 API provides the tools you need to build robust and dynamic
diff --git a/lib/RT/Extension/REST2/Resource/Message.pm b/lib/RT/Extension/REST2/Resource/Message.pm
index a50c5c8..9b05b00 100644
--- a/lib/RT/Extension/REST2/Resource/Message.pm
+++ b/lib/RT/Extension/REST2/Resource/Message.pm
@@ -44,11 +44,43 @@ sub allowed_methods { ['POST'] }
sub charsets_provided { [ 'utf-8' ] }
sub default_charset { 'utf-8' }
sub content_types_provided { [ { 'application/json' => sub {} } ] }
-sub content_types_accepted { [ { 'text/plain' => 'add_message' }, { 'text/html' => 'add_message' }, { 'application/json' => 'from_json' } ] }
+sub content_types_accepted { [ { 'text/plain' => 'add_message' }, { 'text/html' => 'add_message' }, { 'application/json' => 'from_json' }, { 'multipart/form-data' => 'from_multipart' } ] }
+
+sub from_multipart {
+ my $self = shift;
+ my $json_str = $self->request->parameters->{JSON};
+ return error_as_json(
+ $self->response,
+ \400, "JSON is a required field for multipart/form-data")
+ unless $json_str;
+
+ my $json = JSON::decode_json($json_str);
+
+ my @attachments = $self->request->upload('Attachments');
+ foreach my $attachment (@attachments) {
+ open my $filehandle, '<', $attachment->tempname;
+ if (defined $filehandle && length $filehandle) {
+ my ( @content, $buffer );
+ while ( my $bytesread = read( $filehandle, $buffer, 72*57 ) ) {
+ push @content, MIME::Base64::encode_base64($buffer);
+ }
+ close $filehandle;
+
+ push @{$json->{Attachments}},
+ {
+ FileName => $attachment->filename,
+ FileType => $attachment->headers->{'content-type'},
+ FileContent => join("\n", @content),
+ };
+ }
+ }
+
+ return $self->from_json($json);
+}
sub from_json {
my $self = shift;
- my $body = JSON::decode_json( $self->request->content );
+ my $body = shift || JSON::decode_json( $self->request->content );
if ($body->{Attachments}) {
foreach my $attachment (@{$body->{Attachments}}) {
commit cb41cb698e9dfc505840d7732aa786cdbc754873
Author: gibus <gibus at easter-eggs.com>
Date: Sat Oct 20 13:57:18 2018 +0200
Add test for attachments with multipart/form-data
diff --git a/xt/attachments.t b/xt/attachments.t
index 966be4d..99e9b51 100644
--- a/xt/attachments.t
+++ b/xt/attachments.t
@@ -98,7 +98,7 @@ $image_content = MIME::Base64::encode_base64($image_content);
ok(!$attachments->[3]->Subject);
}
-#Comment ticket with image attachment and no content
+# Comment ticket with image attachment and no content through JSON Base64
{
my $payload = {
Subject => 'No content, just an image',
@@ -147,4 +147,113 @@ $image_content = MIME::Base64::encode_base64($image_content);
ok(!$attachments[2]->Subject);
}
+my $json = JSON->new->utf8;
+
+# Comment ticket with image and text attachments through multipart/form-data
+{
+ my $payload = {
+ Content => 'Have you seen this <b>image</b>',
+ ContentType => 'text/html',
+ Subject => 'HTML comment with PNG image and text file',
+ };
+
+ $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1;
+ my $res = $mech->post("$rest_base_path/ticket/$ticket_id/comment",
+ 'Authorization' => $auth,
+ 'Content_Type' => 'form-data',
+ 'Content' => [
+ 'JSON' => $json->encode($payload),
+ 'Attachments' => [$image_path, $image_name, 'Content-Type' => 'image/png'],
+ 'Attachments' => [undef, 'password', 'Content-Type' => 'text/plain', Content => 'Hey this is secret!']]);
+
+ is($res->code, 201);
+ cmp_deeply($mech->json_response, [re(qr/Comments added|Message recorded/)]);
+
+ my $transaction_id = $ticket->Transactions->Last->id;
+ my @attachments = grep { $_->TransactionId == $transaction_id } @{$ticket->Attachments->ItemsArrayRef};
+
+ # 3 attachments + 1 wrapper
+ is(scalar(@attachments), 4);
+
+ # 1st attachment is wrapper
+ is($attachments[0]->TransactionId, $transaction_id);
+ is($attachments[0]->Parent, 0);
+ is($attachments[0]->Subject, 'HTML comment with PNG image and text file');
+ ok(!$attachments[0]->Filename);
+ is($attachments[0]->ContentType, 'multipart/mixed');
+
+ # 2nd attachment is comment's content
+ is($attachments[1]->Parent, $attachments[0]->id);
+ is($attachments[1]->TransactionId, $transaction_id);
+ is($attachments[1]->ContentType, 'text/html');
+ is($attachments[1]->ContentEncoding, 'none');
+ is($attachments[1]->Content, 'Have you seen this <b>image</b>');
+ ok(!$attachments[1]->Subject);
+
+ # 3rd attachment is image
+ my $expected_encoding = $RT::Handle->BinarySafeBLOBs ? 'none' : 'base64';
+ is($attachments[2]->Parent, $attachments[0]->id);
+ is($attachments[2]->TransactionId, $transaction_id);
+ is($attachments[2]->ContentType, 'image/png');
+ is($attachments[2]->ContentEncoding, $expected_encoding);
+ is($attachments[2]->Filename, $image_name);
+ ok(!$attachments[2]->Subject);
+
+ # 4th attachment is text file
+ is($attachments[3]->Parent, $attachments[0]->id);
+ is($attachments[3]->TransactionId, $transaction_id);
+ is($attachments[3]->ContentType, 'text/plain');
+ is($attachments[3]->ContentEncoding, 'none');
+ is($attachments[3]->Filename, 'password');
+ is($attachments[3]->Content, 'Hey this is secret!');
+ ok(!$attachments[3]->Subject);
+}
+
+# Comment ticket with image attachment and no content through multipart/form-data
+{
+ my $payload = {
+ Subject => 'No content, just an image',
+ };
+
+ $HTTP::Request::Common::DYNAMIC_FILE_UPLOAD = 1;
+ my $res = $mech->post("$rest_base_path/ticket/$ticket_id/comment",
+ 'Authorization' => $auth,
+ 'Content_Type' => 'form-data',
+ 'Content' => [
+ 'JSON' => $json->encode($payload),
+ 'Attachments' => [$image_path, $image_name, 'Content-Type' => 'image/png']]);
+
+ is($res->code, 201);
+ cmp_deeply($mech->json_response, [re(qr/Comments added|Message recorded/)]);
+
+ my $transaction_id = $ticket->Transactions->Last->id;
+ my @attachments = grep { $_->TransactionId == $transaction_id } @{$ticket->Attachments->ItemsArrayRef};
+
+ # 2 attachments + 1 wrapper
+ is(scalar(@attachments), 3);
+
+ # 1st attachment is wrapper
+ is($attachments[0]->Parent, 0);
+ is($attachments[0]->Subject, 'No content, just an image');
+ ok(!$attachments[0]->Filename);
+ is($attachments[0]->ContentType, 'multipart/mixed');
+
+ # 2nd attachment is empty comment's content
+ is($attachments[1]->Parent, $attachments[0]->id);
+ is($attachments[1]->TransactionId, $transaction_id);
+ is($attachments[1]->ContentType, 'application/octet-stream');
+ ok(!$attachments[1]->ContentEncoding);
+ ok(!$attachments[1]->Content);
+ ok(!$attachments[1]->Subject);
+
+ # 3rd attachment is image
+ my $expected_encoding = $RT::Handle->BinarySafeBLOBs ? 'none' : 'base64';
+ is($attachments[2]->Parent, $attachments[0]->id);
+ is($attachments[2]->TransactionId, $transaction_id);
+ is($attachments[2]->ContentType, 'image/png');
+ is($attachments[2]->ContentEncoding, $expected_encoding);
+ is($attachments[2]->Filename, $image_name);
+ ok(!$attachments[2]->Subject);
+}
+
done_testing;
commit 6b9d3a89e96d6a98750d3775cf1a84f727793853
Merge: 77e6be3 cb41cb6
Author: sunnavy <sunnavy at bestpractical.com>
Date: Sat May 2 01:13:41 2020 +0800
Merge branch 'upload-attachments'
-----------------------------------------------------------------------
More information about the Bps-public-commit
mailing list