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

(-)a/Koha/REST/V1/Biblio.pm (+173 lines)
Line 0 Link Here
1
package Koha::REST::V1::Biblio;
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
22
use C4::Biblio qw( GetBiblioData AddBiblio ModBiblio DelBiblio );
23
use C4::Items qw ( AddItemBatchFromMarc );
24
use Koha::Biblios;
25
use MARC::Record;
26
use MARC::Batch;
27
use MARC::File::USMARC;
28
use MARC::File::XML;
29
30
use Data::Dumper;
31
32
sub get {
33
    my ($c, $args, $cb) = @_;
34
35
    my $biblio = &GetBiblioData($args->{biblionumber});
36
    unless ($biblio) {
37
        return $c->$cb({error => "Biblio not found"}, 404);
38
    }
39
    return $c->$cb($biblio, 200);
40
}
41
42
sub getitems {
43
    my ($c, $args, $cb) = @_;
44
45
    my $biblio = Koha::Biblios->find($args->{biblionumber});
46
    unless ($biblio) {
47
        return $c->$cb({error => "Biblio not found"}, 404);
48
    }
49
    return $c->$cb({ biblio => $biblio->unblessed, items => $biblio->items->unblessed }, 200);
50
}
51
52
sub getexpanded {
53
    my ($c, $args, $cb) = @_;
54
55
    my $biblio = Koha::Biblios->find($args->{biblionumber});
56
    unless ($biblio) {
57
        return $c->$cb({error => "Biblio not found"}, 404);
58
    }
59
    my $expanded = $biblio->items->unblessed;
60
    for my $item (@{$expanded}) {
61
62
        # we assume item is available by default
63
        $item->{status} = "available";
64
65
        if ($item->{onloan}) {
66
            $item->{status} = "onloan"
67
        }
68
69
        if ($item->{restricted}) {
70
            $item->{status} = "restricted";
71
        }
72
73
        # mark as unavailable if notforloan, damaged, lost, or withdrawn
74
        if ($item->{damaged} || $item->{itemlost} || $item->{withdrawn} || $item->{notforloan}) {
75
            $item->{status} = "unavailable";
76
        }
77
78
        my $holds = Koha::Holds->search({itemnumber => $item->{itemnumber}})->unblessed;
79
80
        # mark as onhold if item marked as hold
81
        if (scalar(@{$holds}) > 0) {
82
            $item->{status} = "onhold";
83
        }
84
    }
85
86
    return $c->$cb({ biblio => $biblio->unblessed, items => $expanded }, 200);
87
}
88
89
sub add {
90
    my ($c, $args, $cb) = @_;
91
92
    my $biblionumber;
93
    my $biblioitemnumber;
94
95
    my $body = $c->req->body;
96
    unless ($body) {
97
        return $c->$cb({error => "Missing MARCXML body"}, 400);
98
    }
99
100
    my $record = eval {MARC::Record::new_from_xml( $body, "utf8", '')};
101
    if ($@) {
102
        return $c->$cb({error => $@}, 400);
103
    } else {
104
        ( $biblionumber, $biblioitemnumber ) = &AddBiblio($record, '');
105
    }
106
    if ($biblionumber) {
107
        $c->res->headers->location($c->url_for('/api/v1/biblios/')->to_abs . $biblionumber);
108
        my ( $itemnumbers, $errors ) = &AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' );
109
        unless (@{$errors}) {
110
            return $c->$cb({biblionumber => $biblionumber, items => join(",", @{$itemnumbers})}, 201);
111
        } else {
112
            warn Dumper($errors);
113
            return $c->$cb({error => "Error creating items, see Koha Logs for details.", biblionumber => $biblionumber, items => join(",", @{$itemnumbers})}, 400);
114
        }
115
    } else {
116
        return $c->$cb({error => "unable to create record"}, 400);
117
    }
118
}
119
120
# NB: This will not update any items, Items should be a separate API route
121
sub update {
122
    my ($c, $args, $cb) = @_;
123
124
    my $biblionumber = $args->{biblionumber};
125
126
    my $biblio = Koha::Biblios->find($biblionumber);
127
    unless ($biblio) {
128
        return $c->$cb({error => "Biblio not found"}, 404);
129
    }
130
131
    my $success;
132
    my $body = $c->req->body;
133
    my $record = eval {MARC::Record::new_from_xml( $body, "utf8", '')};
134
    if ($@) {
135
        return $c->$cb({error => $@}, 400);
136
    } else {
137
        $success = &ModBiblio($record, $biblionumber, '');
138
    }
139
    if ($success) {
140
        $c->res->headers->location($c->url_for('/api/v1/biblios/')->to_abs . $biblionumber);
141
        return $c->$cb({biblio => Koha::Biblios->find($biblionumber)->unblessed}, 200);
142
    } else {
143
        return $c->$cb({error => "unable to update record"}, 400);
144
    }
145
}
146
147
sub delete {
148
    my ($c, $args, $cb) = @_;
149
150
    my $biblio = Koha::Biblios->find($args->{biblionumber});
151
    unless ($biblio) {
152
        return $c->$cb({error => "Biblio not found"}, 404);
153
    }
154
    my @items = $biblio->items;
155
    # Delete items first
156
    my @item_errors = ();
157
    foreach my $item (@items) {
158
        my $res = $item->delete;
159
        unless ($res eq 1) {
160
            push @item_errors, $item->unblessed->{itemnumber};
161
        }
162
    }
163
    my $res = $biblio->delete;
164
    if ($res eq '1') {
165
        return $c->$cb({}, 200);
166
    } elsif ($res eq '-1') {
167
        return $c->$cb({error => "Not found. Error code: " . $res, items => @item_errors}, 404);
168
    } else {
169
        return $c->$cb({error => "Error code: " . $res, items => @item_errors}, 400);
170
    }
171
}
172
173
1;
(-)a/api/v1/swagger/definitions.json (+6 lines)
Lines 13-17 Link Here
13
  },
