<commit_before>From bfd862587adb409e4c70d4c211422a43c8227d69 Mon Sep 17 00:00:00 2001
From: <NAME> <<EMAIL>>
Date: Wed, 27 Apr 2016 13:47:04 +0000
Subject: [PATCH] Bug 16330 - REST API: add routes to add, update and delete
 patrons

This patch adds support for add, edit and delete patrons via REST API.

GET  /api/v1/patrons                   Get patron list from params
GET  /api/v1/patrons/<borrowernumber>  Get single patron
POST /api/v1/patrons                   Create a new patron
PUT  /api/v1/patrons/<borrowernumber>  Update data about patron
DEL  /api/v1/patrons/<borrowernumber>  Delete a patron

Test plan:
1) Apply this patch (on top of dependent bug 15126)
2) Run tests perl t/db_dependent/api/v1/patrons.t
3) Add a user with proper rights to use the REST API
4) play with your favourite REST client (curl/httpie, etc.):
   Authenticate with the user created above and get a CGISESSION id.
   Use the CGISESSION to add, edit and delete patrons via the API.

Please note there is no validation of body input in PUT/POST other
than branchcode,category,userid,cardnumber.
---
 Koha/REST/V1/Patrons.pm          | 157 ++++++++++++++++++++++++++++++++++++-
 api/v1/swagger.json              | 137 ++++++++++++++++++++++++++++++++
 t/db_dependent/api/v1/patrons.t  | 167 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 446 insertions(+), 15 deletions(-)

diff --git a/Koha/REST/V1/Patrons.pm b/Koha/REST/V1/Patrons.pm
index 2851308..0411719 100644
--- a/Koha/REST/V1/Patrons.pm
+++ b/Koha/REST/V1/Patrons.pm
@@ -20,7 +20,10 @@ use Modern::Perl;
 use Mojo::Base 'Mojolicious::Controller';
 
 use C4::Auth qw( haspermission );
+use Koha::AuthUtils qw(hash_password);
 use Koha::Borrowers;
+use Koha::Borrower::Categories;
+use Koha::Libraries;
 
 sub list {
     my ($c, $args, $cb) = @_;
@@ -30,7 +33,17 @@ sub list {
         return $c->$cb({error => "You don't have the required permission"}, 403);
     }
 
-    my $patrons = Koha::Borrowers->search;
+    my $params = $c->req->query_params->to_hash;
+    my $patrons;
+    if (keys %$params) {
+        my @valid_params = Koha::Borrowers->_resultset->result_source->columns;
+        foreach my $key (keys %$params) {
+            delete $params->{$key} unless grep { $key eq $_ } @valid_params;
+        }
+        $patrons = Koha::Borrowers->search($params);
+    } else {
+        $patrons = Koha::Borrowers->search;
+    }
 
     $c->$cb($patrons->unblessed, 200);
 }
@@ -55,4 +68,146 @@ sub get {
     return $c->$cb($patron->unblessed, 200);
 }
 
