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

(-)a/Koha/REST/V1/Cities.pm (-7 / +14 lines)
Lines 29-38 sub list { Link Here
29
    my ( $c, $args, $cb ) = @_;
29
    my ( $c, $args, $cb ) = @_;
30
30
31
    my $cities;
31
    my $cities;
32
    my $filter;
33
34
    $filter->{city_name} = { LIKE => $args->{city_name}."%" }
35
      if defined( $args->{city_name} );
36
    $filter->{city_state} = { LIKE => $args->{city_state}."%" }
37
      if defined( $args->{city_state} );
38
    $filter->{city_country} = { LIKE => $args->{city_country}."%" }
39
      if defined( $args->{city_country} );
40
    $filter->{city_zipcode} = { LIKE => $args->{city_zipcode}."%" }
41
      if defined( $args->{city_zipcode} );
32
42
33
    return try {
43
    return try {
34
        $cities =
44
        $cities = Koha::Cities->search($filter)->unblessed;
35
          Koha::Cities->search( $c->req->query_params->to_hash )->unblessed;
36
        return $c->$cb( $cities, 200 );
45
        return $c->$cb( $cities, 200 );
37
    }
46
    }
38
    catch {
47
    catch {
Lines 60-66 sub get { Link Here
60
sub add {
69
sub add {
61
    my ( $c, $args, $cb ) = @_;
70
    my ( $c, $args, $cb ) = @_;
62
71
63
    my $city = Koha::City->new( $c->req->json );
72
    my $city = Koha::City->new( $args->{body} );
64
73
65
    return try {
74
    return try {
66
        $city->store;
75
        $city->store;
Lines 84-93 sub update { Link Here
84
93
85
    return try {
94
    return try {
86
        $city = Koha::Cities->find( $args->{cityid} );
95
        $city = Koha::Cities->find( $args->{cityid} );
87
        while ( my ( $k, $v ) = each %{ $c->req->json } ) {
96
        $city->set( $args->{body} );
88
            $city->$k($v);
97
        $city->store();
89
        }
90
        $city->store;
91
        return $c->$cb( $city->unblessed, 200 );
98
        return $c->$cb( $city->unblessed, 200 );
92
    }
99
    }
93
    catch {
100
    catch {
(-)a/api/v1/swagger/definitions/city.json (-20 / +22 lines)
Lines 1-24 Link Here
1
{
1
{
2
  "type": "object",
2
  "type": "object",
3
  "properties": {
3
  "properties": {
4
      "cityid": {
4
    "cityid": {
5
        "$ref": "../x-primitives.json#/cityid"
5
      "$ref": "../x-primitives.json#/cityid"
6
      },
6
    },
7
      "city_name": {
7
    "city_name": {
8
        "description": "city name",
8
      "description": "city name",
9
        "type": "string"
9
      "type": "string"
10
      },
10
    },
11
      "city_state": {
11
    "city_state": {
12
        "description": "city state",
12
      "description": "city state",
13
        "type": ["string", "null"]
13
      "type": ["string", "null"]
14
      },
14
    },
15
      "city_zipcode": {
15
    "city_zipcode": {
16
        "description": "city zipcode",
16
      "description": "city zipcode",
17
        "type": ["string", "null"]
17
      "type": ["string", "null"]
18
      },
18
    },
19
      "city_country": {
19
    "city_country": {
20
        "description": "city country",
20
      "description": "city country",
21
        "type": ["string", "null"]
21
      "type": ["string", "null"]
22
      }
22
    }
23
  }
23
  },
24
  "additionalProperties": false,
25
  "required": ["city_name", "city_state", "city_zipcode", "city_country"]
24
}
26
}
(-)a/api/v1/swagger/paths/cities.json (-53 / +69 lines)
Lines 7-12 Link Here
7
      "produces": [
7
      "produces": [
8
        "application/json"
8
        "application/json"
9
      ],
9
      ],
10
      "parameters": [{
11
        "name": "city_name",
12
        "in": "query",
13
        "description": "Case insensative 'starts-with' search on city name",
14
        "required": false,
15
        "type": "string"
16
      }, {
17
        "name": "city_state",
18
        "in": "query",
19
        "description": "Case insensative 'starts-with' search on city state",
20
        "required": false,
21
        "type": "string"
22
      }, {
23
        "name": "city_country",
24
        "in": "query",
25
        "description": "Case insensative 'starts_with' search on city country",
26
        "required": false,
27
        "type": "string"
28
      }, {
29
        "name": "city_zipcode",
30
        "in": "query",
31
        "description": "Case Insensative 'starts-with' search on zipcode",
32
        "required": false,
33
        "type": "string"
34
      }],
10
      "responses": {
35
      "responses": {
11
        "200": {
36
        "200": {
12
          "description": "A list of cities",
37
          "description": "A list of cities",
Lines 24-33 Link Here
24
          }
49
          }
25
        },
50
        },
26
        "500": {
51
        "500": {
27
            "description": "Internal error",
52
          "description": "Internal error",
28
            "schema": {
53
          "schema": {
29
                "$ref": "../definitions.json#/error"
54
            "$ref": "../definitions.json#/error"
30
            }
55
          }
31
        }
56
        }
32
      }
57
      }
33
    },
58
    },
Lines 36-50 Link Here
36
      "operationId": "add",
61
      "operationId": "add",
37
      "tags": ["cities"],
62
      "tags": ["cities"],
38
      "parameters": [{
63
      "parameters": [{
39
          "name": "body",
64
        "name": "body",
40
          "in": "body",
65
        "in": "body",
41
          "description": "A JSON object containing informations about the new hold",
66
        "description": "A JSON object containing informations about the new hold",
42
          "required": true,
67
        "required": true,
43
          "schema": {
68
        "schema": {
44
            "$ref": "../definitions.json#/city"
69
          "$ref": "../definitions.json#/city"
45
          }
46
        }
70
        }
47
      ],
71
      }],
48
      "produces": [
72
      "produces": [
49
        "application/json"
73
        "application/json"
50
      ],
74
      ],
Lines 62-71 Link Here
62
          }
86
          }
63
        },
87
        },
64
        "500": {
88
        "500": {
65
            "description": "Internal error",
89
          "description": "Internal error",
66
            "schema": {
90
          "schema": {
67
                "$ref": "../definitions.json#/error"
91
            "$ref": "../definitions.json#/error"
68
            }
92
          }
69
        }
93
        }
70
      },
94
      },
71
      "x-koha-authorization": {
95
      "x-koha-authorization": {
Lines 80-90 Link Here
80
      "x-mojo-controller": "Koha::REST::V1::Cities",
104
      "x-mojo-controller": "Koha::REST::V1::Cities",
81
      "operationId": "get",
105
      "operationId": "get",
82
      "tags": ["cities"],
106
      "tags": ["cities"],
83
      "parameters": [
107
      "parameters": [{
84
        {
108
        "$ref": "../parameters.json#/cityidPathParam"
85
          "$ref": "../parameters.json#/cityidPathParam"
109
      }],
86
        }
87
      ],
88
      "produces": [
110
      "produces": [
89
        "application/json"
111
        "application/json"
90
      ],
112
      ],
Lines 106-118 Link Here
106
          "schema": {
128
          "schema": {
107
            "$ref": "../definitions.json#/error"
129
            "$ref": "../definitions.json#/error"
108
          }
130
          }
109
        }
131
        },
110
        ,
111
        "500": {
132
        "500": {
112
            "description": "Internal error",
133
          "description": "Internal error",
113
            "schema": {
134
          "schema": {
114
                "$ref": "../definitions.json#/error"
135
            "$ref": "../definitions.json#/error"
115
            }
136
          }
116
        }
137
        }
117
      }