13
  },
14
  "error": {
14
  "error": {
15
    "$ref": "definitions/error.json"
15
    "$ref": "definitions/error.json"
16
  },
17
  "biblios": {
18
    "$ref": "definitions/biblios.json"
19
  },
20
  "biblio": {
21
    "$ref": "definitions/biblio.json"
16
  }
22
  }
17
}
23
}
(-)a/api/v1/swagger/definitions/biblio.json (+52 lines)
Line 0 Link Here
1
{
2
  "type": "object",
3
    "properties": {
4
    "biblionumber": {
5
      "$ref": "../x-primitives.json#/biblionumber"
6
    },
7
    "author": {
8
      "type": ["string", "null"],
9
      "description": "statement of responsibility from MARC record (100$a in MARC21)"
10
    },
11
    "title": {
12
      "type": ["string", "null"],
13
      "description": "title (without the subtitle) from the MARC record (245$a in MARC21)"
14
    },
15
    "unititle": {
16
      "type": ["string", "null"],
17
      "description": "uniform title (without the subtitle) from the MARC record (240$a in MARC21)"
18
    },
19
    "notes": {
20
      "type": ["string", "null"],
21
      "description": "values from the general notes field in the MARC record (500$a in MARC21) split by bar (|)"
22
    },
23
    "serial": {
24
      "type": ["string", "null"],
25
      "description": "Boolean indicating whether biblio is for a serial"
26
    },
27
    "seriestitle": {
28
      "type": ["string", "null"],
29
      "description": ""
30
    },
31
    "copyrightdate": {
32
      "type": ["string", "null"],
33
      "description": "publication or copyright date from the MARC record"
34
    },
35
    "timestamp": {
36
      "type": "string",
37
      "description": "date and time this record was last touched"
38
    },
39
    "datecreated": {
40
      "type": "string",
41
      "description": "the date this record was added to Koha"
42
    },
43
    "abstract": {
44
      "type": ["string", "null"],
45
      "description": "summary from the MARC record (520$a in MARC21)"
46
    },
47
    "frameworkcode": {
48
      "type": "string",
49
      "description": "framework used in cataloging this record"
50
    }
51
  }
52
}
(-)a/api/v1/swagger/definitions/biblios.json (+4 lines)
Line 0 Link Here
1
{
2
    "type": "array",
3
    "items": { "$ref": "biblio.json" }
4
}
(-)a/api/v1/swagger/parameters.json (+3 lines)
Lines 5-10 Link Here
5
  "borrowernumberQueryParam": {
5
  "borrowernumberQueryParam": {
6
    "$ref": "parameters/patron.json#/borrowernumberQueryParam"
6
    "$ref": "parameters/patron.json#/borrowernumberQueryParam"
7
  },
7
  },
