blob: 79eeb90e4934ac5a6474e1abb57fd5764c0569c6 [file] [log] [blame]
Akron0e1ed242018-10-11 13:22:00 +02001#!/usr/bin/env perl
2use Mojolicious::Lite;
3use Mojo::ByteStream 'b';
4use Mojo::Date;
5use Mojo::JSON qw/true false encode_json decode_json/;
6use strict;
7use warnings;
8use Mojo::JWT;
Akron6d49c1f2018-10-11 14:22:21 +02009use Mojo::File qw/path/;
10use Mojo::Util qw/slugify/;
Akron0e1ed242018-10-11 13:22:00 +020011
12# This is an API fake server with fixtures
13
14my $secret = 's3cr3t';
Akron73f36082018-10-25 15:34:59 +020015my $fixture_path = path(Mojo::File->new(__FILE__)->dirname)->child('..', 'fixtures');
Akron0e1ed242018-10-11 13:22:00 +020016
17helper jwt_encode => sub {
18 shift;
19 return Mojo::JWT->new(
20 secret => $secret,
21 token_type => 'api_token',
22 expires => time + (3 * 34 * 60 * 60),
23 claims => { @_ }
24 );
25};
26
27helper jwt_decode => sub {
28 my ($c, $auth) = @_;
29 $auth =~ s/\s*api_token\s+//;
30 return Mojo::JWT->new(secret => $secret)->decode($auth);
31};
32
33
Akron6d49c1f2018-10-11 14:22:21 +020034# Load fixture responses
35helper 'load_response' => sub {
36 my $c = shift;
37 my $q_name = shift;
38 my $file = $fixture_path->child("response_$q_name.json");
Akron8ea84292018-10-24 13:41:52 +020039 $c->app->log->debug("Load response from $file");
40
Akron6d49c1f2018-10-11 14:22:21 +020041 unless (-f $file) {
42 return {
43 status => 500,
44 json => {
45 errors => [[0, 'Unable to load query response from ' . $file]]
46 }
47 }
48 };
Akron8ea84292018-10-24 13:41:52 +020049
Akron6d49c1f2018-10-11 14:22:21 +020050 my $response = $file->slurp;
51 return decode_json($response);
52};
53
54
Akron0e1ed242018-10-11 13:22:00 +020055# Base page
56get '/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +020057 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +020058};
59
Akron32396632018-10-11 17:08:37 +020060
Akron0e1ed242018-10-11 13:22:00 +020061# Search fixtures
62get '/search' => sub {
63 my $c = shift;
64 my $v = $c->validation;
65 $v->optional('q');
66 $v->optional('page');
67 $v->optional('ql');
68 $v->optional('count');
69 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +020070 $v->optional('offset');
71 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +020072
Akron32396632018-10-11 17:08:37 +020073 $c->app->log->debug('Receive request');
74
Akron0e1ed242018-10-11 13:22:00 +020075 # Response q=x&ql=cosmas3
76 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
77 return $c->render(
78 status => 400,
79 json => {
80 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
81 "errors" => [[307,"cosmas3 is not a supported query language!"]]
82 });
83 };
84
Akron6d49c1f2018-10-11 14:22:21 +020085 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +020086 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +020087 };
88
Akron8ea84292018-10-24 13:41:52 +020089 my @slug_base = ($v->param('q'));
90 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
91 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
92 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
93
Akron6d49c1f2018-10-11 14:22:21 +020094 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +020095 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +020096
97 # Check authentification
98 if (my $auth = $c->req->headers->header('Authorization')) {
99 if (my $jwt = $c->jwt_decode($auth)) {
Akron6d49c1f2018-10-11 14:22:21 +0200100 $response->{json}->{meta}->{authorized} = $jwt->{username} if $jwt->{username};
Akron0e1ed242018-10-11 13:22:00 +0200101 };
102 };
103
Akron6d49c1f2018-10-11 14:22:21 +0200104 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200105 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200106 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200107 };
108
Akron0e1ed242018-10-11 13:22:00 +0200109 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200110 $c->render(%$response);
111
112 $c->app->log->debug('Rendered result');
113
114 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200115};
116
Akron80a84b22018-10-24 17:44:24 +0200117# Textinfo fixtures
118get '/corpus/:corpusId/:docId/:textId' => sub {
119 my $c = shift;
120
121 my $file = join('_', (
122 'textinfo',
123 $c->stash('corpusId'),
124 $c->stash('docId'),
125 $c->stash('textId')
126 ));
127
128 my $slug = slugify($file);
129
130 # Get response based on query parameter
131 my $response = $c->load_response($slug);
132 return $c->render(%$response);
133};
134
Akron0e1ed242018-10-11 13:22:00 +0200135
Akronb80341d2018-10-15 19:46:23 +0200136# Matchinfo fixtures
137get '/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
138 my $c = shift;
139
140 my $file = join('_', (
141 'matchinfo',
142 $c->stash('corpusId'),
143 $c->stash('docId'),
144 $c->stash('textId'),
145 $c->stash('matchId')
146 ));
147
Akronb8d0b402018-10-18 23:51:52 +0200148 my $slug = slugify($file);
149
Akronb80341d2018-10-15 19:46:23 +0200150 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200151 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200152 return $c->render(%$response);
153};
154
Akron0e1ed242018-10-11 13:22:00 +0200155
Akronbe61f4c2018-10-20 00:52:58 +0200156# Statistics endpoint
157get '/statistics' => sub {
158 my $c = shift;
159 my $v = $c->validation;
160 $v->optional('corpusQuery');
161
162 my @list = 'corpusinfo';
163 if ($v->param('corpusQuery')) {
164 push @list, $v->param('corpusQuery');
165 };
166 my $slug = slugify(join('_', @list));
167
168 # Get response based on query parameter
169 my $response = $c->load_response($slug);
170 return $c->render(%$response);
171};
172
Akron0e1ed242018-10-11 13:22:00 +0200173############
174# Auth API #
175############
176
177# Request API token
178get '/auth/logout' => sub {
179 my $c = shift;
180
181 if (my $auth = $c->req->headers->header('Authorization')) {
182 if (my $jwt = $c->jwt_decode($auth)) {
183 my $user = $jwt->{username} if $jwt->{username};
184
185 $c->app->log->debug('Server-Logout: ' . $user);
186 return $c->render(json => { msg => [[0, 'Fine!']]});
187 };
188 };
189
190 return $c->render(status => 400, json => { error => [[0, 'No!']]});
191};
192
193
194# Request API token
195get '/auth/apiToken' => sub {
196 my $c = shift;
197
198 # Get auth header
199 my $auth = $c->req->headers->authorization;
200
201 # Authorization missing or not basic
202 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
203 return $c->render(
204 json => {
205 error => [[2, 'x']]
206 }
207 );
208 };
209
210 # Decode header
211 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
212
213 # the password is 'pass'
214 if ($pwd) {
215
216 # the password is 'pass'
217 if ($pwd eq 'pass') {
218
219 # Render info with token
220 my $jwt = $c->jwt_encode(username => $username);
221
222 # Render in the Kustvakt fashion:
223 return $c->render(
224 format => 'html',
225 text => encode_json({
226 %{$jwt->claims},
227 expires => $jwt->expires,
228 token => $jwt->encode,
229 token_type => 'api_token'
230 })
231 );
Akron3d673062019-01-29 15:54:16 +0100232 }
233
234 elsif ($pwd eq 'ldaperr') {
235 return $c->render(
236 format => 'html',
237 status => 401,
238 json => {
239 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
240 }
241 );
Akron0e1ed242018-10-11 13:22:00 +0200242 };
243
244 return $c->render(
245 json => {
246 error => [[2004, undef]]
247 }
248 );
249 };
250
251 return $c->render(
252 json => {
253 error => [[2004, undef]]
254 }
255 );
256};
257
258app->start;
259
260
261__END__
262
263
264 # Temporary:
265 my $collection_query = {
266 '@type' => "koral:docGroup",
267 "operation" => "operation:or",
268 "operands" => [
269 {
270 '@type' => "koral:docGroup",
271 "operation" => "operation:and",
272 "operands" => [
273 {
274 '@type' => "koral:doc",
275 "key" => "title",
276 "match" => "match:eq",
277 "value" => "Der Birnbaum",
278 "type" => "type:string"
279 },
280 {
281 '@type' => "koral:doc",
282 "key" => "pubPlace",
283 "match" => "match:eq",
284 "value" => "Mannheim",
285 "type" => "type:string"
286 },
287 {
288 '@type' => "koral:docGroup",
289 "operation" => "operation:or",
290 "operands" => [
291 {
292 '@type' => "koral:doc",
293 "key" => "subTitle",
294 "match" => "match:eq",
295 "value" => "Aufzucht oder Pflege",
296 "type" => "type:string"
297 },
298 {
299 '@type' => "koral:doc",
300 "key" => "subTitle",
301 "match" => "match:eq",
302 "value" => "Gedichte",
303 "type" => "type:string"
304 }
305 ]
306 }
307 ]
308 },
309 {
310 '@type' => "koral:doc",
311 "key" => "pubDate",
312 "match" => "match:geq",
313 "value" => "2015-03-05",
314 "type" => "type:date"
315 }
316 ]
317 };