diff options
-rw-r--r-- | conf/httpd.conf | 2 | ||||
-rw-r--r-- | db/schema_0004-create_users_from_comments_and_link.sql | 4 | ||||
-rw-r--r-- | perl-external/files.txt | 9 | ||||
-rw-r--r-- | perl-external/minicpan/modules/02packages.details.txt.gz | bin | 13545 -> 19147 bytes | |||
-rw-r--r-- | perl-external/modules.txt | 1 | ||||
-rw-r--r-- | perllib/FixMyStreet/App/Controller/JSON.pm | 114 | ||||
-rw-r--r-- | perllib/FixMyStreet/TestMech.pm | 62 | ||||
-rw-r--r-- | t/app/controller/json.t | 109 | ||||
-rwxr-xr-x | web/json.cgi | 50 |
9 files changed, 311 insertions, 40 deletions
diff --git a/conf/httpd.conf b/conf/httpd.conf index 1a090226b..8d70c475d 100644 --- a/conf/httpd.conf +++ b/conf/httpd.conf @@ -109,7 +109,7 @@ RewriteRule /(.+) /$1 [L] # RewriteRule ^/contact(.*) /contact.cgi$1 [L] RewriteRule ^/flickr(.*) /flickr.cgi$1 [L] RewriteRule ^/fun(.*) /fun.cgi$1 [L] -RewriteRule ^/json(.*) /json.cgi$1 [L] +# RewriteRule ^/json(.*) /json.cgi$1 [L] # RewriteRule ^/photo(.*) /photo.cgi$1 [L] RewriteRule ^/questionnaire(.*) /questionnaire.cgi$1 [L] # RewriteRule ^/reports(.*) /reports.cgi$1 [L] diff --git a/db/schema_0004-create_users_from_comments_and_link.sql b/db/schema_0004-create_users_from_comments_and_link.sql index 6dc50f531..0e4e2b5f2 100644 --- a/db/schema_0004-create_users_from_comments_and_link.sql +++ b/db/schema_0004-create_users_from_comments_and_link.sql @@ -32,10 +32,10 @@ WHERE users.name = ''; ALTER table comment ADD COLUMN anonymous BOOL; -UPDATE comment SET anonymous = true WHERE name = ''; - UPDATE comment SET anonymous = false WHERE name <> ''; +UPDATE comment SET anonymous = true WHERE anonymous is NULL; + -- tidy up now everythings in place ALTER table comment ALTER COLUMN user_id SET NOT NULL; diff --git a/perl-external/files.txt b/perl-external/files.txt index 5df413ba8..82d7fe669 100644 --- a/perl-external/files.txt +++ b/perl-external/files.txt @@ -61,6 +61,7 @@ /authors/id/C/CI/CINE/Lingua-Stem-Snowball-Da-1.01.tar.gz /authors/id/C/CK/CKRAS/DateTime-Format-HTTP-0.40.tar.gz /authors/id/C/CO/COGENT/Tree-DAG_Node-1.06.tar.gz +/authors/id/C/CO/CORION/parent-0.225.tar.gz /authors/id/C/CR/CRENZ/Module-Find-0.10.tar.gz /authors/id/D/DA/DAGOLDEN/CPAN-Meta-2.110580.tar.gz /authors/id/D/DA/DAGOLDEN/CPAN-Meta-YAML-0.003.tar.gz @@ -80,6 +81,12 @@ /authors/id/D/DO/DOY/Package-Stash-0.26.tar.gz /authors/id/D/DO/DOY/Package-Stash-XS-0.22.tar.gz /authors/id/D/DO/DOY/Try-Tiny-0.09.tar.gz +/authors/id/D/DR/DROLSKY/Class-Factory-Util-1.7.tar.gz +/authors/id/D/DR/DROLSKY/DateTime-0.70.tar.gz +/authors/id/D/DR/DROLSKY/DateTime-Format-Builder-0.80.tar.gz +/authors/id/D/DR/DROLSKY/DateTime-Format-Strptime-1.5000.tar.gz +/authors/id/D/DR/DROLSKY/DateTime-Locale-0.45.tar.gz +/authors/id/D/DR/DROLSKY/DateTime-TimeZone-1.34.tar.gz /authors/id/D/DR/DROLSKY/File-ChangeNotify-0.19.tar.gz /authors/id/D/DR/DROLSKY/File-Slurp-9999.13.tar.gz /authors/id/D/DR/DROLSKY/Moose-1.24.tar.gz @@ -128,10 +135,12 @@ /authors/id/G/GB/GBARR/Scalar-List-Utils-1.23.tar.gz /authors/id/G/GE/GETTY/HTTP-Body-1.11.tar.gz /authors/id/G/GR/GRODITI/MooseX-Types-Common-0.001002.tar.gz +/authors/id/G/GR/GROMMEL/Math-Round-0.06.tar.gz /authors/id/I/IL/ILMARI/Class-Unload-0.07.tar.gz /authors/id/I/IN/INGY/Spiffy-0.30.tar.gz /authors/id/I/IN/INGY/Test-Base-0.59.tar.gz /authors/id/J/JE/JESSE/HTTP-Server-Simple-0.44.tar.gz +/authors/id/J/JH/JHOBLITT/DateTime-Format-ISO8601-0.07.tar.gz /authors/id/J/JP/JPEACOCK/version-0.88.tar.gz /authors/id/J/JR/JROCKWAY/Context-Preserve-0.01.tar.gz /authors/id/K/KA/KASEI/Class-Accessor-0.34.tar.gz diff --git a/perl-external/minicpan/modules/02packages.details.txt.gz b/perl-external/minicpan/modules/02packages.details.txt.gz Binary files differindex 3beda027e..c4bc9e1e8 100644 --- a/perl-external/minicpan/modules/02packages.details.txt.gz +++ b/perl-external/minicpan/modules/02packages.details.txt.gz diff --git a/perl-external/modules.txt b/perl-external/modules.txt index 1d027ca1a..63a40b770 100644 --- a/perl-external/modules.txt +++ b/perl-external/modules.txt @@ -21,6 +21,7 @@ DBIx::Class::FilterColumn DBIx::Class::Schema::Loader DBIx::Class::Storage::DBI DateTime::Format::HTTP +DateTime::Format::ISO8601 Email::Address Email::Send Email::Simple diff --git a/perllib/FixMyStreet/App/Controller/JSON.pm b/perllib/FixMyStreet/App/Controller/JSON.pm new file mode 100644 index 000000000..c437aafc0 --- /dev/null +++ b/perllib/FixMyStreet/App/Controller/JSON.pm @@ -0,0 +1,114 @@ +package FixMyStreet::App::Controller::JSON; +use Moose; +use namespace::autoclean; + +BEGIN { extends 'Catalyst::Controller'; } + +use JSON; +use DateTime; +use DateTime::Format::ISO8601; + +=head1 NAME + +FixMyStreet::App::Controller::JSON - Catalyst Controller + +=head1 DESCRIPTION + +Provide information as JSON + +=head1 METHODS + +=head2 json + +=cut + +sub json : Path : Args(0) { + my ( $self, $c ) = @_; + + # gather the parameters + my $type = $c->req->param('type') || ''; + my $start_date = $c->req->param('start_date') || ''; + my $end_date = $c->req->param('end_date') || ''; + + my $yyyy_mm_dd = qr{^\d{4}-\d\d-\d\d$}; + if ( $start_date !~ $yyyy_mm_dd + || $end_date !~ $yyyy_mm_dd ) + { + $c->stash->{error} = 'Invalid dates supplied'; + return; + } + + # convert the dates to datetimes and trap errors + my $iso8601 = DateTime::Format::ISO8601->new; + my $start_dt = eval { $iso8601->parse_datetime($start_date); }; + my $end_dt = eval { $iso8601->parse_datetime($end_date); }; + unless ( $start_dt && $end_dt ) { + $c->stash->{error} = 'Invalid dates supplied'; + return; + } + + # check that the dates are sane + if ( $start_dt > $end_dt ) { + $c->stash->{error} = 'Start date after end date'; + return; + } + + # check that the type is supported + unless ( $type eq 'new_problems' || $type eq 'fixed_problems' ) { + $c->stash->{error} = 'Invalid type supplied'; + return; + } + + # query the database + $c->stash->{response} = + $type eq 'new_problems' + ? Problems::created_in_interval( $start_date, $end_date ) + : Problems::fixed_in_interval( $start_date, $end_date ); +} + +# If we convert this code to be fully DBIC based then the following snippet is a +# good start. The roadblock to doing it fully is the 'site_restriction' in the +# SQL which is currently provided as SQL, rather than something that could be +# easily added to the DBIC query. The hardest cobrand to change would be the +# cities - so perhaps do it after we know wether that needs to be kept or not. +# +# my $state = +# $type eq 'new_problems' ? 'confirmed' +# : $type eq 'fixed_problems' ? 'fixed_problems' +# : die; +# +# my $one_day = DateTime::Duration->new( days => 1 ); +# +# my $problems = $c->model('DB::Problem')->search( +# { +# created => { +# '>=' => $start_dt, +# '<=' => $end_dt + $one_day, +# }, +# state => $state, +# # ------ add is site_restriction here ------- +# }, +# { +# columns => [ +# 'id', 'title', 'council', 'category', +# 'detail', 'name', 'anonymous', 'confirmed', +# 'whensent', 'service', +# ] +# } +# ); + +sub end : Private { + my ( $self, $c ) = @_; + + my $response = + $c->stash->{error} + ? { error => $c->stash->{error} } + : $c->stash->{response}; + + $c->res->content_type('application/json; charset=utf-8'); + $c->res->body( encode_json( $response || {} ) ); +} + +__PACKAGE__->meta->make_immutable; + +1; diff --git a/perllib/FixMyStreet/TestMech.pm b/perllib/FixMyStreet/TestMech.pm index 35f934299..4c4a3b3eb 100644 --- a/perllib/FixMyStreet/TestMech.pm +++ b/perllib/FixMyStreet/TestMech.pm @@ -15,6 +15,7 @@ use Web::Scraper; use Carp; use Email::Send::Test; use Digest::SHA1 'sha1_hex'; +use JSON; =head1 NAME @@ -53,6 +54,26 @@ sub logged_in_ok { "logged in" ); } +=head2 create_user_ok + + $user = $mech->create_user_ok( $email ); + +Create a test user (or find it and return if it already exists). + +=cut + +sub create_user_ok { + my $self = shift; + my ($email) = @_; + + my $user = + FixMyStreet::App->model('DB::User') + ->find_or_create( { email => $email } ); + ok $user, "found/created user for $email"; + + return $user; +} + =head2 log_in_ok $user = $mech->log_in_ok( $email_address ); @@ -65,10 +86,7 @@ sub log_in_ok { my $mech = shift; my $email = shift; - my $user = - FixMyStreet::App->model('DB::User') - ->find_or_create( { email => $email } ); - ok $user, "found/created user for $email"; + my $user = $mech->create_user_ok($email); # store the old password and then change it my $old_password_sha1 = $user->password; @@ -129,14 +147,11 @@ sub delete_user { $mech->log_out_ok; for my $p ( $user->problems ) { - ok( $_->delete, "delete comment " . $_->text ) - for $p->comments; + ok( $_->delete, "delete comment " . $_->text ) for $p->comments; ok( $p->delete, "delete problem " . $p->title ); } - ok( $_->delete, "delete comment " . $_->text ) - for $user->comments; - ok( $_->delete, "delete alert " . $_->alert_type ) - for $user->alerts; + ok( $_->delete, "delete comment " . $_->text ) for $user->comments; + ok( $_->delete, "delete alert " . $_->alert_type ) for $user->alerts; ok $user->delete, "delete test user " . $user->email; return 1; @@ -223,7 +238,7 @@ arrayref of TEXTs. If none found return empty arrayref. sub page_errors { my $mech = shift; my $result = scraper { - process 'p.error', 'errors[]', 'TEXT'; + process 'p.error', 'errors[]', 'TEXT'; process 'ul.error', 'errors[]', 'TEXT'; } ->scrape( $mech->response ); @@ -332,7 +347,6 @@ sub extract_problem_title { return $result->{title}; } - =head2 extract_problem_banner $banner = $mech->extract_problem_banner; @@ -434,4 +448,28 @@ sub session_cookie_expiry { return $expires || 0; } +=head2 get_ok_json + + $decoded = $mech->get_ok_json( $url ); + +Get the url, check that it was JSON and then decode and return the body. + +=cut + +sub get_ok_json { + my $mech = shift; + my $url = shift; + + # try to get the response + $mech->get_ok($url) + || return undef; + my $res = $mech->response; + + # check that the content-type of response is correct + croak "Response was not JSON" + unless $res->header('Content-Type') =~ m{^application/json\b}; + + return decode_json( $res->content ); +} + 1; diff --git a/t/app/controller/json.t b/t/app/controller/json.t new file mode 100644 index 000000000..2c9ff4a61 --- /dev/null +++ b/t/app/controller/json.t @@ -0,0 +1,109 @@ +use strict; +use warnings; + +use Test::More; + +use FixMyStreet::TestMech; +my $mech = FixMyStreet::TestMech->new; + +subtest "check that a bad request produces the appropriate response" => sub { + + my $bad_date = "Invalid dates supplied"; + my $mad_date = "Start date after end date"; + my $bad_type = "Invalid type supplied"; + + my %tests = ( + '' => $bad_date, + 'foo=bar' => $bad_date, + 'type=&start_date=&end_date=' => $bad_date, + 'type=&start_date=bad&end_date=2000-02-01' => $bad_date, + 'type=&start_date=2000-01-01&end_date=bad' => $bad_date, + 'type=&start_date=2000-02-31&end_date=2000-02-01' => $bad_date, + 'type=&start_date=2000-01-01&end_date=2000-02-31' => $bad_date, + + 'type=&start_date=2000-02-01&end_date=2000-01-01' => $mad_date, + + 'type=&start_date=2000-01-01&end_date=2000-02-01' => $bad_type, + 'type=foo&start_date=2000-01-01&end_date=2000-02-01' => $bad_type, + ); + + foreach my $q ( sort keys %tests ) { + is_deeply # + $mech->get_ok_json("/json?$q"), # + { error => $tests{$q} }, # + "correct error for query '$q'"; + } + +}; + +is_deeply # + $mech->get_ok_json( + "/json?type=new_problems&start_date=2000-01-01&end_date=2000-02-01"), # + [], # + "correct response"; + +# put an entry in the database for this test +my $user = $mech->create_user_ok('test@example.com'); + +my $problem_args = { + postcode => 'sw1a 1aa', + council => '2501', + areas => ',105164,11806,11827,2247,2501,34817,42011,66045,70786,8519,', + category => 'test category', + title => 'Test title', + detail => 'Test detail', + used_map => 't', + name => 'Test Name', + created => '2000-01-01 12:00:00', + confirmed => '2000-01-01 12:01:00', + state => 'confirmed', + lang => 'en-gb', + service => '', + cobrand => '', + cobrand_data => '', + lastupdate => '2000-01-01 12:00:00', + whensent => undef, + send_questionnaire => 't', + latitude => '51.4531988729771', + longitude => '-0.23021896608596', +}; +my $problem = $user->add_to_problems( { %$problem_args, anonymous => 0 } ); +my $anon_problem = $user->add_to_problems( { %$problem_args, anonymous => 1 } ); + +ok $problem, "created normal test problem"; +ok $anon_problem, "created anon test problem"; + +is_deeply # + $mech->get_ok_json( + "/json?type=new_problems&start_date=2000-01-01&end_date=2000-02-01"), # + [ + { + 'anonymous' => 0, + 'category' => 'test category', + 'confirmed' => '2000-01-01 12:01:00', + 'council' => 'Wandsworth Borough Council', + 'detail' => 'Test detail', + 'id' => $problem->id, + 'name' => 'Test Name', + 'service' => 'Web interface', + 'title' => 'Test title', + 'whensent' => undef + }, + { + 'anonymous' => 1, + 'category' => 'test category', + 'confirmed' => '2000-01-01 12:01:00', + 'council' => 'Wandsworth Borough Council', + 'detail' => 'Test detail', + 'id' => $anon_problem->id, + 'name' => '', + 'service' => 'Web interface', + 'title' => 'Test title', + 'whensent' => undef + } + ], + "correct response"; + +$mech->delete_user($user); + +done_testing(); diff --git a/web/json.cgi b/web/json.cgi index 512750988..70ae2a76f 100755 --- a/web/json.cgi +++ b/web/json.cgi @@ -8,28 +8,28 @@ # # $Id: json.cgi,v 1.4 2010-01-20 11:31:26 matthew Exp $ -use strict; -use Error qw(:try); -use JSON; -use Standard; - -sub main { - my $q = shift; - my $problems; - my $type = $q->param('type') || ''; - my $start_date = $q->param('start_date') || ''; - my $end_date = $q->param('end_date') || ''; - if ($start_date !~ /^\d{4}-\d\d-\d\d$/ || $end_date !~ /^\d{4}-\d\d-\d\d$/) { - $problems = { error => 'Invalid dates supplied' }; - } elsif ($type eq 'new_problems') { - $problems = Problems::created_in_interval($start_date, $end_date); - } elsif ($type eq 'fixed_problems') { - $problems = Problems::fixed_in_interval($start_date, $end_date); - } - print $q->header( -type => 'application/json; charset=utf-8' ); - print JSON::to_json($problems); -} - - -Page::do_fastcgi(\&main); - +# use strict; +# use Error qw(:try); +# use JSON; +# use Standard; +# +# sub main { +# my $q = shift; +# my $problems; +# my $type = $q->param('type') || ''; +# my $start_date = $q->param('start_date') || ''; +# my $end_date = $q->param('end_date') || ''; +# if ($start_date !~ /^\d{4}-\d\d-\d\d$/ || $end_date !~ /^\d{4}-\d\d-\d\d$/) { +# $problems = { error => 'Invalid dates supplied' }; +# } elsif ($type eq 'new_problems') { +# $problems = Problems::created_in_interval($start_date, $end_date); +# } elsif ($type eq 'fixed_problems') { +# $problems = Problems::fixed_in_interval($start_date, $end_date); +# } +# print $q->header( -type => 'application/json; charset=utf-8' ); +# print JSON::to_json($problems); +# } +# +# +# Page::do_fastcgi(\&main); +# |