+sub add {
+    my ($c, $args, $cb) = @_;
+
+    my $user = $c->stash('koha.user');
+
+    unless ( $user && haspermission($user->userid, {borrowers => 1}) ) {
+        return $c->$cb({error => "You don't have the required permission"}, 403);
+    }
+
+    my $body = $c->req->json;
+
+    # patron cardnumber and/or userid unique?
+    if ($body->{cardnumber} || $body->{userid}) {
+        my $patron = Koha::Borrowers->find({cardnumber => $body->{cardnumber}, userid => $body->{userid} });
+        if ($patron) {
+            return $c->$cb({
+                error => "Patron cardnumber and userid must be unique",
+                conflict => { cardnumber => $patron->cardnumber, userid => $patron->userid }
+            }, 409);
+        }
+    }
+
+    my $branch = Koha::Libraries->find({branchcode => $body->{branchcode} });
+    unless ($branch) {
+        return $c->$cb({error => "Library with branchcode \"" . $body->{branchcode} . "\" does not exist"}, 404);
+    }
+    my $category = Koha::Borrower::Categories->find({ categorycode => $body->{categorycode} });
+    unless ($category) {
+        return $c->$cb({error => "Patron category \"" . $body->{categorycode} . "\" does not exist"}, 404);
+    }
+    # All OK - save new patron
+
+    if ($body->{password}) { $body->{password} = hash_password($body->{password}) }; # bcrypt password if given
+
+    my $patron = eval {
+        Koha::Borrower->new($body)->store;
+    };
+
+    unless ($patron) {
+        return $c->$cb({error => "Something went wrong, check Koha logs for details"}, 500);
+    }
+
+    return $c->$cb($patron->unblessed, 201);
+}
+
+sub edit {
+    my ($c, $args, $cb) = @_;
+
+    my $user = $c->stash('koha.user');
+
+    unless ( $user && haspermission($user->userid, {borrowers => 1}) ) {
+        return $c->$cb({error => "You don't have the required permission"}, 403);
+    }
+
+    my $patron = Koha::Borrowers->find($args->{borrowernumber});
+
+    unless ($patron) {
+        return $c->$cb({error => "Patron not found"}, 404);
+    }
+
+    my $body = $c->req->json;
+
+    # Can we change userid and/or cardnumber? in that case check that they are altered first
+    if ($body->{cardnumber} || $body->{userid}) {
+        if ( ($body->{cardnumber} && $body->{cardnumber} ne $patron->cardnumber) || ($body->{userid} && $body->{userid} ne $patron->userid) ) {
+            my $conflictingPatron = Koha::Borrowers->find({cardnumber => $body->{cardnumber}, userid => $body->{userid} });
+            if ($conflictingPatron) {
+                return $c->$cb({
+                    error => "Patron cardnumber and userid must be unique",
+                    conflict => { cardnumber => $conflictingPatron->cardnumber, userid => $conflictingPatron->userid }
+                }, 409);
+            }
+        }
+    }
+
+    if ($body->{branchcode}) {
+        my $branch = Koha::Libraries->find({branchcode => $body->{branchcode} });
+        unless ($branch) {
+            return $c->$cb({error => "Library with branchcode \"" . $body->{branchcode} . "\" does not exist"}, 404);
+        }
+    }
+
+    if ($body->{categorycode}) {
+        my $category = Koha::Borrower::Categories->find({ categorycode => $body->{categorycode} });
+        unless ($category) {
+            return $c->$cb({error => "Patron category \"" . $body->{categorycode} . "\" does not exist"}, 404);
+        }
+    }
+    # ALL OK - Update patron
+    # Perhaps limit/validate what should be updated here? flags, et.al.
+    if ($body->{password}) { $body->{password} = hash_password($body->{password}) }; # bcrypt password if given
+
+    my $updatedpatron = eval {
+        $patron->set($body);
+    };
+
+    if ($updatedpatron) {
+        if ($updatedpatron->is_changed) {
+
+            my $res = eval {
+                $updatedpatron->store;<commit_msg>koha-patched: fix 015-bug16330-patron-API patch<commit_after>From bfd862587adb409e4c70d4c211422a43c8227d69 Mon Sep 17 00:00:00 2001
From: <NAME> <<EMAIL>>
Date: Wed, 27 Apr 2016 13:47:04 +0000
Subject: [PATCH] Bug 16330 - REST API: add routes to add, update and delete
 patrons

This patch adds support for add, edit and delete patrons via REST API.

GET  /api/v1/patrons                   Get patron list from params
GET  /api/v1/patrons/<borrowernumber>  Get single patron
POST /api/v1/patrons                   Create a new patron
PUT  /api/v1/patrons/<borrowernumber>  Update data about patron
DEL  /api/v1/patrons/<borrowernumber>  Delete a patron

Test plan:
1) Apply this patch (on top of dependent bug 15126)
2) Run tests perl t/db_dependent/api/v1/patrons.t
3) Add a user with proper rights to use the REST API
4) play with your favourite REST client (curl/httpie, etc.):
   Authenticate with the user created above and get a CGISESSION id.
   Use the CGISESSION to add, edit and delete patrons via the API.

Please note there is no validation of body input in PUT/POST other
than branchcode,category,userid,cardnumber.
---
 Koha/REST/V1/Patrons.pm          | 157 ++++++++++++++++++++++++++++++++++++-
 api/v1/swagger.json              | 137 ++++++++++++++++++++++++++++++++
 t/db_dependent/api/v1/patrons.t  | 167 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 446 insertions(+), 15 deletions(-)

diff --git a/Koha/REST/V1/Patrons.pm b/Koha/REST/V1/Patrons.pm
index 2851308..0411719 100644
--- a/Koha/REST/V1/Patrons.pm
+++ b/Koha/REST/V1/Patrons.pm
@@ -20,7 +20,10 @@ use Modern::Perl;
 use Mojo::Base 'Mojolicious::Controller';
 
 use C4::Auth qw( haspermission );
+use Koha::AuthUtils qw(hash_password);
 use Koha::Borrowers;
+use Koha::Patron::Categories;
+use Koha::Branches;
 
 sub list {
     my ($c, $args, $cb) = @_;
@@ -30,7 +33,17 @@ sub list {
         return $c->$cb({error => "You don't have the required permission"}, 403);
     }
 
-    my $patrons = Koha::Borrowers->search;
+    my $params = $c->req->query_params->to_hash;
+    my $patrons;
+    if (keys %$params) {
+        my @valid_params = Koha::Borrowers->_resultset->result_source->columns;
+        foreach my $key (keys %$params) {
+            delete $params->{$key} unless grep { $key eq $_ } @valid_params;
+        }
+        $patrons = Koha::Borrowers->search($params);
+    } else {
+        $patrons = Koha::Borrowers->search;
+    }
 
     $c->$cb($patrons->unblessed, 200);
 }
