aboutsummaryrefslogtreecommitdiffstats
path: root/perllib
diff options
context:
space:
mode:
authorMatthew Somerville <matthew@mysociety.org>2020-06-24 22:44:37 +0100
committerM Somerville <matthew-github@dracos.co.uk>2020-11-11 10:29:20 +0000
commit26ca5c069d168eded92b95abd3be0d7b7349b430 (patch)
tree538f93cc1e6d52343a07def933afc9f638b8a7ed /perllib
parentcabc4f91d55b952ab2521ec85ec745de4c354d8c (diff)
[Bromley] Push notification from Echo.
Make sure a 200 response is always sent for a valid notification.
Diffstat (limited to 'perllib')
-rw-r--r--perllib/FixMyStreet/App/Controller/Open311/Updates.pm18
-rw-r--r--perllib/FixMyStreet/App/Controller/Waste.pm107
2 files changed, 122 insertions, 3 deletions
diff --git a/perllib/FixMyStreet/App/Controller/Open311/Updates.pm b/perllib/FixMyStreet/App/Controller/Open311/Updates.pm
index d5754bab7..8881a1b87 100644
--- a/perllib/FixMyStreet/App/Controller/Open311/Updates.pm
+++ b/perllib/FixMyStreet/App/Controller/Open311/Updates.pm
@@ -43,6 +43,12 @@ sub receive : Regex('^open311/v2/servicerequestupdates.(xml|json)$') : Args(0) {
$request->{$_} = $c->get_param($_) || $c->detach('bad_request', [ $_ ]);
}
+ $c->forward('process_update', [ $body, $request ]);
+}
+
+sub process_update : Private {
+ my ($self, $c, $body, $request) = @_;
+
my $updates = Open311::GetServiceRequestUpdates->new(
system_user => $body->comment_user,
current_body => $body,
@@ -51,16 +57,22 @@ sub receive : Regex('^open311/v2/servicerequestupdates.(xml|json)$') : Args(0) {
my $p = $updates->find_problem($request);
$c->detach('bad_request', [ 'not found' ]) unless $p;
- my $comment = $p->comments->search( { external_id => $request->{update_id} } )->first;
- $c->detach('bad_request', [ 'already exists' ]) if $comment;
+ $c->forward('check_existing', [ $p, $request, $updates ]);
- $comment = $updates->process_update($request, $p);
+ my $comment = $updates->process_update($request, $p);
my $data = { service_request_updates => { update_id => $comment->id } };
$c->forward('/open311/format_output', [ $data ]);
}
+sub check_existing : Private {
+ my ($self, $c, $p, $request, $updates) = @_;
+
+ my $comment = $p->comments->search( { external_id => $request->{update_id} } )->first;
+ $c->detach('bad_request', [ 'already exists' ]) if $comment;
+}
+
sub bad_request : Private {
my ($self, $c, $comment) = @_;
$c->response->status(400);
diff --git a/perllib/FixMyStreet/App/Controller/Waste.pm b/perllib/FixMyStreet/App/Controller/Waste.pm
index 7587795c8..152c7c28e 100644
--- a/perllib/FixMyStreet/App/Controller/Waste.pm
+++ b/perllib/FixMyStreet/App/Controller/Waste.pm
@@ -11,6 +11,7 @@ use FixMyStreet::App::Form::Waste::AboutYou;
use FixMyStreet::App::Form::Waste::Request;
use FixMyStreet::App::Form::Waste::Report;
use FixMyStreet::App::Form::Waste::Enquiry;
+use Open311::GetServiceRequestUpdates;
sub auto : Private {
my ( $self, $c ) = @_;
@@ -457,6 +458,112 @@ sub setup_categories_and_bodies : Private {
@$contacts = grep { grep { $_ eq 'Waste' } @{$_->groups} } @$contacts;
}
+sub receive_echo_event_notification : Path('/waste/echo') : Args(0) {
+ my ($self, $c) = @_;
+ $c->stash->{format} = 'xml';
+ $c->response->header(Content_Type => 'application/soap+xml');
+
+ require SOAP::Lite;
+
+ $c->detach('soap_error', [ 'Invalid method', 405 ]) unless $c->req->method eq 'POST';
+
+ my $echo = $c->cobrand->feature('echo');
+ $c->detach('soap_error', [ 'Missing config', 500 ]) unless $echo;
+
+ # Make sure we log entire request for debugging
+ $c->detach('soap_error', [ 'Missing body' ]) unless $c->req->body;
+ my $soap = join('', $c->req->body->getlines);
+ $c->log->info($soap);
+
+ my $body = $c->cobrand->body;
+ $c->detach('soap_error', [ 'Bad jurisdiction' ]) unless $body;
+
+ my $env = SOAP::Deserializer->deserialize($soap);
+
+ my $header = $env->header;
+ $c->detach('soap_error', [ 'Missing SOAP header' ]) unless $header;
+ my $action = $header->{Action};
+ $c->detach('soap_error', [ 'Incorrect Action' ]) unless $action && $action eq $echo->{receive_action};
+ $header = $header->{Security};
+ $c->detach('soap_error', [ 'Missing Security header' ]) unless $header;
+ my $token = $header->{UsernameToken};
+ $c->detach('soap_error', [ 'Authentication failed' ])
+ unless $token && $token->{Username} eq $echo->{receive_username} && $token->{Password} eq $echo->{receive_password};
+
+ my $event = $env->result;
+
+ my $cfg = { echo => Integrations::Echo->new(%$echo) };
+ my $request = $c->cobrand->construct_waste_open311_update($cfg, $event);
+ $request->{updated_datetime} = DateTime::Format::W3CDTF->format_datetime(DateTime->now);
+ $request->{service_request_id} = $event->{Guid};
+
+ my $updates = Open311::GetServiceRequestUpdates->new(
+ system_user => $body->comment_user,
+ current_body => $body,
+ );
+
+ my $p = $updates->find_problem($request);
+ if ($p) {
+ $c->forward('check_existing_update', [ $p, $request, $updates ]);
+ my $comment = $updates->process_update($request, $p);
+ }
+ # Still want to say it is okay, even if we did nothing with it
+ $c->forward('soap_ok');
+}
+
+sub soap_error : Private {
+ my ($self, $c, $comment, $code) = @_;
+ $code ||= 400;
+ $c->response->status($code);
+ my $type = $code == 500 ? 'Server' : 'Client';
+ $c->response->body(SOAP::Serializer->fault($type, "Bad request: $comment", soap_header()));
+}
+
+sub soap_ok : Private {
+ my ($self, $c) = @_;
+ $c->response->status(200);
+ my $method = SOAP::Data->name("NotifyEventUpdatedResponse")->attr({
+ xmlns => "http://www.twistedfish.com/xmlns/echo/api/v1"
+ });
+ $c->response->body(SOAP::Serializer->envelope(method => $method, soap_header()));
+}
+
+sub soap_header {
+ my $attr = "http://www.twistedfish.com/xmlns/echo/api/v1";
+ my $action = "NotifyEventUpdatedResponse";
+ my $header = SOAP::Header->name("Action")->attr({
+ xmlns => 'http://www.w3.org/2005/08/addressing',
+ 'soap:mustUnderstand' => 1,
+ })->value("$attr/ReceiverService/$action");
+
+ my $dt = DateTime->now();
+ my $dt2 = $dt->clone->add(minutes => 5);
+ my $w3c = DateTime::Format::W3CDTF->new;
+ my $header2 = SOAP::Header->name("Security")->attr({
+ 'soap:mustUnderstand' => 'true',
+ 'xmlns' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
+ })->value(
+ \SOAP::Header->name(
+ "Timestamp" => \SOAP::Header->value(
+ SOAP::Header->name('Created', $w3c->format_datetime($dt)),
+ SOAP::Header->name('Expires', $w3c->format_datetime($dt2)),
+ )
+ )->attr({
+ xmlns => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd",
+ })
+ );
+ return ($header, $header2);
+}
+
+sub check_existing_update : Private {
+ my ($self, $c, $p, $request, $updates) = @_;
+
+ my $cfg = { updates => $updates };
+ $c->detach('soap_ok')
+ unless $c->cobrand->waste_check_last_update(
+ $cfg, $p, $request->{status}, $request->{external_status_code});
+}
+
__PACKAGE__->meta->make_immutable;
1;