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

(-)a/Koha/REST/Plugin/PluginRoutes.pm (-2 / +71 lines)
Lines 106-112 sub merge_spec { Link Here
106
        my $plugin_spec = $plugin->api_routes;
106
        my $plugin_spec = $plugin->api_routes;
107
107
108
        foreach my $route ( keys %{ $plugin_spec } ) {
108
        foreach my $route ( keys %{ $plugin_spec } ) {
109
            my $THE_route = '/contrib/' . $plugin->api_namespace . $route;
109
            my $THE_route = get_normalized_plugin_path($plugin->api_namespace, $route);
110
            if ( exists $spec->{ $THE_route } ) {
110
            if ( exists $spec->{ $THE_route } ) {
111
                # Route exists, overwriting is forbidden
111
                # Route exists, overwriting is forbidden
112
                Koha::Exceptions::Plugin::ForbiddenAction->throw(
112
                Koha::Exceptions::Plugin::ForbiddenAction->throw(
Lines 118-129 sub merge_spec { Link Here
118
        }
118
        }
119
    }
119
    }
120
120
121
    if($plugin->can('api_spec')) {
122
        my $plugin_namespace = $plugin->api_namespace;
123
        my $plugin_spec = $plugin->api_spec;
124
        my @errors;
125
        if ($plugin_spec->{definitions}) {
126
            while (my ($k, $v) = each %{$plugin_spec->{definitions}}) {
127
                if ($spec->{definitions}->{$k}) {
128
                    push(@errors, "Duplicate OpenAPI definition '$k'. Plugin needs to rename one of it's definitions.");
129
                } else {
130
                    $spec->{definitions}->{$k} = $v;
131
                }
132
            }
133
        }
134
        if ($plugin_spec->{parameters}) {
135
            while (my ($k, $v) = each %{$plugin_spec->{parameters}}) {
136
                if ($spec->{parameters}->{$k}) {
137
                    push(@errors, "Duplicate OpenAPI parameter '$k'. Plugin needs to rename one of it's parameters.");
138
                } else {
139
                    $spec->{parameters}->{$k} = $v;
140
                }
141
            }
142
        }
143
        if ($plugin_spec->{tags}) {
144
            if ($spec->{tags}) {
145
                push(@{$spec->{tags}}, @{$plugin_spec->{tags}});
146
            } else {
147
                $spec->{tags} = $plugin_spec->{tags};
148
            }
149
        }
150
        if ($plugin_spec->{paths}) {
151
            while (my ($k, $v) = each %{$plugin_spec->{paths}}) {
152
                if ($spec->{paths}->{$k}) {
153
                    push(@errors, "Duplicate OpenAPI parameter '$k'. Plugin needs to rename one of it's paths.");
154
                } else {
155
                    $spec->{paths}->{get_normalized_plugin_path($plugin_namespace, $k)} = $v;
156
                }
157
            }
158
        }
159
        if (@errors) {
160
            Koha::Exceptions::Plugin::ForbiddenAction->throw(
161
                "Plugin '".$plugin->{class}."' had the following errors while merging OpenAPI specs:\n".join(', ', @errors)
162
            );
163
        }
164
    }
165
121
    if($plugin->can('static_routes')) {
166
    if($plugin->can('static_routes')) {
122
        my $plugin_spec = $plugin->static_routes;
167
        my $plugin_spec = $plugin->static_routes;
123
168
124
        foreach my $route ( keys %{ $plugin_spec } ) {
169
        foreach my $route ( keys %{ $plugin_spec } ) {
125
170
126
            my $THE_route = '/contrib/' . $plugin->api_namespace . '/static'.$route;
171
            my $THE_route = get_normalized_plugin_static_path($plugin->api_namespace, $route);
127
            if ( exists $spec->{ $THE_route } ) {
172
            if ( exists $spec->{ $THE_route } ) {
128
                # Route exists, overwriting is forbidden
173
                # Route exists, overwriting is forbidden
129
                Koha::Exceptions::Plugin::ForbiddenAction->throw(
174
                Koha::Exceptions::Plugin::ForbiddenAction->throw(
Lines 153-156 sub spec_ok { Link Here
153
    return !$schema->is_invalid;
198
    return !$schema->is_invalid;
154
}
199
}
155
200
201
=head3 get_normalized_plugin_path
202
203
public static subroutine
204
Normalizes a plugin's route path to /contrib/<plugin-namespace>/<plugin-route-path>
205
206
=cut
207
208
sub get_normalized_plugin_path {
209
    my ($plugin_api_namespace, $plugin_path_name) = @_;
210
    return '/contrib/' . $plugin_api_namespace . $plugin_path_name;
211
}
212
213
=head3 get_normalized_plugin_static_path
214
215
public static subroutine
216
Normalizes a plugin's static route path to /contrib/<plugin-namespace>/static/<plugin-route-path>
217
218
=cut
219
220
sub get_normalized_plugin_static_path {
221
    my ($plugin_api_namespace, $plugin_path_name) = @_;
222
    return '/contrib/' . $plugin_api_namespace . '/static' . $plugin_path_name;
223
}
224
156
1;
225
1;
(-)a/t/db_dependent/Koha/REST/Plugin/PluginRoutes.t (+1 lines)
Lines 164-169 subtest 'Permissions and access to plugin routes tests' => sub { Link Here
164
    my $routes = get_defined_routes($t);
164
    my $routes = get_defined_routes($t);
165
    ok( exists $routes->{'/contrib/testplugin/patrons/bother'}, 'Route exists' );
165
    ok( exists $routes->{'/contrib/testplugin/patrons/bother'}, 'Route exists' );
166
    ok( exists $routes->{'/contrib/testplugin/public/patrons/bother'}, 'Route exists' );
166
    ok( exists $routes->{'/contrib/testplugin/public/patrons/bother'}, 'Route exists' );
167
    ok( exists $routes->{'/contrib/bundledass/patrons/<:patron_id>/finger'}, 'Bundled Route exists' );
167
168
168
    C4::Context->set_preference( 'RESTPublicAnonymousRequests', 0 );
169
    C4::Context->set_preference( 'RESTPublicAnonymousRequests', 0 );
169
170
(-)a/t/lib/plugins/Koha/Plugin/BundledApiSpec.pm (+46 lines)
Line 0 Link Here
1
package Koha::Plugin::BundledApiSpec;
2
3
use Modern::Perl;
4
5
use base qw(Koha::Plugins::Base);
6
7
our $VERSION = 0.01;
8
our $metadata = {
9
    name            => 'Bundled OpenAPI Spec Plugin',
10
    author          => 'Olli-Antti Kivilahti',
11
    description     => 'Test plugin for testing the bundling of OpenAPI schema fragments',
12
    date_authored   => '2023-',
13
    date_updated    => '2023-04-06',
14
    minimum_version => '21.11',
15
    maximum_version => undef,
16
    version         => $VERSION,
17
    my_example_tag  => 'bundled_api_spec',
18
};
19
20
sub new {
21
    my ( $class, $args ) = @_;
22
    $args->{'metadata'} = $metadata;
23
    my $self = $class->SUPER::new($args);
24
    return $self;
25
}
26
27
sub api_namespace {
28
    return "bundledass";
29
}
30
31
# One should never define these both, but only the other.
32
#sub api_route {
33
#    my ( $self, $args ) = @_;
34
#    return JSON::Validator->new->schema($self->mbf_dir() . "/openapi.yaml")->schema->{data};
35
#}
36
37
# One should never define these both, but only the other. Automated test suite should disable the other for testing.
38
sub api_spec {
39
    my ( $self, $args ) = @_;
40
41
    my $schema2 = JSON::Validator::Schema::OpenAPIv2->new;
42
    $schema2->resolve($self->mbf_dir() . "/openapi.yaml");
43
    return $schema2->bundle->data;
44
}
45
46
1;
(-)a/t/lib/plugins/Koha/Plugin/BundledApiSpec/openapi.yaml (+69 lines)
Line 0 Link Here
1
---
2
3
swagger: "2.0"
4
info:
5
  title: API Title
6
  version: "1.0"
7
8
paths:
9
  /patrons/{patron_id}/finger:
10
    delete:
11
      x-mojo-to: Koha::Plugin::BundledApiSpec#finger_delete
12
      operationId: finger_delete
13
      tags:
14
        - plugin
15
      produces:
16
        - application/json
17
      parameters:
18
        - $ref: openapi/parameters.json#/patron_idPathParam
19
      x-koha-authorization:
20
        permissions:
21
          borrowers: finger
22
      responses:
23
        "200":
24
          description: Deleted ok
25
          schema:
26
            type: object
27
            required:
28
              - deleted_count
29
            properties:
30
              deleted_count:
31
                description: How many Block-objects have been deleted in the DB
32
                type: integer
33
    get:
34
      x-mojo-to: Koha::Plugin::BundledApiSpec#finger_get
35
      operationId: finger_get
36
      tags:
37
        - plugin
38
      produces:
39
        - application/json
40
      parameters:
41
        - $ref: openapi/parameters.json#/patron_idPathParam
42
      x-koha-authorization:
43
        permissions:
44
          borrowers: finger
45
      responses:
46
        "200":
47
          description: List of fingers
48
          schema:
49
            type: array
50
            items:
51
              $ref: openapi/definitions.json#/finger
52
    post:
53
      x-mojo-to: Koha::Plugin::BundledApiSpec#finger_post
54
      operationId: finger_post
55
      tags:
56
        - plugin
57
      produces:
58
        - application/json
59
      parameters:
60
        - $ref: openapi/parameters.json#/patron_idPathParam
61
        - $ref: openapi/parameters.json#/finger
62
      x-koha-authorization:
63
        permissions:
64
          borrowers: finger
65
      responses:
66
        "200":
67
          description: The created self-service block
68
          schema:
69
            $ref: openapi/definitions.json#/finger
(-)a/t/lib/plugins/Koha/Plugin/BundledApiSpec/openapi/definitions.json (+5 lines)
Line 0 Link Here
1
{
2
  "finger": {
3
    "$ref": "./definitions/finger.json"
4
  }
5
}
(-)a/t/lib/plugins/Koha/Plugin/BundledApiSpec/openapi/definitions/finger.json (+12 lines)
Line 0 Link Here
1
{
2
  "type": "object",
3
  "properties": {
4
    "patron_id": {
5
      "type": "integer",
6
      "description": "The user that has the finger"
7
    },
8
    "type": {
9
      "type": "string"
10
    }
11
  }
12
}
(-)a/t/lib/plugins/Koha/Plugin/BundledApiSpec/openapi/parameters.json (-1 / +18 lines)
Line 0 Link Here
0
- 
1
{
2
  "patron_idPathParam": {
3
    "name": "patron_id",
4
    "in": "path",
5
    "description": "Internal Patron identifier",
6
    "required": true,
7
    "type": "integer"
8
  },
9
  "finger": {
10
    "name": "finger",
11
    "description": "Finger",
12
    "in": "body",
13
    "required": true,
14
    "schema": {
15
      "$ref": "./definitions.json#/finger"
16
    }
17
  }
18
}

Return to bug 33503