@@ -55,4 +68,146 @@ sub get {
     return $c->$cb($patron->unblessed, 200);
 }
 
+sub add {
+    my ($c, $args, $cb) = @_;
+
+    my $user = $c->stash('koha.user');
+
+    unless ( $user && haspermission($user->userid, {borrowers => 1}) ) {
+        return $c->$cb({error => "You don't have the required permission"}, 403);
+    }
+
+    my $body = $c->req->json;
+
+    # patron cardnumber and/or userid unique?
+    if ($body->{cardnumber} || $body->{userid}) {
+        my $patron = Koha::Borrowers->find({cardnumber => $body->{cardnumber}, userid => $body->{userid} });
+        if ($patron) {
+            return $c->$cb({
+                error => "Patron cardnumber and userid must be unique",
+                conflict => { cardnumber => $patron->cardnumber, userid => $patron->userid }
+            }, 409);
+        }
+    }
+
+    my $branch = Koha::Branches->find({branchcode => $body->{branchcode} });
+    unless ($branch) {
+        return $c->$cb({error => "Library with branchcode \"" . $body->{branchcode} . "\" does not exist"}, 404);
+    }
+    my $category = Koha::Patron::Categories->find({ categorycode => $body->{categorycode} });
+    unless ($category) {
+        return $c->$cb({error => "Patron category \"" . $body->{categorycode} . "\" does not exist"}, 404);
+    }
+    # All OK - save new patron
+
+    if ($body->{password}) { $body->{password} = hash_password($body->{password}) }; # bcrypt password if given
+
+    my $patron = eval {
+        Koha::Borrower->new($body)->store;
+    };
+
+    unless ($patron) {
+        return $c->$cb({error => "Something went wrong, check Koha logs for details"}, 500);
+    }
+
+    return $c->$cb($patron->unblessed, 201);
+}
+
+sub edit {
+    my ($c, $args, $cb) = @_;
+
+    my $user = $c->stash('koha.user');
+
+    unless ( $user && haspermission($user->userid, {borrowers => 1}) ) {
+        return $c->$cb({error => "You don't have the required permission"}, 403);
+    }
+
+    my $patron = Koha::Borrowers->find($args->{borrowernumber});
+
+    unless ($patron) {
+        return $c->$cb({error => "Patron not found"}, 404);
+    }
+
+    my $body = $c->req->json;
+
+    # Can we change userid and/or cardnumber? in that case check that they are altered first
+    if ($body->{cardnumber} || $body->{userid}) {
+        if ( ($body->{cardnumber} && $body->{cardnumber} ne $patron->cardnumber) || ($body->{userid} && $body->{userid} ne $patron->userid) ) {
+            my $conflictingPatron = Koha::Borrowers->find({cardnumber => $body->{cardnumber}, userid => $body->{userid} });
+            if ($conflictingPatron) {
+                return $c->$cb({
+                    error => "Patron cardnumber and userid must be unique",
+                    conflict => { cardnumber => $conflictingPatron->cardnumber, userid => $conflictingPatron->userid }
+                }, 409);
+            }
+        }
+    }
+
+    if ($body->{branchcode}) {
+        my $branch = Koha::Branches->find({branchcode => $body->{branchcode} });
+        unless ($branch) {
+            return $c->$cb({error => "Library with branchcode \"" . $body->{branchcode} . "\" does not exist"}, 404);
+        }
+    }
+
+    if ($body->{categorycode}) {
+        my $category = Koha::Patron::Categories->find({ categorycode => $body->{categorycode} });
+        unless ($category) {
+            return $c->$cb({error => "Patron category \"" . $body->{categorycode} . "\" does not exist"}, 404);
+        }
+    }
+    # ALL OK - Update patron
+    # Perhaps limit/validate what should be updated here? flags, et.al.
+    if ($body->{password}) { $body->{password} = <PASSWORD>_password($body->{password}) }; # bcrypt password if given
+
+    my $updatedpatron = eval {
+        $patron->set($body);
+    };
+
+    if ($updatedpatron) {
+        if ($updatedpatron->is_changed) {
+
+            my $res = eval {
+                $updatedpatron->store;
{'fraction_non_alphanumeric': 0.19661468536948515, 'fraction_numerical': 0.029464775487814433, 'mean_word_length': 2.8209580838323354, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 234, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6204342', 'n_tokens_mistral': 4763, 'n_tokens_neox': 4351, 'n_words': 1060}