View | Details | Raw Unified | Return to bug 16825
Collapse All | Expand All

(-)a/Koha/REST/V1/Item.pm (+72 lines)
Line 0 Link Here
1
package Koha::REST::V1::Item;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it under the
6
# terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 3 of the License, or (at your option) any later
8
# version.
9
#
10
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
11
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along
15
# with Koha; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
use Mojo::JSON;
22
23
use C4::Auth qw( haspermission );
24
use C4::Items qw( GetHiddenItemnumbers );
25
26
use Koha::Items;
27
28
use Try::Tiny;
29
30
sub get {
31
    my $c = shift->openapi->valid_input or return;
32
33
    my $item;
34
    try {
35
        $item = Koha::Items->find($c->validation->param('itemnumber'));
36
37
        # Hide non-public itemnotes if user has no staff access
38
        my $user = $c->stash('koha.user');
39
        unless ($user && haspermission($user->userid, {catalogue => 1})) {
40
41
            my @hiddenitems = C4::Items::GetHiddenItemnumbers( ({
42
                itemnumber => $item->itemnumber}) );
43
            my %hiddenitems = map { $_ => 1 } @hiddenitems;
44
45
            # Pretend it was not found as it's hidden from OPAC to regular users
46
            if ($hiddenitems{$item->itemnumber}) {
47
                return $c->render( status => 404,
48
                                   openapi => { error => 'Item not found'} ) ;
49
            }
50
51
            $item->set({ itemnotes_nonpublic => undef });
52
        }
53
54
        return $c->render( status => 200, openapi => $item );
55
    }
56
    catch {
57
        unless ( defined $item ) {
58
            return $c->render( status => 404,
59
                               openapi => { error => 'Item not found'} );
60
        }
61
        if ( $_->isa('DBIx::Class::Exception') ) {
62
            return $c->render( status  => 500,
63
                               openapi => { error => $_->{msg} } );
64
        }
65
        else {
66
            return $c->render( status => 500,
67
                openapi => { error => "Something went wrong, check the logs."} );
68
        }
69
    };
70
}
71
72
1;
(-)a/api/v1/swagger/definitions.json (+3 lines)
Lines 11-16 Link Here
11
  "holds": {
11
  "holds": {
12
    "$ref": "definitions/holds.json"
12
    "$ref": "definitions/holds.json"
13
  },
13
  },
14
  "item": {
15
    "$ref": "definitions/item.json"
16
  },
14
  "patron": {
17
  "patron": {
15
    "$ref": "definitions/patron.json"
18
    "$ref": "definitions/patron.json"
16
  }
19
  }
(-)a/api/v1/swagger/definitions/hold.json (-1 / +2 lines)
Lines 47-53 Link Here
47
      "description": "date and time the hold was last updated"
47
      "description": "date and time the hold was last updated"
48
    },
48
    },
49
    "itemnumber": {
49
    "itemnumber": {
50
      "$ref": "../x-primitives.json#/itemnumber"
50
      "type": "integer",
51
      "description": "internally assigned item identifier"
51
    },
52
    },