138
      }
118
    },
139
    },
Lines 120-139 Link Here
120
      "x-mojo-controller": "Koha::REST::V1::Cities",
141
      "x-mojo-controller": "Koha::REST::V1::Cities",
121
      "operationId": "update",
142
      "operationId": "update",
122
      "tags": ["cities"],
143
      "tags": ["cities"],
123
      "parameters": [
144
      "parameters": [{
124
        {
145
        "$ref": "../parameters.json#/cityidPathParam"
125
          "$ref": "../parameters.json#/cityidPathParam"
146
      }, {
126
        },
147
        "name": "body",
127
        {
148
        "in": "body",
128
          "name": "body",
149
        "description": "A JSON object containing informations about the new hold",
129
          "in": "body",
150
        "required": true,
130
          "description": "A JSON object containing informations about the new hold",
151
        "schema": {
131
          "required": true,
152
          "$ref": "../definitions.json#/city"
132
          "schema": {
133
            "$ref": "../definitions.json#/city"
134
          }
135
        }
153
        }
136
      ],
154
      }],
137
      "produces": [
155
      "produces": [
138
        "application/json"
156
        "application/json"
139
      ],
157
      ],
Lines 157-166 Link Here
157
          }
175
          }
158
        },
176
        },
159
        "500": {
177
        "500": {
160
            "description": "Internal error",
178
          "description": "Internal error",
161
            "schema": {
179
          "schema": {
162
                "$ref": "../definitions.json#/error"
180
            "$ref": "../definitions.json#/error"
163
            }
181
          }
164
        }
182
        }
165
      },
183
      },
