File Coverage

blib/lib/Yancy/Controller/Yancy/API.pm
Criterion Covered Total %
statement 9 87 10.3
branch 0 48 0.0
condition 0 17 0.0
subroutine 3 10 30.0
pod 4 4 100.0
total 16 166 9.6


line stmt bran cond sub pod time code
1             package Yancy::Controller::Yancy::API;
2             our $VERSION = '1.087';
3             # ABSTRACT: (DEPRECATED) An OpenAPI REST controller for the Yancy editor
4              
5             #pod =head1 DESCRIPTION
6             #pod
7             #pod B: This module has been merged into the
8             #pod L class and the editor now uses that class by
9             #pod default.
10             #pod
11             #pod This module contains the routes that L uses to work with the
12             #pod backend data. This API is used by the Yancy editor.
13             #pod
14             #pod =head1 SUBCLASSING
15             #pod
16             #pod To change how the API provides access to the data in your database, you
17             #pod can create a custom controller. To do so, you should extend
18             #pod L and override the desired methods to provide
19             #pod the desired functionality.
20             #pod
21             #pod package MyApp::Controller::CustomYancyAPI;
22             #pod use Mojo::Base 'Yancy::Controller::Yancy';
23             #pod sub list {
24             #pod my ( $c ) = @_;
25             #pod return unless $c->openapi->valid_input;
26             #pod my $items = $c->yancy->backend->list( $c->stash( 'schema' ) );
27             #pod return $c->render(
28             #pod status => 200,
29             #pod openapi => $items,
30             #pod );
31             #pod }
32             #pod
33             #pod package main;
34             #pod use Mojolicious::Lite;
35             #pod push @{ app->routes->namespaces }, 'MyApp::Controller';
36             #pod plugin Yancy => {
37             #pod editor => {
38             #pod default_controller => 'CustomYancyAPI',
39             #pod },
40             #pod };
41             #pod
42             #pod For an example, you could extend this class to add authorization based
43             #pod on your own requirements.
44             #pod
45             #pod =head1 SEE ALSO
46             #pod
47             #pod L, L
48             #pod
49             #pod =cut
50              
51 1     1   887 use Mojo::Base 'Mojolicious::Controller';
  1         5  
  1         11  
52 1     1   311 use Mojo::JSON qw( to_json );
  1         2  
  1         75  
53 1     1   7 use Yancy::Util qw( derp );
  1         3  
  1         1884  
