diff options
41 files changed, 140 insertions, 3899 deletions
diff --git a/bin/install_perl_modules b/bin/install_perl_modules index d757facb6..74b30fbdd 100755 --- a/bin/install_perl_modules +++ b/bin/install_perl_modules @@ -4,7 +4,7 @@ set -e DIR="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd | sed -e 's/\/bin$//' )" -$DIR/vendor/bin/carton install --deployment --without uk --without zurich --without open311-endpoint +$DIR/vendor/bin/carton install --deployment --without uk --without zurich if ! perl -MImage::Magick -e 'exit()' >/dev/null 2>&1 then @@ -100,15 +100,6 @@ feature 'uk', 'FixMyStreet.com specific requirements' => sub { requires 'SOAP::Lite'; }; -feature 'open311-endpoint', 'Open311::Endpoint specific requirements' => sub { - requires 'Web::Simple'; - requires 'Data::Rx'; - requires 'MooX::HandlesVia'; - requires 'Types::Standard'; - requires 'DateTime::Format::Oracle'; # for EXOR - requires 'Convert::NLS_DATE_FORMAT', '0.06'; # Perl 5.22 upgrade -}; - feature 'zurich', 'Zueri wie neu specific requirements' => sub { # Geocoder requires 'SOAP::Lite'; diff --git a/cpanfile.snapshot b/cpanfile.snapshot index c391e67ce..9fe2f566f 100644 --- a/cpanfile.snapshot +++ b/cpanfile.snapshot @@ -925,13 +925,6 @@ DISTRIBUTIONS Test::Exception 0 Test::More 0 ok 0 - Convert-NLS_DATE_FORMAT-0.06 - pathname: K/KO/KOLIBRIE/Convert-NLS_DATE_FORMAT-0.06.tar.gz - provides: - Convert::NLS_DATE_FORMAT 0.06 - requirements: - Module::Build::Tiny 0.035 - perl 5.006001 Cpanel-JSON-XS-3.0210 pathname: R/RU/RURBAN/Cpanel-JSON-XS-3.0210.tar.gz provides: @@ -1379,74 +1372,6 @@ DISTRIBUTIONS Module::Build 0.35 Test::Exception 0 Test::More 0 - Data-Perl-0.002007 - pathname: M/MA/MATTP/Data-Perl-0.002007.tar.gz - provides: - Data::Perl 0.002007 - Data::Perl::Bool 0.002007 - Data::Perl::Code 0.002007 - Data::Perl::Collection::Array 0.002007 - Data::Perl::Collection::Hash 0.002007 - Data::Perl::Counter 0.002007 - Data::Perl::Number 0.002007 - Data::Perl::Role::Bool 0.002007 - Data::Perl::Role::Code 0.002007 - Data::Perl::Role::Collection::Array 0.002007 - Data::Perl::Role::Collection::Hash 0.002007 - Data::Perl::Role::Counter 0.002007 - Data::Perl::Role::Number 0.002007 - Data::Perl::Role::String 0.002007 - Data::Perl::String 0.002007 - requirements: - Class::Method::Modifiers 0 - ExtUtils::MakeMaker 6.30 - List::MoreUtils 0 - List::Util 0 - Module::Runtime 0 - Role::Tiny 0 - Scalar::Util 0 - parent 0 - strictures 0 - Data-Rx-0.200005 - pathname: R/RJ/RJBS/Data-Rx-0.200005.tar.gz - provides: - Data::Rx 0.200005 - Data::Rx::CommonType 0.200005 - Data::Rx::CommonType::EasyNew 0.200005 - Data::Rx::CoreType 0.200005 - Data::Rx::CoreType::all 0.200005 - Data::Rx::CoreType::any 0.200005 - Data::Rx::CoreType::arr 0.200005 - Data::Rx::CoreType::bool 0.200005 - Data::Rx::CoreType::def 0.200005 - Data::Rx::CoreType::fail 0.200005 - Data::Rx::CoreType::int 0.200005 - Data::Rx::CoreType::map 0.200005 - Data::Rx::CoreType::nil 0.200005 - Data::Rx::CoreType::num 0.200005 - Data::Rx::CoreType::one 0.200005 - Data::Rx::CoreType::rec 0.200005 - Data::Rx::CoreType::seq 0.200005 - Data::Rx::CoreType::str 0.200005 - Data::Rx::Failure 0.200005 - Data::Rx::FailureSet 0.200005 - Data::Rx::TypeBundle 0.200005 - Data::Rx::TypeBundle::Core 0.200005 - Data::Rx::Util 0.200005 - requirements: - Carp 0 - ExtUtils::MakeMaker 6.30 - File::Find::Rule 0 - JSON 2 - List::Util 0 - Number::Tolerant 0 - Scalar::Util 0 - Test::More 0.96 - autodie 0 - overload 0 - parent 0 - strict 0 - warnings 0 Data-Visitor-0.28 pathname: D/DO/DOY/Data-Visitor-0.28.tar.gz provides: @@ -1536,16 +1461,6 @@ DISTRIBUTIONS Module::Build 0 Params::Validate 0.67 Test::More 0.47 - DateTime-Format-Oracle-0.06 - pathname: K/KO/KOLIBRIE/DateTime-Format-Oracle-0.06.tar.gz - provides: - DateTime::Format::Oracle 0.06 - requirements: - Convert::NLS_DATE_FORMAT 0.03 - DateTime 0 - DateTime::Format::Builder 0 - ExtUtils::MakeMaker 0 - Test::More 0 DateTime-Format-Pg-0.16008 pathname: D/DM/DMAKI/DateTime-Format-Pg-0.16008.tar.gz provides: @@ -3960,22 +3875,6 @@ DISTRIBUTIONS Module::Runtime 0.012 Role::Tiny 1.003 strictures 1.004003 - MooX-HandlesVia-0.001005 - pathname: M/MA/MATTP/MooX-HandlesVia-0.001005.tar.gz - provides: - Data::Perl::Bool::MooseLike 0.001005 - Data::Perl::Collection::Array::MooseLike 0.001005 - Data::Perl::Collection::Hash::MooseLike 0.001005 - Data::Perl::Number::MooseLike 0.001005 - Data::Perl::String::MooseLike 0.001005 - MooX::HandlesVia 0.001005 - requirements: - Class::Method::Modifiers 0 - Data::Perl 0.002006 - ExtUtils::MakeMaker 6.30 - Module::Runtime 0 - Moo 1.003000 - Role::Tiny 0 MooX-Types-MooseLike-0.29 pathname: M/MA/MATEU/MooX-Types-MooseLike-0.29.tar.gz provides: @@ -4584,27 +4483,6 @@ DISTRIBUTIONS Carp 0 ExtUtils::MakeMaker 0 POSIX 0 - Number-Tolerant-1.703 - pathname: R/RJ/RJBS/Number-Tolerant-1.703.tar.gz - provides: - Number::Tolerant 1.703 - Number::Tolerant::Constant 1.703 - Number::Tolerant::Type 1.703 - Number::Tolerant::Union 1.703 - Test::Tolerant 1.703 - requirements: - Carp 0 - ExtUtils::MakeMaker 6.30 - Math::BigFloat 0 - Math::BigRat 0 - Scalar::Util 0 - Sub::Exporter 0.950 - Sub::Exporter::Util 0 - Test::Builder 0 - overload 0 - parent 0 - strict 0 - warnings 0 Object-Signature-1.07 pathname: A/AD/ADAMK/Object-Signature-1.07.tar.gz provides: @@ -6068,48 +5946,6 @@ DISTRIBUTIONS constant 0 strict 0 warnings 0 - Type-Tiny-0.040 - pathname: T/TO/TOBYINK/Type-Tiny-0.040.tar.gz - provides: - Devel::TypeTiny::Perl56Compat 0.040 - Devel::TypeTiny::Perl58Compat 0.040 - Error::TypeTiny 0.040 - Error::TypeTiny::Assertion 0.040 - Error::TypeTiny::Compilation 0.040 - Error::TypeTiny::WrongNumberOfParameters 0.040 - Eval::TypeTiny 0.040 - Reply::Plugin::TypeTiny 0.040 - Test::TypeTiny 0.040 - Type::Coercion 0.040 - Type::Coercion::FromMoose 0.040 - Type::Coercion::Union 0.040 - Type::Library 0.040 - Type::Params 0.040 - Type::Parser 0.040 - Type::Registry 0.040 - Type::Tiny 0.040 - Type::Tiny::Class 0.040 - Type::Tiny::Duck 0.040 - Type::Tiny::Enum 0.040 - Type::Tiny::Intersection 0.040 - Type::Tiny::Role 0.040 - Type::Tiny::Union 0.040 - Type::Utils 0.040 - Types::Common::Numeric 0.040 - Types::Common::String 0.040 - Types::Standard 0.040 - Types::Standard::ArrayRef 0.040 - Types::Standard::Dict 0.040 - Types::Standard::HashRef 0.040 - Types::Standard::Map 0.040 - Types::Standard::ScalarRef 0.040 - Types::Standard::Tuple 0.040 - Types::TypeTiny 0.040 - requirements: - CPAN::Meta::Requirements 2.000 - Exporter::Tiny 0.026 - ExtUtils::MakeMaker 6.17 - perl 5.006001 UNIVERSAL-can-1.20140328 pathname: C/CH/CHROMATIC/UNIVERSAL-can-1.20140328.tar.gz provides: diff --git a/perllib/FixMyStreet/App/Controller/Report/New.pm b/perllib/FixMyStreet/App/Controller/Report/New.pm index 6934c6d79..cfb572d83 100644 --- a/perllib/FixMyStreet/App/Controller/Report/New.pm +++ b/perllib/FixMyStreet/App/Controller/Report/New.pm @@ -615,7 +615,7 @@ sub setup_categories_and_bodies : Private { unless ( $seen{$contact->category} ) { push @category_options, $contact->category; - my $metas = $contact->get_extra_fields; + my $metas = $contact->get_metadata_for_input; if (scalar @$metas) { foreach (@$metas) { if (ref $_->{values} eq 'HASH') { @@ -861,7 +861,7 @@ sub process_report : Private { my @extra; foreach my $contact (@contacts) { - my $metas = $contact->get_extra_fields; + my $metas = $contact->get_metadata_for_input; foreach my $field ( @$metas ) { if ( lc( $field->{required} ) eq 'true' ) { unless ( $c->get_param($field->{code}) ) { diff --git a/perllib/FixMyStreet/DB/Result/Contact.pm b/perllib/FixMyStreet/DB/Result/Contact.pm index dab5432c6..58d8e58de 100644 --- a/perllib/FixMyStreet/DB/Result/Contact.pm +++ b/perllib/FixMyStreet/DB/Result/Contact.pm @@ -68,4 +68,15 @@ use namespace::clean -except => [ 'meta' ]; with 'FixMyStreet::Roles::Extra'; +sub get_metadata_for_input { + my $self = shift; + my $id_field = $self->id_field; + return [ grep { $_->{code} !~ /^(easting|northing|$id_field)$/ } @{$self->get_extra_fields} ]; +} + +sub id_field { + my $self = shift; + return $self->get_extra_metadata('id_field') || 'fixmystreet_id'; +} + 1; diff --git a/perllib/FixMyStreet/DB/ResultSet/Problem.pm b/perllib/FixMyStreet/DB/ResultSet/Problem.pm index 488030928..9ce7da1c0 100644 --- a/perllib/FixMyStreet/DB/ResultSet/Problem.pm +++ b/perllib/FixMyStreet/DB/ResultSet/Problem.pm @@ -235,7 +235,7 @@ sub categories_summary { sub send_reports { my ( $rs, $site_override ) = @_; require FixMyStreet::Script::Reports; - FixMyStreet::Script::Reports::send($site_override); + return FixMyStreet::Script::Reports::send($site_override); } 1; diff --git a/perllib/FixMyStreet/Script/Reports.pm b/perllib/FixMyStreet/Script/Reports.pm index 311d8fec4..a51923456 100644 --- a/perllib/FixMyStreet/Script/Reports.pm +++ b/perllib/FixMyStreet/Script/Reports.pm @@ -23,6 +23,7 @@ sub send(;$) { # Set up site, language etc. my ($verbose, $nomail, $debug_mode) = CronFns::options(); + my $test_data; my $base_url = FixMyStreet->config('BASE_URL'); my $site = $site_override || CronFns::site($base_url); @@ -205,18 +206,18 @@ sub send(;$) { for my $sender ( keys %reporters ) { debug_print("sending using " . $sender, $row->id) if $debug_mode; - $result *= $reporters{ $sender }->send( $row, \%h ); - if ( $reporters{ $sender }->unconfirmed_counts) { - foreach my $e (keys %{ $reporters{ $sender }->unconfirmed_counts } ) { - foreach my $c (keys %{ $reporters{ $sender }->unconfirmed_counts->{$e} }) { - $notgot{$e}{$c} += $reporters{ $sender }->unconfirmed_counts->{$e}{$c}; + $sender = $reporters{$sender}; + $result *= $sender->send( $row, \%h ); + if ( $sender->unconfirmed_counts) { + foreach my $e (keys %{ $sender->unconfirmed_counts } ) { + foreach my $c (keys %{ $sender->unconfirmed_counts->{$e} }) { + $notgot{$e}{$c} += $sender->unconfirmed_counts->{$e}{$c}; } } - %note = ( - %note, - %{ $reporters{ $sender }->unconfirmed_notes } - ); + %note = (%note, %{ $sender->unconfirmed_notes }); } + $test_data->{test_req_used} = $sender->open311_test_req_used + if FixMyStreet->test_mode && $sender->can('open311_test_req_used'); } unless ($result) { @@ -272,6 +273,8 @@ sub send(;$) { print "The following reports had problems sending:\n$sending_errors"; } } + + return $test_data; } sub _send_report_sent_email { diff --git a/perllib/FixMyStreet/SendReport/Open311.pm b/perllib/FixMyStreet/SendReport/Open311.pm index bf5ed3e30..ee40f371a 100644 --- a/perllib/FixMyStreet/SendReport/Open311.pm +++ b/perllib/FixMyStreet/SendReport/Open311.pm @@ -12,6 +12,11 @@ use Readonly; Readonly::Scalar my $COUNCIL_ID_OXFORDSHIRE => 2237; Readonly::Scalar my $COUNCIL_ID_WARWICKSHIRE => 2243; Readonly::Scalar my $COUNCIL_ID_GREENWICH => 2493; +Readonly::Scalar my $COUNCIL_ID_BROMLEY => 2482; + +has open311_test_req_used => ( + is => 'rw', +); sub send { my $self = shift; @@ -28,21 +33,11 @@ sub send { my $extended_desc = 1; - # To rollback temporary changes made by this function - my $revert = 0; + my $extra = $row->get_extra_fields(); # Extra bromley fields - if ( $row->bodies_str eq '2482' ) { - - $revert = 1; - - my $extra = $row->get_extra_fields(); - if ( $row->used_map || ( !$row->used_map && !$row->postcode ) ) { - push @$extra, { name => 'northing', value => $h->{northing} }; - push @$extra, { name => 'easting', value => $h->{easting} }; - } + if ( $row->bodies_str eq $COUNCIL_ID_BROMLEY ) { push @$extra, { name => 'report_url', value => $h->{url} }; - push @$extra, { name => 'service_request_id_ext', value => $row->id }; push @$extra, { name => 'report_title', value => $row->title }; push @$extra, { name => 'public_anonymity_required', value => $row->anonymous ? 'TRUE' : 'FALSE' }; push @$extra, { name => 'email_alerts_requested', value => 'FALSE' }; # always false as can never request them @@ -54,41 +49,47 @@ sub send { my ( $firstname, $lastname ) = ( $row->name =~ /(\w+)\.?\s+(.+)/ ); push @$extra, { name => 'last_name', value => $lastname }; } - $row->set_extra_fields( @$extra ); - $always_send_latlong = 0; $send_notpinpointed = 1; - $use_service_as_deviceid = 0; $extended_desc = 0; - } - - # extra Oxfordshire fields: send nearest street, postcode, northing and easting, and the FMS id - if ( $row->bodies_str =~ /\b(?:$COUNCIL_ID_OXFORDSHIRE|$COUNCIL_ID_WARWICKSHIRE)\b/ ) { - - my $extra = $row->get_extra_fields; + } elsif ( $row->bodies_str =~ /\b$COUNCIL_ID_OXFORDSHIRE\b/ ) { + # Oxfordshire doesn't have category metadata to fill these + $extended_desc = 'oxfordshire'; push @$extra, { name => 'external_id', value => $row->id }; push @$extra, { name => 'closest_address', value => $h->{closest_address} } if $h->{closest_address}; if ( $row->used_map || ( !$row->used_map && !$row->postcode ) ) { push @$extra, { name => 'northing', value => $h->{northing} }; push @$extra, { name => 'easting', value => $h->{easting} }; } - $row->set_extra_fields( @$extra ); - - if ($row->bodies_str =~ /$COUNCIL_ID_OXFORDSHIRE/) { - $extended_desc = 'oxfordshire'; - } - elsif ($row->bodies_str =~ /$COUNCIL_ID_WARWICKSHIRE/) { - $extended_desc = 'warwickshire'; - } + } elsif ( $row->bodies_str =~ /\b$COUNCIL_ID_WARWICKSHIRE\b/ ) { + $extended_desc = 'warwickshire'; + push @$extra, { name => 'closest_address', value => $h->{closest_address} } if $h->{closest_address}; + } elsif ( $row->bodies_str == $COUNCIL_ID_GREENWICH ) { + # Greenwich doesn't have category metadata to fill this + push @$extra, { name => 'external_id', value => $row->id }; } - # FIXME: we've already looked this up before + # Try and fill in some ones that we've been asked for, but not asked the user for + my $contact = $row->result_source->schema->resultset("Contact")->find( { deleted => 0, body_id => $body->id, category => $row->category } ); + my $id_field = $contact->id_field; + foreach (@{$contact->get_extra_fields}) { + if ($_->{code} eq $id_field) { + push @$extra, { name => $id_field, value => $row->id }; + } elsif ($_->{code} =~ /^(easting|northing)$/) { + if ( $row->used_map || ( !$row->used_map && !$row->postcode ) ) { + push @$extra, { name => $_->{code}, value => $h->{$_->{code}} }; + } + } + } + + $row->set_extra_fields( @$extra ) if @$extra; + my %open311_params = ( jurisdiction => $conf->jurisdiction, endpoint => $conf->endpoint, @@ -117,25 +118,20 @@ sub send { # non-standard Oxfordshire endpoint (because it's just a script, not a full Open311 service) if ( $row->bodies_str =~ /$COUNCIL_ID_OXFORDSHIRE/ ) { $open311->endpoints( { requests => 'open311_service_request.cgi' } ); - $revert = 1; } # required to get round issues with CRM constraints if ( $row->bodies_str =~ /2218/ ) { $row->user->name( $row->user->id . ' ' . $row->user->name ); - $revert = 1; - } - - if ($row->bodies_str =~ /$COUNCIL_ID_GREENWICH/) { - # Greenwich endpoint expects external_id as an attribute - $row->set_extra_fields( { 'name' => 'external_id', 'value' => $row->id } ); - $revert = 1; } my $resp = $open311->send_service_request( $row, $h, $contact->email ); + if (FixMyStreet->test_mode) { + $self->open311_test_req_used($open311->test_req_used); + } # make sure we don't save user changes from above - $row->discard_changes() if $revert; + $row->discard_changes(); if ( $resp ) { $row->external_id( $resp ); diff --git a/perllib/Open311/Endpoint.pm b/perllib/Open311/Endpoint.pm deleted file mode 100644 index 425a708ef..000000000 --- a/perllib/Open311/Endpoint.pm +++ /dev/null @@ -1,817 +0,0 @@ -package Open311::Endpoint; - -=head1 NAME - -Open311::Endpoint - a generic Open311 endpoint implementation - -=cut - -use Web::Simple; - -use JSON::MaybeXS; -use XML::Simple; - -use Open311::Endpoint::Result; -use Open311::Endpoint::Service; -use Open311::Endpoint::Service::Request; -use Open311::Endpoint::Spark; -use Open311::Endpoint::Schema; - -use MooX::HandlesVia; - -use Data::Dumper; -use Scalar::Util 'blessed'; -use List::Util 'first'; -use Types::Standard ':all'; - -use DateTime::Format::W3CDTF; - -=head1 DESCRIPTION - -An implementation of L<http://wiki.open311.org/GeoReport_v2> with a -dispatcher written as a L<Plack> application, designed to be easily -deployed. - -This is a generic wrapper, designed to be a conformant Open311 server. -However, it knows nothing about your business logic! You should subclass it -and provide the necessary methods. - -=head1 SUBCLASSING - - package My::Open311::Endpoint; - use Web::Simple; - extends 'Open311::Endpoint'; - -See also t/open311/endpoint/Endpoint1.pm and Endpoint2.pm as examples. - -=head2 methods to override - -These are the important methods to override. They are passed a list of -simple arguments, and should generally return objects like -L<Open311::Endpoint::Request>. - - services - service - post_service_request - get_service_requests - get_service_request - requires_jurisdiction_ids - check_jurisdiction_id - -The dispatch framework will take care of actually formatting the output -into conformant XML or JSON. - -TODO document better - -=cut - -sub services { - # this should be overridden in your subclass! - (); -} -sub service { - # this stub implementation is a simple lookup on $self->services, and - # should *probably* be overridden in your subclass! - # (for example, to look up in App DB, with $args->{jurisdiction_id}) - - my ($self, $service_code, $args) = @_; - - return first { $_->service_code eq $service_code } $self->services; -} - -sub post_service_request { - my ($self, $service, $args) = @_; - - die "abstract method post_service_request not overridden"; -} - -sub get_service_requests { - my ($self, $args) = @_; - die "abstract method get_service_requests not overridden"; -} - -sub get_service_request { - my ($self, $service_request_id, $args) = @_; - - die "abstract method get_service_request not overridden"; -} - -sub requires_jurisdiction_ids { - # you may wish to subclass this - return shift->has_multiple_jurisdiction_ids; -} - -sub check_jurisdiction_id { - my ($self, $jurisdiction_id) = @_; - - # you may wish to override this stub implementation which: - # - always succeeds if no jurisdiction_id is set - # - accepts no jurisdiction_id if there is only one set - # - otherwise checks that the id passed is one of those set - # - return 1 unless $self->has_jurisdiction_ids; - - if (! defined $jurisdiction_id) { - return $self->requires_jurisdiction_ids ? 1 : undef; - } - - return first { $jurisdiction_id eq $_ } $self->get_jurisdiction_ids; -} - -=head2 Configurable arguments - - * default_service_notice - default for <service_notice> if not - set by the service or an individual request - * jurisdictions - an array of jurisdiction_ids - you may want to subclass the methods: - - requires_jurisdiction_ids - - check_jurisdiction_id - * default_identifier_type - Open311 doesn't mandate what these types look like, but a backend - server may! The module provides an example identifier type which allows - ascii "word" characters .e.g [a-zA-Z0-9_] as an example default. - You can also override these individually using: - - identifier_types => { - api_key => '//str', # - jurisdiction_id => ... - service_code => ... - service_request_id => ... - # etc. - } - * request_class - class to instantiate for requests via new_request - -=cut - -has default_identifier_type => ( - is => 'ro', - isa => Str, - default => '/open311/example/identifier', -); - -has identifier_types => ( - is => 'ro', - isa => HashRef[Str], - default => sub { {} }, - handles_via => 'Hash', - handles => { - get_identifier_type => 'get', - }, -); - -around get_identifier_type => sub { - my ($orig, $self, $type) = @_; - return $self->$orig($type) // $self->default_identifier_type; -}; - -has default_service_notice => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has jurisdiction_ids => ( - is => 'ro', - isa => Maybe[ArrayRef], - default => sub { [] }, - handles_via => 'Array', - handles => { - has_jurisdiction_ids => 'count', - get_jurisdiction_ids => 'elements', - } -); - -has request_class => ( - is => 'ro', - isa => Str, - default => 'Open311::Endpoint::Service::Request', -); - -sub new_request { - my ($self, %args) = @_; - return $self->request_class->new( %args ); -} - -=head2 Other accessors - -You may additionally wish to replace the following objects. - - * schema - Data::Rx schema for validating Open311 protocol inputs and - outputs - * spark - methods for munging base data-structure for output - * json - JSON output object - * xml - XML::Simple output object - -=cut - -has schema => ( - is => 'lazy', - default => sub { - my $self = shift; - Open311::Endpoint::Schema->new( endpoint => $self ), - }, - handles => { - rx => 'schema', - format_boolean => 'format_boolean', - }, -); - -sub learn_additional_types { - # my ($self, $rx) = @_; - ## no-op, but override in ::Role or implementation! -} - -has spark => ( - is => 'lazy', - default => sub { - Open311::Endpoint::Spark->new(); - }, -); - -has json => ( - is => 'lazy', - default => sub { - JSON->new->pretty->allow_blessed->convert_blessed; - }, -); - -has xml => ( - is => 'lazy', - default => sub { - XML::Simple->new( - NoAttr=> 1, - KeepRoot => 1, - SuppressEmpty => 0, - ); - }, -); - -has w3_dt => ( - is => 'lazy', - default => sub { DateTime::Format::W3CDTF->new }, -); - -has time_zone => ( - is => 'ro', - default => 'Europe/London', -); - -sub maybe_inflate_datetime { - my ($self, $dt) = @_; - return unless $dt; - return $self->w3_dt->parse_datetime($dt); -} - -=head2 Dispatching - -The method dispatch_request returns a list of all the dispatcher routines -that will be checked in turn by L<Web::Simple>. - -You may extend this in a subclass, or with a role. - -=cut - -sub dispatch_request { - my $self = shift; - - sub (.*) { - my ($self, $ext) = @_; - $self->format_response($ext); - }, - - sub (GET + /services + ?*) { - my ($self, $args) = @_; - $self->call_api( GET_Service_List => $args ); - }, - - sub (GET + /services/* + ?*) { - my ($self, $service_id, $args) = @_; - $self->call_api( GET_Service_Definition => $service_id, $args ); - }, - - sub (POST + /requests + %*) { - my ($self, $args) = @_; - $self->call_api( POST_Service_Request => $args ); - }, - - sub (GET + /tokens/*) { - return Open311::Endpoint::Result->error( 400, 'not implemented' ); - }, - - sub (GET + /requests + ?*) { - my ($self, $args) = @_; - $self->call_api( GET_Service_Requests => $args ); - }, - - sub (GET + /requests/* + ?*) { - my ($self, $service_request_id, $args) = @_; - $self->call_api( GET_Service_Request => $service_request_id, $args ); - }, -} - -sub GET_Service_List_input_schema { - return shift->get_jurisdiction_id_validation; -} - -sub GET_Service_List_output_schema { - return { - type => '//rec', - required => { - services => { - type => '//arr', - contents => '/open311/service', - }, - } - }; -} - -sub GET_Service_List { - my ($self, @args) = @_; - - my @services = map { - my $service = $_; - { - keywords => (join ',' => @{ $service->keywords } ), - metadata => $self->format_boolean( $service->has_attributes ), - map { $_ => $service->$_ } - qw/ service_name service_code description type group /, - } - } $self->services; - return { - services => \@services, - }; -} - -sub GET_Service_Definition_input_schema { - my $self = shift; - return { - type => '//seq', - contents => [ - $self->get_identifier_type('service_code'), - $self->get_jurisdiction_id_validation, - ], - }; -} - -sub GET_Service_Definition_output_schema { - return { - type => '//rec', - required => { - service_definition => { - type => '/open311/service_definition', - }, - } - }; -} - -sub GET_Service_Definition { - my ($self, $service_id, $args) = @_; - - my $service = $self->service($service_id, $args) or return; - my $order = 0; - my $service_definition = { - service_definition => { - service_code => $service_id, - attributes => [ - map { - my $attribute = $_; - { - order => ++$order, - variable => $self->format_boolean( $attribute->variable ), - required => $self->format_boolean( $attribute->required ), - $attribute->has_values ? ( - values => [ - map { - my ($key, $name) = @$_; - +{ - key => $key, - name => $name, - } - } sort { $a->[0] cmp $b->[0] } $attribute->values_kv - ]) : (), - map { $_ => $attribute->$_ } - qw/ code datatype datatype_description description /, - } - } $service->get_attributes, - ], - }, - }; - return $service_definition; -} - -sub POST_Service_Request_input_schema { - my ($self, $args) = @_; - - my $service_code = $args->{service_code}; - unless ($service_code && $args->{api_key}) { - # return a simple validator - # to give a nice error message - return { - type => '//rec', - required => { - service_code => $self->get_identifier_type('service_code'), - api_key => $self->get_identifier_type('api_key') }, - rest => '//any', - }; - } - - my $service = $self->service($service_code) - or return; # we can't fetch service, so signal error TODO - - my %attributes = ( required => {}, optional => {} ); - for my $attribute ($service->get_attributes) { - my $section = $attribute->required ? 'required' : 'optional'; - my $key = sprintf 'attribute[%s]', $attribute->code; - my $def = $attribute->schema_definition; - - $attributes{ $section }{ $key } = $def; - } - - # we have to supply at least one of these, but can supply more - my @address_options = ( - { lat => '//num', long => '//num' }, - { address_string => '//str' }, - { address_id => '//str' }, - ); - - my @address_schemas; - while (my $address_required = shift @address_options) { - push @address_schemas, - { - type => '//rec', - required => { - service_code => $self->get_identifier_type('service_code'), - api_key => $self->get_identifier_type('api_key'), - %{ $attributes{required} }, - %{ $address_required }, - $self->get_jurisdiction_id_required_clause, - }, - optional => { - email => '//str', - device_id => '//str', - account_id => '//str', - first_name => '//str', - last_name => '//str', - phone => '//str', - description => '//str', - media_url => '//str', - %{ $attributes{optional} || {}}, - (map %$_, @address_options), - $self->get_jurisdiction_id_optional_clause, - }, - }; - } - - return { - type => '//any', - of => \@address_schemas, - }; -} - -sub POST_Service_Request_output_schema { - my ($self, $args) = @_; - - my $service_code = $args->{service_code}; - my $service = $self->service($service_code); - - my %return_schema = ( - ($service->type eq 'realtime') ? ( service_request_id => $self->get_identifier_type('service_request_id') ) : (), - ($service->type eq 'batch') ? ( token => '//str' ) : (), - ); - - return { - type => '//rec', - required => { - service_requests => { - type => '//arr', - contents => { - type => '//rec', - required => { - %return_schema, - }, - optional => { - service_notice => '//str', - account_id => '//str', - - }, - }, - }, - }, - }; -} - -sub POST_Service_Request { - my ($self, $args) = @_; - - # TODO pass this through instead of calculating again? - my $service_code = $args->{service_code}; - my $service = $self->service($service_code); - - for my $k (keys %$args) { - if ($k =~ /^attribute\[(\w+)\]$/) { - my $value = delete $args->{$k}; - $args->{attributes}{$1} = $value; - } - } - - my @service_requests = $self->post_service_request( $service, $args ); - - return { - service_requests => [ - map { - my $service_notice = - $_->service_notice - || $service->default_service_notice - || $self->default_service_notice; - +{ - ($service->type eq 'realtime') ? ( service_request_id => $_->service_request_id ) : (), - ($service->type eq 'batch') ? ( token => $_->token ) : (), - $service_notice ? ( service_notice => $service_notice ) : (), - $_->has_account_id ? ( account_id => $_->account_id ) : (), - } - } @service_requests, - ], - }; -} - -sub GET_Service_Requests_input_schema { - my $self = shift; - return { - type => '//rec', - required => { - $self->get_jurisdiction_id_required_clause, - }, - optional => { - $self->get_jurisdiction_id_optional_clause,, - service_request_id => { - type => '/open311/comma', - contents => $self->get_identifier_type('service_request_id'), - }, - service_code => { - type => '/open311/comma', - contents => $self->get_identifier_type('service_code'), - }, - start_date => '/open311/datetime', - end_date => '/open311/datetime', - status => { - type => '/open311/comma', - contents => '/open311/status', - }, - }, - }; -} - -sub GET_Service_Requests_output_schema { - my $self = shift; - return { - type => '//rec', - required => { - service_requests => { - type => '//arr', - contents => '/open311/service_request', - }, - }, - }; -} - -sub GET_Service_Requests { - my ($self, $args) = @_; - - my @service_requests = $self->get_service_requests({ - - jurisdiction_id => $args->{jurisdiction_id}, - start_date => $args->{start_date}, - end_date => $args->{end_date}, - - map { - $args->{$_} ? - ( $_ => [ split ',' => $args->{$_} ] ) - : () - } qw/ service_request_id service_code status /, - }); - - $self->format_service_requests(@service_requests); -} - -sub GET_Service_Request_input_schema { - my $self = shift; - return { - type => '//seq', - contents => [ - $self->get_identifier_type('service_request_id'), - { - type => '//rec', - required => { - $self->get_jurisdiction_id_required_clause, - }, - optional => { - $self->get_jurisdiction_id_optional_clause, - } - } - ], - }; -} - -sub GET_Service_Request_output_schema { - my $self = shift; - return { - type => '//rec', - required => { - service_requests => { - type => '//seq', # e.g. a single service_request - contents => [ - '/open311/service_request', - ] - }, - }, - }; -} - -sub GET_Service_Request { - my ($self, $service_request_id, $args) = @_; - - my $service_request = $self->get_service_request($service_request_id, $args); - - $self->format_service_requests($service_request); -} - -sub format_service_requests { - my ($self, @service_requests) = @_; - return { - service_requests => [ - map { - my $request = $_; - +{ - ( - map { - $_ => $request->$_, - } - qw/ - service_request_id - status - service_name - service_code - address - address_id - zipcode - lat - long - media_url - / - ), - ( - map { - if (my $dt = $request->$_) { - $_ => $self->w3_dt->format_datetime( $dt ) - } - else { - () - } - } - qw/ - requested_datetime - updated_datetime - / - ), - ( - map { - my $value = $request->$_; - $value ? ( $_ => $value ) : (), - } - qw/ - description - agency_responsible - service_notice - / - ), - } - } @service_requests, - ], - }; -} - -sub has_multiple_jurisdiction_ids { - return shift->has_jurisdiction_ids > 1; -} - -sub get_jurisdiction_id_validation { - my $self = shift; - - # jurisdiction_id is documented as "Required", but with the note - # 'This is only required if the endpoint serves multiple jurisdictions' - # i.e. it is optional as regards the schema, but the server may choose - # to error if it is not provided. - return { - type => '//rec', - ($self->requires_jurisdiction_ids ? 'required' : 'optional') => { - jurisdiction_id => $self->get_identifier_type('jurisdiction_id'), - }, - }; -} - -sub get_jurisdiction_id_required_clause { - my $self = shift; - $self->requires_jurisdiction_ids ? (jurisdiction_id => $self->get_identifier_type('jurisdiction_id')) : (); -} - -sub get_jurisdiction_id_optional_clause { - my $self = shift; - $self->requires_jurisdiction_ids ? () : (jurisdiction_id => $self->get_identifier_type('jurisdiction_id')); -} - -sub call_api { - my ($self, $api_name, @args) = @_; - - my $api_method = $self->can($api_name) - or die "No such API $api_name!"; - - if (my $input_schema_method = $self->can("${api_name}_input_schema")) { - my $input_schema = $self->$input_schema_method(@args) - or return Open311::Endpoint::Result->error( 400, - 'Bad request' ); - - my $schema = $self->rx->make_schema( $input_schema ); - my $input = (scalar @args == 1) ? $args[0] : [@args]; - eval { - $schema->assert_valid( $input ); - }; - if ($@) { - return Open311::Endpoint::Result->error( 400, - "Error in input for $api_name", - split /\n/, $@, - # map $_->struct, @{ $@->failures }, # bit cheeky, spec suggests it wants strings only - ); - } - } - - my $data = eval { $self->$api_method(@args) } - or return Open311::Endpoint::Result->error( - $@ ? (500 => $@) : (404 => 'Resource not found') - ); - - if (my $output_schema_method = $self->can("${api_name}_output_schema")) { - my $definition = $self->$output_schema_method(@args); - my $schema = $self->rx->make_schema( $definition ); - eval { - $schema->assert_valid( $data ); - }; - if ($@) { - use Data::Dumper; - return Open311::Endpoint::Result->error( 500, - "Error in output for $api_name", - Dumper($data), - split /\n/, $@, - # map $_->struct, @{ $@->failures }, - ); - } - } - - return Open311::Endpoint::Result->success( $data ); -} - -sub format_response { - my ($self, $ext) = @_; - response_filter { - my $response = shift; - return $response unless blessed $response; - my $status = $response->status; - my $data = $response->data; - if ($ext eq 'json') { - return [ - $status, - [ 'Content-Type' => 'application/json' ], - [ $self->json->encode( - $self->spark->process_for_json( $data ) - )] - ]; - } - elsif ($ext eq 'xml') { - return [ - $status, - [ 'Content-Type' => 'text/xml' ], - [ qq(<?xml version="1.0" encoding="utf-8"?>\n), - $self->xml->XMLout( - $self->spark->process_for_xml( $data ) - )], - ]; - } - else { - return [ - 404, - [ 'Content-Type' => 'text/plain' ], - [ 'Bad extension. We support .xml and .json' ], - ] - } - } -} - -=head1 AUTHOR and LICENSE - - hakim@mysociety.org 2014 - -This is released under the same license as FixMyStreet. -see https://github.com/mysociety/fixmystreet/blob/master/LICENSE.txt - -=cut - -__PACKAGE__->run_if_script; diff --git a/perllib/Open311/Endpoint/Integration/Exor.pm b/perllib/Open311/Endpoint/Integration/Exor.pm deleted file mode 100644 index 0d5264115..000000000 --- a/perllib/Open311/Endpoint/Integration/Exor.pm +++ /dev/null @@ -1,458 +0,0 @@ -package Open311::Endpoint::Integration::Exor; -use Web::Simple; -extends 'Open311::Endpoint'; -with 'Open311::Endpoint::Role::mySociety'; -with 'Open311::Endpoint::Role::ConfigFile'; -use DBI; -use MooX::HandlesVia; -use DateTime::Format::Oracle; # default format 'YYYY-MM-DD HH24:MI:SS' # NB: hh24 (not hh) -use Encode qw(from_to); - -# declare our constants, as we may not be able to easily install DBD::Oracle -# on a development system! -# t/open311/endpoint/warwick.t disables DBD::Oracle from loading, so the default -# stubbed values will be used instead: -sub ORA_DATE (); -sub ORA_NUMBER (); -sub ORA_VARCHAR2 (); -no warnings 'redefine'; -use DBD::Oracle qw(:ora_types); - -BEGIN { -*ORA_DATE = *ORA_NUMBER = *ORA_VARCHAR2 = sub () { 1 } - unless $DBD::Oracle::VERSION; -} - -has ora_dt => ( - is => 'lazy', - default => sub { - $ENV{NLS_DATE_FORMAT} = 'YYYY-MM-DD HH24:MI'; - return 'DateTime::Format::Oracle' - }, - # NB: we just return the class name. This is to smooth over odd API, - # for consistency with w3_dt -); - -sub parse_ora_date { - my ($self, $date_string) = @_; - - my $date = $self->ora_dt->parse_datetime( $date_string ); - - # will be in floating time_zone so set - $date->set_time_zone( $self->time_zone ); - - return $date; -} - -has max_limit => ( - is => 'ro', - default => 1000, -); - -has encode_to_win1252 => ( - is => 'ro', - default => 1, -); - -has _connection_details => ( - is => 'lazy', - default => sub { - my $self = shift; - my $DB_HOST = $self->db_host; - my $ORACLE_SID = $self->oracle_sid; - my $DB_PORT = $self->db_port; - my $USERNAME = $self->db_username; - my $PASSWORD = $self->db_password; - return [ "dbi:Oracle:host=$DB_HOST;sid=$ORACLE_SID;port=$DB_PORT", $USERNAME, $PASSWORD ] - }, - handles_via => 'Array', - handles => { - connection_details => 'elements', - dsn => [ get => 0 ], - }, -); - -has dbh => ( - is => 'lazy', - default => sub { - my $self = shift; - return DBI->connect( $self->connection_details ); - } -); - -has db_host => ( - is => 'ro', - default => 'localhost', -); - -has oracle_sid => ( - is => 'ro', - default => '1000', # DUMMY -); - -has db_port => ( - is => 'ro', - default => 1531, -); - -has db_username => ( - is => 'ro', - default => 'FIXMYSTREET', -); - -has db_password => ( - is => 'ro', - default => 'SUPERSEEKRIT', # DUMMY -); - -has strip_control_characters => ( - is => 'ro', - default => 'ruthless', -); - -has testing => ( - is => 'ro', - default => 0, -); - -has ce_cat => ( - is => 'ro', - default => 'DEF', -); - -has ce_class => ( - is => 'ro', - default => 'N/A', -); - -has ce_cpr_id => ( - is => 'ro', - default => 5, -); - -has ce_contact_type => ( - is => 'ro', - default => 'PU', -); - -has ce_status_code => ( - is => 'ro', - default => 'RE', -); - -has ce_compl_user_type => ( - is => 'ro', - default => 'USER', -); - -#------------------------------------------------------------------ -# pem_field_types -# return hash of types by field name: any not explicitly set here -# can be defaulted to VARCHAR2 -#------------------------------------------------------------------ -has get_pem_field_types => ( - is => 'ro', - handles_via => 'Hash', - default => sub { - { - ':ce_incident_datetime' => ORA_DATE, - ':ce_x' => ORA_NUMBER, - ':ce_y' => ORA_NUMBER, - ':ce_date_expires' => ORA_DATE, - ':ce_issue_number' => ORA_NUMBER, - ':ce_status_date' => ORA_DATE, - ':ce_compl_ack_date' => ORA_DATE, - ':ce_compl_peo_date' => ORA_DATE, - ':ce_compl_target' => ORA_DATE, - ':ce_compl_complete' => ORA_DATE, - ':ce_compl_from' => ORA_DATE, - ':ce_compl_to' => ORA_DATE, - ':ce_compl_corresp_date' => ORA_DATE, - ':ce_compl_corresp_deliv_date' => ORA_DATE, - ':ce_compl_no_of_petitioners' => ORA_NUMBER, - ':ce_compl_est_cost' => ORA_NUMBER, - ':ce_compl_adv_cost' => ORA_NUMBER, - ':ce_compl_act_cost' => ORA_NUMBER, - ':ce_compl_follow_up1' => ORA_DATE, - ':ce_compl_follow_up2' => ORA_DATE, - ':ce_compl_follow_uo3' => ORA_DATE, - ':ce_date_time_arrived' => ORA_DATE, - ':error_value' => ORA_NUMBER, - ':ce_doc_id' => ORA_NUMBER, - } - }, - handles => { - get_pem_field_type => 'get', - - }, -); - -sub pem_field_type { - my ($self, $field) = @_; - return $self->get_pem_field_type($field) || ORA_VARCHAR2; -} - - -sub services { - # not currently used as Warwick.pm uses a hardcoded list. - die "TODO"; -} - -sub _strip_ruthless { - my $text = shift or return ''; - $text =~ s/[[:cntrl:]]/ /g; # strip all control chars, simples - return $text; -} - -sub _strip_non_ruthless { - my $text = shift or return ''; - # slightly odd doubly negated character class - $text =~ s/[^\t\n[:^cntrl:]]/ /g; # leave tabs and newlines - return $text; -} -sub strip { - my ($self, $text, $max_len, $prefer_non_ruthless) = @_; - use Carp 'confess'; - confess 'EEEK' unless $self; - if (my $scc = $self->strip_control_characters) { - if ($scc eq 'ruthless') { - $text = _strip_ruthless($text); - } - elsif ($prefer_non_ruthless) { - $text = _strip_non_ruthless($text); - } - else { - $text = _strip_ruthless($text); - } - } - return $max_len ? substr($text, 0, $max_len) : $text; -} - -sub post_service_request { - my ($self, $service, $args) = @_; - die "No such service" unless $service; - - if ($args->{media_url}) { - # don't put URL for full images into the database (because they're too big to see on a Blackberry) - $args->{media_url} =~ s/\.full(\.jpe?g)$/$1/; - $args->{description} .= $self->strip( "\n\n") . 'Photo: ' . $args->{media_url}; - } - my $attributes = $args->{attributes}; - my $location = $attributes->{closest_address}; - - if ($location) { - # strip out everything apart from "Nearest" preamble - $location=~s/(Nearest road)[^:]+:/$1:/; - $location=~s/(Nearest postcode)[^:]+:(.*?)(\(\w+ away\))?\s*(\n|$)/$1: $2/; - } - - my %bindings; - # comments here are suggested values - # field lengths are from OCC's Java portlet - # fixed values (configurable via config) - $bindings{":ce_cat"} = $self->ce_cat; - $bindings{":ce_class"} = $self->ce_class; - $bindings{":ce_contact_type"} = $self->ce_contact_type; - $bindings{":ce_status_code"} = $self->ce_status_code; - $bindings{":ce_compl_user_type"}= $self->ce_compl_user_type; - $bindings{":ce_cpr_id"} = $self->ce_cpr_id; - - # ce_incident_datetime is *not* an optional param, but FMS isn't sending it at the moment - $bindings{":ce_incident_datetime"}=$args->{requested_datetime} - || $self->ora_dt->format_datetime( DateTime->now ); - - # especially FMS-specific: - $bindings{":ce_source"} = "FMS"; # important, and specific to this script! - $bindings{":ce_doc_reference"} = $attributes->{external_id}; # FMS ID - $bindings{":ce_enquiry_type"} = $args->{service_code}; - - # incoming data - $bindings{":ce_x"} = $attributes->{easting}; - $bindings{":ce_y"} = $attributes->{northing}; - $bindings{":ce_forename"} = uc $self->strip($args->{first_name}, 30); # 'CLIFF' - $bindings{":ce_surname"} = uc $self->strip($args->{last_name}, 30); # 'STEWART' - $bindings{":ce_work_phone"} = $self->strip($args->{phone}, 25); # '0117 600 4200' - $bindings{":ce_email"} = uc $self->strip($args->{email}, 50); # 'info@exor.co.uk' - $bindings{":ce_description"} = $self->strip($args->{description}, 1970, 1); # 'Large Pothole' - - # nearest address guesstimate - $bindings{":ce_location"} = $self->strip($location, 254); - - if ($self->testing) { - warn Dumper(\%bindings); use Data::Dumper; - } - - my ($pem_id, $error_value, $error_product) = $self->insert_into_db(\%bindings); - - # if error, maybe need to look it up: - # error_value is the index HER_NO in table HIG_ERRORS, which has messages - # actually err_product not helpful (will always be "DOC") - die "$error_value $error_product" if $error_value || $error_product; - - my $request = $self->new_request( - - # NB: possible race condition between next_request_id and _add_request - # (this is fine for synchronous test-cases) - - service => $service, - service_request_id => $pem_id, - status => 'open', - description => $args->{description}, - agency_responsible => '', - requested_datetime => DateTime->now(), - updated_datetime => DateTime->now(), - address => $args->{address_string} // '', - address_id => $args->{address_id} // '', - media_url => $args->{media_url} // '', - zipcode => $args->{zipcode} // '', - attributes => $attributes, - - ); - - return $request; -} - -sub insert_into_db { - my ($self, $bindings) = @_; - my %bindings = %$bindings; - - my ($pem_id, $error_value, $error_product); - - my $dbh = $self->dbh; - - my $sth = $dbh->prepare(q# - BEGIN - PEM.create_enquiry( - ce_cat => :ce_cat, - ce_class => :ce_class, - ce_cpr_id => :ce_cpr_id, - ce_forename => :ce_forename, - ce_surname => :ce_surname, - ce_contact_type => :ce_contact_type, - ce_location => :ce_location, - ce_work_phone => :ce_work_phone, - ce_email => :ce_email, - ce_description => :ce_description, - ce_enquiry_type => :ce_enquiry_type, - ce_source => :ce_source, - ce_incident_datetime => to_Date(:ce_incident_datetime,'YYYY-MM-DD HH24:MI'), - ce_x => :ce_x, - ce_y => :ce_y, - ce_doc_reference => :ce_doc_reference, - ce_status_code => :ce_status_code, - ce_compl_user_type => :ce_compl_user_type, - error_value => :error_value, - error_product => :error_product, - ce_doc_id => :ce_doc_id); - END; - #); - - foreach my $name (sort keys %bindings) { - next if grep {$name eq $_} (':error_value', ':error_product', ':ce_doc_id'); # return values (see below) - $sth->bind_param( - $name, - $bindings{$name}, - $self->pem_field_type( $name ), - ); - } - # return values are bound explicitly here: - $sth->bind_param_inout(":error_value", \$error_value, 12); #> l_ERROR_VALUE # number - $sth->bind_param_inout(":error_product", \$error_product, 10); #> l_ERROR_PRODUCT (will always be 'DOC') - $sth->bind_param_inout(":ce_doc_id", \$pem_id, 12); #> l_ce_doc_id # number - - # not used, but from the example docs, for reference - # $sth->bind_param(":ce_contact_title", $undef); # 'MR' - # $sth->bind_param(":ce_postcode", $undef); # 'BS11EJ' NB no spaces, upper case - # $sth->bind_param(":ce_building_no", $undef); # '1' - # $sth->bind_param(":ce_building_name", $undef); # 'CLIFTON HEIGHTS' - # $sth->bind_param(":ce_street", $undef); # 'HIGH STREET' - # $sth->bind_param(":ce_town", $undef); # 'BRSITOL' - # $sth->bind_param(":ce_rse_he_id", $undef); #> nm3net.get_ne_id('1200D90970/09001','L') - # $sth->bind_param(":ce_compl_target", $undef); # '08-JAN-2004' - # $sth->bind_param(":ce_compl_corresp_date",$undef); # '02-JAN-2004' - # $sth->bind_param(":ce_compl_corresp_deliv_date", $undef); # '02-JAN-2004' - # $sth->bind_param(":ce_resp_of", $undef); # 'GBOWLER' - # $sth->bind_param(":ce_hct_vip", $undef); # 'CO' - # $sth->bind_param(":ce_hct_home_phone", $undef); # '0117 900 6201' - # $sth->bind_param(":ce_hct_mobile_phone", $undef); # '07111 1111111' - # $sth->bind_param(":ce_compl_remarks", $undef); # remarks (notes) max 254 char - - $sth->execute(); - $dbh->disconnect; - - return ($pem_id, $error_value, $error_product); -} - -sub get_service_request_updates { - my ($self, $args) = @_; - - # ignore jurisdiction_id for now - # - my $start_date = $self->maybe_inflate_datetime( $args->{start_date} ); - my $end_date = $self->maybe_inflate_datetime( $args->{end_date} ); - - unless ($self->testing) { - $start_date = DateTime->now->subtract( days => 1 ) - unless ($start_date or $end_date); - } - - my $w3_dt = $self->w3_dt; - my $ora_dt = $self->ora_dt; - my $ORA_DT_FORMAT = $ora_dt->nls_date_format; - - my @where; - - push @where, sprintf - 'updated_timedate >= to_date(%s, %s)', - $ora_dt->format_datetime($start_date), $ORA_DT_FORMAT - if $start_date; - - push @where, sprintf - 'updated_timedate <= to_date(%s, %s)', - $ora_dt->format_datetime($end_date), $ORA_DT_FORMAT - if $end_date; - - push @where, "(status='OPEN' OR status='CLOSED')" - unless $self->testing; - - my $WHERE_CLAUSE = @where ? - 'WHERE ' . join(' AND ', grep {$_} @where) - : ''; - - my $sql = qq( - SELECT - row_id, - service_request_id, - to_char(updated_timedate, '$ORA_DT_FORMAT'), - status, - description - FROM higatlas.fms_update - $WHERE_CLAUSE - ORDER BY updated_timedate DESC); - - my $limit = $self->max_limit; # also allow testing to modify this? - $sql = "SELECT * FROM ($sql) WHERE ROWNUM <= $limit" if $limit; - - my @data = $self->get_updates_from_sql( $sql ); - - my @updates = map { - Open311::Endpoint::Service::Request::Update->new( - update_id => $_->{row_id}, - service_request_id => $_->{service_request_id}, - updated_datetime => $self->parse_ora_date( $_->{updated_datetime} ), - status => $_->{status}, - description => $_->{description} - ) - } @data; - - return @updates; -} - -sub get_updates_from_sql { - my ($self, $sql) = @_; - my $dbh = $self->dbh; - my $ary_ref = $dbh->selectall_arrayref($sql, { Slice => {} } ); - return @$ary_ref; -} - -1; diff --git a/perllib/Open311/Endpoint/Integration/Warwick.pm b/perllib/Open311/Endpoint/Integration/Warwick.pm deleted file mode 100644 index bc57a8e8c..000000000 --- a/perllib/Open311/Endpoint/Integration/Warwick.pm +++ /dev/null @@ -1,47 +0,0 @@ -package Open311::Endpoint::Integration::Warwick; -use Web::Simple; -extends 'Open311::Endpoint::Integration::Exor'; -use Open311::Endpoint::Service::Exor; - -has '+default_service_notice' => ( - default => 'Warwickshire Open311 Endpoint', -); - -sub services { - # TODO, get this from ::Exor - my @services = ( - # [ BR => 'Bridges' ], - # [ CD => 'Carriageway Defect' ], - # [ CD => 'Roads/Highways' ], - # [ DR => 'Drainage' ], - # [ DS => 'Debris/Spillage' ], - # [ FE => 'Fences' ], - # [ 'F D' => 'Pavements' ], - # [ GC => 'Gully & Catchpits' ], - # [ IS => 'Ice/Snow' ], - # [ MD => 'Mud & Debris' ], - # [ MH => 'Manhole' ], - # [ OS => 'Oil Spillage' ], - # [ OT => 'Other' ], - [ PO => 'Pothole' ], - # [ PD => 'Property Damage' ], - # [ RM => 'Road Marking' ], - # [ SN => 'Road traffic signs' ], - # [ SP => 'Traffic' ], - # [ UT => 'Utilities' ], - # [ VG => 'Vegetation' ], - ); - return map { - my ($code, $name) = @$_; - Open311::Endpoint::Service::Exor->new( - service_code => $code, - service_name => $name, - description => $name, - type => 'realtime', - keywords => [qw/ /], - group => 'highways', - ), - } @services; -} - -1; diff --git a/perllib/Open311/Endpoint/Result.pm b/perllib/Open311/Endpoint/Result.pm deleted file mode 100644 index 61454e749..000000000 --- a/perllib/Open311/Endpoint/Result.pm +++ /dev/null @@ -1,38 +0,0 @@ -package Open311::Endpoint::Result; -use Moo; - -has status => ( - is => 'ro', -); -has data => ( - is => 'ro', -); - -sub success { - my ($class, $data) = @_; - return $class->new({ - status => 200, - data => $data, - }); -} - -sub error { - my ($class, $code, @errors) = @_; - $code ||= 400; - return $class->new({ - status => $code, - data => { - errors => [ - map { - ref $_ eq 'HASH' ? $_ : - { - code => $code, - description => "$_", - } - } @errors, - ], - }, - }); -} - -1; diff --git a/perllib/Open311/Endpoint/Role/ConfigFile.pm b/perllib/Open311/Endpoint/Role/ConfigFile.pm deleted file mode 100644 index 1c4b83355..000000000 --- a/perllib/Open311/Endpoint/Role/ConfigFile.pm +++ /dev/null @@ -1,30 +0,0 @@ -package Open311::Endpoint::Role::ConfigFile; -use Moo::Role; -use Path::Tiny 'path'; -use Carp 'croak'; -use YAML (); -use Types::Standard qw( Maybe Str ); - -has config_file => ( - is => 'ro', - isa => Maybe[Str], -); - -around BUILDARGS => sub { - my $next = shift; - my $class = shift; - - my %args = @_; - if (my $config_file = $args{config_file}) { - my $cfg = path($config_file); - croak "$config_file is not a file" unless $cfg->is_file; - - my $config = YAML::LoadFile($cfg) or croak "Couldn't load config from $config_file"; - return $class->$next(%$config, %args); - } - else { - return $class->$next(%args); - } -}; - -1; diff --git a/perllib/Open311/Endpoint/Role/mySociety.pm b/perllib/Open311/Endpoint/Role/mySociety.pm deleted file mode 100644 index de65baab6..000000000 --- a/perllib/Open311/Endpoint/Role/mySociety.pm +++ /dev/null @@ -1,159 +0,0 @@ -package Open311::Endpoint::Role::mySociety; - -=head1 NAME - -Open311::Endpoint::Role::mySociety - mySociety's proposed Open311 extensions - -=head1 SYNOPSIS - -See mySociety's -L<blog post|https://www.mysociety.org/2013/02/20/open311-extended/> -and -L<proposal|https://github.com/mysociety/FixMyStreet/wiki/Open311-FMS---Proposed-differences-to-Open311> -for a full explanation of the spec extension. - -You can use the extensions as follows: - - package My::Open311::Endpoint; - use Web::Simple; - extends 'Open311::Endpoint'; - with 'Open311::Endpoint::Role::mySociety'; - -You will have to provide implementations of - - get_service_request_updates - post_service_request_update - -You will need to return L<Open311::Endpoint::Service::Request::Update> -objects. However, the root L<Open311::Endpoint::Service::Request> is not -aware of updates, so you may may find it easier to ensure that the ::Service -objects you create (with get_service_request etc.) return -L<Open311::Endpoint::Service::Request::mySociety> objects. - -=cut - -use Moo::Role; -no warnings 'illegalproto'; - -use Open311::Endpoint::Service::Request::mySociety; -has '+request_class' => ( - is => 'ro', - default => 'Open311::Endpoint::Service::Request::mySociety', -); - -around dispatch_request => sub { - my ($orig, $self, @args) = @_; - my @dispatch = $self->$orig(@args); - return ( - @dispatch, - - sub (GET + /servicerequestupdates + ?*) { - my ($self, $args) = @_; - $self->call_api( GET_Service_Request_Updates => $args ); - }, - - sub (POST + /servicerequestupdates + ?*) { - my ($self, $args) = @_; - $self->call_api( POST_Service_Request_Update => $args ); - }, - - ); -}; - -sub GET_Service_Request_Updates_input_schema { - my $self = shift; - return { - type => '//rec', - required => { - $self->get_jurisdiction_id_required_clause, - }, - optional => { - $self->get_jurisdiction_id_optional_clause, - api_key => $self->get_identifier_type('api_key'), - start_date => '/open311/datetime', - end_date => '/open311/datetime', - } - }; -} - -sub GET_Service_Request_Updates_output_schema { - my $self = shift; - return { - type => '//rec', - required => { - service_request_updates => { - type => '//arr', - contents => '/open311/service_request_update', - }, - }, - }; -} - -sub GET_Service_Request_Updates { - my ($self, $args) = @_; - - my @updates = $self->get_service_request_updates({ - jurisdiction_id => $args->{jurisdiction_id}, - start_date => $args->{start_date}, - end_date => $args->{end_date}, - }); - - $self->format_updates(@updates); -} - -sub format_updates { - my ($self, @updates) = @_; - return { - service_request_updates => [ - map { - my $update = $_; - +{ - ( - map { - $_ => $update->$_, - } - qw/ - update_id - service_request_id - status - description - media_url - / - ), - ( - map { - $_ => $self->w3_dt->format_datetime( $update->$_ ), - } - qw/ - updated_datetime - / - ), - } - } @updates - ] - }; -} - -sub get_service_request_updates { - my ($self, $args) = @_; - die "abstract method get_service_request_updates not overridden"; -} - -sub learn_additional_types { - my ($self, $schema) = @_; - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/service_request_update', - { - type => '//rec', - required => { - service_request_id => $self->get_identifier_type('service_request_id'), - update_id => $self->get_identifier_type('update_id'), - status => '/open311/status', - updated_datetime => '/open311/datetime', - description => '//str', - media_url => '//str', - }, - } - ); -} - -1; diff --git a/perllib/Open311/Endpoint/Schema.pm b/perllib/Open311/Endpoint/Schema.pm deleted file mode 100644 index 9a2ad81e5..000000000 --- a/perllib/Open311/Endpoint/Schema.pm +++ /dev/null @@ -1,177 +0,0 @@ -package Open311::Endpoint::Schema; -use Moo; -use Data::Rx; - -use Open311::Endpoint::Schema::Comma; -use Open311::Endpoint::Schema::Regex; - -use Carp 'confess'; -has endpoint => ( - is => 'ro', - handles => [qw/ - get_jurisdiction_id_required_clause - get_jurisdiction_id_optional_clause - get_identifier_type - learn_additional_types - /], -); - -sub enum { - my ($self, $type, @values) = @_; - return { - type => '//any', - of => [ map { - { - type => $type, - value => $_, - } - } @values ], - }; -} - -sub format_boolean { - my ($self, $value) = @_; - return $value ? 'true' : 'false'; -} - -has schema => ( - is => 'lazy', - default => sub { - my $self = shift; - - my $schema = Data::Rx->new({ - sort_keys => 1, - prefix => { - open311 => 'tag:wiki.open311.org,GeoReport_v2:rx/', - }, - type_plugins => [qw( - Open311::Endpoint::Schema::Comma - Open311::Endpoint::Schema::Regex - )], - }); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/bool', - $self->enum( '//str', qw[ true false ] )); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/datetime', - { - type => '/open311/regex', - pattern => qr{ - ^ - \d{4} - \d{2} - \d{2} # yyyy-mm-dd - T - \d{2} : \d{2} : \d{2} # hh:mm:ss - (?: - Z # "Zulu" time, e.g. UTC - | [+-] \d{2} : \d{2} # +/- hh:mm offset - ) - $ - }ax, # use ascii semantics so /d means [0-9], and allow formatting - message => "found value isn't a datetime", - }); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/example/identifier', - { - type => '/open311/regex', - pattern => qr{^ \w+ $}ax, - message => "found value isn't a valid identifier", - }); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/status', - $self->enum( '//str', qw[ open closed ] )); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/post_type', - $self->enum( '//str', qw[ realtime batch blackbox ] )); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/service', - { - type => '//rec', - required => { - service_name => '//str', - type => '/open311/post_type', - metadata => '/open311/bool', - description => '//str', - service_code => '//str', - }, - optional => { - keywords => '//str', - group => '//str', - } - } - ); - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/value', - { - type => '//rec', - required => { - key => '//str', - name => '//str', - } - } - ); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/attribute', - { - type => '//rec', - required => { - code => '//str', - datatype => $self->enum( '//str', qw[ string number datetime text singlevaluelist multivaluelist ] ), - datatype_description => '//str', - description => '//str', - order => '//int', - required => '/open311/bool', - variable => '/open311/bool', - }, - optional => { - values => { - type => '//arr', - contents => '/open311/value', - }, - }, - } - ); - - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/service_definition', - { - type => '//rec', - required => { - service_code => '//str', - attributes => { - type => '//arr', - contents => '/open311/attribute', - } - }, - } - ); - $schema->learn_type( 'tag:wiki.open311.org,GeoReport_v2:rx/service_request', - { - type => '//rec', - required => { - service_request_id => $self->get_identifier_type('service_request_id'), - status => '/open311/status', - service_name => '//str', - service_code => $self->get_identifier_type('service_code'), - requested_datetime => '/open311/datetime', - updated_datetime => '/open311/datetime', - address => '//str', - address_id => '//str', - zipcode => '//str', - lat => '//num', - long => '//num', - media_url => '//str', - }, - optional => { - request => '//str', - description => '//str', - agency_responsible => '//str', - service_notice => '//str', - }, - } - ); - - $self->learn_additional_types($schema); - - return $schema; - }, -); - -1; diff --git a/perllib/Open311/Endpoint/Schema/Comma.pm b/perllib/Open311/Endpoint/Schema/Comma.pm deleted file mode 100644 index f6ac1bcc7..000000000 --- a/perllib/Open311/Endpoint/Schema/Comma.pm +++ /dev/null @@ -1,53 +0,0 @@ -use strict; use warnings; -package Open311::Endpoint::Schema::Comma; -use parent 'Data::Rx::CommonType::EasyNew'; - -use Carp (); - -sub type_uri { - 'tag:wiki.open311.org,GeoReport_v2:rx/comma', -} - -sub guts_from_arg { - my ($class, $arg, $rx) = @_; - $arg ||= {}; - - my $contents = delete $arg->{contents} - or Carp::croak "No contents for comma-separated list"; - my $trim = delete $arg->{trim}; - if (my @unexpected = keys %$arg) { - Carp::croak sprintf "Unknown arguments %s in constructing %s", - (join ',' => @unexpected), $class->type_uri; - } - - return { - trim => $trim, - str_schema => $rx->make_schema('//str'), - subschema => $rx->make_schema( $contents ), - }; -} - -sub assert_valid { - my ($self, $value) = @_; - - $self->{str_schema}->assert_valid( $value ); - - my @values = split ',' => $value; - - my $subschema = $self->{subschema}; - my $trim = $self->{trim}; - - for my $subvalue (@values) { - - if ($self->{trim}) { - $subvalue =~s/^\s*//; - $subvalue =~s/\s*$//; - } - - $subschema->assert_valid( $subvalue ); - } - - return 1; -} - -1; diff --git a/perllib/Open311/Endpoint/Schema/Regex.pm b/perllib/Open311/Endpoint/Schema/Regex.pm deleted file mode 100644 index a79542198..000000000 --- a/perllib/Open311/Endpoint/Schema/Regex.pm +++ /dev/null @@ -1,43 +0,0 @@ -use strict; use warnings; -package Open311::Endpoint::Schema::Regex; -use parent 'Data::Rx::CommonType::EasyNew'; - -use Carp (); - -sub type_uri { - 'tag:wiki.open311.org,GeoReport_v2:rx/regex', -} - -sub guts_from_arg { - my ($class, $arg, $rx) = @_; - $arg ||= {}; - - my $pattern = delete $arg->{pattern}; - my $message = delete $arg->{message}; - if (my @unexpected = keys %$arg) { - Carp::croak sprintf "Unknown arguments %s in constructing %s", - (join ',' => @unexpected), $class->type_uri; - } - - return { - str_schema => $rx->make_schema('//str'), - pattern => qr/$pattern/, - message => $message, - }; -} - -sub assert_valid { - my ($self, $value) = @_; - - $self->{str_schema}->assert_valid( $value ); - - return 1 if $value =~ $self->{pattern}; - - $self->fail({ - error => [ qw(type) ], - message => $self->{message} || "found value doesn't match regex", - value => $value, - }) -} - -1; diff --git a/perllib/Open311/Endpoint/Service.pm b/perllib/Open311/Endpoint/Service.pm deleted file mode 100644 index 2c28c6d79..000000000 --- a/perllib/Open311/Endpoint/Service.pm +++ /dev/null @@ -1,55 +0,0 @@ -package Open311::Endpoint::Service; -use Moo; -use MooX::HandlesVia; -use Types::Standard ':all'; -use namespace::clean; - -has service_name => ( - is => 'ro', - isa => Str, -); - -has service_code => ( - is => 'ro', - isa => Str, -); - -has default_service_notice => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has description => ( - is => 'ro', - isa => Str, -); - -has keywords => ( - is => 'ro', - isa => ArrayRef[Str], - default => sub { [] }, -); - -has group => ( - is => 'ro', - isa => Str, -); - -has type => ( - is => 'ro', - isa => Enum[qw/ realtime batch blackbox /], -); - -has attributes => ( - is => 'ro', - isa => ArrayRef[ InstanceOf['Open311::Endpoint::Service::Attribute'] ], - default => sub { [] }, - handles_via => 'Array', - handles => { - has_attributes => 'count', - get_attributes => 'elements', - } -); - -1; diff --git a/perllib/Open311/Endpoint/Service/Attribute.pm b/perllib/Open311/Endpoint/Service/Attribute.pm deleted file mode 100644 index f88919408..000000000 --- a/perllib/Open311/Endpoint/Service/Attribute.pm +++ /dev/null @@ -1,82 +0,0 @@ -package Open311::Endpoint::Service::Attribute; -use Moo; -use MooX::HandlesVia; -use Types::Standard ':all'; -use namespace::clean; - -# from http://wiki.open311.org/GeoReport_v2#GET_Service_Definition - -# A unique identifier for the attribute -has code => ( - is => 'ro', - isa => Str, -); - -# true denotes that user input is needed -# false means the attribute is only used to present information to the user within the description field -# -# NB: unsure what false means for the rest of the options here, e.g. should remainder of fields by Maybe[] ? -has variable => ( - is => 'ro', - isa => Bool, - default => sub { 1 }, -); - -# Denotes the type of field used for user input. -has datatype => ( - is => 'ro', - isa => Enum[qw/ string number datetime text singlevaluelist multivaluelist /], -); - -has required => ( - is => 'ro', - isa => Bool, -); - -# A description of the datatype which helps the user provide their input -has datatype_description => ( - is => 'ro', - isa => Str, -); - -# A description of the attribute field with instructions for the user to find -# and identify the requested information -has description => ( - is => 'ro', - isa => Str, -); - -# NB: we don't model the "Order" field here, as that's really for the Service -# object to return - -# only relevant for singlevaluelist or multivaluelist -has values => ( - is => 'ro', - isa => HashRef, - default => sub { {} }, - handles_via => 'Hash', - handles => { - get_value => 'get', - get_values => 'keys', - has_values => 'count', - values_kv => 'kv', - } -); - -sub schema_definition { - my $self = shift; - - my @values = map +{ type => '//str', value => $_ }, $self->get_values; - my %schema_types = ( - string => '//str', - number => '//num', - datetime => '//str', # TODO - text => '//str', - singlevaluelist => { type => '//any', of => [@values] }, - multivaluelist => { type => '//arr', of => [@values] }, - ); - - return $schema_types{ $self->datatype }; -} - -1; diff --git a/perllib/Open311/Endpoint/Service/Exor.pm b/perllib/Open311/Endpoint/Service/Exor.pm deleted file mode 100644 index 6261875c1..000000000 --- a/perllib/Open311/Endpoint/Service/Exor.pm +++ /dev/null @@ -1,44 +0,0 @@ -package Open311::Endpoint::Service::Exor; -use Moo; -extends 'Open311::Endpoint::Service'; -use Open311::Endpoint::Service::Attribute; - -has '+attributes' => ( - is => 'ro', - default => sub { [ - Open311::Endpoint::Service::Attribute->new( - code => 'easting', - variable => 0, # set by server - datatype => 'number', - required => 1, - datatype_description => 'a number', - description => 'easting', - ), - Open311::Endpoint::Service::Attribute->new( - code => 'northing', - variable => 0, # set by server - datatype => 'number', - required => 1, - datatype_description => 'a number', - description => 'northing', - ), - Open311::Endpoint::Service::Attribute->new( - code => 'closest_address', - variable => 0, # set by server - datatype => 'string', - required => 1, - datatype_description => 'an address', - description => 'closest address', - ), - Open311::Endpoint::Service::Attribute->new( - code => 'external_id', - variable => 0, # set by server - datatype => 'string', - required => 1, - datatype_description => 'an id', - description => 'external system ID', - ), - ] }, -); - -1; diff --git a/perllib/Open311/Endpoint/Service/Request.pm b/perllib/Open311/Endpoint/Service/Request.pm deleted file mode 100644 index 8dfa5df3a..000000000 --- a/perllib/Open311/Endpoint/Service/Request.pm +++ /dev/null @@ -1,110 +0,0 @@ -package Open311::Endpoint::Service::Request; -use Moo; -use MooX::HandlesVia; -use Types::Standard ':all'; -use namespace::clean; - -has service => ( - is => 'ro', - isa => InstanceOf['Open311::Endpoint::Service'], - handles => [ - qw/ service_code service_name / - ], -); - -has service_request_id => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has token => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has service_notice => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has account_id => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has status => ( - is => 'rw', - isa => Enum[qw/ open closed /], - default => sub { 'open' }, -); - -has description => ( - is => 'ro', - isa => Maybe[Str], -); - -has agency_responsible => ( - is => 'ro', - isa => Maybe[Str], -); - -has requested_datetime => ( - is => 'ro', - isa => Maybe[ InstanceOf['DateTime'] ], - default => sub { DateTime->now() }, -); - -has updated_datetime => ( - is => 'rw', - isa => Maybe[ InstanceOf['DateTime'] ], - default => sub { DateTime->now() }, -); - -has expected_datetime => ( - is => 'ro', - isa => Maybe[ InstanceOf['DateTime'] ], -); - -has address => ( - is => 'ro', - isa => Str, - default => sub { '' }, -); - -has address_id => ( - is => 'ro', - isa => Str, - default => sub { '' }, -); - -has zipcode => ( - is => 'ro', - isa => Str, - default => sub { '' }, -); - -has latlong => ( - is => 'ro', - isa => Tuple[ Num, Num ], - default => sub { [0,0] }, - handles_via => 'Array', - handles => { - #lat => [ get => 0 ], - #long => [ get => 1 ], - } -); - -sub lat { shift->latlong->[0] } -sub long { shift->latlong->[1] } - -has media_url => ( - is => 'ro', - isa => Str, - default => sub { '' }, -); - -1; diff --git a/perllib/Open311/Endpoint/Service/Request/Update.pm b/perllib/Open311/Endpoint/Service/Request/Update.pm deleted file mode 100644 index b881af9ce..000000000 --- a/perllib/Open311/Endpoint/Service/Request/Update.pm +++ /dev/null @@ -1,57 +0,0 @@ -package Open311::Endpoint::Service::Request::Update; -use Moo; -use Types::Standard ':all'; -use namespace::clean; - -sub BUILDARGS { - my ($class, %args) = @_; - my $service_request = delete $args{service_request}; - - if (! $args{status}) { - $args{status} = $service_request->status; - } - - return \%args; -} - -has update_id => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has service_request_id => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has token => ( - is => 'ro', - isa => Maybe[Str], - predicate => 1, -); - -has status => ( - is => 'ro', - isa => Enum[qw/ open closed /], -); - -has description => ( - is => 'ro', - isa => Maybe[Str], -); - -has media_url => ( - is => 'ro', - isa => Str, - default => sub { '' }, -); - -has updated_datetime => ( - is => 'ro', - isa => InstanceOf['DateTime'], - default => sub { DateTime->now() }, -); - -1; diff --git a/perllib/Open311/Endpoint/Service/Request/mySociety.pm b/perllib/Open311/Endpoint/Service/Request/mySociety.pm deleted file mode 100644 index 85e31b26f..000000000 --- a/perllib/Open311/Endpoint/Service/Request/mySociety.pm +++ /dev/null @@ -1,51 +0,0 @@ -package Open311::Endpoint::Service::Request::mySociety; -use Moo; -use MooX::HandlesVia; -extends 'Open311::Endpoint::Service::Request'; - -use DateTime; -use Open311::Endpoint::Service::Request::Update; -use Types::Standard ':all'; - -has updates => ( - is => 'rw', - isa => ArrayRef[InstanceOf['Open311::Endpoint::Service::Request::Update']], - default => sub { [] }, - handles_via => 'Array', - handles => { - _add_update => 'push', - get_updates => 'elements', - get_update => 'get', - has_updates => 'count', - filter_updates => 'grep', - } -); - -sub add_update { - my ($self, %args) = @_; - my $update = Open311::Endpoint::Service::Request::Update->new( - %args, - service_request => $self, - service_request_id => $self->service_request_id, - ); - $self->_add_update($update); -} - -sub last_update { - my $self = shift; - return $self->has_updates ? $self->get_update(-1) : undef; -} - -around updated_datetime => sub { - my ($orig, $self) = @_; - my $last_update = $self->last_update or return; - return $last_update->updated_datetime; -}; - -around status => sub { - my ($orig, $self) = @_; - my $last_update = $self->last_update or return 'open'; - return $last_update->status; -}; - -1; diff --git a/perllib/Open311/Endpoint/Spark.pm b/perllib/Open311/Endpoint/Spark.pm deleted file mode 100644 index 292a66996..000000000 --- a/perllib/Open311/Endpoint/Spark.pm +++ /dev/null @@ -1,117 +0,0 @@ -package Open311::Endpoint::Spark; -use Moo; - -=head1 NAME - -Open311::Endpoint::Spark - transform from canonical data-structure to XML or JSON - -=head1 SUMMARY - -The Open311 docs discuss the Spark convention, to transform between XML and JSON. - - http://wiki.open311.org/JSON_and_XML_Conversion#The_Spark_Convention - -These options seem fragile, and require starting with the verbose XML form, -which isn't really natural in Perl. Instead, we'll start with a standard -Perl data structure, with a single extra hash wrapper, and will: - - * for JSON, remove the outside hash wrapper - - * for XML, for arrays, insert an extra layer with the singular name: - (this is the way XML::Simple knows how to do this nesting) - -So: - - # FROM - { - foo => { - bars => [ 1, 2, 3 ] - } - } - - # JSON (note the 'foo' has been removed - { - bars: [ - 1, - 2, - 3 - ] - } - - # XML intermediate - { - foo => { - bars => { - bar => [ 1, 2, 3 ] - } - } - } - - # XML result - <foo> - <bars> - <bar>1</bar> - <bar>2</bar> - <bar>3</bar> - </bars> - </foo> - -=cut - -sub process_for_json { - my ($self, $data) = @_; - if (ref $data eq 'HASH' and scalar keys %$data == 1) { - my $inner = $data->{ (keys %$data)[0] }; - $data = $inner if ref $inner; - } - return $data; -} - -sub process_for_xml { - my ($self, $data) = @_; - - # NB: in place mutation - _process_for_xml($data); - return $data; -} - -# NB: in place mutation -sub _process_for_xml { - my $data = shift; - return unless ref $data; - - if (ref $data eq 'HASH') { - while ( my ($k, $v) = each %$data) { - if (ref $v eq 'ARRAY') { - my $singular = _singularize($k); - # add extra layer - $data->{$k} = { - $singular => $v, - }; - } - _process_for_xml($v); - } - } - elsif (ref $data eq 'ARRAY') { - for my $item (@$data) { - _process_for_xml($item); - } - } -} - -my %singular_map = ( - service_requests => 'request', - service_request_updates => 'request_update', -); - -sub _singularize { - my $name = shift; - return $singular_map{ $name } - || do { - # strip final 's' if present - $name =~ s/s$//; - return $name; - }; -} - -1; diff --git a/perllib/Open311/PopulateServiceList.pm b/perllib/Open311/PopulateServiceList.pm index 949e2baa9..c5f17334b 100644 --- a/perllib/Open311/PopulateServiceList.pm +++ b/perllib/Open311/PopulateServiceList.pm @@ -227,26 +227,26 @@ sub _add_meta_to_contact { # for attributes which we *don't* want to display to the user (e.g. as # fields in "category_extras" + if ($self->_current_body->name eq 'Bromley Council') { + $contact->set_extra_metadata( id_field => 'service_request_id_ext'); + } elsif ($self->_current_body->name eq 'Warwickshire County Council') { + $contact->set_extra_metadata( id_field => 'external_id'); + } + my %override = ( #2482 'Bromley Council' => [qw( - service_request_id_ext requested_datetime report_url title last_name email - easting - northing report_title public_anonymity_required email_alerts_requested ) ], #2243, 'Warwickshire County Council' => [qw( - external_id - easting - northing closest_address ) ], ); diff --git a/t/app/controller/admin_permissions.t b/t/app/controller/admin_permissions.t index bb4a95718..3809a9d67 100644 --- a/t/app/controller/admin_permissions.t +++ b/t/app/controller/admin_permissions.t @@ -33,7 +33,9 @@ my $dt = DateTime->new( second => 23 ); -my ($report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test'); +my ($report) = $mech->create_problems_for_body(1, $oxfordshire->id, 'Test', { + areas => ',2237,', +}); my $report_id = $report->id; ok $report, "created test report - $report_id"; diff --git a/t/cobrand/bromley.t b/t/cobrand/bromley.t index e39bcbe4c..43d936684 100644 --- a/t/cobrand/bromley.t +++ b/t/cobrand/bromley.t @@ -2,17 +2,25 @@ use strict; use warnings; use Test::More; +use CGI::Simple; use FixMyStreet::TestMech; my $mech = FixMyStreet::TestMech->new; # Create test data my $user = $mech->create_user_ok( 'bromley@example.com' ); my $body = $mech->create_body_ok( 2482, 'Bromley Council', id => 2482 ); -$mech->create_contact_ok( +my $contact = $mech->create_contact_ok( body_id => $body->id, category => 'Other', email => 'LIGHT', ); +$contact->set_extra_metadata(id_field => 'service_request_id_ext'); +$contact->set_extra_fields( + { code => 'easting', datatype => 'number', }, + { code => 'northing', datatype => 'number', }, + { code => 'service_request_id_ext', datatype => 'number', }, +); +$contact->update; my @reports = $mech->create_problems_for_body( 1, $body->id, 'Test', { cobrand => 'bromley', @@ -45,15 +53,24 @@ subtest 'testing special Open311 behaviour', sub { $report->set_extra_fields(); $report->update; $body->update( { send_method => 'Open311', endpoint => 'http://bromley.endpoint.example.com', jurisdiction => 'FMS', api_key => 'test' } ); + my $test_data; FixMyStreet::override_config { SEND_REPORTS_ON_STAGING => 1, + ALLOWED_COBRANDS => [ 'fixmystreet', 'bromley' ], }, sub { - FixMyStreet::DB->resultset('Problem')->send_reports(); + $test_data = FixMyStreet::DB->resultset('Problem')->send_reports(); }; $report->discard_changes; ok $report->whensent, 'Report marked as sent'; is $report->send_method_used, 'Open311', 'Report sent via Open311'; is $report->external_id, 248, 'Report has right external ID'; + + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + is $c->param('attribute[easting]'), 529025, 'Request had easting'; + is $c->param('attribute[northing]'), 179716, 'Request had northing'; + is $c->param('attribute[service_request_id_ext]'), $report->id, 'Request had correct ID'; + is $c->param('jurisdiction_id'), 'FMS', 'Request had correct jurisdiction'; }; for my $test ( diff --git a/t/open311/endpoint.t b/t/open311/endpoint.t deleted file mode 100644 index a2a4ea83e..000000000 --- a/t/open311/endpoint.t +++ /dev/null @@ -1,351 +0,0 @@ -use strict; use warnings; - -use Test::More; -use Test::LongString; -use Test::MockTime ':all'; - -use Open311::Endpoint; -use Data::Dumper; -use JSON::MaybeXS; - -use t::open311::endpoint::Endpoint1; - -my $endpoint = t::open311::endpoint::Endpoint1->new; - -subtest "GET Service List" => sub { - my $res = $endpoint->run_test_request( GET => '/services.xml' ); - ok $res->is_success, 'xml success' - or diag $res->content; - is_string $res->content, <<CONTENT, 'xml string ok'; -<?xml version="1.0" encoding="utf-8"?> -<services> - <service> - <description>Pothole Repairs Service</description> - <group>highways</group> - <keywords>deep,hole,wow</keywords> - <metadata>true</metadata> - <service_code>POT</service_code> - <service_name>Pothole Repairs</service_name> - <type>realtime</type> - </service> - <service> - <description>Bin Enforcement Service</description> - <group>sanitation</group> - <keywords>bin</keywords> - <metadata>false</metadata> - <service_code>BIN</service_code> - <service_name>Bin Enforcement</service_name> - <type>realtime</type> - </service> -</services> -CONTENT - - $res = $endpoint->run_test_request( GET => '/services.json' ); - ok $res->is_success, 'json success'; - is_deeply decode_json($res->content), - [ { - "keywords" => "deep,hole,wow", - "group" => "highways", - "service_name" => "Pothole Repairs", - "type" => "realtime", - "metadata" => "true", - "description" => "Pothole Repairs Service", - "service_code" => "POT" - }, { - "keywords" => "bin", - "group" => "sanitation", - "service_name" => "Bin Enforcement", - "type" => "realtime", - "metadata" => "false", - "description" => "Bin Enforcement Service", - "service_code" => "BIN" - } ], 'json structure ok'; - -}; - -subtest "GET Service Definition" => sub { - my $res = $endpoint->run_test_request( GET => '/services/POT.xml' ); - ok $res->is_success, 'xml success', - or diag $res->content; - is_string $res->content, <<CONTENT, 'xml string ok'; -<?xml version="1.0" encoding="utf-8"?> -<service_definition> - <attributes> - <attribute> - <code>depth</code> - <datatype>number</datatype> - <datatype_description>an integer</datatype_description> - <description>depth of pothole, in centimetres</description> - <order>1</order> - <required>true</required> - <variable>true</variable> - </attribute> - <attribute> - <code>shape</code> - <datatype>singlevaluelist</datatype> - <datatype_description>square | circle | triangle</datatype_description> - <description>shape of the pothole</description> - <order>2</order> - <required>false</required> - <values> - <value> - <name>Circle</name> - <key>circle</key> - </value> - <value> - <name>Square</name> - <key>square</key> - </value> - <value> - <name>Triangle</name> - <key>triangle</key> - </value> - </values> - <variable>true</variable> - </attribute> - </attributes> - <service_code>POT</service_code> -</service_definition> -CONTENT - - $res = $endpoint->run_test_request( GET => '/services/POT.json' ); - ok $res->is_success, 'json success'; - is_deeply decode_json($res->content), - { - "service_code" => "POT", - "attributes" => [ - { - "order" => 1, - "code" => "depth", - "required" => "true", - "variable" => "true", - "datatype_description" => "an integer", - "description" => "depth of pothole, in centimetres", - "datatype" => "number", - }, - { - "order" => 2, - "code" => "shape", - "variable" => "true", - "datatype_description" => "square | circle | triangle", - "description" => "shape of the pothole", - "required" => "false", - "datatype" => "singlevaluelist", - "values" => [ - { - "name" => "Circle", - "key" => "circle" - }, - { - "name" => "Square", - "key" => "square" - }, - { - "name" => "Triangle", - "key" => "triangle" - }, - ], - } - ], - }, 'json structure ok'; -}; - -subtest "POST Service Request validation" => sub { - my $res = $endpoint->run_test_request( - POST => '/requests.json', - ); - ok ! $res->is_success, 'no service_code'; - - $res = $endpoint->run_test_request( - POST => '/requests.json', - service_code => 'BIN', - ); - ok ! $res->is_success, 'no api_key'; - - $res = $endpoint->run_test_request( - POST => '/requests.json', - api_key => 'test', - service_code => 'BADGER', # has moved the goalposts - ); - ok ! $res->is_success, 'bad service_code'; - - $res = $endpoint->run_test_request( - POST => '/requests.json', - api_key => 'test', - service_code => 'POT', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - ); - ok ! $res->is_success, 'no required attributes'; - - $res = $endpoint->run_test_request( - POST => '/requests.json', - api_key => 'test', - service_code => 'POT', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - 'attribute[depth]' => 100, - 'attribute[shape]' => 'starfish', - ); - ok ! $res->is_success, 'bad attribute'; -}; - -subtest "POST Service Request valid test" => sub { - - set_fixed_time('2014-01-01T12:00:00Z'); - my $res = $endpoint->run_test_request( - POST => '/requests.json', - api_key => 'test', - service_code => 'POT', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - 'attribute[depth]' => 100, - 'attribute[shape]' => 'triangle', - ); - ok $res->is_success, 'valid request' - or diag $res->content; - - is_deeply decode_json($res->content), - [ { - "service_notice" => "This is a test service", - "service_request_id" => 0 - } ], 'correct json returned'; - - set_fixed_time('2014-02-01T12:00:00Z'); - $res = $endpoint->run_test_request( - POST => '/requests.xml', - api_key => 'test', - service_code => 'POT', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - 'attribute[depth]' => 100, - 'attribute[shape]' => 'triangle', - ); - - ok $res->is_success, 'valid request' - or diag $res->content; - - is_string $res->content, <<CONTENT, 'xml string ok'; -<?xml version="1.0" encoding="utf-8"?> -<service_requests> - <request> - <service_notice>This is a test service</service_notice> - <service_request_id>1</service_request_id> - </request> -</service_requests> -CONTENT -}; - -subtest "GET Service Requests" => sub { - - my $res = $endpoint->run_test_request( GET => '/requests.xml', ); - ok $res->is_success, 'valid request' - or die $res->content; - my $xml = <<CONTENT; -<?xml version="1.0" encoding="utf-8"?> -<service_requests> - <request> - <address>22 Acacia Avenue</address> - <address_id></address_id> - <lat>0</lat> - <long>0</long> - <media_url></media_url> - <requested_datetime>2014-01-01T12:00:00Z</requested_datetime> - <service_code>POT</service_code> - <service_name>Pothole Repairs</service_name> - <service_request_id>0</service_request_id> - <status>open</status> - <updated_datetime>2014-01-01T12:00:00Z</updated_datetime> - <zipcode></zipcode> - </request> - <request> - <address>22 Acacia Avenue</address> - <address_id></address_id> - <lat>0</lat> - <long>0</long> - <media_url></media_url> - <requested_datetime>2014-02-01T12:00:00Z</requested_datetime> - <service_code>POT</service_code> - <service_name>Pothole Repairs</service_name> - <service_request_id>1</service_request_id> - <status>open</status> - <updated_datetime>2014-02-01T12:00:00Z</updated_datetime> - <zipcode></zipcode> - </request> -</service_requests> -CONTENT - - is_string $res->content, $xml, 'xml string ok'; - - $res = $endpoint->run_test_request( GET => '/requests.xml?service_code=POT', ); - ok $res->is_success, 'valid request'; - - is_string $res->content, $xml, 'xml string ok POT' - or diag $res->content; - - $res = $endpoint->run_test_request( GET => '/requests.xml?service_code=BIN', ); - ok $res->is_success, 'valid request'; - is_string $res->content, <<CONTENT, 'xml string ok BIN (no requests)'; -<?xml version="1.0" encoding="utf-8"?> -<service_requests> -</service_requests> -CONTENT -}; - -subtest "GET Service Request" => sub { - my @req=(<<REQ0,<<REQ1); -<?xml version="1.0" encoding="utf-8"?> -<service_requests> - <request> - <address>22 Acacia Avenue</address> - <address_id></address_id> - <lat>0</lat> - <long>0</long> - <media_url></media_url> - <requested_datetime>2014-01-01T12:00:00Z</requested_datetime> - <service_code>POT</service_code> - <service_name>Pothole Repairs</service_name> - <service_request_id>0</service_request_id> - <status>open</status> - <updated_datetime>2014-01-01T12:00:00Z</updated_datetime> - <zipcode></zipcode> - </request> -</service_requests> -REQ0 -<?xml version="1.0" encoding="utf-8"?> -<service_requests> - <request> - <address>22 Acacia Avenue</address> - <address_id></address_id> - <lat>0</lat> - <long>0</long> - <media_url></media_url> - <requested_datetime>2014-02-01T12:00:00Z</requested_datetime> - <service_code>POT</service_code> - <service_name>Pothole Repairs</service_name> - <service_request_id>1</service_request_id> - <status>open</status> - <updated_datetime>2014-02-01T12:00:00Z</updated_datetime> - <zipcode></zipcode> - </request> -</service_requests> -REQ1 - - my $res = $endpoint->run_test_request( GET => '/requests/0.xml', ); - ok $res->is_success, 'valid request'; - - is_string $res->content, $req[0], 'Request 0 ok' - or diag $res->content;; - - $res = $endpoint->run_test_request( GET => '/requests/1.xml', ); - ok $res->is_success, 'valid request'; - - is_string $res->content, $req[1], 'Request 1 ok'; -}; - -restore_time(); -done_testing; diff --git a/t/open311/endpoint/Endpoint1.pm b/t/open311/endpoint/Endpoint1.pm deleted file mode 100644 index ae12172b8..000000000 --- a/t/open311/endpoint/Endpoint1.pm +++ /dev/null @@ -1,114 +0,0 @@ -package t::open311::endpoint::Endpoint1; -use Web::Simple; -extends 'Open311::Endpoint'; -use Types::Standard ':all'; -use MooX::HandlesVia; - -use Open311::Endpoint::Service; -use t::open311::endpoint::ServiceType1; -use Open311::Endpoint::Service::Attribute; -use Open311::Endpoint::Service::Request; - -sub services { - return ( - t::open311::endpoint::ServiceType1->new( - service_code => 'POT', - service_name => 'Pothole Repairs', - description => 'Pothole Repairs Service', - attributes => [ - Open311::Endpoint::Service::Attribute->new( - code => 'depth', - required => 1, - datatype => 'number', - datatype_description => 'an integer', - description => 'depth of pothole, in centimetres', - ), - Open311::Endpoint::Service::Attribute->new( - code => 'shape', - required => 0, - datatype => 'singlevaluelist', - datatype_description => 'square | circle | triangle', - description => 'shape of the pothole', - values => { - square => 'Square', - circle => 'Circle', - triangle => 'Triangle', - }, - ), - ], - type => 'realtime', - keywords => [qw/ deep hole wow/], - group => 'highways', - ), - t::open311::endpoint::ServiceType1->new( - service_code => 'BIN', - service_name => 'Bin Enforcement', - description => 'Bin Enforcement Service', - attributes => [], - type => 'realtime', - keywords => [qw/ bin /], - group => 'sanitation', - ) - ); -} - -# FOR TESTING, we'll just maintain requests in a *global* array... -# obviously a real Service driver will use a DB or API call! -{ - our @SERVICE_REQUESTS; - has _requests => ( - is => 'ro', - isa => ArrayRef[ InstanceOf[ 'Open311::Endpoint::Service::Request' ] ], - default => sub { \@SERVICE_REQUESTS }, - handles_via => 'Array', - handles => { - next_request_id => 'count', - _add_request => 'push', - get_request => 'get', - get_requests => 'elements', - filter_requests => 'grep', - } - ); -} - -sub post_service_request { - my ($self, $service, $args) = @_; - - my $request = $self->new_request( - - # NB: possible race condition between next_request_id and _add_request - # (this is fine for synchronous test-cases) - - service => $service, - service_request_id => $self->next_request_id, - status => 'open', - description => $args->{description}, - agency_responsible => '', - requested_datetime => DateTime->now(), - updated_datetime => DateTime->now(), - address => $args->{address_string} // '', - address_id => $args->{address_id} // '', - media_url => $args->{media_url} // '', - zipcode => $args->{zipcode} // '', - # NB: other info is passed in that would be stored by an Open311 - # endpoint, see Open311::Endpoint::Service::Request for full list, - # but we don't need to handle all of those in this test - ); - $self->_add_request( $request ); - - return ( $request ); -} - -sub get_service_requests { - my ($self, $args) = @_; - - my $service_code = $args->{service_code} or return $self->get_requests; - return $self->filter_requests( sub { my $c = shift->service->service_code; grep { $_ eq $c } @$service_code }); -} - -sub get_service_request { - my ($self, $service_request_id, $args) = @_; - return $self->get_request( $service_request_id ); -} - -1; diff --git a/t/open311/endpoint/Endpoint2.pm b/t/open311/endpoint/Endpoint2.pm deleted file mode 100644 index 664a7f3d6..000000000 --- a/t/open311/endpoint/Endpoint2.pm +++ /dev/null @@ -1,25 +0,0 @@ -package t::open311::endpoint::Endpoint2; -use Web::Simple; -extends 't::open311::endpoint::Endpoint1'; -with 'Open311::Endpoint::Role::mySociety'; - -sub get_service_request_updates { - my ($self, $args) = @_; - - my $start_date = $self->maybe_inflate_datetime($args->{start_date}); - my $end_date = $self->maybe_inflate_datetime($args->{end_date}); - - my @requests = $self->filter_requests( sub { $_[0]->has_updates } ); - - return map { - $_->filter_updates( sub { - my $update = shift; - my $updated_datetime = $update->updated_datetime or return; - if ($start_date) { return unless $updated_datetime >= $start_date } - if ($end_date) { return unless $updated_datetime <= $end_date } - return 1; - }); - } @requests; -} - -1; diff --git a/t/open311/endpoint/Endpoint_Warwick.pm b/t/open311/endpoint/Endpoint_Warwick.pm deleted file mode 100644 index c097f177e..000000000 --- a/t/open311/endpoint/Endpoint_Warwick.pm +++ /dev/null @@ -1,31 +0,0 @@ -package t::open311::endpoint::Endpoint_Warwick; -use Web::Simple; - -our %BINDINGS; -our $UPDATES_SQL; - -extends 'Open311::Endpoint::Integration::Warwick'; - -sub insert_into_db { - my ($self, $bindings) = @_; - - %BINDINGS = %$bindings; - # return ($pem_id, $error_value, $error_product); - return (1001); -} - -sub get_updates_from_sql { - my ($self, $sql) = @_; - $UPDATES_SQL = $sql; - return ( - { - row_id => 999, - service_request_id => 1001, - updated_datetime => '2014-07-23 11:07:00', - status => 'closed', - description => 'Closed the ticket', - } - ); -} - -1; diff --git a/t/open311/endpoint/ServiceType1.pm b/t/open311/endpoint/ServiceType1.pm deleted file mode 100644 index e35e44fda..000000000 --- a/t/open311/endpoint/ServiceType1.pm +++ /dev/null @@ -1,10 +0,0 @@ -package t::open311::endpoint::ServiceType1; -use Moo; -extends 'Open311::Endpoint::Service'; -use DateTime; - -has '+default_service_notice' => ( - default => 'This is a test service', -); - -1; diff --git a/t/open311/endpoint/config1.yml b/t/open311/endpoint/config1.yml deleted file mode 100644 index c444f32c5..000000000 --- a/t/open311/endpoint/config1.yml +++ /dev/null @@ -1 +0,0 @@ -foo: baz diff --git a/t/open311/endpoint/configfile.t b/t/open311/endpoint/configfile.t deleted file mode 100644 index 7f41468ba..000000000 --- a/t/open311/endpoint/configfile.t +++ /dev/null @@ -1,23 +0,0 @@ -use strict; use warnings; - -BEGIN { - package Foo; - use Moo; - with 'Open311::Endpoint::Role::ConfigFile'; - - has foo => ( is => 'ro', default => 'foo' ); -} - -package main; -use Test::More; - -is +Foo->new->foo, - 'foo', 'sanity'; -is +Foo->new( foo => 'bar')->foo, - 'bar', 'override'; -is +Foo->new( config_file => 't/open311/endpoint/config1.yml' )->foo, - 'baz', 'with config'; -is +Foo->new( config_file => 't/open311/endpoint/config1.yml', foo => 'qux' )->foo, - 'qux', 'with config, overridden'; - -done_testing; diff --git a/t/open311/endpoint/exor/DBD/Oracle.pm b/t/open311/endpoint/exor/DBD/Oracle.pm deleted file mode 100644 index d84580d10..000000000 --- a/t/open311/endpoint/exor/DBD/Oracle.pm +++ /dev/null @@ -1,8 +0,0 @@ -package DBD::Oracle; # test -use strict; use warnings; - -sub ORA_DATE () { 2 }; -sub ORA_NUMBER () { 3 } ; -sub ORA_VARCHAR2 () { 5 }; - -1; diff --git a/t/open311/endpoint/mysociety.t b/t/open311/endpoint/mysociety.t deleted file mode 100644 index d0ad60602..000000000 --- a/t/open311/endpoint/mysociety.t +++ /dev/null @@ -1,160 +0,0 @@ -use strict; use warnings; - -use Test::More; -use Test::LongString; -use Test::MockTime ':all'; - -use Open311::Endpoint; -use Data::Dumper; -use JSON::MaybeXS; - -use t::open311::endpoint::Endpoint2; - -my $endpoint = t::open311::endpoint::Endpoint2->new; - -subtest "POST OK" => sub { - diag "Serves as sanity test of subclassing, as well as preparing our data"; - # TODO, refactor repeated code lifted from t/open311/endpoint.t - - set_fixed_time('2014-01-01T12:00:00Z'); - my $res = $endpoint->run_test_request( - POST => '/requests.json', - api_key => 'test', - service_code => 'POT', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - 'attribute[depth]' => 100, - 'attribute[shape]' => 'triangle', - ); - ok $res->is_success, 'valid request' - or diag $res->content; - - is_deeply decode_json($res->content), - [ { - "service_notice" => "This is a test service", - "service_request_id" => 0 - } ], 'correct json returned'; - - set_fixed_time('2014-02-01T12:00:00Z'); - $res = $endpoint->run_test_request( - POST => '/requests.xml', - api_key => 'test', - service_code => 'POT', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - 'attribute[depth]' => 100, - 'attribute[shape]' => 'triangle', - ); - - ok $res->is_success, 'valid request' - or diag $res->content; -}; - -subtest "GET Service Request Updates" => sub { - - my $empty_xml = <<CONTENT; -<?xml version="1.0" encoding="utf-8"?> -<service_request_updates> -</service_request_updates> -CONTENT - - my $update_0_xml = <<CONTENT; -<?xml version="1.0" encoding="utf-8"?> -<service_request_updates> - <request_update> - <description>Fixed</description> - <media_url></media_url> - <service_request_id>0</service_request_id> - <status>closed</status> - <update_id>1</update_id> - <updated_datetime>2014-01-01T13:00:00Z</updated_datetime> - </request_update> -</service_request_updates> -CONTENT - -my $updates_xml = <<CONTENT; -<?xml version="1.0" encoding="utf-8"?> -<service_request_updates> - <request_update> - <description>Fixed</description> - <media_url></media_url> - <service_request_id>0</service_request_id> - <status>closed</status> - <update_id>1</update_id> - <updated_datetime>2014-01-01T13:00:00Z</updated_datetime> - </request_update> - <request_update> - <description>Have investigated. Looks tricky!</description> - <media_url></media_url> - <service_request_id>1</service_request_id> - <status>open</status> - <update_id>2</update_id> - <updated_datetime>2014-03-01T13:00:00Z</updated_datetime> - </request_update> -</service_request_updates> -CONTENT - - subtest 'No updates' => sub { - my $res = $endpoint->run_test_request( GET => '/servicerequestupdates.xml', ); - ok $res->is_success, 'valid request' or diag $res->content; - - is_string $res->content, $empty_xml, 'xml string ok' - or diag $res->content; - }; - - subtest 'Updated 1 ticket' => sub { - # an agent updates the first ticket - set_fixed_time('2014-01-01T13:00:00Z'); - my $request = $endpoint->get_request(0); - $request->add_update( - update_id => 1, - status => 'closed', - description => 'Fixed', - ); - - is $request->status, 'closed', 'Status updated'; - - my $before='2014-01-01T12:00:00Z'; - my $after ='2014-01-01T14:00:00Z'; - - for my $scenario ( - [ '', $update_0_xml, 'Basic test', ], - [ "?start_date=$before", $update_0_xml, 'start date' ], - [ "?end_date=$after", $update_0_xml, 'end_date' ], - [ "?start_date=$before&end_date=$after", $update_0_xml, 'both dates' ], - [ "?start_date=$after", $empty_xml, 'Not found if start date after update' ], - [ "?end_date=$before", $empty_xml, 'Not found if end date before update' ] - ) { - my ($query, $xml, $description) = @$scenario; - - my $res = $endpoint->run_test_request( GET => '/servicerequestupdates.xml' . $query, ); - ok $res->is_success, 'valid request' or diag $res->content; - is_string $res->content, $xml, $description; - } - }; - - subtest 'Updated another ticket' => sub { - set_fixed_time('2014-03-01T13:00:00Z'); - my $request = $endpoint->get_request(1); - $request->add_update( - update_id => 2, - description => 'Have investigated. Looks tricky!', - ); - - for my $scenario ( - [ '', $updates_xml, 'Both reports', ], - [ "?end_date=2014-01-01T14:00:00Z", $update_0_xml, 'end_date before second update' ], - ) { - my ($query, $xml, $description) = @$scenario; - - my $res = $endpoint->run_test_request( GET => '/servicerequestupdates.xml' . $query, ); - ok $res->is_success, 'valid request' or diag $res->content; - is_string $res->content, $xml, $description or diag $res->content; - } - }; -}; - -restore_time(); -done_testing; diff --git a/t/open311/endpoint/schema.t b/t/open311/endpoint/schema.t deleted file mode 100644 index b669ca4a5..000000000 --- a/t/open311/endpoint/schema.t +++ /dev/null @@ -1,82 +0,0 @@ -use strict; use warnings; - -use Test::More; -use Test::Exception; - -use Data::Rx; -use Open311::Endpoint; - -my $endpoint = Open311::Endpoint->new; -my $schema = $endpoint->rx; - -subtest 'comma tests' => sub { - - dies_ok { - my $comma = $schema->make_schema({ - type => '/open311/comma', - }); - } 'Construction dies on no contents'; - - dies_ok { - my $comma = $schema->make_schema({ - type => '/open311/comma', - contents => '/open311/status', - zirble => 'fleem', - }); - } 'Construction dies on extra arguments'; - - my $comma = $schema->make_schema({ - type => '/open311/comma', - contents => '/open311/status', - trim => 1, - }); - - ok ! $comma->check( undef ), 'Undef is not a valid string'; - ok ! $comma->check( [] ), 'Reference is not a valid string'; - - ok ! $comma->check( 'zibble' ), 'invalid string'; - ok ! $comma->check( 'open,zibble' ), 'an invalid element'; - - ok $comma->check( 'open' ), 'single value'; - ok $comma->check( 'open,closed' ), 'multiple values ok'; - ok $comma->check( 'open, closed ' ), 'spaces trimmed ok'; -}; - -subtest 'datetime tests' => sub { - - dies_ok { - my $comma = $schema->make_schema({ - type => '/open311/datetime', - zirble => 'fleem', - }); - } 'Construction dies on extra keys'; - - my $dt = $schema->make_schema({ - type => '/open311/datetime', - }); - - ok ! $dt->check( undef ), 'Undef is not a valid string'; - ok ! $dt->check( [] ), 'Reference is not a valid string'; - - ok ! $dt->check( '9th Feb 2012' ), 'invalid datetime format'; - - ok $dt->check( '1994-11-05T08:15:30-05:00' ), 'datetime format with offset'; - ok $dt->check( '1994-11-05T08:15:30+05:00' ), 'datetime format with positive'; - ok $dt->check( '1994-11-05T13:15:30Z' ), 'datetime format zulu'; -}; - -subtest 'identifier tests' => sub { - my $id = $schema->make_schema( '/open311/example/identifier' ); - - ok ! $id->check( undef ), 'Undef is not a valid string'; - ok ! $id->check( '' ), 'Empty string is not a valid identifier'; - ok ! $id->check( 'foo bar' ), 'String with spaces is not a valid identifier'; - - ok $id->check( 'foo' ), 'Ascii word string is a valid identifier'; - ok $id->check( 'foo_bar' ), 'Ascii word string is a valid identifier'; - ok $id->check( 'foo_123' ), 'Ascii word/num string is a valid identifier'; -}; - -done_testing; - -1; diff --git a/t/open311/endpoint/spark.t b/t/open311/endpoint/spark.t deleted file mode 100644 index 015876c94..000000000 --- a/t/open311/endpoint/spark.t +++ /dev/null @@ -1,62 +0,0 @@ -use strict; use warnings; - -use Test::More; - -use Open311::Endpoint; -use Data::Dumper; - -my $endpoint = Open311::Endpoint->new; - -subtest "Spark test" => sub { - my $spark = $endpoint->spark; - my $struct = { - foo => { - service_requests => [ 1,2,3 ], - quxes => [ - { - values => [1,2], - }, - { - values => [3,4], - }, - ], - }, - }; - is_deeply $spark->process_for_json($struct), - { - service_requests => [ 1,2,3 ], - quxes => [ - { - values => [1,2], - }, - { - values => [3,4], - }, - ], - }; - - my $xml_struct = $spark->process_for_xml($struct); - is_deeply $xml_struct, - { - foo => { - service_requests => { request => [ 1,2,3 ] }, - quxes => { - quxe => [ - { - values => { - value => [1,2], - }, - }, - { - values => { - value => [3,4], - }, - }, - ] - }, - } - } - or warn Dumper($xml_struct); -}; - -done_testing; diff --git a/t/open311/endpoint/warwick.t b/t/open311/endpoint/warwick.t deleted file mode 100644 index e5f124c8d..000000000 --- a/t/open311/endpoint/warwick.t +++ /dev/null @@ -1,443 +0,0 @@ -use strict; use warnings; - -use Test::More; -use Test::LongString; -use Test::MockTime ':all'; - -use Data::Dumper; -use JSON::MaybeXS; - -use FixMyStreet::DB; - -use Module::Loaded; -BEGIN { mark_as_loaded('DBD::Oracle') } - -use t::open311::endpoint::Endpoint_Warwick; - -use LWP::Protocol::PSGI; -use Open311::PopulateServiceList; -use Open311::GetServiceRequestUpdates; - -my $endpoint = t::open311::endpoint::Endpoint_Warwick->new; - -subtest "GET Service List" => sub { - my $res = $endpoint->run_test_request( GET => '/services.xml' ); - ok $res->is_success, 'xml success'; - my $expected = <<XML; -<?xml version="1.0" encoding="utf-8"?> -<services> - <service> - <description>Pothole</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>PO</service_code> - <service_name>Pothole</service_name> - <type>realtime</type> - </service> -</services> -XML - is $res->content, $expected - or diag $res->content; -}; - -subtest "POST OK" => sub { - set_fixed_time('2014-01-01T12:00:00Z'); - my $res = $endpoint->run_test_request( - POST => '/requests.json', - api_key => 'test', - service_code => 'PO', - address_string => '22 Acacia Avenue', - first_name => 'Bob', - last_name => 'Mould', - 'attribute[easting]' => 100, - 'attribute[northing]' => 100, - 'attribute[external_id]' => 1001, - 'attribute[closest_address]' => '22 Acacia Avenue', - ); - ok $res->is_success, 'valid request' - or diag $res->content; - - is_deeply decode_json($res->content), - [ { - "service_notice" => "Warwickshire Open311 Endpoint", - "service_request_id" => 1001 - } ], 'correct json returned'; - - is_deeply \%t::open311::endpoint::Endpoint_Warwick::BINDINGS, - { - ':ce_surname' => 'MOULD', - ':ce_y' => '100', - ':ce_x' => '100', - ':ce_work_phone' => '', - ':ce_contact_type' => 'PU', - ':ce_source' => 'FMS', - ':ce_doc_reference' => '1001', - ':ce_enquiry_type' => 'PO', - ':ce_email' => '', - ':ce_description' => '', - ':ce_location' => '22 Acacia Avenue', - ':ce_incident_datetime' => '2014-01-01 12:00', - ':ce_class' => 'N/A', - ':ce_cpr_id' => 5, - ':ce_compl_user_type' => 'USER', - ':ce_status_code' => 'RE', - ':ce_cat' => 'DEF', - ':ce_forename' => 'BOB' - }, - 'bindings as expected'; -}; - -subtest 'updates' => sub { - my $res = $endpoint->run_test_request( GET => '/servicerequestupdates.xml', ); - ok $res->is_success, 'valid request' or diag $res->content; - -my $expected = <<XML; -<?xml version="1.0" encoding="utf-8"?> -<service_request_updates> - <request_update> - <description>Closed the ticket</description> - <media_url></media_url> - <service_request_id>1001</service_request_id> - <status>closed</status> - <update_id>999</update_id> - <updated_datetime>2014-07-23T11:07:00+01:00</updated_datetime> - </request_update> -</service_request_updates> -XML - - is_string $res->content, $expected, 'xml string ok' - or diag $res->content; - - chomp (my $expected_sql = <<SQL); -SELECT * FROM ( - SELECT - row_id, - service_request_id, - to_char(updated_timedate, 'YYYY-MM-DD HH24:MI'), - status, - description - FROM higatlas.fms_update - WHERE updated_timedate >= to_date(2013-12-31 12:00, YYYY-MM-DD HH24:MI) AND (status='OPEN' OR status='CLOSED') - ORDER BY updated_timedate DESC) WHERE ROWNUM <= 1000 -SQL - - is_string $t::open311::endpoint::Endpoint_Warwick::UPDATES_SQL, $expected_sql, 'SQL as expected'; -}; - -subtest "End to end" => sub { - - # We create and instance of the endpoint as a PSGI app - # And then bind it to the dummy URL. This mocks that whole hostname, so that FMS's - # calls via Open311.pm are rerouted to our PSGI app. - # (This saves us all the faff of having to launch and manage a new server process - # for this test) - - my $endpoint_psgi = t::open311::endpoint::Endpoint_Warwick->run_if_script; - - my $ENDPOINT = 'open311.warwickshire.gov.uk'; - LWP::Protocol::PSGI->register($endpoint_psgi, host => $ENDPOINT); - - my $WARWICKSHIRE_MAPIT_ID = 2243; - - my $db = FixMyStreet::DB->connect; - - $db->txn_begin; - - my $body = FixMyStreet::DB->resultset('Body')->find_or_create( { - id => $WARWICKSHIRE_MAPIT_ID, - name => 'Warwickshire County Council', - }); - - my $user = FixMyStreet::DB->resultset('User') - ->find_or_create( { email => 'test@example.com', name => 'Test User' } ); - - $body->update({ - jurisdiction => 'wcc', - endpoint => "http://$ENDPOINT", - api_key => 'SEEKRIT', - send_method => 'Open311', - send_comments => 1, - comment_user_id => $user->id, - }); - - $body->body_areas->find_or_create({ - area_id => $WARWICKSHIRE_MAPIT_ID - } ); - - subtest "Populate service list" => sub { - # as per bin/open311-populate-service-list - - $body->contacts->delete; - - is $body->contacts->count, 0, 'sanity check'; - - my $bodies = self_rs($body); - - my $p = Open311::PopulateServiceList->new( bodies => $bodies, verbose => 0, schema => $db ); - $p->process_bodies; - - is $body->contacts->count, 1, 'Categories imported from Open311'; - }; - - set_fixed_time('2014-07-20T15:05:00Z'); - - my $problem = FixMyStreet::DB->resultset('Problem')->create({ - postcode => 'WC1 1AA', - bodies_str => $WARWICKSHIRE_MAPIT_ID, - areas => ",$WARWICKSHIRE_MAPIT_ID,", - category => 'Pothole', - title => 'Testing', - detail => 'Testing Detail', - used_map => 1, - name => 'Joe Bloggs', - anonymous => 0, - state => 'confirmed', - confirmed => '2014-07-20 15:00:00', - lastupdate => '2014-07-20 15:00:00', - lang => 'en-gb', - service => '', - cobrand => 'fixmystreet', # e.g. UK - cobrand_data => '', - send_questionnaire => 0, - latitude => '52.2804', - longitude => '-1.5897', - user_id => $user->id, - }); - - subtest "Send report" => sub { - # as per bin/send-reports - - FixMyStreet::override_config { - ALLOWED_COBRANDS => [ 'fixmystreet' ], - SEND_REPORTS_ON_STAGING => 1, - MAPIT_URL => 'http://mapit.mysociety.org/', - }, sub { - ## we can't (yet) just do following due to - ## https://github.com/mysociety/fixmystreet/issues/893 - # self_rs($problem)->send_reports; - - ## instead, as we are in a transaction, we'll just delete everything else. - my $rs = FixMyStreet::DB->resultset('Problem'); - $rs->search({ id => { '!=', $problem->id } })->delete; - $rs->send_reports; - }; - $problem->discard_changes; - - # Our test endpoint returns a hardcoded external ID. - ok $problem->whensent, 'whensent has been set'; - is $problem->external_id, 1001, 'External ID set correctly' - or die; - }; - - subtest "Send update" => sub { - # as per bin/send-reports - - $problem->update({ lastupdate => '2014-07-20 15:05:00' }); # override - - set_fixed_time('2014-07-23T11:07:00Z'); - - is $problem->comments->count, 0, 'sanity check update count'; - is $problem->state, 'confirmed', 'sanity check status'; - - - my $updates = Open311::GetServiceRequestUpdates->new( verbose => 1, schema => $db ); - $updates->fetch; - - $problem->discard_changes; - is $problem->comments->count, 1, 'comment has been added'; - - - my $update = $problem->comments->single; - is $update->user_id, $user->id, 'update user correct'; - is $update->state, 'confirmed', 'update itself is confirmed'; - - is $update->problem_state, 'fixed - council', 'update marked problem as closed'; - is $problem->state, 'fixed - council', 'has been closed'; - }; - - $db->txn_rollback; -}; - -restore_time(); -done_testing; - -sub self_rs { - my ($row) = @_; - # create a result-set with just this body (see also DBIx::Class::Helper::Row::SelfResultSet) - return $row->result_source->resultset->search( $row->ident_condition ); -} - -__END__ - <service> - <description>Bridges</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>BR</service_code> - <service_name>Bridges</service_name> - <type>realtime</type> - </service> - <service> - <description>Carriageway Defect</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>CD</service_code> - <service_name>Carriageway Defect</service_name> - <type>realtime</type> - </service> - <service> - <description>Roads/Highways</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>CD</service_code> - <service_name>Roads/Highways</service_name> - <type>realtime</type> - </service> - <service> - <description>Drainage</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>DR</service_code> - <service_name>Drainage</service_name> - <type>realtime</type> - </service> - <service> - <description>Debris/Spillage</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>DS</service_code> - <service_name>Debris/Spillage</service_name> - <type>realtime</type> - </service> - <service> - <description>Fences</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>FE</service_code> - <service_name>Fences</service_name> - <type>realtime</type> - </service> - <service> - <description>Pavements</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>F D</service_code> - <service_name>Pavements</service_name> - <type>realtime</type> - </service> - <service> - <description>Gully & Catchpits</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>GC</service_code> - <service_name>Gully & Catchpits</service_name> - <type>realtime</type> - </service> - <service> - <description>Ice/Snow</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>IS</service_code> - <service_name>Ice/Snow</service_name> - <type>realtime</type> - </service> - <service> - <description>Mud & Debris</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>MD</service_code> - <service_name>Mud & Debris</service_name> - <type>realtime</type> - </service> - <service> - <description>Manhole</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>MH</service_code> - <service_name>Manhole</service_name> - <type>realtime</type> - </service> - <service> - <description>Oil Spillage</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>OS</service_code> - <service_name>Oil Spillage</service_name> - <type>realtime</type> - </service> - <service> - <description>Other</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>OT</service_code> - <service_name>Other</service_name> - <type>realtime</type> - </service> - - <service> - <description>Property Damage</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>PD</service_code> - <service_name>Property Damage</service_name> - <type>realtime</type> - </service> - <service> - <description>Road Marking</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>RM</service_code> - <service_name>Road Marking</service_name> - <type>realtime</type> - </service> - <service> - <description>Road traffic signs</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>SN</service_code> - <service_name>Road traffic signs</service_name> - <type>realtime</type> - </service> - <service> - <description>Traffic</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>SP</service_code> - <service_name>Traffic</service_name> - <type>realtime</type> - </service> - <service> - <description>Utilities</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>UT</service_code> - <service_name>Utilities</service_name> - <type>realtime</type> - </service> - <service> - <description>Vegetation</description> - <group>highways</group> - <keywords></keywords> - <metadata>true</metadata> - <service_code>VG</service_code> - <service_name>Vegetation</service_name> - <type>realtime</type> - </service> diff --git a/t/open311/endpoint/warwick_dbd.t b/t/open311/endpoint/warwick_dbd.t deleted file mode 100644 index 9120c6467..000000000 --- a/t/open311/endpoint/warwick_dbd.t +++ /dev/null @@ -1,17 +0,0 @@ -use strict; use warnings; - -=head1 NAME - -warwick_dbd.t - test that Oracle constants can be imported if present - -=cut - -use Test::More; - -use lib 't/open311/endpoint/exor/'; - -use DBD::Oracle; # fake from above test lib (or real if installed) -use t::open311::endpoint::Endpoint_Warwick; - -ok 1; -done_testing; diff --git a/t/sendreport/open311.t b/t/sendreport/open311.t new file mode 100644 index 000000000..636faba31 --- /dev/null +++ b/t/sendreport/open311.t @@ -0,0 +1,50 @@ +use strict; +use warnings; +use Test::More; + +use CGI::Simple; +use FixMyStreet::TestMech; +my $mech = FixMyStreet::TestMech->new; + +my $user = $mech->create_user_ok( 'eh@example.com' ); +my $body = $mech->create_body_ok( 2342, 'East Hertfordshire Council'); +my $contact = $mech->create_contact_ok( body_id => $body->id, category => 'Potholes', email => 'POT' ); +$contact->set_extra_fields( + { code => 'easting', datatype => 'number' }, + { code => 'northing', datatype => 'number' }, + { code => 'fixmystreet_id', datatype => 'number' }, +); +$contact->update; + +my ($report) = $mech->create_problems_for_body( 1, $body->id, 'Test', { + cobrand => 'fixmystreet', + category => 'Potholes', + user => $user, +}); + +subtest 'testing Open311 behaviour', sub { + $body->update( { send_method => 'Open311', endpoint => 'http://endpoint.example.com', jurisdiction => 'FMS', api_key => 'test' } ); + my $test_data; + FixMyStreet::override_config { + SEND_REPORTS_ON_STAGING => 1, + ALLOWED_COBRANDS => [ 'fixmystreet' ], + }, sub { + $test_data = FixMyStreet::DB->resultset('Problem')->send_reports(); + }; + $report->discard_changes; + ok $report->whensent, 'Report marked as sent'; + is $report->send_method_used, 'Open311', 'Report sent via Open311'; + is $report->external_id, 248, 'Report has right external ID'; + + my $req = $test_data->{test_req_used}; + my $c = CGI::Simple->new($req->content); + is $c->param('attribute[easting]'), 529025, 'Request had easting'; + is $c->param('attribute[northing]'), 179716, 'Request had northing'; + is $c->param('attribute[fixmystreet_id]'), $report->id, 'Request had correct ID'; + is $c->param('jurisdiction_id'), 'FMS', 'Request had correct jurisdiction'; +}; + +# Clean up +$mech->delete_user($user); +$mech->delete_body($body); +done_testing(); |