From c95c924230cfb28b4ebbda51369bf1e5db98051a Mon Sep 17 00:00:00 2001 From: H Barry MacLean <81651480+hbmaclean@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:43:39 -0400 Subject: [PATCH 1/2] new Questionnaire->publish method and tests --- lib/TPS/Questionnaire/Controller/API.pm | 43 +++++-- lib/TPS/Questionnaire/Model/Questionnaire.pm | 39 +++++- t/integration/api.t | 121 +++++++++++++------ 3 files changed, 156 insertions(+), 47 deletions(-) diff --git a/lib/TPS/Questionnaire/Controller/API.pm b/lib/TPS/Questionnaire/Controller/API.pm index 49bc053..a6cd21c 100644 --- a/lib/TPS/Questionnaire/Controller/API.pm +++ b/lib/TPS/Questionnaire/Controller/API.pm @@ -34,21 +34,41 @@ With an ID, posts a response to the questionnaire. sub post_questionnaire :Path('questionnaire') POST CaptureArgs(1) Consumes(JSON) { my ($self, $c, $id) = (shift, @_); - if ($id) { - my %posted = %{$c->request->body_data}; - $posted{questionnaire_id} = $id; - my $qa = 'TPS::Questionnaire::Model::QuestionnaireAnswer'->from_hashref(\%posted); - $qa->save($c->schema); + my $posted_body = $c->request->body_data; + + my $q = $id + ? TPS::Questionnaire::Model::QuestionnaireAnswer->from_hashref( { %$posted_body, questionnaire_id => $id } ) + : TPS::Questionnaire::Model::Questionnaire->from_hashref( $posted_body ); + + $q->save($c->schema); + $c->stash->{'status'} = 'ok'; + $c->stash->{'result'} = $q->to_hashref; + $c->forward('View::JSON'); + return 1; +} + +=head2 put_questionnaire + +Handles PUTs to /api/questionnaire/{id}. + +Currently, simply looks to publish the given questionnaire. + +Returns the status of the underlying publish() method. + +=cut + +sub put_questionnaire :Path('questionnaire') PUT CaptureArgs(1) Consumes(JSON) { + my ($self, $c, $id) = (shift, @_); + + if (my $q = TPS::Questionnaire::Model::Questionnaire->publish($c->schema, $id)) { $c->stash->{'status'} = 'ok'; - $c->stash->{'result'} = $qa->to_hashref; + $c->stash->{'result'} = $q->to_hashref; $c->forward('View::JSON'); + return 1; } else { - my $q = 'TPS::Questionnaire::Model::Questionnaire'->from_hashref($c->request->body_data); - $q->save($c->schema); - $c->stash->{'status'} = 'ok'; - $c->stash->{'result'} = $q->to_hashref; - $c->forward('View::JSON'); + # some kind of feedback + return; } } @@ -84,7 +104,6 @@ sub get_questionnaire :Path('questionnaire') GET CaptureArgs(1) { $c->forward('View::JSON'); } - __PACKAGE__->meta->make_immutable; 1; diff --git a/lib/TPS/Questionnaire/Model/Questionnaire.pm b/lib/TPS/Questionnaire/Model/Questionnaire.pm index ed91d0a..6b8fd96 100644 --- a/lib/TPS/Questionnaire/Model/Questionnaire.pm +++ b/lib/TPS/Questionnaire/Model/Questionnaire.pm @@ -197,7 +197,7 @@ sub save { # Even though this object has 'rw' attributes, questionnaires are # conceptually write-once. if ($self->has_id) { - carp 'Save questionnaire which already exists'; + carp(sprintf('Save questionnaire %d which already exists', $self->id)); return $self->id; } @@ -218,6 +218,43 @@ sub save { return $self->id; } +=head2 publish + +Looks to publish an unpublished questionnaire. + +Reports an error if questionnaire does not exist or is already published. + + Param schema + Param questionnaire_id + Returns Questionnaire object on success + false on failure + +=cut + +sub publish { + my ($self, $schema, $id) = (shift, @_); + + my $result = $schema + ->resultset('Questionnaire') + ->search({ questionnaire_id => $id }) + ->next; + + if (!$result) { + carp(sprintf('Questionnaire %d not found, cannot be published', $id)); + return; + } + + if ($result->is_published) { + carp(sprintf('Questionnaire %d is already published', $id)); + return; + } + + $result->is_published(1); + $result->update(); + + return $self->from_db_object($schema, $result); +} + =head2 summary_list($schema) Returns a list of hashrefs, each of which have an "id" and "title" key. diff --git a/t/integration/api.t b/t/integration/api.t index edfda3f..0d8cb72 100644 --- a/t/integration/api.t +++ b/t/integration/api.t @@ -25,44 +25,97 @@ BEGIN { } use Catalyst::Test 'TPS::Questionnaire'; -is( - decode_json(request('/api/questionnaire')->decoded_content), - { - result => { count => 0, list => [] }, - status => 'ok', - }, - 'GET /api/questionnaire -> ok', -); +confirm_no_initial_questionnaires(); +post_new_questionnaire(); +publish_unpublished_questionnaire(); -is( - request('/api/questionnaire/1')->code, - 404, - 'GET /api/questionnaire/1 -> 404', -); +done_testing(); + +sub confirm_no_initial_questionnaires { + is( + decode_json(request('/api/questionnaire')->decoded_content), + { + result => { count => 0, list => [] }, + status => 'ok', + }, + 'GET /api/questionnaire -> ok', + ); + + # And thus, cannot GET id 1 + is( + request('/api/questionnaire/1')->code, + 404, + 'GET /api/questionnaire/1 -> 404', + ); +} + + +sub post_new_questionnaire { + is( + request( + POST '/api/questionnaire', + Content_Type => 'application/json', + Content => encode_json({ + title => 'Sample Questionnaire', + is_published => \1, + questions => [ 'Foo?', 'Bar?' ] + }) + )->code, + 200, + 'POST /api/questionnaire -> ok', + ); + + is( + decode_json(request('/api/questionnaire')->decoded_content), + { + result => { count => 1, list => [ + { id => 1, title => 'Sample Questionnaire' }, + ] }, + status => 'ok', + }, + 'GET /api/questionnaire -> ok', + ); +} -is( - decode_json(request( - POST '/api/questionnaire', +sub publish_unpublished_questionnaire { + is( + request( + POST '/api/questionnaire', Content_Type => 'application/json', Content => encode_json({ - title => 'Sample Questionnaire', - is_published => \1, - questions => [ 'Foo?', 'Bar?' ], + title => 'New Questionnaire', + is_published => \0, + questions => [ 'When?', 'Why?' ] }) - )->code), - 200, - 'POST /api/questionnaire -> ok', -); + )->code, + 200, + 'POST /api/questionnaire (unpublished) - ok' + ); + + # Cannot see it when unpublished: + is( + decode_json(request('/api/questionnaire')->decoded_content), + { + result => { count => 1, list => [ + { id => 1, title => 'Sample Questionnaire' }, + ] }, + status => 'ok', + }, + 'Still only one questionnaire displayed' + ); -is( - decode_json(request('/api/questionnaire')->decoded_content), - { - result => { count => 1, list => [ - { id => 1, title => 'Sample Questionnaire' }, - ] }, - status => 'ok', - }, - 'GET /api/questionnaire -> ok', -); + is( + request(PUT '/api/questionnaire/2', Content_Type => 'application/json')->code, + 200, + 'PUT /api/questionnaire -> ok (published!)' + ); -done_testing(); + my $r = request('/api/questionnaire'); + + is( + decode_json(request('/api/questionnaire')->decoded_content)->{result}{count}, + 2, + 'Second questionnaire displayed' + ); + +} From 5016b2ad7640a582a9187f840669f1ef7bbb3e3b Mon Sep 17 00:00:00 2001 From: bmaclean <81651480+hbmaclean@users.noreply.github.com> Date: Mon, 18 Oct 2021 21:12:48 -0400 Subject: [PATCH 2/2] two more tests, cannot publish twice or unknown id --- t/integration/api.t | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/t/integration/api.t b/t/integration/api.t index 0d8cb72..5036098 100644 --- a/t/integration/api.t +++ b/t/integration/api.t @@ -118,4 +118,13 @@ sub publish_unpublished_questionnaire { 'Second questionnaire displayed' ); + # Confirm cannot re-publish. + # Underlying carp() goes to STDERR - capture and inspect. + my @err; + local $SIG{__WARN__} = sub { push @err, $_[0] }; + eval { request(PUT '/api/questionnaire/2', Content_Type => 'application/json') }; + like($err[-1], qr/already published/, 'Cannot re-publish published questionnaire'); + + eval { request(PUT '/api/questionnaire/2222222222222222222', Content_Type => 'application/json') }; + like($err[-1], qr/not found, cannot be published/, 'Cannot publish non-existant questionnaire'); }