diff options
author | Marius Halden <marius.h@lden.org> | 2021-10-07 13:32:40 +0200 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2021-10-07 13:32:40 +0200 |
commit | 09dacfc6b8bf62addeee16c20b1d90c2a256da96 (patch) | |
tree | 7caa2bf9e92227ab74448f9b746dd28bbcb81b2a /perllib/Integrations | |
parent | 585e57484f9c6332668bf1ac0a6a3b39dbe32223 (diff) | |
parent | cea89fb87a96943708a1db0f646492fbfaaf000f (diff) |
Merge tag 'v3.1' into fiksgatami-devfiksgatami-dev
Diffstat (limited to 'perllib/Integrations')
-rw-r--r-- | perllib/Integrations/Echo.pm | 436 |
1 files changed, 436 insertions, 0 deletions
diff --git a/perllib/Integrations/Echo.pm b/perllib/Integrations/Echo.pm new file mode 100644 index 000000000..d6783677c --- /dev/null +++ b/perllib/Integrations/Echo.pm @@ -0,0 +1,436 @@ +package Integrations::Echo; + +use strict; +use warnings; +use DateTime; +use Moo; +use Tie::IxHash; +use FixMyStreet; + +has attr => ( is => 'ro', default => 'http://www.twistedfish.com/xmlns/echo/api/v1' ); +has action => ( is => 'lazy', default => sub { $_[0]->attr . "/Service/" } ); +has username => ( is => 'ro' ); +has password => ( is => 'ro' ); +has url => ( is => 'ro' ); + +has sample_data => ( is => 'ro', default => 0 ); + +has endpoint => ( + is => 'lazy', + default => sub { + my $self = shift; + $ENV{PERL_LWP_SSL_CA_PATH} = '/etc/ssl/certs'; + SOAP::Lite->soapversion(1.2); + my $soap = SOAP::Lite->on_action( sub { $self->action . $_[1]; } )->proxy($self->url); + $soap->serializer->register_ns("http://schemas.microsoft.com/2003/10/Serialization/Arrays", 'msArray'), + $soap->serializer->register_ns("http://schemas.datacontract.org/2004/07/System", 'dataContract'); + return $soap; + }, +); + +has security => ( + is => 'lazy', + default => sub { + my $self = shift; + SOAP::Header->name("Security")->attr({ + 'mustUnderstand' => 'true', + 'xmlns' => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' + })->value( + \SOAP::Header->name( + "UsernameToken" => \SOAP::Header->value( + SOAP::Header->name('Username', $self->username), + SOAP::Header->name('Password', $self->password), + ) + ) + ); + }, +); + +sub action_hdr { + my ($self, $method) = @_; + SOAP::Header->name("Action")->attr({ + 'xmlns' => 'http://www.w3.org/2005/08/addressing', + })->value( + $self->action . $method + ); +} + +sub call { + my ($self, $method, @params) = @_; + + require SOAP::Lite; + @params = make_soap_structure(@params); + my $res = $self->endpoint->call( + SOAP::Data->name($method)->attr({ xmlns => $self->attr }), + $self->security, + $self->action_hdr($method), + @params + ); + $res = $res->result; + return $res; +} + +# Given a list of task handles as two-value array refs (as returned in e.g. the +# LastInstance part of GetServiceUnitsForObject), returns a list of the +# corresponding tasks. +sub GetTasks { + my $self = shift; + + my @refs; + foreach my $ref (@_) { + tie(my %a, 'Tie::IxHash', + Key => 'Handle', + Type => "Task", + Value => [ + { 'msArray:anyType' => $ref->[0] }, + { 'msArray:anyType' => $ref->[1] }, + ], + ); + push @refs, \%a; + } + + if ($self->sample_data) { + my %lookup = map { $_->[0] . ',' . $_->[1] => 1 } @_; + my $data = []; + push @$data, { + Ref => { Value => { anyType => [ 123, 456 ] } }, + State => { Name => 'Completed' }, + Resolution => { Ref => { Value => { anyType => 187 } }, Name => 'Wrong Bin Out' }, + TaskTypeId => 3216, + CompletedDate => { DateTime => '2020-05-27T10:00:00Z' } + } if $lookup{"123,456"}; + push @$data, { + Ref => { Value => { anyType => [ 234, 567 ] } }, + State => { Name => 'Outstanding' }, + CompletedDate => undef + } if $lookup{"234,567"}; + push @$data, { + Ref => { Value => { anyType => [ 345, 678 ] } }, + State => { Name => 'Not Completed' } + } if $lookup{"345,678"}; + push @$data, { + Ref => { Value => { anyType => [ 456, 789 ] } }, + CompletedDate => undef + } if $lookup{"456,789"}; + return $data; + } + + # This creates XML of the form <taskRefs><ObjectRef>...</ObjectRef><ObjectRef>...</ObjectRef>...</taskRefs> + # uncoverable statement + my $res = $self->call('GetTasks', + taskRefs => [ + map { { ObjectRef => $_ } } @refs + ], + options => { + IncludePoints => 'false', + }, + ); + # uncoverable statement + return force_arrayref($res, 'Task'); +} + +sub _id_ref { + require SOAP::Lite; + my $id = shift; + tie(my %obj, 'Tie::IxHash', + Key => 'Id', + Type => 'PointAddress', + Value => [ + { 'msArray:anyType' => SOAP::Data->value($id) }, + ], + ); + return \%obj; +} + +sub GetPointAddress { + my $self = shift; + my $id = shift; + my $obj = _id_ref($id); + return { + Id => '12345', + SharedRef => { Value => { anyType => '1000000002' } }, + PointType => 'PointAddress', + PointAddressType => { Name => 'House' }, + Coordinates => { GeoPoint => { Latitude => 51.401546, Longitude => 0.015415 } }, + Description => '2 Example Street, Bromley, BR1 1AA', + } if $self->sample_data; + $self->call('GetPointAddress', ref => $obj); +} + +# Given a postcode, returns an arrayref of addresses +sub FindPoints { + my $self = shift; + my $pc = shift; + tie(my %obj, 'Tie::IxHash', + PointType => 'PointAddress', + Postcode => $pc, + ); + return [ + { Description => '1 Example Street, Bromley, BR1 1AA', Id => '11345', SharedRef => { Value => { anyType => 1000000001 } } }, + { Description => '2 Example Street, Bromley, BR1 1AA', Id => '12345', SharedRef => { Value => { anyType => 1000000002 } } }, + { Description => '3 Example Street, Bromley, BR1 1AA', Id => '13345', SharedRef => { Value => { anyType => 1000000003 } } }, + { Description => '4 Example Street, Bromley, BR1 1AA', Id => '14345', SharedRef => { Value => { anyType => 1000000004 } } }, + { Description => '5 Example Street, Bromley, BR1 1AA', Id => '15345', SharedRef => { Value => { anyType => 1000000005 } } }, + ] if $self->sample_data; + my $res = $self->call('FindPoints', query => \%obj); + return force_arrayref($res, 'PointInfo'); +} + +sub GetServiceUnitsForObject { + my $self = shift; + my $id = shift; + my $obj = _id_ref($id); + my $from = DateTime->now->set_time_zone(FixMyStreet->local_time_zone); + return [ { + Id => 1001, + ServiceId => 101, + ServiceName => 'Refuse collection', + ServiceTasks => { ServiceTask => { + Id => 401, + ScheduleDescription => 'every Wednesday', + ServiceTaskSchedules => { ServiceTaskSchedule => { + EndDate => { DateTime => '2050-01-01T00:00:00Z' }, + NextInstance => { + CurrentScheduledDate => { DateTime => '2020-06-03T00:00:00Z' }, + OriginalScheduledDate => { DateTime => '2020-06-03T00:00:00Z' }, + }, + LastInstance => { + OriginalScheduledDate => { DateTime => '2020-05-27T00:00:00Z' }, + CurrentScheduledDate => { DateTime => '2020-05-27T00:00:00Z' }, + Ref => { Value => { anyType => [ 123, 456 ] } }, + }, + } }, + } }, + }, { + Id => 1002, + ServiceId => 537, + ServiceName => 'Paper recycling collection', + ServiceTasks => { ServiceTask => { + Id => 402, + ScheduleDescription => 'every other Wednesday', + ServiceTaskSchedules => { ServiceTaskSchedule => { + EndDate => { DateTime => '2050-01-01T00:00:00Z' }, + NextInstance => { + CurrentScheduledDate => { DateTime => '2020-06-10T00:00:00Z' }, + OriginalScheduledDate => { DateTime => '2020-06-10T00:00:00Z' }, + }, + LastInstance => { + OriginalScheduledDate => { DateTime => '2020-05-27T00:00:00Z' }, + CurrentScheduledDate => { DateTime => '2020-05-27T00:00:00Z' }, + Ref => { Value => { anyType => [ 234, 567 ] } }, + }, + } }, + } }, + }, { + Id => 1003, + ServiceId => 535, + ServiceName => 'Domestic Container Mix Collection', + ServiceTasks => { ServiceTask => { + Id => 403, + ScheduleDescription => 'every other Wednesday', + ServiceTaskSchedules => { ServiceTaskSchedule => { + EndDate => { DateTime => '2050-01-01T00:00:00Z' }, + NextInstance => { + CurrentScheduledDate => { DateTime => '2020-06-03T00:00:00Z' }, + OriginalScheduledDate => { DateTime => '2020-06-03T00:00:00Z' }, + }, + LastInstance => { + OriginalScheduledDate => { DateTime => '2020-05-18T00:00:00Z' }, + CurrentScheduledDate => { DateTime => '2020-05-20T00:00:00Z' }, + Ref => { Value => { anyType => [ 345, 678 ] } }, + }, + } }, + } }, + }, { + Id => 1004, + ServiceId => 542, + ServiceName => 'Food waste collection', + ServiceTasks => { ServiceTask => { + Id => 404, + ScheduleDescription => 'every other Monday', + ServiceTaskSchedules => { ServiceTaskSchedule => [ { + EndDate => { DateTime => '2020-01-01T00:00:00Z' }, + LastInstance => { + OriginalScheduledDate => { DateTime => '2019-12-31T00:00:00Z' }, + CurrentScheduledDate => { DateTime => '2019-12-31T00:00:00Z' }, + }, + }, { + EndDate => { DateTime => '2050-01-01T00:00:00Z' }, + NextInstance => { + CurrentScheduledDate => { DateTime => '2020-06-02T00:00:00Z' }, + OriginalScheduledDate => { DateTime => '2020-06-01T00:00:00Z' }, + }, + LastInstance => { + OriginalScheduledDate => { DateTime => '2020-05-18T00:00:00Z' }, + CurrentScheduledDate => { DateTime => '2020-05-18T00:00:00Z' }, + Ref => { Value => { anyType => [ 456, 789 ] } }, + }, + } ] }, + } }, + } ] if $self->sample_data; + # uncoverable statement + my $res = $self->call('GetServiceUnitsForObject', + objectRef => $obj, + query => ixhash( + From => dt_to_hash($from), + IncludeTaskInstances => 'true', + ), + ); + # uncoverable statement + return force_arrayref($res, 'ServiceUnit'); +} + +sub GetServiceTaskInstances { + my ($self, @tasks) = @_; + + my @objects; + foreach (@tasks) { + my $obj = ixhash( + Key => 'Id', + Type => 'ServiceTask', + Value => [ + { 'msArray:anyType' => $_ }, + ], + ); + push @objects, { ObjectRef => $obj }; + } + my $start = DateTime->now->set_time_zone(FixMyStreet->local_time_zone)->truncate( to => 'day' ); + my $end = $start->clone->add(months => 3); + my $query = ixhash( + From => dt_to_hash($start), + To => dt_to_hash($end), + ); + return [ + { ServiceTaskRef => { Value => { anyType => 401 } }, + Instances => { ScheduledTaskInfo => [ + { CurrentScheduledDate => { DateTime => '2020-07-01T00:00:00Z' } }, + ] } + }, + { ServiceTaskRef => { Value => { anyType => 402 } }, + Instances => { ScheduledTaskInfo => [ + { CurrentScheduledDate => { DateTime => '2020-07-08T00:00:00Z' } }, + ] } + }, + ] if $self->sample_data; + # uncoverable statement + my $res = $self->call('GetServiceTaskInstances', + serviceTaskRefs => \@objects, + query => $query, + ); + return force_arrayref($res, 'ServiceTaskInstances'); +} + +sub GetEvent { + my ($self, $guid) = @_; + $self->call('GetEvent', ref => ixhash( + Key => 'Guid', + Type => 'Event', + Value => { 'msArray:anyType' => $guid }, + )); +} + +sub GetEventType { + my ($self, $id) = @_; + $self->call('GetEventType', ref => ixhash( + Key => 'Id', + Type => 'EventType', + Value => { 'msArray:anyType' => $id }, + )); +} + +sub GetEventsForObject { + my ($self, $type, $id, $event_type) = @_; + my $from = DateTime->now->set_time_zone(FixMyStreet->local_time_zone)->subtract(months => 3); + if ($self->sample_data) { + return [ { + # Missed collection for service 542 (food waste) + EventTypeId => 2100, + ServiceId => 542, + }, { # And a gate not closed + EventTypeId => 2118, + ServiceId => 542, + }, { + # Request for a new paper container, currently out of stock + EventTypeId => 2104, + Data => { ExtensibleDatum => [ + { Value => 2, DatatypeName => 'Source' }, + { + ChildData => { ExtensibleDatum => [ + { Value => 1, DatatypeName => 'Action' }, + { Value => 12, DatatypeName => 'Container Type' }, + ] }, + }, + ] }, + ServiceId => 535, + ResolutionCodeId => 584, + } ] if $type eq 'PointAddress'; + return [ { + # Missed collection for service 537 (paper) + EventTypeId => 2099, + ServiceId => 537, + } ] if $type eq 'ServiceUnit' && $id == 1002; + return []; + } + + # uncoverable statement + my $res = $self->call('GetEventsForObject', + objectRef => ixhash( + Key => 'Id', + Type => $type, + Value => { 'msArray:anyType' => $id }, + ), + query => ixhash( + $event_type ? (EventTypeRef => ixhash( + Key => 'Id', + Type => 'EventType', + Value => { 'msArray:anyType' => $event_type }, + )) : (), + From => dt_to_hash($from), + ), + ); + return force_arrayref($res, 'Event'); +} + +sub ixhash { + tie (my %data, 'Tie::IxHash', @_); + return \%data; +} + +sub dt_to_hash { + my $dt = shift; + my $utc = $dt->clone->set_time_zone('UTC'); + $dt = ixhash( + 'dataContract:DateTime' => $utc->ymd . 'T' . $utc->hms . 'Z', + 'dataContract:OffsetMinutes' => $dt->offset / 60, + ); + return $dt; +} + +sub force_arrayref { + my ($res, $key) = @_; + return [] unless $res; + my $data = $res->{$key}; + return [] unless $data; + $data = [ $data ] unless ref $data eq 'ARRAY'; + return $data; +} + +sub make_soap_structure { + my @out; + for (my $i=0; $i<@_; $i+=2) { + my $name = $_[$i] =~ /:/ ? $_[$i] : $_[$i]; + my $v = $_[$i+1]; + my $val = $v; + my $d = SOAP::Data->name($name); + if (ref $v eq 'HASH') { + $val = \SOAP::Data->value(make_soap_structure(%$v)); + } elsif (ref $v eq 'ARRAY') { + my @map = map { make_soap_structure(%$_) } @$v; + $val = \SOAP::Data->value(SOAP::Data->name('dummy' => @map)); + } + push @out, $d->value($val); + } + return @out; +} + +1; |