Added query reference plugin to connect and mock
query reference api endpoints

Change-Id: I2a6a068c66055692d1057f6972193b0160ed6449
diff --git a/Changes b/Changes
index 400d0cf..85fa8b5 100755
--- a/Changes
+++ b/Changes
@@ -17,6 +17,7 @@
           (lerepp).
         - List tokens of a client.
         - Upgrade Mojolicious dependency to 9.19.
+        - Added query reference API.
 
         WARNING: Upgrading to Mojolicious 9.19 will
           invalidate all sessions. This is a security update.
diff --git a/lib/Kalamar/Plugin/KalamarErrors.pm b/lib/Kalamar/Plugin/KalamarErrors.pm
index 9a8d9d8..c8df749 100644
--- a/lib/Kalamar/Plugin/KalamarErrors.pm
+++ b/lib/Kalamar/Plugin/KalamarErrors.pm
@@ -94,6 +94,9 @@
           return;
         };
 
+
+
+        
         # Notify on warnings
         $c->notify_on_warnings($json);
 
diff --git a/lib/Kalamar/Plugin/QueryReference.pm b/lib/Kalamar/Plugin/QueryReference.pm
new file mode 100644
index 0000000..bb1b390
--- /dev/null
+++ b/lib/Kalamar/Plugin/QueryReference.pm
@@ -0,0 +1,415 @@
+package Kalamar::Plugin::QueryReference;
+use Mojo::Base 'Mojolicious::Plugin';
+
+# This is a plugin to support query references.
+# It currently only mocks endpoints for development purposes.
+
+sub register {
+  my ($plugin, $mojo) = @_;
+
+  # Namespace not yet established
+  if ($ENV{QUERY_REF_MOCKUP}) {
+    unless (exists $mojo->renderer->helpers->{chi} &&
+              $mojo->chi('queryref')) {
+      $mojo->plugin(CHI => {
+        queryref => {
+          driver => 'Memory',
+          global => 1
+        }
+      });
+    };
+  };
+
+  # Establishes routes under '/query'
+  my $r = $mojo->routes;
+
+  $r->add_type('qname' => qr![-_\.a-zA-Z0-9]+!);
+
+
+  # List queries
+  $r->get('/query')->to(
+    cb => sub {
+      my $c = shift;
+
+      # Use mock up
+      if ($ENV{QUERY_REF_MOCKUP}) {
+        my $chi = $c->chi('queryref');
+        my $qs = $chi->get('~queries') // [];
+        my @queries = ();
+        foreach (@$qs) {
+          push @queries, $chi->get($_);
+        };
+        return $c->render(json => \@queries);
+      };
+
+      # Get user handle
+      my $user = $c->user_handle;
+      if ($user eq 'not_logged_in') {
+        return $c->render(
+          json => {
+            errors => [
+              message => 'User not logged in'
+            ]
+          }, status => 400
+        );
+      };
+
+      $c->render_later;
+
+      # API for query adding
+      my $url = Mojo::URL->new($c->korap->api);
+      $url->path->merge(
+        Mojo::Path->new->parts(['query', '~' . $user])
+        )->trailing_slash(0);
+
+      # Issue backend request
+      $c->korap_request('get', $url)->then(
+        sub {
+          my $tx = shift;
+
+          # Catch errors and warnings
+          if ($c->catch_errors_and_warnings($tx)) {
+            my $json = $tx->res->json;
+            if ($json) {
+              $c->notify_on_warnings($json);
+              $c->stash(api_response => $json);
+            };
+            return $c->render(
+              status => $tx->res->code,
+              json => $json
+            );
+          };
+          Mojo::Promise->reject;
+        }
+      )->catch(
+        sub {
+          my $err_msg = shift;
+
+          # Only raised in case of connection errors
+          if ($err_msg) {
+            $c->notify(error => { src => 'Backend' } => $err_msg)
+          };
+
+          return $c->render(
+            status => 400,
+            json => $c->notifications(json => {})
+          );
+        }
+      );
+    }
+  );
+
+
+  # Create query
+  $r->put('/query/<qname:qname>')->to(
+    cb => sub {
+      my $c = shift;
+      my $v = $c->validation;
+
+      # Missing: definition
+      $v->required('q');
+      $v->optional('ql');
+      $v->optional('desc');
+
+      my $qname = $c->stash('qname');
+
+      if ($v->has_error()) {
+        return $c->render(
+          json => {
+            errors => [
+              {
+                message => 'Unable to store query reference'
+              }
+            ]
+          }, status => 400
+        );
+      };
+
+      # Use mock up
+      if ($ENV{QUERY_REF_MOCKUP}) {
+        my $chi = $c->chi('queryref');
+        if ($chi->is_valid($qname)) {
+          return $c->render(
+            json => {
+              errors => [
+                {
+                  message => 'Unable to store query reference'
+                }
+              ]
+            }, status => 400
+          );
+        };
+
+        my $json = {
+          name => $qname,
+          # TODO: Actually this is KQ - not pq or similar
+          koralQuery => $v->param('q'),
+          q => $v->param('q'),
+          ql => ($v->param('ql') // 'poliqarp')
+        };
+
+        if ($v->param('desc')) {
+          $json->{description} = $v->param('desc');
+        };
+
+        # Set query reference
+        $chi->set($qname => $json);
+
+        my $queries = $chi->get('~queries') // [];
+        push @$queries, $qname;
+        $chi->set('~queries' => $queries);
+
+        return $c->render(
+          status => 201,
+          text => ''
+        );
+      };
+
+      # Get user handle
+      my $user = $c->user_handle;
+      if ($user eq 'not_logged_in') {
+        return $c->render(
+          json => {
+            errors => [
+              message => 'User not logged in'
+            ]
+          }, status => 400
+        );
+      };
+
+      # API for query adding
+      my $url = Mojo::URL->new($c->korap->api);
+      $url->path->merge(
+        Mojo::Path->new->parts(['query', '~' . $user, $qname])
+        )->trailing_slash(0);
+
+      my $json = {
+        type => 'PRIVATE',
+        queryType => 'QUERY',
+        queryLanguage => ($v->param('ql') // 'poliqarp'),
+        query => $v->param('q')
+      };
+
+      if ($v->param('desc')) {
+        $json->{description} = $v->param('desc');
+      };
+
+      $c->render_later;
+
+      # Issue backend request
+      $c->korap_request('put', $url => json => $json)->then(
+        sub {
+          my $tx = shift;
+
+          if ($tx->res->is_error) {
+            my $json = $tx->res->json;
+            $c->notify_on_warnings($json);
+            $c->stash(api_response => $json);
+            return $c->render(
+              status => $tx->res->code,
+              json => $json
+            );
+          };
+
+          return $c->render(
+            status => 201,
+            text => ''
+          );
+        }
+      )->catch(
+        sub {
+          my $err_msg = shift;
+
+          # Only raised in case of connection errors
+          if ($err_msg) {
+            $c->notify(error => { src => 'Backend' } => $err_msg)
+          };
+
+          return $c->render(
+            status => 400,
+            json => $c->notifications(json => {})
+          );
+        }
+      );
+    }
+  );
+
+
+  # Delete query
+  $r->delete('/query/<qname:qname>')->to(
+    cb => sub {
+      my $c = shift;
+      my $qname = $c->stash('qname');
+
+      # Use mock up
+      if ($ENV{QUERY_REF_MOCKUP}) {
+        my $chi = $c->chi('queryref');
+
+        $chi->remove($qname);
+
+        my $queries = $chi->get('~queries') // [];
+
+        my @clean = ();
+        foreach (@$queries) {
+          push @clean, $_ unless $_ eq $qname
+        };
+        $chi->set('~queries' => \@clean);
+
+        return $c->render(
+          status => 200,
+          text => ''
+        );
+      };
+
+      # Get user handle
+      my $user = $c->user_handle;
+      if ($user eq 'not_logged_in') {
+        return $c->render(
+          json => {
+            errors => [
+              message => 'User not logged in'
+            ]
+          }, status => 400
+        );
+      };
+
+      $c->render_later;
+
+      # API for query adding
+      my $url = Mojo::URL->new($c->korap->api);
+      $url->path->merge(
+        Mojo::Path->new->parts(['query', '~' . $user, $qname])
+        )->trailing_slash(0);
+
+      # Issue backend request
+      $c->korap_request('delete', $url)->then(
+        sub {
+          my $tx = shift;
+
+          if ($tx->res->is_error) {
+            my $json = $tx->res->json;
+            $c->notify_on_warnings($json);
+            $c->stash(api_response => $json);
+            return $c->render(
+              status => $tx->res->code,
+              json => $json
+            );
+          };
+
+          return $c->render(
+            status => 200,
+            text => ''
+          );
+        }
+      )->catch(
+        sub {
+          my $err_msg = shift;
+
+          # Only raised in case of connection errors
+          if ($err_msg) {
+            $c->notify(error => { src => 'Backend' } => $err_msg)
+          };
+
+          return $c->render(
+            status => 400,
+            json => $c->notifications(json => {})
+          );
+        }
+      );
+    }
+  );
+
+
+  # Retrieve query
+  $r->get('/query/<qname:qname>')->to(
+    cb => sub {
+      my $c = shift;
+      my $qname = $c->stash('qname');
+
+      # Use mock up
+      if ($ENV{QUERY_REF_MOCKUP}) {
+        my $chi = $c->chi('queryref');
+        my $json = $chi->get($qname);
+
+        if ($json) {
+          return $c->render(
+            json => $json
+          );
+        };
+
+        return $c->render(
+          json => {
+            errors => [
+              {
+                message => 'Query reference not found'
+              }
+            ]
+          }, status => 404
+        );
+      };
+
+      # Get user handle
+      my $user = $c->user_handle;
+      if ($user eq 'not_logged_in') {
+        return $c->render(
+          json => {
+            errors => [
+              message => 'User not logged in'
+            ]
+          }, status => 400
+        );
+      };
+
+      # API for query adding
+      my $url = Mojo::URL->new($c->korap->api);
+      $url->path->merge(
+        Mojo::Path->new->parts(['query', '~' . $user, $qname])
+        )->trailing_slash(0);
+
+      # Issue backend request
+      $c->korap_request('get', $url)->then(
+        sub {
+          my $tx = shift;
+
+          if ($tx->res->code == 404) {
+            return $c->render(
+              status => 404,
+              json => $tx->res->json
+            );
+          };
+
+          # Catch errors and warnings
+          if ($c->catch_errors_and_warnings($tx)) {
+            my $json = $tx->res->json;
+            if ($json) {
+              $c->notify_on_warnings($json);
+              $c->stash(api_response => $json);
+            };
+            return $c->render(
+              status => $tx->res->code,
+              json => $json
+            );
+          };
+          Mojo::Promise->reject;
+        }
+      )->catch(
+        sub {
+          my $err_msg = shift;
+
+          # Only raised in case of connection errors
+          if ($err_msg) {
+            $c->notify(error => { src => 'Backend' } => $err_msg)
+          };
+
+          return $c->render(
+            status => 400,
+            json => $c->notifications(json => {})
+          );
+        }
+      );
+    }
+  );
+};
+
+
+1;
diff --git a/t/plugin/query_reference.t b/t/plugin/query_reference.t
new file mode 100644
index 0000000..97b7345
--- /dev/null
+++ b/t/plugin/query_reference.t
@@ -0,0 +1,179 @@
+use Mojo::Base -strict;
+use Mojo::File 'path';
+use Test::More;
+use Test::Mojo;
+
+#####################
+# Start Fake server #
+#####################
+my $mount_point = '/realapi/';
+$ENV{KALAMAR_API} = $mount_point;
+$ENV{QUERY_REF_MOCKUP} = 1;
+
+# Test app
+my $t = Test::Mojo->new('Kalamar' => {
+  Kalamar => {
+    plugins => ['Auth','QueryReference']
+  }
+});
+
+# Mount fake backend
+# Get the fixture path
+my $fixtures_path = path(Mojo::File->new(__FILE__)->dirname, '..', 'server');
+my $fake_backend = $t->app->plugin(
+  Mount => {
+    $mount_point =>
+      $fixtures_path->child('mock.pl')
+  }
+);
+# Configure fake backend
+$fake_backend->pattern->defaults->{app}->log($t->app->log);
+
+$t->put_ok('/query/baum')
+  ->status_is(400)
+  ->json_like('/errors/0/message', qr!unable!i)
+  ;
+$t->put_ok('/query/baum?q=[orth=Baum]&desc=Eine Baum-Query')
+  ->status_is(201)
+  ->content_is('')
+  ;
+$t->put_ok('/query/frage?q=[orth=Frage]&desc=Eine Frage-Query&ql=poliqarp')
+  ->status_is(201)
+  ->content_is('')
+  ;
+$t->put_ok('/query/baum?q=[orth=Baum]&desc=Eine Baum-Query')
+  ->status_is(400)
+  ->json_like('/errors/0/message', qr!unable!i)
+  ;
+
+$t->get_ok('/query')
+  ->status_is(200)
+  ->json_is('/0/name', 'baum')
+  ->json_is('/1/name', 'frage')
+  ;
+
+$t->get_ok('/query/frage')
+  ->status_is(200)
+  ->json_is('/name', 'frage')
+  ->json_is('/description', 'Eine Frage-Query')
+  ->json_is('/koralQuery', '[orth=Frage]')
+  ->json_is('/q', '[orth=Frage]')
+  ->json_is('/ql', 'poliqarp')
+  ;
+$t->delete_ok('/query/frage')
+  ->status_is(200)
+  ->content_is('')
+  ;
+$t->delete_ok('/query/frage2')
+  ->status_is(200)
+  ->content_is('')
+  ;
+$t->get_ok('/query')
+  ->status_is(200)
+  ->json_is('/0/name', 'baum')
+  ->json_hasnt('/1')
+  ;
+$t->get_ok('/query/frage')
+  ->status_is(404)
+  ->json_is('/errors/0/message', 'Query reference not found')
+  ;
+$t->get_ok('/query/baum')
+  ->status_is(200)
+  ->json_is('/name', 'baum')
+  ->json_is('/description', 'Eine Baum-Query')
+  ->json_is('/koralQuery', '[orth=Baum]')
+  ->json_is('/q', '[orth=Baum]')
+  ->json_is('/ql', 'poliqarp')
+  ;
+
+## REALAPI
+$ENV{QUERY_REF_MOCKUP} = 0;
+
+$t->get_ok('/realapi/v1.0')
+  ->status_is(200)
+  ->content_is('Fake server available')
+  ;
+
+# Login
+my $csrf = $t->get_ok('/')
+  ->status_is(200)
+  ->tx->res->dom->at('input[name=csrf_token]')->attr('value')
+  ;
+
+$t->post_ok('/user/login' => form => {
+  handle => 'test',
+  pwd => 'pass',
+  csrf_token => $csrf
+})
+  ->status_is(302)
+  ->content_is('')
+  ->header_is('Location' => '/');
+
+$t->put_ok('/query/baum')
+  ->status_is(400)
+  ->json_like('/errors/0/message', qr!unable!i)
+  ;
+
+$t->put_ok('/query/baum?q=[orth=Baum]')
+  ->status_is(201)
+  ->content_is('')
+  ;
+
+$t->put_ok('/query/frage?q=[orth=Frage]&desc=Eine Frage-Query&ql=poliqarp')
+  ->status_is(201)
+  ->content_is('')
+  ;
+
+$t->put_ok('/query/baum?q=[orth=Baum]&desc=Eine Baum-Query')
+  ->status_is(400)
+  ->json_like('/errors/0/message', qr!unable!i)
+  ;
+
+$t->get_ok('/query')
+  ->status_is(200)
+  ->json_is('/refs/0/name', 'baum')
+  ->json_is('/refs/1/name', 'frage')
+  ;
+
+$t->get_ok('/query/frage')
+  ->status_is(200)
+  ->json_is('/description', 'Eine Frage-Query')
+  ->json_is('/type', 'PRIVATE')
+  ->json_is('/queryType', 'QUERY')
+  ->json_is('/query', '[orth=Frage]')
+  ->json_is('/queryLanguage', 'poliqarp')
+  ->json_is('/koralQuery/@type', 'Okay')
+  ;
+
+$t->delete_ok('/query/frage')
+  ->status_is(200)
+  ->content_is('')
+  ;
+
+$t->delete_ok('/query/frage2')
+  ->status_is(200)
+  ->content_is('')
+  ;
+
+$t->get_ok('/query')
+  ->status_is(200)
+  ->json_is('/refs/0/name', 'baum')
+  ->json_hasnt('/1')
+  ;
+
+$t->get_ok('/query/frage')
+  ->status_is(404)
+  ->json_is('/errors/0/message', 'Query reference not found')
+  ;
+
+$t->get_ok('/query/baum')
+  ->status_is(200)
+  ->json_is('/name', 'baum')
+  ->json_hasnt('/description')
+  ->json_is('/koralQuery/@type', 'Okay')
+  ->json_is('/query', '[orth=Baum]')
+  ->json_is('/queryLanguage', 'poliqarp')
+  ;
+
+
+done_testing();
diff --git a/t/server/mock.pl b/t/server/mock.pl
index 7e3eedb..c3808f6 100644
--- a/t/server/mock.pl
+++ b/t/server/mock.pl
@@ -652,6 +652,125 @@
 };
 
 
+#######################
+# Query Reference API #
+#######################
+
+use CHI;
+my $chi = CHI->new(
+  driver => 'Memory',
+  global => 1
+);
+
+# Store query
+put '/v1.0/query/~:user/:query_name' => sub {
+  my $c = shift;
+  my $user = $c->stash('user');
+  my $qname = $c->stash('query_name');
+
+  if ($chi->is_valid($qname)) {
+    return $c->render(
+      json => {
+        errors => [
+          {
+            message => 'Unable to store query reference'
+          }
+        ]
+      }, status => 400
+    );
+  };
+
+  my $json = $c->req->json;
+
+  my $store = {
+    name => $qname,
+    koralQuery => { '@type' => 'Okay' },
+    query => $json->{query},
+    queryType => $json->{queryType},
+    type => $json->{type},
+    queryLanguage => $json->{queryLanguage},
+  };
+
+  if (exists $json->{description}) {
+    $store->{description} = $json->{description}
+  };
+
+  # Set query reference
+  $chi->set($qname => $store);
+
+  my $queries = $chi->get('~queries') // [];
+  push @$queries, $qname;
+  $chi->set('~queries' => $queries);
+
+  return $c->render(
+    status => 201,
+    text => ''
+  );
+};
+
+# Get query
+get '/v1.0/query/~:user/:query_name' => sub {
+  my $c = shift;
+
+  my $user = $c->stash('user');
+  my $qname = $c->stash('query_name');
+
+  my $json = $chi->get($qname);
+
+  if ($json) {
+    return $c->render(
+      json => $json
+    );
+  };
+
+  return $c->render(
+    json => {
+      errors => [
+        {
+          message => 'Query reference not found'
+        }
+      ]
+    }, status => 404
+  );
+};
+
+
+# Get all queries
+get '/v1.0/query/~:user' => sub {
+  my $c = shift;
+  my $user = $c->stash('user');
+  my $qs = $chi->get('~queries') // [];
+  my @queries = ();
+  foreach (@$qs) {
+    push @queries, $chi->get($_);
+  };
+  return $c->render(json => { refs => \@queries });
+};
+
+
+# Store query
+del '/v1.0/query/~:user/:query_name' => sub {
+  my $c = shift;
+  my $user = $c->stash('user');
+  my $qname = $c->stash('query_name');
+
+  $chi->remove($qname);
+
+  my $queries = $chi->get('~queries') // [];
+
+  my @clean = ();
+  foreach (@$queries) {
+    push @clean, $_ unless $_ eq $qname
+  };
+
+  $chi->set('~queries' => \@clean);
+
+  return $c->render(
+    status => 200,
+    text => ''
+  );
+};
+
 
 app->start;