8
  "biblionumberPathParam": {
9
    "$ref": "parameters/biblio.json#/biblionumberPathParam"
10
  },
8
  "cityidPathParam": {
11
  "cityidPathParam": {
9
    "$ref": "parameters/city.json#/cityidPathParam"
12
    "$ref": "parameters/city.json#/cityidPathParam"
10
  },
13
  },
(-)a/api/v1/swagger/parameters/biblio.json (+9 lines)
Line 0 Link Here
1
{
2
   "biblionumberPathParam": {
3
     "name": "biblionumber",
4
     "in": "path",
5
     "description": "Internal biblio identifier",
6
     "required": true,
7
     "type": "integer"
8
  }
9
}
(-)a/api/v1/swagger/paths.json (+12 lines)
Lines 16-20 Link Here
16
  },
16
  },
17
  "/patrons/{borrowernumber}": {
17
  "/patrons/{borrowernumber}": {
18
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
18
    "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}"
19
  },
20
  "/biblios": {
21
    "$ref": "paths/biblios.json#/~1biblios"
22
  },
23
  "/biblios/{biblionumber}": {
24
    "$ref": "paths/biblios.json#/~1biblios~1{biblionumber}"
25
  },
26
  "/biblios/{biblionumber}/items": {
27
    "$ref": "paths/biblios.json#/~1biblios~1{biblionumber}~1items"
28
  },
29
  "/biblios/{biblionumber}/expanded": {
30
    "$ref": "paths/biblios.json#/~1biblios~1{biblionumber}~1expanded"
19
  }
31
  }