52
    "waitingdate": {
53
    "waitingdate": {
53
      "type": ["string", "null"],
54
      "type": ["string", "null"],
(-)a/api/v1/swagger/definitions/item.json (+188 lines)
Line 0 Link Here
1
{
2
  "type": "object",
3
  "properties": {
4
    "itemnumber": {
5
      "$ref": "../x-primitives.json#/itemnumber"
6
    },
7
    "biblionumber": {
8
      "$ref": "../x-primitives.json#/biblionumber"
9
    },
10
    "biblioitemnumber": {
11
      "$ref": "../x-primitives.json#/biblioitemnumber"
12
    },
13
    "barcode": {
14
      "type": ["string", "null"],
15
      "description": "item barcode"
16
    },
17
    "dateaccessioned": {
18
      "type": ["string", "null"],
19
      "format": "date",
20
      "description": "date the item was acquired or added to Koha"
21
    },
22
    "booksellerid": {
23
      "type": ["string", "null"],
24
      "description": "where the item was purchased"
25
    },
26
    "homebranch": {
27
      "type": ["string", "null"],
28
      "description": "library that owns this item"
29
    },
30
    "price": {
31
      "type": ["number", "null"],
32
      "description": "purchase price"
33
    },
34
    "replacementprice": {
35
      "type": ["number", "null"],
36
      "description": "cost the library charges to replace the item if it has been marked lost"
37
    },
38
    "replacementpricedate": {
39
      "type": ["string", "null"],
40
      "format": "date",
41
      "description": "the date the price is effective from"
42
    },
43
    "datelastborrowed": {
44
      "type": ["string", "null"],
45
      "format": "date",
46
      "description": "the date the item was last checked out/issued"
47
    },
48
    "datelastseen": {
49
      "type": ["string", "null"],
50
      "format": "date",
51
      "description": "the date the item was last see (usually the last time the barcode was scanned or inventory was done)"
52
    },
53
    "stack": {
54
      "type": ["integer", "null"],
55
      "description": "?"
56
    },
57
    "notforloan": {
58
      "type": "integer",
59
      "description": "authorized value defining why this item is not for loan"
60
    },
61
    "damaged": {
62
      "type": "integer",
63
      "description": "authorized value defining this item as damaged"
64
    },
65
    "itemlost": {
66
      "type": "integer",
67
      "description": "authorized value defining this item as lost"
68
    },
69
    "itemlost_on": {
70
      "type": ["string", "null"],
71
      "format": "date-time",
72
      "description": "the date and time an item was last marked as lost, NULL if not lost"
73
    },
74
    "withdrawn": {
75
      "type": "integer",
76
      "description": "authorized value defining this item as withdrawn"
77
    },
78
    "withdrawn_on": {
79
      "type": ["string", "null"],
80
      "format": "date-time",
81
      "description": "the date and time an item was last marked as withdrawn, NULL if not withdrawn"
82
    },
83
    "itemcallnumber": {
84
      "type": ["string", "null"],
85
      "description": "call number for this item"
86
    },
87
    "coded_location_qualifier": {
88
      "type": ["string", "null"],
89
      "description": "coded location qualifier"
90
    },
91
    "issues": {
92
      "type": ["integer", "null"],
93
      "description": "number of times this item has been checked out/issued"
94
    },
95
    "renewals": {
96
      "type": ["integer", "null"],
97
      "description": "number of times this item has been renewed"
98
    },
99
    "reserves": {
100
      "type": ["integer", "null"],
101
      "description": "number of times this item has been placed on hold/reserved"
102
    },
103
    "restricted": {
104
      "type": ["integer", "null"],
105
      "description": "authorized value defining use restrictions for this item"
106
    },
107
    "itemnotes": {
108
      "type": ["string", "null"],
109
      "description": "public notes on this item"
110
    },
111
    "itemnotes_nonpublic": {
112
      "type": ["string", "null"],
113
      "description": "non-public notes on this item"
114
    },
115
    "holdingbranch": {
116
      "type": ["string", "null"],
117
      "description": "library that is currently in possession item"
118
    },
119
    "paidfor": {
120
      "type": ["string", "null"],
121
      "description": "?"
122
    },
123
    "timestamp": {
124
      "type": "string",
125
      "format": "date-time",
126
      "description": "date and time this item was last altered"
127
    },
128
    "location": {
129
      "type": ["string", "null"],
130
      "description": "authorized value for the shelving location for this item"
131
    },
132
    "permanent_location": {
133
      "type": ["string", "null"],
134
      "description": "linked to the CART and PROC temporary locations feature, stores the permanent shelving location"
135
    },
136
    "onloan": {
137
      "type": ["string", "null"],
138
      "format": "date",
139
      "description": "defines if item is checked out (NULL for not checked out, and checkout date for checked out)"
140
    },
141
    "cn_source": {
142
      "type": ["string", "null"],
143
      "description": "classification source used on this item"
144
    },
145
    "cn_sort": {
146
      "type": ["string", "null"],
147
      "description": "?"
148
    },
149
    "ccode": {
150
      "type": ["string", "null"],
151
      "description": "authorized value for the collection code associated with this item"
152
    },
153
    "materials": {
154
      "type": ["string", "null"],
155
      "description": "materials specified"
156
    },
157
    "uri": {
158
      "type": ["string", "null"],
159
      "description": "URL for the item"
160
    },
161
    "itype": {
162
      "type": ["string", "null"],
163
      "description": "itemtype defining the type for this item"
164
    },
165
    "more_subfields_xml": {
166
      "type": ["string", "null"],
167
      "description": "additional 952 subfields in XML format"
168
    },
169
    "enumchron": {
170
      "type": ["string", "null"],
171
      "description": "serial enumeration/chronology for the item"
172
    },
173
    "copynumber": {
174
      "type": ["string", "null"],
175
      "description": "copy number"
176
    },
177
    "stocknumber": {
178
      "type": ["string", "null"],
179
      "description": "inventory number"
180
    },
181
    "new_status": {
182
      "type": ["string", "null"],
183
      "description": "'new' value, whatever free-text information."
184
    }
185
  },
186
  "additionalProperties": false,
187
  "required": ["itemnumber", "biblionumber", "biblioitemnumber", "notforloan", "damaged", "itemlost", "withdrawn"]
188
}
(-)a/api/v1/swagger/parameters.json (+3 lines)
Lines 10-14 Link Here
10
  },
10
  },
