]> git.ktnx.net Git - lsl.git/blob - lib/App/LazyShoppingList/API/v1.pm
protocol change: supply list name in the list of lists
[lsl.git] / lib / App / LazyShoppingList / API / v1.pm
1 use v5.26;
2 use warnings;
3 use utf8;
4
5 package App::LazyShoppingList::API::v1;
6 use App::LazyShoppingList::API;
7 use Dancer2;
8
9 our $VERSION = '0.1';
10
11 use JSON();
12
13 use experimental 'signatures';
14
15 set charset      => 'UTF-8';
16 set serializer   => 'JSON';
17 set content_type => 'application/json';
18
19
20 # get the URI for the list of shopping lists
21 get '/' => sub {
22     return { lists => uri_for('/list') };
23 };
24
25 # get the list of shopping lists
26 get '/list' => sub {
27     my $dbh = get_database;
28     $dbh->txn_begin;
29
30     my %r = ( lists_version => get_lists_version($dbh), lists => [] );
31     for my $list (
32         $dbh->resultset('ShoppingList')
33         ->search( undef,
34             { order_by => { -asc => 'name' } } )->all
35         )
36     {
37         push @{ $r{lists} },
38             { uri => uri_for( "/list/" . $list->id ), name => $list->name };
39     }
40
41     $dbh->txn_commit;
42     return \%r;
43 };
44
45 # create shopping list
46 post '/list' => sub {
47     my $req = request_data;
48
49     my $name = $req->{name};
50     unless ($name) {
51         status 400;
52         content_type 'text/plain';
53         return "Missing list name";
54     }
55
56     my $dbh = get_database;
57     $dbh->txn_begin;
58     my $list = $dbh->resultset('ShoppingList')->create({
59             name => $name});
60     $list->discard_changes;
61
62     my  $lists_ver = increment_lists_version($dbh);
63
64     $dbh->txn_commit;
65
66     return {
67         uri           => uri_for( '/list/' . $list->id ),
68         version       => $list->version,
69         lists_version => $lists_ver
70     };
71 };
72
73 # get shopping list items
74 get '/list/:list_id' => sub {
75     my $list_id = route_parameters->get('list_id');
76     length($list_id) and $list_id =~ /^\d{1,18}$/ or return invalid_input;
77
78     my $dbh = get_database;
79
80     warn $dbh;
81     my %r = ( items => [] );
82     $dbh->txn_begin;
83
84     $r{lists_version} = get_lists_version($dbh);
85
86     my $list = $dbh->resultset('ShoppingList')->find($list_id);
87
88     unless ($list) {
89         $dbh->txn_commit;
90         status 404;
91         content_type 'text/plain';
92         return "No list with that ID found";
93     }
94
95     $r{version} = $list->version;
96
97     my @items = $dbh->resultset('ShoppingListItem')
98         ->search( undef, { order_by => { -asc => 'id' } } )->all;
99     for my $item (@items) {
100         push @{ $r{items} },
101             {   uri =>
102                 uri_for( sprintf( "/list/%s/%s", $list->id, $item->id ) ),
103                 description => $item->description,
104                 done        => JSON->boolean( $item->done ),
105                 version     => $item->version,
106             };
107     }
108
109     $dbh->txn_commit;
110
111     return \%r;
112 };
113
114 # create shopping list item
115 post '/list/:id' => sub {
116     my $list_id = route_parameters->get('id');
117     length($list_id) and $list_id =~ /^\d{1,18}$/
118         or return invalid_input('bad list ID');
119
120     my $req = request_data;
121
122     my $descr = $req->{description};
123     my $done  = JSON->boolean( $req->{done} // 0 );
124
125     my $dbh = get_database;
126     $dbh->txn_begin;
127
128     my %r = (
129         lists_version => get_lists_version($dbh),
130     );
131
132     my $list = $dbh->resultset('ShoppingList')->find($list_id);
133
134     unless ($list) {
135         $dbh->txn_commit;
136         status 404;
137         content_type 'text/plain';
138         return "No such list";
139     }
140
141     my $item = $dbh->resultset('ShoppingListItem')->create(
142         {   shopping_list => $list->id,
143             description   => $descr,
144             done          => $done,
145         }
146     );
147     $item->discard_changes;
148
149     $list->update( { version => $list->version + 1 } );
150
151     $r{uri} = uri_for( sprintf( "list/%s/%s", $list->id, $item->id ) );
152     $r{version} = $item->version;
153     $r{list_version} = $list->version;
154
155     $dbh->txn_commit;
156
157     return \%r;
158 };
159
160 # modify shopping list item
161 put '/list/:list_id/:item_id' => sub {
162     my $list_id = route_parameters->get('list_id');
163     length($list_id) and $list_id =~ /^\d{1,18}$/
164         or return invalid_input('bad list ID');
165
166     my $item_id = route_parameters->get('item_id');
167     length($item_id) and $item_id =~ /^\d{1,18}$/
168         or return invalid_input('bad item ID');
169
170     my $req = request_data;
171
172     my $descr = $req->{description};
173     my $done  = JSON->boolean( $req->{done} // 0 );
174     my $version = $req->{version};
175
176     length($version) and $version =~ /^\d{1,18}$/
177         or return invalid_input('bad version');
178
179     my $dbh = get_database;
180     $dbh->txn_begin;
181
182     my %r = (
183         lists_version => get_lists_version($dbh),
184     );
185
186     my $list = $dbh->resultset('ShoppingList')->find($list_id);
187     unless ($list) {
188         $dbh->txn_commit;
189         status 404;
190         content_type 'text/plain';
191         return "No such list";
192     }
193
194     my $item = $dbh->resultset('ShoppingListItem')->find({shopping_list => $list->id, id => $item_id});
195     unless ($item) {
196         $dbh->txn_commit;
197         status 404;
198         content_type 'text/plain';
199         return "No such item";
200     }
201
202     # in case no real changes are needed will return the current state
203     if (   defined($descr) and $descr ne ( $item->description // '' )
204         or defined($done) and $done != $item->done )
205     {
206         unless ($version == $item->version) {
207             $dbh->txn_commit;
208             status 409;
209             content_type 'text/plain';
210             return
211                 sprintf( 'Outdated version (current is %d)', $item->version );
212         }
213
214         $item->update({
215             defined($descr) ? (description  => $descr) : (),
216             defined($done) ? (done =>  $done) : (),
217             version => $version + 1,
218         });
219
220         $list->update( { version => $list->version + 1 } );
221     }
222
223     $r{uri} = uri_for( sprintf( "list/%s/%s", $list->id, $item->id ) );
224     $r{version} = $item->version;
225     $r{list_version} = $list->version;
226
227     $dbh->txn_commit;
228
229     return \%r;
230 };
231
232 true;