From daae0f3acc6cb8f9657eb7a3b27a39716914fcdb Mon Sep 17 00:00:00 2001
From: Tomas Cohen Arazi <tomascohen@theke.io>
Date: Fri, 3 Mar 2017 15:52:22 +0000
Subject: [PATCH] Bug 17361: REST api | Add /items/messages endpoints

This patch adds the following endpoints:
- /items/{itemnumber}/messages
- /items/{itemnumber}/messages/{item_message_id}
for CRUD operation on item messages.

Unit tests to come.
---
 Koha/REST/V1/Items/Messages.pm               | 152 ++++++++++++++++
 api/v1/swagger/definitions.json              |   3 +
 api/v1/swagger/definitions/item_message.json |  25 +++
 api/v1/swagger/parameters.json               |   6 +
 api/v1/swagger/parameters/item.json          |   9 +
 api/v1/swagger/parameters/item_message.json  |   9 +
 api/v1/swagger/paths.json                    |   6 +
 api/v1/swagger/paths/item_messages.json      | 255 +++++++++++++++++++++++++++
 api/v1/swagger/x-primitives.json             |  10 +-
 9 files changed, 473 insertions(+), 2 deletions(-)
 create mode 100644 Koha/REST/V1/Items/Messages.pm
 create mode 100644 api/v1/swagger/definitions/item_message.json
 create mode 100644 api/v1/swagger/parameters/item.json
 create mode 100644 api/v1/swagger/parameters/item_message.json
 create mode 100644 api/v1/swagger/paths/item_messages.json

diff --git a/Koha/REST/V1/Items/Messages.pm b/Koha/REST/V1/Items/Messages.pm
new file mode 100644
index 0000000000..360d00deb7
--- /dev/null
+++ b/Koha/REST/V1/Items/Messages.pm
@@ -0,0 +1,152 @@
+package Koha::REST::V1::Items::Messages;
+
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Mojo::Base 'Mojolicious::Controller';
+
+use Koha::Items;
+use Koha::Item::Message;
+use Koha::Item::Messages;
+use Koha::DateUtils;
+
+use Try::Tiny;
+
+sub list_item_messages {
+    my ( $c, $args, $cb ) = @_;
+
+    if ( !Koha::Items->search({ itemnumber => $args->{itemnumber} })->count > 0 ) {
+        return $c->$cb( { error => 'Item not found' }, 400 );
+    }
+
+    return try {
+        my $item_messages = Koha::Item::Messages->search({ itemnumber => $args->{itemnumber} });
+        return $c->$cb( $item_messages, 200 );
+    }
+    catch {
+        if ( $_->isa('DBIx::Class::Exception') ) {
+            return $c->$cb( { error => $_->{msg} }, 500 );
+        }
+        else {
+            return $c->$cb( { error => "Something went wrong, check the logs." }, 500 );
+        }
+    };
+}
+
+sub get_item_message {
+    my ( $c, $args, $cb ) = @_;
+
+    if ( !Koha::Items->search( { itemnumber => $args->{itemnumber} } )->count > 0 ) {
+        return $c->$cb( { error => 'Item not found' }, 400 );
+    }
+
+    return try {
+        my $item_message
+            = Koha::Item::Messages->search(
+            { itemnumber => $args->{itemnumber}, item_message_id => $args->{item_message_id} } )
+            ->next;
+        if ($item_message) {
+            return $c->$cb( $item_message, 200 );
+        }
+        else {
+            return $c->$cb( { error => 'Item message not found' }, 400 );
+        }
+    }
+    catch {
+        if ( $_->isa('DBIx::Class::Exception') ) {
+            return $c->$cb( { error => $_->{msg} }, 500 );
+        }
+        else {
+            return $c->$cb( { error => "Something went wrong, check the logs." }, 500 );
+        }
+    };
+}
+
+sub add_item_message {
+    my ( $c, $args, $cb ) = @_;
+
+    my $item_message = Koha::Item::Message->new( $args->{body} );
+    $item_message->itemnumber( $args->{itemnumber} );
+
+    return try {
+        $item_message->store();
+        $item_message = Koha::Item::Messages->find( $item_message->id );
+        return $c->$cb( $item_message, 200 );
+    }
+    catch {
+        if ( $_->isa('DBIx::Class::Exception') ) {
+            return $c->$cb( { error => $_->msg }, 500 );
+        }
+        else {
+            return $c->$cb( { error => "Something went wrong, check the logs." }, 500 );
+        }
+    };
+}
+
+sub update_item_message {
+    my ( $c, $args, $cb ) = @_;
+
+    my $item_message;
+
+    return try {
+        $item_message
+            = Koha::Item::Messages->search(
+            { itemnumber => $args->{itemnumber}, item_message_id => $args->{item_message_id} } )
+            ->next;
+        $item_message->set( $args->{body} );
+        $item_message->store();
+        return $c->$cb( $item_message, 200 );
+    }
+    catch {
+        if ( not defined $item_message ) {
+            return $c->$cb( { error => "Item message not found" }, 404 );
+        }
+        elsif ( $_->isa('Koha::Exceptions::Object') ) {
+            return $c->$cb( { error => $_->message }, 500 );
+        }
+        else {
+            return $c->$cb( { error => "Something went wrong, check the logs." }, 500 );
+        }
+    };
+
+}
+
+sub delete_item_message {
+    my ( $c, $args, $cb ) = @_;
+
+    my $item_message;
+
+    return try {
+        $item_message = Koha::Item::Messages->search(
+            { itemnumber => $args->{itemnumber}, item_message_id => $args->{item_message_id} } );
+        $item_message->delete;
+        return $c->$cb( q{}, 200 );
+    }
+    catch {
+        if ( not defined $item_message ) {
+            return $c->$cb( { error => "Item message not found" }, 404 );
+        }
+        elsif ( $_->isa('DBIx::Class::Exception') ) {
+            return $c->$cb( { error => $_->msg }, 500 );
+        }
+        else {
+            return $c->$cb( { error => "Something went wrong, check the logs." }, 500 );
+        }
+    };
+}
+
+1;
diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json
index d27167b924..145e5c06a3 100644
--- a/api/v1/swagger/definitions.json
+++ b/api/v1/swagger/definitions.json
@@ -11,6 +11,9 @@
   "hold": {
     "$ref": "definitions/hold.json"
   },