11
  "holdIdPathParam": {
11
  "holdIdPathParam": {
12
    "$ref": "parameters/hold.json#/holdIdPathParam"
12
    "$ref": "parameters/hold.json#/holdIdPathParam"
13
  },
14
  "itemnumberPathParam": {
15
    "$ref": "parameters/item.json#/itemnumberPathParam"
13
  }
16
  }
14
}
17
}
(-)a/api/v1/swagger/parameters/item.json (+9 lines)
Line 0 Link Here
1
{
2
  "itemnumberPathParam": {
3
    "name": "itemnumber",
4
    "in": "path",
5
    "description": "Internal item identifier",
6
    "required": true,
7
    "type": "integer"
8
  }
9
}
(-)a/api/v1/swagger/paths.json (+3 lines)
Lines 11-16 Link Here
11
  "/holds/{reserve_id}": {
11
  "/holds/{reserve_id}": {
12
    "$ref": "paths/holds.json#/~1holds~1{reserve_id}"
12
    "$ref": "paths/holds.json#/~1holds~1{reserve_id}"
13
  },
13
  },
14
  "/items/{itemnumber}": {
15
    "$ref": "paths/items.json#/~1items~1{itemnumber}"
16
  },
14
  "/patrons": {
17
  "/patrons": {
15
    "$ref": "paths/patrons.json#/~1patrons"
18
    "$ref": "paths/patrons.json#/~1patrons"
16
  },
19
  },