166
      "x-koha-authorization": {
184
      "x-koha-authorization": {
Lines 173-183 Link Here
173
      "x-mojo-controller": "Koha::REST::V1::Cities",
191
      "x-mojo-controller": "Koha::REST::V1::Cities",
174
      "operationId": "delete",
192
      "operationId": "delete",
175
      "tags": ["cities"],
193
      "tags": ["cities"],
176
      "parameters": [
194
      "parameters": [{
177
        {
195
        "$ref": "../parameters.json#/cityidPathParam"
178
          "$ref": "../parameters.json#/cityidPathParam"
196
      }],
179
        }
180
      ],
181
      "produces": [
197
      "produces": [
182
        "application/json"
198
        "application/json"
183
      ],
199
      ],
Lines 201-210 Link Here
201
          }
217
          }
202
        },
218
        },
203
        "500": {
219
        "500": {
204
            "description": "Internal error",
220
          "description": "Internal error",
205
            "schema": {
221
          "schema": {
206
                "$ref": "../definitions.json#/error"
222
            "$ref": "../definitions.json#/error"
207
            }
223
          }
208
        }
224
        }
209
      },
225
      },
210
      "x-koha-authorization": {
226
      "x-koha-authorization": {
(-)a/t/db_dependent/api/v1/cities.t (-22 / +65 lines)
Lines 32-37 use Koha::Database; Link Here
32
32
33
my $schema  = Koha::Database->new->schema;
33
my $schema  = Koha::Database->new->schema;
34
my $builder = t::lib::TestBuilder->new;
34
my $builder = t::lib::TestBuilder->new;
35
35
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
36
# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling
36
# this affects the other REST api tests
37
# this affects the other REST api tests
37
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
38
t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' );
Lines 41-47 my $t = Test::Mojo->new('Koha::REST::V1'); Link Here
41
42
42
subtest 'list() tests' => sub {
43
subtest 'list() tests' => sub {
43
44
44
    plan tests => 19;
45
    plan tests => 18;
45
46
46
    $schema->storage->txn_begin;
47
    $schema->storage->txn_begin;
47
48
Lines 82-88 subtest 'list() tests' => sub { Link Here
82
      $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country );
83
      $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country );
83
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
84
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
84
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
85
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
85
    $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] );
86
    my $result =
87
      $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] );
86
88
87
    $tx = $t->ua->build_tx(
89
    $tx = $t->ua->build_tx(
88
        GET => "/api/v1/cities?city_name=" . $city->{city_name} );
90
        GET => "/api/v1/cities?city_name=" . $city->{city_name} );