+  "item_message": {
+    "$ref": "definitions/item_message.json"
+  },
   "error": {
     "$ref": "definitions/error.json"
   }
diff --git a/api/v1/swagger/definitions/item_message.json b/api/v1/swagger/definitions/item_message.json
new file mode 100644
index 0000000000..1fcf6da8c1
--- /dev/null
+++ b/api/v1/swagger/definitions/item_message.json
@@ -0,0 +1,25 @@
+{
+  "type": "object",
+  "properties": {
+    "item_message_id": {
+      "$ref": "../x-primitives.json#/item_message_id"
+    },
+    "itemnumber": {
+      "$ref": "../x-primitives.json#/itemnumber"
+    },
+    "type": {
+      "type": ["string", "null"],
+      "description": "date and time the item message was created"
+    },
+    "message": {
+      "type": "string",
+      "description": "date and time the item message was created"
+    },
+    "created_on": {
+      "type": "string",
+      "description": "date and time the item message was created"
+    }
+  },
+  "additionalProperties": false,
+  "required": ["itemnumber","message"]
+}
diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json
index eeb156a7b8..0b780b045f 100644
--- a/api/v1/swagger/parameters.json
+++ b/api/v1/swagger/parameters.json
@@ -10,5 +10,11 @@
   },
   "holdIdPathParam": {
     "$ref": "parameters/hold.json#/holdIdPathParam"
+  },
+  "itemIdPathParam": {
+    "$ref": "parameters/item.json#/itemIdPathParam"
+  },
+  "itemMessageIdPathParam": {
+    "$ref": "parameters/item_message.json#/itemMessageIdPathParam"
   }
 }