54              
55             #pod =method list
56             #pod
57             #pod List the items in a schema. The schema name should be in the
58             #pod stash key C.
59             #pod
60             #pod Each returned item will be filtered by filters conforming with
61             #pod L that are passed in the
62             #pod array-ref in stash key C.
63             #pod
64             #pod C<$limit>, C<$offset>, and C<$order_by> may be provided as query parameters.
65             #pod
66             #pod =cut
67              
68             sub list {
69 0     0 1   my ( $c ) = @_;
70 0 0         return unless $c->openapi->valid_input;
71 0           my $args = $c->validation->output;
72              
73             my %opt = (
74             limit => delete $args->{'$limit'},
75 0           offset => delete $args->{'$offset'},
76             );
77 0 0         if ( my $order_by = delete $args->{'$order_by'} ) {
78             $opt{order_by} = [
79 0           map +{ "-$_->[0]" => $_->[1] },
80             map +[ split /:/ ],
81             split /,/, $order_by
82             ];
83             }
84              
85 0           my %filter;
86 0 0         if ( $c->stash( 'collection' ) ) {
87 0           derp '"collection" stash key is now "schema" in controller configuration';
88             }
89 0   0       my $schema_name = $c->stash( 'schema' ) || $c->stash( 'collection' );
90 0           my $schema = $c->yancy->schema( $schema_name ) ;
91 0           my $props = $schema->{properties};
92 0           for my $key ( keys %$args ) {
93 0           my $value = $args->{ $key };
94 0   0       my $type = $props->{$key}{type} || 'string';
95 0 0         if ( _is_type( $type, 'string' ) ) {
    0          
    0          
96 0 0         if ( ( $value =~ tr/*/%/ ) <= 0 ) {
97 0           $value = "\%$value\%";
98             }
99 0           $filter{ $key } = { -like => $value };
100             }
101             elsif ( grep _is_type( $type, $_ ), qw(number integer) ) {
102 0           $filter{ $key } = $value ;
103             }
104             elsif ( _is_type( $type, 'boolean' ) ) {
105 0 0         $filter{ $value ? '-bool' : '-not_bool' } = $key;
106             }
107             else {
108 0           die "Sorry type '" .
109             to_json( $type ) .
110             "' is not handled yet, only string|number|integer|boolean is supported."
111             }
112             }
113              
114 0           my $res = $c->yancy->backend->list( $schema_name, \%filter, \%opt );
115 0           _delete_null_values( @{ $res->{items} } );
  0            
116             $res->{items} = [
117 0 0         map _apply_op_filters( $schema_name, $_, $c->stash( 'filters_out' ), $c->yancy->filters ), @{ $res->{items} }
  0            
118             ] if $c->stash( 'filters_out' );
119              
120 0           return $c->render(
121             status => 200,
122             openapi => $res,
123             );
124             }
125              
126             sub _is_type {
127 0     0     my ( $type, $is_type ) = @_;
128 0 0         return unless $type;
129             return ref $type eq 'ARRAY'
130 0 0         ? !!grep { $_ eq $is_type } @$type
  0            
131             : $type eq $is_type;
132             }
133              
134             #pod =method get
135             #pod
136             #pod Get a single item from a schema. The schema should be in the stash key
137             #pod C.
138             #pod
139             #pod The item's ID field-name is in the stash key C. The ID itself
140             #pod is extracted from the OpenAPI input, under a parameter of that name.
141             #pod
142             #pod The return value is filtered like each result is in L.
143             #pod
144             #pod =cut
145              
146             sub get {
147 0     0 1   my ( $c ) = @_;
148 0 0         return unless $c->openapi->valid_input;
149 0           my $args = $c->validation->output;
150 0           my $id = $args->{ $c->stash( 'id_field' ) };
151 0 0         if ( $c->stash( 'collection' ) ) {
152 0           derp '"collection" stash key is now "schema" in controller configuration';
153             }
154 0   0       my $schema_name = $c->stash( 'schema' ) || $c->stash( 'collection' );
155 0           my $res = _delete_null_values( $c->yancy->backend->get( $schema_name, $id ) );
156 0 0         $res = _apply_op_filters( $schema_name, $res, $c->stash( 'filters_out' ), $c->yancy->filters )
157             if $c->stash( 'filters_out' );
158 0           return $c->render(
159             status => 200,
160             openapi => $res,
161             );
162             }
163              
164             #pod =method set
165             #pod
166             #pod Create or update an item in a schema. The schema should be in the stash
167             #pod key C.
168             #pod
169             #pod The item to be updated is determined as with L, if any.
170             #pod The new item is extracted from the OpenAPI input, under parameter name
171             #pod C, and must be a hash/JSON "object". It will be filtered by
172             #pod filters conforming with L
173             #pod that are passed in the array-ref in stash key C, after the
174             #pod schema and property filters have been applied.
175             #pod
176             #pod The return value is filtered like each result is in L.
177             #pod
178             #pod =cut
179              
180             sub set {
181 0     0 1   my ( $c ) = @_;
182 0 0         return unless $c->openapi->valid_input;
183 0           my $args = $c->validation->output;
184 0           my $id = $args->{ $c->stash( 'id_field' ) };
185 0 0         if ( $c->stash( 'collection' ) ) {
186 0           derp '"collection" stash key is now "schema" in controller configuration';
187             }
188 0   0       my $schema_name = $c->stash( 'schema' ) || $c->stash( 'collection' );
189 0           my $item = $c->yancy->filter->apply( $schema_name, $args->{ newItem } );
190 0 0         $item = _apply_op_filters( $schema_name, $item, $c->stash( 'filters' ), $c->yancy->filters )
191             if $c->stash( 'filters' );
192              
193 0 0         if ( $id ) {
194 0           $c->yancy->backend->set( $schema_name, $id, $item );
195             # ID field may have changed
196 0   0       $id = $item->{ $c->stash( 'id_field' ) } || $id;
197             }
198             else {
199 0           $id = $c->yancy->backend->create( $schema_name, $item );
200             }
201              
202 0           my $res = _delete_null_values( $c->yancy->backend->get( $schema_name, $id ) );
203 0 0         $res = _apply_op_filters( $schema_name, $res, $c->stash( 'filters_out' ), $c->yancy->filters )
204             if $c->stash( 'filters_out' );
205 0           return $c->render(
206             status => 200,
207             openapi => $res,
208             );
209             }
210              
211             #pod =method delete
212             #pod
213             #pod Delete an item from a schema. The schema name should be in the
214             #pod stash key C.
215             #pod
216             #pod The item to be deleted is determined as with L.
217             #pod
218             #pod =cut
219              
220             sub delete {
221 0     0 1   my ( $c ) = @_;
222 0 0         return unless $c->openapi->valid_input;
223 0           my $args = $c->validation->output;
224 0           my $id = $args->{ $c->stash( 'id_field' ) };
225 0 0         if ( $c->stash( 'collection' ) ) {
226 0           derp '"collection" stash key is now "schema" in controller configuration';
227             }
228 0   0       my $schema_name = $c->stash( 'schema' ) || $c->stash( 'collection' );
229 0           $c->yancy->backend->delete( $schema_name, $id );
230 0           return $c->rendered( 204 );
231             }
232              
233             #=sub _delete_null_values
234             #
235             # _delete_null_values( @items );
236             #
237             # Remove all the keys with a value of C from the given items.
238             # This prevents the user from having to explicitly declare fields that
239             # can be C as C<< [ 'string', 'null' ] >>. Since this validation
240             # really only happens when a response is generated, it can be very
241             # confusing to understand what the problem is
242             sub _delete_null_values {
243 0     0     for my $item ( @_ ) {
244 0           delete $item->{ $_ } for grep !defined $item->{ $_ }, keys %$item;
245             }
246 0 0         return wantarray ? @_ : $_[0];
247             }
248              
249             #=sub _apply_op_filters
250             #
251             # Similar to the helper 'yancy.filter.apply' - filters input item,
252             # returns updated version.
253             sub _apply_op_filters {
254 0     0     my ( $schema_name, $item, $filters, $app_filters ) = @_;
255 0           $item = { %$item }; # no mutate input
256 0           for my $filter ( @$filters ) {
257 0 0         ( $filter, my @params ) = @$filter if ref $filter eq 'ARRAY';
258 0           my $sub = $app_filters->{ $filter };
259 0 0         die "Unknown filter: $filter" unless $sub;
260 0           $item = $sub->( $schema_name, $item, {}, @params );
261             }
262 0           $item;
263             }
264              
265             1;
266              
267             __END__