Lines 90-104 subtest 'list() tests' => sub { Link Here
90
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
92
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
91
    $t->request_ok($tx)->status_is(200)->json_is( [$city] );
93
    $t->request_ok($tx)->status_is(200)->json_is( [$city] );
92
94
95
    # Warn on unsupported query parameter
93
    $tx = $t->ua->build_tx( GET => '/api/v1/cities?city_blah=blah' );
96
    $tx = $t->ua->build_tx( GET => '/api/v1/cities?city_blah=blah' );
94
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
97
    $tx->req->cookies( { name => 'CGISESSID', value => $session_id } );
95
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
98
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
96
99
    $t->request_ok($tx)->status_is(400)
97
    warning_like {
100
      ->json_is( [{ path => '/query/city_blah', message => 'Malformed query string'}] );
98
        $t->request_ok($tx)->status_is(500)
99
          ->json_like( '/error' => qr/Unknown column/ );
100
    }
101
    qr/Unknown column/, 'Wrong parameters raise warnings';
102
101
103
    $schema->storage->txn_rollback;
102
    $schema->storage->txn_rollback;
104
};
103
};
Lines 130-136 subtest 'get() tests' => sub { Link Here
130
129
131
subtest 'add() tests' => sub {
130
subtest 'add() tests' => sub {
132
131
133
    plan tests => 10;
132
    plan tests => 11;
134
133
135
    $schema->storage->txn_begin;
134
    $schema->storage->txn_begin;
136
135
Lines 163-168 subtest 'add() tests' => sub { Link Here
163
      ->json_is( '/city_zipcode' => $city->{city_zipcode} )
162
      ->json_is( '/city_zipcode' => $city->{city_zipcode} )
164
      ->json_is( '/city_country' => $city->{city_country} );
163
      ->json_is( '/city_country' => $city->{city_country} );
165
164
165
    # Authorized attempt to write invalid data
166
    my $city_with_invalid_field = {
166
    my $city_with_invalid_field = {
167
        city_blah    => "City Blah",
167
        city_blah    => "City Blah",
168
        city_state   => "City State",
168
        city_state   => "City State",
Lines 170-189 subtest 'add() tests' => sub { Link Here
170
        city_country => "City Country"
170
        city_country => "City Country"
171
    };
171
    };
172
172
173
    # Authorized attempt to write invalid data
174
    $tx = $t->ua->build_tx(
173
    $tx = $t->ua->build_tx(
175
        POST => "/api/v1/cities/" => json => $city_with_invalid_field );
174
        POST => "/api/v1/cities/" => json => $city_with_invalid_field );
176
    $tx->req->cookies(
175
    $tx->req->cookies(
177
        { name => 'CGISESSID', value => $authorized_session_id } );
176
        { name => 'CGISESSID', value => $authorized_session_id } );
178
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
177
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
179
    $t->request_ok($tx)->status_is(500);
178
    $t->request_ok($tx)->status_is(400)->json_is(
179
        "/errors" => [
180
            {
181
                message => "Properties not allowed: city_blah.",
182
                path    => "/body"
183
            }
184
        ]
185
    );
180
186
181
    $schema->storage->txn_rollback;
187
    $schema->storage->txn_rollback;
182
};
188
};
183
189
184
subtest 'update() tests' => sub {
190
subtest 'update() tests' => sub {
185
191
186
    plan tests => 10;
192
    plan tests => 13;
187
193
188
    $schema->storage->txn_begin;
194
    $schema->storage->txn_begin;
189
195
Lines 202-227 subtest 'update() tests' => sub { Link Here
202
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
208
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
203
    $t->request_ok($tx)->status_is(403);
209
    $t->request_ok($tx)->status_is(403);
204
210
211
    # Attempt partial update on a PUT
212
    my $city_with_missing_field = {
213
        city_name    => 'New name',
214
        city_state   => 'New state',
215
        city_country => 'New country'
216
    };
217
205
    $tx = $t->ua->build_tx(
218
    $tx = $t->ua->build_tx(
206
        PUT => "/api/v1/cities/$city_id" => json => { city_name => 'New name' }
219
        PUT => "/api/v1/cities/$city_id" => json => $city_with_missing_field );
207
    );
208
    $tx->req->cookies(
220
    $tx->req->cookies(
209
        { name => 'CGISESSID', value => $authorized_session_id } );
221
        { name => 'CGISESSID', value => $authorized_session_id } );
210
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
222
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
211
    $t->request_ok($tx)->status_is(200)->json_is( '/city_name' => 'New name' );
223
    $t->request_ok($tx)->status_is(400)
224
      ->json_is( "/errors" =>
225
          [ { message => "Missing property.", path => "/body/city_zipcode" } ]
226
      );
227
228
    # Full object update on PUT
229
    my $city_with_updated_field = {
230
        city_name    => "London",
231
        city_state   => "City State",
232
        city_zipcode => "City Zipcode",
233
        city_country => "City Country"
234
    };
212
235
213
    $tx = $t->ua->build_tx(
236
    $tx = $t->ua->build_tx(
214
        PUT => "/api/v1/cities/$city_id" => json => { city_blah => 'New blah' }
237
        PUT => "/api/v1/cities/$city_id" => json => $city_with_updated_field );
215
    );
216
    $tx->req->cookies(
238
    $tx->req->cookies(
217
        { name => 'CGISESSID', value => $authorized_session_id } );
239
        { name => 'CGISESSID', value => $authorized_session_id } );
218
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
240
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
219
    $t->request_ok($tx)->status_is(500)
241
    $t->request_ok($tx)->status_is(200)->json_is( '/city_name' => 'London' );
220
      ->json_is( '/error' => "No method city_blah for Koha::City" );
242
243
    # Authorized attempt to write invalid data
244
    my $city_with_invalid_field = {
245
        city_blah    => "City Blah",
246
        city_state   => "City State",
247
        city_zipcode => "City Zipcode",
248
        city_country => "City Country"
249
    };
250
251
    $tx = $t->ua->build_tx(
252
        PUT => "/api/v1/cities/$city_id" => json => $city_with_invalid_field );
253
    $tx->req->cookies(
254
        { name => 'CGISESSID', value => $authorized_session_id } );
255
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
256
    $t->request_ok($tx)->status_is(400)->json_is(
257
        "/errors" => [
258
            {
259
                message => "Properties not allowed: city_blah.",
260
                path    => "/body"
261
            }
262
        ]
263
    );
221
264
222
    my $non_existent_id = $city_id + 1;
265
    my $non_existent_id = $city_id + 1;
223
    $tx = $t->ua->build_tx( PUT => "/api/v1/cities/$non_existent_id" => json =>
266
    $tx =
224
          { city_name => 'New name' } );
267
      $t->ua->build_tx( PUT => "/api/v1/cities/$non_existent_id" => json =>
268
          $city_with_updated_field );
225
    $tx->req->cookies(
269
    $tx->req->cookies(
226
        { name => 'CGISESSID', value => $authorized_session_id } );
270
        { name => 'CGISESSID', value => $authorized_session_id } );
227
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
271
    $tx->req->env( { REMOTE_ADDR => $remote_address } );
228
- 

Return to bug 17428