diff --git a/api/v1/swagger/parameters/item.json b/api/v1/swagger/parameters/item.json
new file mode 100644
index 0000000000..3c618976f5
--- /dev/null
+++ b/api/v1/swagger/parameters/item.json
@@ -0,0 +1,9 @@
+{
+  "itemIdPathParam": {
+    "name": "itemnumber",
+    "in": "path",
+    "description": "Internal item identifier",
+    "required": true,
+    "type": "integer"
+  }
+}
diff --git a/api/v1/swagger/parameters/item_message.json b/api/v1/swagger/parameters/item_message.json
new file mode 100644
index 0000000000..5d9bb393db
--- /dev/null
+++ b/api/v1/swagger/parameters/item_message.json
@@ -0,0 +1,9 @@
+{
+  "itemMessageIdPathParam": {
+    "name": "item_message_id",
+    "in": "path",
+    "description": "Internal item message identifier",
+    "required": true,
+    "type": "integer"
+  }
+}
diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json
index b1fcf40f5e..df09176abb 100644
--- a/api/v1/swagger/paths.json
+++ b/api/v1/swagger/paths.json
@@ -11,6 +11,12 @@
   "/holds/{reserve_id}": {
     "$ref": "paths/holds.json#/~1holds~1{reserve_id}"
   },
+  "/items/{itemnumber}/messages": {
+    "$ref": "paths/item_messages.json#/~1items~1{itemnumber}~1messages"
+  },
+  "/items/{itemnumber}/messages/{item_message_id}": {
+    "$ref": "paths/item_messages.json#/~1items~1{itemnumber}~1messages~1{item_message_id}"
+  },
   "/patrons": {
     "$ref": "paths/patrons.json#/~1patrons"
   },
