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