20
}
32
}
(-)a/api/v1/swagger/paths/biblios.json (+153 lines)
Line 0 Link Here
1
{
2
  "/biblios/{biblionumber}": {
3
    "get": {
4
      "operationId": "getBiblio",
5
      "tags": ["biblios"],
6
      "parameters": [
7
        { "$ref": "../parameters.json#/biblionumberPathParam" }
8
      ],
9
      "produces": [
10
        "application/json"
11
      ],
12
      "responses": {
13
        "200": {
14
          "description": "A biblio record",
15
          "schema": { "$ref": "../definitions.json#/biblio" }
16
17
        },
18
        "404": {
19
          "description": "Biblio not found",
20
          "schema": {
21
            "$ref": "../definitions.json#/error"
22
          }
23
        }
24
      }
25
    },
26
    "put": {
27
      "operationId": "updateBiblio",
28
      "tags": ["biblios"],
29
      "parameters": [
30
        { "$ref": "../parameters.json#/biblionumberPathParam" }
31
      ],
32
      "produces": [
33
        "application/json"
34
      ],
35
      "responses": {
36
        "200": {
37
          "description": "An updated biblio record"
38
        },
39
        "400": {
40
          "description": "Biblio update failed",
41
          "schema": { "$ref": "../definitions.json#/error" }
42
        },
43
        "404": {
44
          "description": "Biblio not found",
45
          "schema": {
46
            "$ref": "../definitions.json#/error"
47
          }
48
        }
49
      },
50
      "x-koha-authorization": {
51
        "permissions": {
52
          "editcatalogue": "1"
53
        }
54
      }
55
    },
56
    "delete": {
57
      "operationId": "deleteBiblio",
58
      "tags": ["biblios"],
59
      "parameters": [
60
        { "$ref": "../parameters.json#/biblionumberPathParam" }
61
      ],
62
      "produces": ["application/json"],
63
      "responses": {
64
        "200": {
65
          "description": "Biblio record deleted successfully",
66
          "schema": {
67
            "type": "object"
68
          }
69
        },
70
        "400": {
71
          "description": "Biblio deletion failed",
72
          "schema": { "$ref": "../definitions.json#/error" }
73
        },
74
        "404": {
75
          "description": "Biblio not found",
76
          "schema": { "$ref": "../definitions.json#/error" }
77
        }
78
      },
79
      "x-koha-authorization": {
80
        "permissions": {
81
          "editcatalogue": "1"
82
        }
83
      }
84
    }
85
  },
86
  "/biblios/{biblionumber}/items": {
87
    "get": {
88
      "operationId": "getitemsByBiblio",
89
      "tags": ["biblios", "items"],
90
      "parameters": [
91
        { "$ref": "../parameters.json#/biblionumberPathParam" }
92
      ],
93
      "produces": [
94
        "application/json"
95
      ],
96
      "responses": {
97
        "200": {
98
          "description": "A biblio record with items"
99
        },
100
        "404": {
101
          "description": "Biblio not found",
102
          "schema": {
103
            "$ref": "../definitions.json#/error"
104
          }
105
        }
106
      }
107
    }
108
  },
109
  "/biblios/{biblionumber}/expanded": {
110
    "get": {
111
      "operationId": "getexpandedByBiblio",
112
      "tags": ["biblios", "items", "item status"],
113
      "parameters": [
114
        { "$ref": "../parameters.json#/biblionumberPathParam" }
115
      ],
116
      "produces": [
117
        "application/json"
118
      ],
119
      "responses": {
120
        "200": {
121
          "description": "A biblio record with items and item statuses"
122
        },
123
        "404": {
124
          "description": "Biblio not found",
125
          "schema": {
126
            "$ref": "../definitions.json#/error"
127
          }
128
        }
129
      }
130
    }
131
  },
132
  "/biblios": {
133
    "post": {
134
      "operationId": "addBiblio",
135
      "tags": ["biblios"],
136
      "produces": ["application/json"],
137
      "responses": {
138
        "201": {
139
          "description": "A new biblio record"
140
        },
141
        "400": {
142
          "description": "Unable to create biblio record",
143
          "schema": { "$ref": "../definitions.json#/error" }
144
        }
145
      }
146
    },
147
    "x-koha-authorization": {
148
      "permissions": {
149
        "editcatalogue": "1"
150
      }
151
    }
152
  }
153
}
(-)a/t/db_dependent/api/v1/biblios.t (-1 / +90 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/env perl
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 t::lib::TestBuilder;
21
22
use Test::More tests => 3;
23
use Test::Mojo;
24
use Data::Dumper;
25
use C4::Auth;
26
use C4::Context;
27
use Koha::Database;
28
use MARC::File::XML ( BinaryEncoding => 'utf8', RecordFormat => 'UNIMARC' );
29
30
BEGIN {
31
    use_ok('Koha::Biblios');
32
}
33
34
my $schema  = Koha::Database->schema;
35
my $dbh     = C4::Context->dbh;
36
my $builder = t::lib::TestBuilder->new;
37
38
$ENV{REMOTE_ADDR} = '127.0.0.1';
39
my $t = Test::Mojo->new('Koha::REST::V1');
40
41
$schema->storage->txn_begin;
42
43
my $file = MARC::File::XML->in( 't/db_dependent/Record/testrecords/marcxml_utf8.xml' );
44
my $record = $file->next();
45
my ( $biblionumber, $itemnumber );
46
47
my $librarian = $builder->build({
48
    source => "Borrower",
49
    value => {
50
        categorycode => 'S',
51
        branchcode => 'NPL',
52
        flags => 1, # editcatalogue
53
    },
54
});
55
56
my $session = C4::Auth::get_session('');
57
$session->param('number', $librarian->{ borrowernumber });
58
$session->param('id', $librarian->{ userid });
59
$session->param('ip', '127.0.0.1');
60
$session->param('lasttime', time());
61
$session->flush;
62
63
subtest 'Create biblio' => sub {
64
	plan tests => 5;
65
66
	my $tx = $t->ua->build_tx(POST => '/api/v1/biblios' => $record->as_xml());
67
	$tx->req->env({REMOTE_ADDR => '127.0.0.1'});
68
	$tx->req->cookies({name => 'CGISESSID', value => $session->id});
69
	$t->request_ok($tx)
70
	  ->status_is(201);
71
	$biblionumber = $tx->res->json->{biblionumber};
72
	$itemnumber   = $tx->res->json->{items};
73
74
	$t->json_is('/biblionumber' => $biblionumber)
75
	  ->json_is('/items'      => $itemnumber)
76
	  ->header_like(Location => qr/$biblionumber/, 'Location header contains biblionumber');
77
};
78
79
subtest 'Delete biblio' => sub {
80
	plan tests => 2;
81
82
	my $tx = $t->ua->build_tx(DELETE => "/api/v1/biblios/$biblionumber");
83
	$tx->req->env({REMOTE_ADDR => '127.0.0.1'});
84
	$tx->req->cookies({name => 'CGISESSID', value => $session->id});
85
	$t->request_ok($tx)
86
	  ->status_is(200);
87
};
88
89
90
$schema->storage->txn_rollback;

Return to bug 17371