diff --git a/api/v1/swagger/paths/item_messages.json b/api/v1/swagger/paths/item_messages.json
new file mode 100644
index 0000000000..e88b249eb4
--- /dev/null
+++ b/api/v1/swagger/paths/item_messages.json
@@ -0,0 +1,255 @@
+{
+  "/items/{itemnumber}/messages": {
+    "get": {
+      "x-mojo-controller": "Koha::REST::V1::Items::Messages",
+      "operationId": "listItemMessages",
+      "tags": ["items", "item_messages"],
+      "parameters": [
+        {
+          "$ref": "../parameters.json#/itemIdPathParam"
+        }
+      ],
+      "produces": [
+        "application/json"
+      ],
+      "responses": {
+        "200": {
+          "description": "A list of item messages",
+          "schema": {
+            "type": "array",
+            "items": {
+              "$ref": "../definitions.json#/item_message"
+            }
+          }
+        },
+        "403": {
+            "description": "Access forbidden",
+            "schema": {
+              "$ref": "../definitions.json#/error"
+            }
+        },
+        "404": {
+          "description": "Object not found",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        }
+      },
+      "x-koha-authorization": {
+        "permissions": {
+          "catalogue": "1"
+        }
+      }
+    },
+    "post": {
+      "x-mojo-controller": "Koha::REST::V1::Items::Messages",
+      "operationId": "addItemMessage",
+      "tags": ["items", "item_messages"],
+      "parameters": [
+        {
+          "$ref": "../parameters.json#/itemIdPathParam"
+        },
+        {
+          "name": "body",
+          "in": "body",
+          "description": "A JSON object representing an item message",
+          "required": true,
+          "schema": {
+            "$ref": "../definitions.json#/item_message"
+          }
+        }
+      ],
+      "produces": [
+        "application/json"
+      ],
+      "responses": {
+        "200": {
+          "description": "Item message added",
+          "schema": {
+            "$ref": "../definitions.json#/item_message"
+          }
+        },
+        "400": {
+          "description": "Missing or wrong parameters",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "403": {
+          "description": "Access forbidden",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "404": {
+          "description": "Object not found",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "500": {
+          "description": "Internal error",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        }
+      },
+      "x-koha-authorization": {
+        "allow-owner": true,
+        "permissions": {
+          "editcatalogue": "1"
+        }
+      }
+    }
+  },
+  "/items/{itemnumber}/messages/{item_message_id}": {
+    "get": {
+      "x-mojo-controller": "Koha::REST::V1::Items::Messages",
+      "operationId": "getItemMessage",
+      "tags": ["items","item_messages"],
+      "parameters": [
+        {
+          "$ref": "../parameters.json#/itemIdPathParam"
+        },
+        {
+          "$ref": "../parameters.json#/itemMessageIdPathParam"
+        }
+      ],
+      "produces": [
+        "application/json"
+      ],
+      "responses": {
+        "200": {
+          "description": "An item messge",
+          "schema": {
+            "$ref": "../definitions.json#/item_message"
+          }
+        },
+        "403": {
+          "description": "Access forbidden",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "404": {
+          "description": "Vendor not found",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "500": {
+          "description": "Internal error",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        }
+      },
+      "x-koha-authorization": {
+        "permissions": {
+          "catalogue": "1"
+        }
+      }
+    },
+    "put": {
+      "x-mojo-controller": "Koha::REST::V1::Items::Messages",
+      "operationId": "updateItemMessage",
+      "tags": ["items","item_messages"],
+      "parameters": [
+        {
+          "$ref": "../parameters.json#/itemIdPathParam"
+        },
+        {
+          "$ref": "../parameters.json#/itemMessageIdPathParam"
+        },
+        {
+          "name": "body",
+          "in": "body",
+          "description": "A JSON object representing an item message",
+          "required": true,
+          "schema": {
+            "$ref": "../definitions.json#/item_message"
+          }
+        }
+      ],
+      "produces": [
+        "application/json"
+      ],
+      "responses": {
+        "200": {
+          "description": "Updated item message",
+          "schema": {
+            "$ref": "../definitions.json#/item_message"
+          }
+        },
+        "400": {
+          "description": "Missing or wrong parameters",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "403": {
+          "description": "Access forbidden",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "404": {
+          "description": "Object not found",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        }
+      },
+      "x-koha-authorization": {
+        "permissions": {
+          "editcatalogue": "1"
+        }
+      }
+    },
+    "delete": {
+      "x-mojo-controller": "Koha::REST::V1::Items::Messages",
+      "operationId": "deleteItemMessage",
+      "tags": ["items","item_messages"],
+      "parameters": [
+        {
+          "$ref": "../parameters.json#/itemIdPathParam"
+        },
+        {
+          "$ref": "../parameters.json#/itemMessageIdPathParam"
+        }
+      ],
+      "produces": ["application/json"],
+      "responses": {
+        "200": {
+          "description": "Item message deleted",
+          "schema": {
+            "type": "string"
+          }
+        },
+        "403": {
+          "description": "Access forbidden",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "404": {
+          "description": "Object not found",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        },
+        "500": {
+          "description": "Internal error",
+          "schema": {
+            "$ref": "../definitions.json#/error"
+          }
+        }
+      },
+      "x-koha-authorization": {
+        "permissions": {
+          "editcatalogue": "1"
+        }
+      }
+    }
+  }
+}
diff --git a/api/v1/swagger/x-primitives.json b/api/v1/swagger/x-primitives.json
index ae4e7395f6..c17855dd45 100644
--- a/api/v1/swagger/x-primitives.json
+++ b/api/v1/swagger/x-primitives.json
@@ -16,6 +16,11 @@
     "description": "internally assigned city identifier",
     "readOnly": true
   },
+  "item_message_id": {
+    "type": "integer",
+    "description": "internally assigned item message identifier",
+    "readOnly": true
+  },
   "email": {
     "type": ["string", "null"],
     "description": "primary email address for patron's primary address"
@@ -25,8 +30,9 @@
     "description": "patron's first name"
   },
   "itemnumber": {
-    "type": ["integer", "null"],
-    "description": "internally assigned item identifier"
+    "type": "integer",
+    "description": "internally assigned item identifier",
+    "readOnly": true
   },
   "phone": {
     "type": ["string", "null"],
-- 
2.12.0