5 package App::LazyShoppingList::API::v1;
6 use App::LazyShoppingList::API;
13 use experimental 'signatures';
15 set charset => 'UTF-8';
16 set serializer => 'JSON';
18 hook before_request => sub {
19 get_database->txn_begin;
22 hook after_request => sub {
23 my $dbh = get_database;
25 if (response->status =~ /^5/) {
33 # get the URI for the list of shopping lists
35 return { lists => uri_for('/list') };
38 # get the list of shopping lists
40 my $dbh = get_database;
42 my %r = ( lists_version => get_lists_version($dbh), lists => [] );
44 $dbh->resultset('ShoppingList')
46 { order_by => { -asc => 'name' } } )->all
50 { uri => uri_for( "/list/" . $list->id ), name => $list->name };
56 # create shopping list
58 my $req = request_data;
60 my $name = $req->{name};
62 return exception 400, "Missing list name";
65 my $dbh = get_database;
66 my $list = $dbh->resultset('ShoppingList')->create({
68 $list->discard_changes;
70 my $lists_ver = increment_lists_version($dbh);
73 uri => uri_for( '/list/' . $list->id ),
74 version => $list->version,
75 lists_version => $lists_ver
79 # get shopping list items
80 get '/list/:list_id' => sub {
81 my $list_id = route_parameters->get('list_id');
82 length($list_id) and $list_id =~ /^\d{1,18}$/ or return invalid_input;
84 my $dbh = get_database;
87 my %r = ( items => [] );
89 $r{lists_version} = get_lists_version($dbh);
91 my $list = $dbh->resultset('ShoppingList')->find($list_id);
94 return exception 404, "No list with that ID found";
97 $r{version} = $list->version;
99 my @items = $dbh->resultset('ShoppingListItem')->search(
100 { shopping_list => $list->id },
101 { order_by => { -asc => 'id' } }
103 for my $item (@items) {
106 uri_for( sprintf( "/list/%s/%s", $list->id, $item->id ) ),
107 description => $item->description,
108 done => JSON->boolean( $item->done ),
109 version => $item->version,
116 # create shopping list item
117 post '/list/:id' => sub {
118 my $list_id = route_parameters->get('id');
119 length($list_id) and $list_id =~ /^\d{1,18}$/
120 or return invalid_input('bad list ID');
122 my $req = request_data;
124 my $descr = $req->{description};
125 my $done = JSON->boolean( $req->{done} // 0 );
127 my $dbh = get_database;
130 lists_version => get_lists_version($dbh),
133 my $list = $dbh->resultset('ShoppingList')->find($list_id);
136 exception 404, "No such list";
139 my $item = $dbh->resultset('ShoppingListItem')->create(
140 { shopping_list => $list->id,
141 description => $descr,
145 $item->discard_changes;
147 $list->update( { version => $list->version + 1 } );
149 $r{uri} = uri_for( sprintf( "list/%s/%s", $list->id, $item->id ) );
150 $r{version} = $item->version;
151 $r{list_version} = $list->version;
156 # modify shopping list item
157 put '/list/:list_id/:item_id' => sub {
158 my $list_id = route_parameters->get('list_id');
159 length($list_id) and $list_id =~ /^\d{1,18}$/
160 or return invalid_input('bad list ID');
162 my $item_id = route_parameters->get('item_id');
163 length($item_id) and $item_id =~ /^\d{1,18}$/
164 or return invalid_input('bad item ID');
166 my $req = request_data;
168 my $descr = $req->{description};
169 my $done = JSON->boolean( $req->{done} // 0 );
170 my $version = $req->{version};
172 length($version) and $version =~ /^\d{1,18}$/
173 or return invalid_input('bad version');
175 my $dbh = get_database;
178 lists_version => get_lists_version($dbh),
181 my $list = $dbh->resultset('ShoppingList')->find($list_id);
183 return exception 404, "No such list";
186 my $item = $dbh->resultset('ShoppingListItem')->find({shopping_list => $list->id, id => $item_id});
188 return exception 404, "No such item";
191 # in case no real changes are needed will return the current state
192 if ( defined($descr) and $descr ne ( $item->description // '' )
193 or defined($done) and $done != $item->done )
195 unless ($version == $item->version) {
196 return exception 409,
197 sprintf( 'Outdated version (current is %d)', $item->version );
201 defined($descr) ? (description => $descr) : (),
202 defined($done) ? (done => $done) : (),
203 version => $version + 1,
206 $list->update( { version => $list->version + 1 } );
209 $r{uri} = uri_for( sprintf( "list/%s/%s", $list->id, $item->id ) );
210 $r{version} = $item->version;
211 $r{list_version} = $list->version;
216 # modify shipping list
217 put '/list/:list_id' => sub {
218 my $list_id = route_parameters->get('list_id');
219 length($list_id) and $list_id =~ /^\d{1,18}$/
220 or return invalid_input('bad list ID');
222 my $req = request_data;
224 my $name = $req->{name} // '';
225 my $version = $req->{version} // -1;
227 length($version) and $version =~ /^\d{1,18}$/
228 or return invalid_input('bad version');
230 my $dbh = get_database;
232 my $list = $dbh->resultset('ShoppingList')->find($list_id);
234 return exception 404, "No such list";
237 # in case no real changes are needed will return the current state
238 if ( $name ne ( $list->name // '') ) {
239 unless ($version == $list->version) {
240 return exception 409,
241 sprintf( 'Outdated version (current is %d)', $list->version );
246 version => $version + 1,
251 version => $list->version,
252 lists_version => increment_lists_version($dbh),
256 # delete shopping list
257 del '/list/:list_id' => sub {
258 my $list_id = route_parameters->get('list_id');
259 length($list_id) and $list_id =~ /^\d{1,18}$/
260 or return invalid_input('bad list ID');
262 my $dbh = get_database;
265 lists_version => get_lists_version($dbh),
268 my $list = $dbh->resultset('ShoppingList')->find($list_id);
271 $r{lists_version} = increment_lists_version($dbh)