Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publish questionnaires via PUT #1

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions lib/TPS/Questionnaire/Controller/API.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -84,7 +104,6 @@ sub get_questionnaire :Path('questionnaire') GET CaptureArgs(1) {
$c->forward('View::JSON');
}


__PACKAGE__->meta->make_immutable;

1;
39 changes: 38 additions & 1 deletion lib/TPS/Questionnaire/Model/Questionnaire.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -218,6 +218,43 @@ sub save {
return $self->id;
}

=head2 publish
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this more: Depending on the scope and anticipated lifetime of the project, I might rather write a generic "update()" method that takes a hashref of columns and values to be updated. In fact I'd prefer that save() were generic, functioning like a merge statement (insert or update) - so that the "new vs. update" thinking is when the object is instantiated; and then the object's setters can be used; and finally saved... But again, it depends on how many data changes are likely and how long the project will remain.


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.
Expand Down
136 changes: 99 additions & 37 deletions t/integration/api.t
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,106 @@ BEGIN {
}
use Catalyst::Test 'TPS::Questionnaire';

is(
decode_json(request('/api/questionnaire')->decoded_content),
{
result => { count => 0, list => [] },
status => 'ok',
},
'GET /api/questionnaire -> ok',
);

is(
request('/api/questionnaire/1')->code,
404,
'GET /api/questionnaire/1 -> 404',
);

is(
decode_json(request(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was failing. I initially assumed it was me and my new environment; but removing decode_json() here fixed it.

POST '/api/questionnaire',
confirm_no_initial_questionnaires();
post_new_questionnaire();
publish_unpublished_questionnaire();

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',
);
}

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',
);

is(
decode_json(request('/api/questionnaire')->decoded_content),
{
result => { count => 1, list => [
{ id => 1, title => 'Sample Questionnaire' },
] },
status => 'ok',
},
'GET /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'
);

done_testing();
is(
request(PUT '/api/questionnaire/2', Content_Type => 'application/json')->code,
200,
'PUT /api/questionnaire -> ok (published!)'
);

my $r = request('/api/questionnaire');

is(
decode_json(request('/api/questionnaire')->decoded_content)->{result}{count},
2,
'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');
}