blob: 31cfacaffdd266156084f842eac0ddad2ae3d2c8 [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;
Akrona3c353c2019-02-14 23:50:00 +010051 my $decode = decode_json($response);
52 unless ($decode) {
53 return {
54 status => 500,
55 json => {
56 errors => [[0, 'Unable to parse JSON']]
57 }
58 }
59 };
60
61 return $decode;
Akron6d49c1f2018-10-11 14:22:21 +020062};
63
64
Akron0e1ed242018-10-11 13:22:00 +020065# Base page
66get '/' => sub {
Akron6d49c1f2018-10-11 14:22:21 +020067 shift->render(text => 'Fake server available');
Akron0e1ed242018-10-11 13:22:00 +020068};
69
Akron32396632018-10-11 17:08:37 +020070
Akron0e1ed242018-10-11 13:22:00 +020071# Search fixtures
72get '/search' => sub {
73 my $c = shift;
74 my $v = $c->validation;
75 $v->optional('q');
76 $v->optional('page');
77 $v->optional('ql');
78 $v->optional('count');
79 $v->optional('context');
Akron8ea84292018-10-24 13:41:52 +020080 $v->optional('offset');
81 $v->optional('cutoff')->in(qw/true false/);
Akron0e1ed242018-10-11 13:22:00 +020082
Akron32396632018-10-11 17:08:37 +020083 $c->app->log->debug('Receive request');
84
Akron0e1ed242018-10-11 13:22:00 +020085 # Response q=x&ql=cosmas3
86 if ($v->param('ql') && $v->param('ql') eq 'cosmas3') {
87 return $c->render(
88 status => 400,
89 json => {
90 "\@context" => "http://korap.ids-mannheim.de/ns/koral/0.3/context.jsonld",
91 "errors" => [[307,"cosmas3 is not a supported query language!"]]
92 });
93 };
94
Akron6d49c1f2018-10-11 14:22:21 +020095 if (!$v->param('q')) {
Akron8ea84292018-10-24 13:41:52 +020096 return $c->render(%{$c->load_response('query_no_query')});
Akron0e1ed242018-10-11 13:22:00 +020097 };
98
Akron8ea84292018-10-24 13:41:52 +020099 my @slug_base = ($v->param('q'));
100 push @slug_base, 'o' . $v->param('offset') if defined $v->param('offset');
101 push @slug_base, 'c' . $v->param('count') if defined $v->param('count');
102 push @slug_base, 'co' . $v->param('cutoff') if defined $v->param('cutoff');
103
Akron6d49c1f2018-10-11 14:22:21 +0200104 # Get response based on query parameter
Akron8ea84292018-10-24 13:41:52 +0200105 my $response = $c->load_response('query_' . slugify(join('_', @slug_base)));
Akron0e1ed242018-10-11 13:22:00 +0200106
107 # Check authentification
108 if (my $auth = $c->req->headers->header('Authorization')) {
109 if (my $jwt = $c->jwt_decode($auth)) {
Akron6d49c1f2018-10-11 14:22:21 +0200110 $response->{json}->{meta}->{authorized} = $jwt->{username} if $jwt->{username};
Akron0e1ed242018-10-11 13:22:00 +0200111 };
112 };
113
Akron6d49c1f2018-10-11 14:22:21 +0200114 # Set page parameter
Akron0e1ed242018-10-11 13:22:00 +0200115 if ($v->param('page')) {
Akron6d49c1f2018-10-11 14:22:21 +0200116 $response->{json}->{meta}->{startIndex} = $v->param("startIndex");
Akron0e1ed242018-10-11 13:22:00 +0200117 };
118
Akron0e1ed242018-10-11 13:22:00 +0200119 # Simple search fixture
Akron32396632018-10-11 17:08:37 +0200120 $c->render(%$response);
121
122 $c->app->log->debug('Rendered result');
123
124 return 1;
Akron0e1ed242018-10-11 13:22:00 +0200125};
126
Akron80a84b22018-10-24 17:44:24 +0200127# Textinfo fixtures
128get '/corpus/:corpusId/:docId/:textId' => sub {
129 my $c = shift;
130
131 my $file = join('_', (
132 'textinfo',
133 $c->stash('corpusId'),
134 $c->stash('docId'),
135 $c->stash('textId')
136 ));
137
138 my $slug = slugify($file);
139
140 # Get response based on query parameter
141 my $response = $c->load_response($slug);
142 return $c->render(%$response);
143};
144
Akron0e1ed242018-10-11 13:22:00 +0200145
Akronb80341d2018-10-15 19:46:23 +0200146# Matchinfo fixtures
147get '/corpus/:corpusId/:docId/:textId/:matchId/matchInfo' => sub {
148 my $c = shift;
149
150 my $file = join('_', (
151 'matchinfo',
152 $c->stash('corpusId'),
153 $c->stash('docId'),
154 $c->stash('textId'),
155 $c->stash('matchId')
156 ));
157
Akronb8d0b402018-10-18 23:51:52 +0200158 my $slug = slugify($file);
159
Akronb80341d2018-10-15 19:46:23 +0200160 # Get response based on query parameter
Akronb8d0b402018-10-18 23:51:52 +0200161 my $response = $c->load_response($slug);
Akronb80341d2018-10-15 19:46:23 +0200162 return $c->render(%$response);
163};
164
Akron0e1ed242018-10-11 13:22:00 +0200165
Akronbe61f4c2018-10-20 00:52:58 +0200166# Statistics endpoint
167get '/statistics' => sub {
168 my $c = shift;
169 my $v = $c->validation;
170 $v->optional('corpusQuery');
171
172 my @list = 'corpusinfo';
173 if ($v->param('corpusQuery')) {
174 push @list, $v->param('corpusQuery');
175 };
176 my $slug = slugify(join('_', @list));
177
178 # Get response based on query parameter
179 my $response = $c->load_response($slug);
180 return $c->render(%$response);
181};
182
Akron0e1ed242018-10-11 13:22:00 +0200183############
184# Auth API #
185############
186
187# Request API token
188get '/auth/logout' => sub {
189 my $c = shift;
190
191 if (my $auth = $c->req->headers->header('Authorization')) {
192 if (my $jwt = $c->jwt_decode($auth)) {
193 my $user = $jwt->{username} if $jwt->{username};
194
195 $c->app->log->debug('Server-Logout: ' . $user);
196 return $c->render(json => { msg => [[0, 'Fine!']]});
197 };
198 };
199
200 return $c->render(status => 400, json => { error => [[0, 'No!']]});
201};
202
203
204# Request API token
205get '/auth/apiToken' => sub {
206 my $c = shift;
207
208 # Get auth header
209 my $auth = $c->req->headers->authorization;
210
211 # Authorization missing or not basic
212 if (!$auth || $auth !~ s/\s*Basic\s+//gi) {
213 return $c->render(
214 json => {
215 error => [[2, 'x']]
216 }
217 );
218 };
219
220 # Decode header
221 my ($username, $pwd) = @{b($auth)->b64_decode->split(':')->to_array};
222
223 # the password is 'pass'
224 if ($pwd) {
225
226 # the password is 'pass'
227 if ($pwd eq 'pass') {
228
229 # Render info with token
230 my $jwt = $c->jwt_encode(username => $username);
231
232 # Render in the Kustvakt fashion:
233 return $c->render(
234 format => 'html',
235 text => encode_json({
236 %{$jwt->claims},
237 expires => $jwt->expires,
238 token => $jwt->encode,
239 token_type => 'api_token'
240 })
241 );
Akron3d673062019-01-29 15:54:16 +0100242 }
243
244 elsif ($pwd eq 'ldaperr') {
245 return $c->render(
246 format => 'html',
247 status => 401,
248 json => {
249 "errors" => [[2022,"LDAP Authentication failed due to unknown user or password!"]]
250 }
251 );
Akron0e1ed242018-10-11 13:22:00 +0200252 };
253
254 return $c->render(
255 json => {
256 error => [[2004, undef]]
257 }
258 );
259 };
260
261 return $c->render(
262 json => {
263 error => [[2004, undef]]
264 }
265 );
266};
267
268app->start;
269
270
271__END__
272
273
274 # Temporary:
275 my $collection_query = {
276 '@type' => "koral:docGroup",
277 "operation" => "operation:or",
278 "operands" => [
279 {
280 '@type' => "koral:docGroup",
281 "operation" => "operation:and",
282 "operands" => [
283 {
284 '@type' => "koral:doc",
285 "key" => "title",
286 "match" => "match:eq",
287 "value" => "Der Birnbaum",
288 "type" => "type:string"
289 },
290 {
291 '@type' => "koral:doc",
292 "key" => "pubPlace",
293 "match" => "match:eq",
294 "value" => "Mannheim",
295 "type" => "type:string"
296 },
297 {
298 '@type' => "koral:docGroup",
299 "operation" => "operation:or",
300 "operands" => [
301 {
302 '@type' => "koral:doc",
303 "key" => "subTitle",
304 "match" => "match:eq",
305 "value" => "Aufzucht oder Pflege",
306 "type" => "type:string"
307 },
308 {
309 '@type' => "koral:doc",
310 "key" => "subTitle",
311 "match" => "match:eq",
312 "value" => "Gedichte",
313 "type" => "type:string"
314 }
315 ]
316 }
317 ]
318 },
319 {
320 '@type' => "koral:doc",
321 "key" => "pubDate",
322 "match" => "match:geq",
323 "value" => "2015-03-05",
324 "type" => "type:date"
325 }
326 ]
327 };