(-)a/api/v1/swagger/paths/items.json (+47 lines)
Line 0 Link Here
1
{
2
  "/items/{itemnumber}": {
3
    "get": {
4
      "x-mojo-to": "Item#get",
5
      "operationId": "getItem",
6
      "tags": ["items"],
7
      "parameters": [{
8
          "$ref": "../parameters.json#/itemnumberPathParam"
9
        }
10
      ],
11
      "consumes": ["application/json"],
12
      "produces": ["application/json"],
13
      "responses": {
14
        "200": {
15
          "description": "An item",
16
          "schema": {
17
            "$ref": "../definitions.json#/item"
18
          }
19
        },
20
        "400": {
21
          "description": "Missing or wrong parameters",
22
          "schema": {
23
            "$ref": "../definitions.json#/error"
24
          }
25
        },
26
        "404": {
27
          "description": "Item not found",
28
          "schema": {
29
            "$ref": "../definitions.json#/error"
30
          }
31
        },
32
        "500": {
33
          "description": "Internal server error",
34
          "schema": {
35
            "$ref": "../definitions.json#/error"
36
          }
37
        },
38
        "503": {
39
          "description": "Under maintenance",
40
          "schema": {
41
            "$ref": "../definitions.json#/error"
42
          }
43
        }
44
      }
45
    }
46
  }
47
}
(-)a/api/v1/swagger/x-primitives.json (-1 / +5 lines)
Lines 1-4 Link Here
1
{
1
{
2
  "biblioitemnumber": {
3
    "type": "integer",
4
    "description": "internally assigned biblioitem identifier"
5
  },
2
  "biblionumber": {
6
  "biblionumber": {
3
    "type": "integer",
7
    "type": "integer",
4
    "description": "internally assigned biblio identifier"
8
    "description": "internally assigned biblio identifier"
Lines 25-31 Link Here
25
    "description": "patron's first name"
29
    "description": "patron's first name"
26
  },
30
  },
27
  "itemnumber": {
31
  "itemnumber": {
28
    "type": ["integer", "null"],
32
    "type": "integer",
29
    "description": "internally assigned item identifier"
33
    "description": "internally assigned item identifier"
30
  },
34
  },
31
  "phone": {
35
  "phone": {
(-)a/t/db_dependent/api/v1/items.t (-1 / +151 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
2
3
# Copyright 2016 Koha-Suomi
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
use Modern::Perl;
21
22
use Test::More tests => 4;
23
use Test::Mojo;
24
use Test::Warn;
25
26
use t::lib::TestBuilder;
27
use t::lib::Mocks;
28
29
use C4::Auth;
30
use Koha::Items;
31
use Koha::Database;
32
33
my $schema  = Koha::Database->new->schema;
34
my $builder = t::lib::TestBuilder->new;
35
36
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
37
# this affects the other REST api tests
38
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
39
40
my $remote_address = '127.0.0.1';
41
my $t              = Test::Mojo->new('Koha::REST::V1');
42
43
subtest 'list() tests' => sub {
44
    plan tests => 2;
45
46
    # Not yet implemented
47
    my $tx = $t->ua->build_tx(GET => "/api/v1/items");
48
    $t->request_ok($tx)
49
      ->status_is(404);
50
};
51
52
subtest 'get() tests' => sub {
53
    plan tests => 8;
54
55
    $schema->storage->txn_begin;
56
57
    my $biblio = $builder->build({
58
        source => 'Biblio'
59
    });
60
    my $biblionumber = $biblio->{biblionumber};
61
    my $item = $builder->build({
62
        source => 'Item',
63
        value => {
64
            biblionumber => $biblionumber,
65
            withdrawn => 1, # tested with OpacHiddenItems
66
        }
67
    });
68
    my $itemnumber = $item->{itemnumber};
69
70
    my ($patron, $sessionid) = create_user_and_session({ authorized=>0 });
71
    my ($librarian, $lib_sessionid) = create_user_and_session({ authorized=>4 });
72
73
    my $nonExistentItemnumber = -14362719;
74
    my $tx = $t->ua->build_tx(GET => "/api/v1/items/$nonExistentItemnumber");
75
    $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
76
    $t->request_ok($tx)
77
      ->status_is(404);
78
79
    subtest 'Confirm effectiveness of OpacHiddenItems' => sub {
80
        plan tests => 7;
81
82
        t::lib::Mocks::mock_preference('OpacHiddenItems', '');
83
        $tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber");
84
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
85
        $t->request_ok($tx)
86
          ->status_is(200)
87
          ->json_is('/itemnumber' => $itemnumber)
88
          ->json_is('/biblionumber' => $biblionumber)
89
          ->json_is('/itemnotes_nonpublic' => undef);
90
91
        t::lib::Mocks::mock_preference('OpacHiddenItems', 'withdrawn: [1]');
92
        $tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber");
93
        $tx->req->cookies({name => 'CGISESSID', value => $sessionid});
94
        $t->request_ok($tx)
95
          ->status_is(404);
96
    };
97
98
    $tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber");
99
    $tx->req->cookies({name => 'CGISESSID', value => $lib_sessionid});
100
    $t->request_ok($tx)
101
      ->status_is(200)
102
      ->json_is('/itemnumber' => $itemnumber)
103
      ->json_is('/biblionumber' => $biblionumber)
104
      ->json_is('/itemnotes_nonpublic' => $item->{itemnotes_nonpublic});
105
106
    $schema->storage->txn_rollback;
107
};
108
109
subtest 'update() tests' => sub {
110
    plan tests => 2;
111
112
    # Not yet implemented
113
    my $tx = $t->ua->build_tx(PUT => "/api/v1/items/1");
114
    $t->request_ok($tx)
115
      ->status_is(404);
116
};
117
118
subtest 'delete() tests' => sub {
119
    plan tests => 2;
120
121
    # Not yet implemented
122
    my $tx = $t->ua->build_tx(DELETE => "/api/v1/items/1");
123
    $t->request_ok($tx)
124
      ->status_is(404);
125
};
126
127
sub create_user_and_session {
128
129
    my $args  = shift;
130
    my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0;
131
    my $dbh   = C4::Context->dbh;
132
133
    my $user = $builder->build(
134
        {
135
            source => 'Borrower',
136
            value  => {
137
                flags => $flags
138
            }
139
        }
140
    );
141
142
    # Create a session for the authorized user
143
    my $session = C4::Auth::get_session('');
144
    $session->param( 'number',   $user->{borrowernumber} );
145
    $session->param( 'id',       $user->{userid} );
146
    $session->param( 'ip',       '127.0.0.1' );
147
    $session->param( 'lasttime', time() );
148
    $session->flush;
149
150
    return ( $user->{borrowernumber}, $session->id );
151
}

Return to bug 16825