From 99df7fe7302688846b78508127738137549cf7a3 Mon Sep 17 00:00:00 2001 From: Doug Bell Date: Thu, 16 May 2019 17:51:41 -0500 Subject: [PATCH] rename "collection" to "schema" The idea here is that "schema" is easier to say, but also in the future where we put "schema" can be either the _name_ of a schema or an actual schema (useful for Form plugins and Controllers). This commit leaves all the tests largely as they were, triggering all the deprecation warnings as a test. The next commit will remove all the deprecation triggers in the tests, as well as make the deprecation warnings slightly less annoying... Refs #25 --- lib/Mojolicious/Plugin/Yancy.pm | 256 +++++++++--------- .../Yancy/resources/public/yancy/app.js | 113 ++++---- .../yancy/auth/password/register.html.ep | 2 +- .../resources/templates/yancy/index.html.ep | 42 +-- lib/Yancy.pm | 2 +- lib/Yancy/Backend.pod | 60 ++-- lib/Yancy/Backend/Dbic.pm | 85 +++--- lib/Yancy/Backend/Mysql.pm | 16 +- lib/Yancy/Backend/Pg.pm | 16 +- lib/Yancy/Backend/Role/MojoAsync.pm | 14 +- lib/Yancy/Backend/Role/Relational.pm | 44 +-- lib/Yancy/Backend/Sqlite.pm | 26 +- lib/Yancy/Controller/Yancy.pm | 108 ++++---- lib/Yancy/Controller/Yancy/API.pm | 82 +++--- lib/Yancy/Controller/Yancy/MultiTenant.pm | 39 +-- lib/Yancy/Help/Config.pod | 106 ++++---- lib/Yancy/Help/Standalone.pod | 10 +- lib/Yancy/Plugin/Auth.pm | 16 +- lib/Yancy/Plugin/Auth/Basic.pm | 28 +- lib/Yancy/Plugin/Auth/Github.pm | 22 +- lib/Yancy/Plugin/Auth/Password.pm | 58 ++-- lib/Yancy/Plugin/Auth/Token.pm | 32 ++- lib/Yancy/Plugin/Form.pm | 14 +- lib/Yancy/Plugin/Form/Bootstrap4.pm | 2 +- lib/Yancy/Util.pm | 16 +- t/api.t | 9 +- t/backend/mock.t | 2 +- t/config.t | 10 +- t/controller/multi_tenant.t | 8 +- t/controller/yancy.t | 29 +- t/filter.t | 30 +- t/helpers.t | 10 +- t/lib/Local/Test.pm | 8 +- t/lib/Yancy/Backend/Test.pm | 10 +- t/share/openapi-spec.json | 4 +- 35 files changed, 704 insertions(+), 625 deletions(-) diff --git a/lib/Mojolicious/Plugin/Yancy.pm b/lib/Mojolicious/Plugin/Yancy.pm index eabcf8e2..6bd01af3 100644 --- a/lib/Mojolicious/Plugin/Yancy.pm +++ b/lib/Mojolicious/Plugin/Yancy.pm @@ -7,7 +7,7 @@ our $VERSION = '1.026'; use Mojolicious::Lite; plugin Yancy => { backend => 'pg://postgres@/mydb', - collections => { ... }, + schema => { ... }, }; ## With custom auth routine @@ -21,7 +21,7 @@ our $VERSION = '1.026'; } ); $app->plugin( 'Yancy', { backend => 'pg://postgres@/mydb', - collections => { ... }, + schema => { ... }, route => $auth_route, }); } @@ -81,7 +81,7 @@ This plugin adds some helpers for use in routes, templates, and plugins. my $config = $c->yancy->config; The current configuration for Yancy. Through this, you can edit the -C configuration as needed. +C configuration as needed. =head2 yancy.backend @@ -110,7 +110,7 @@ Yancy features and are found in the L namespace. use Mojolicious::Lite; plugin 'Yancy'; - app->yancy->plugin( 'Auth::Basic', { collection => 'users' } ); + app->yancy->plugin( 'Auth::Basic', { schema => 'users' } ); You can also add the Yancy::Plugin namespace into the default plugin lookup locations. This allows you to treat them like any other @@ -135,9 +135,9 @@ Yancy does not do this for you to avoid namespace collisions. =head2 yancy.list - my @items = $c->yancy->list( $collection, \%param, \%opt ); + my @items = $c->yancy->list( $schema, \%param, \%opt ); -Get a list of items from the backend. C<$collection> is a collection +Get a list of items from the backend. C<$schema> is a schema name. C<\%param> is a L. Some basic examples: @@ -167,9 +167,9 @@ access the backend methods directly. =head2 yancy.get - my $item = $c->yancy->get( $collection, $id ); + my $item = $c->yancy->get( $schema, $id ); -Get an item from the backend. C<$collection> is the collection name. +Get an item from the backend. C<$schema> is the schema name. C<$id> is the ID of the item to get. See L. This helper will filter out password values in the returned data. To get @@ -178,9 +178,9 @@ backend directly. =head2 yancy.set - $c->yancy->set( $collection, $id, $item_data, %opt ); + $c->yancy->set( $schema, $id, $item_data, %opt ); -Update an item in the backend. C<$collection> is the collection name. +Update an item in the backend. C<$schema> is the schema name. C<$id> is the ID of the item to update. C<$item_data> is a hash of data to update. See L. C<%opt> is a list of options with the following keys: @@ -209,9 +209,9 @@ backend object directly via L. =head2 yancy.create - my $item = $c->yancy->create( $collection, $item_data ); + my $item = $c->yancy->create( $schema, $item_data ); -Create a new item. C<$collection> is the collection name. C<$item_data> +Create a new item. C<$schema> is the schema name. C<$item_data> is a hash of data for the new item. See L. This helper will validate the data against the configuration and run any @@ -232,17 +232,17 @@ backend object directly via L. =head2 yancy.delete - $c->yancy->delete( $collection, $id ); + $c->yancy->delete( $schema, $id ); -Delete an item from the backend. C<$collection> is the collection name. +Delete an item from the backend. C<$schema> is the schema name. C<$id> is the ID of the item to delete. See L. =head2 yancy.validate - my @errors = $c->yancy->validate( $collection, $item, %opt ); + my @errors = $c->yancy->validate( $schema, $item, %opt ); Validate the given C<$item> data against the configuration for the -C<$collection>. If there are any errors, they are returned as an array +C<$schema>. If there are any errors, they are returned as an array of L objects. C<%opt> is a list of options with the following keys: @@ -271,11 +271,11 @@ at least three arguments: =over -=item * $name - The name of the collection/field being filtered +=item * $name - The name of the schema/field being filtered =item * $value - The value to filter, either the entire item, or a single field -=item * $conf - The configuration for the collection/field +=item * $conf - The configuration for the schema/field =item * @params - Other parameters if configured @@ -296,7 +296,7 @@ And you configure this on a field using C<< x-filter >> and C<< x-digest >>: # mysite.conf { - collections => { + schema => { users => { properties => { username => { type => 'string' }, @@ -327,7 +327,7 @@ The alternative configuration: # mysite.conf { - collections => { + schema => { users => { properties => { username => { type => 'string' }, @@ -341,21 +341,21 @@ The alternative configuration: }, } -Collections can also have filters. A collection filter will get the +Schemas can also have filters. A schema filter will get the entire hash reference as its value. For example, here's a filter that updates the C field with the current time: $c->yancy->filter->add( 'timestamp' => sub { - my ( $coll_name, $item, $coll_conf ) = @_; + my ( $schema_name, $item, $schema_conf ) = @_; $item->{last_updated} = time; return $item; } ); -And you configure this on the collection using C<< x-filter >>: +And you configure this on the schema using C<< x-filter >>: # mysite.conf { - collections => { + schema => { people => { 'x-filter' => [ 'timestamp' ], properties => { @@ -368,7 +368,7 @@ And you configure this on the collection using C<< x-filter >>: } You can configure filters on OpenAPI operations' inputs. These will -probably want to operate on hash-refs as in the collection-level filters +probably want to operate on hash-refs as in the schema-level filters above. The config passed will be an empty hash. The filter can be applied to either or both of the path, or the individual operation, and will be executed in that order. E.g.: @@ -431,7 +431,7 @@ This configuration will achieve the same as the above with C: # mysite.conf { - collections => { + schema => { people => { properties => { name => { type => 'string' }, @@ -463,7 +463,7 @@ This configuration will achieve the same as the above with C: # mysite.conf { - collections => { + schema => { people => { 'x-filter' => [ [ 'yancy.overlay_from_helper' => 'last_updated', 'current_time' ] @@ -518,13 +518,13 @@ operates inside-outward. =head2 yancy.filter.apply - my $filtered_data = $c->yancy->filter->apply( $collection, $item_data ); + my $filtered_data = $c->yancy->filter->apply( $schema, $item_data ); -Run the configured filters on the given C<$item_data>. C<$collection> is -a collection name. Returns the hash of C<$filtered_data>. +Run the configured filters on the given C<$item_data>. C<$schema> is +a schema name. Returns the hash of C<$filtered_data>. -The property-level filters will run before any collection-level filter, -so that collection-level filters can take advantage of any values set by +The property-level filters will run before any schema-level filter, +so that schema-level filters can take advantage of any values set by the inner filters. =head2 yancy.filters @@ -538,8 +538,8 @@ the code-refs. $c->yancy->schema( $name => $schema ); my $schemas = $c->yancy->schema; -Get or set the JSON schema for the given collection C<$name>. If no -collection name is given, returns a hashref of all the collections. +Get or set the JSON schema for the given schema C<$name>. If no +schema name is given, returns a hashref of all the schema. =head2 yancy.openapi @@ -605,7 +605,12 @@ sub register { # Create some safe copies of data structures we're about to mutate $config = { %$config }; - $config->{collections} &&= dclone $config->{collections}; + if ( my $schema = $config->{schema} || $config->{collections} ) { + $config->{schema} = dclone( $schema ); + } + if ( $config->{collections} ) { + warn '"collection" config key is now "schema"'; + } my $route = $config->{route} // $app->routes->any( '/yancy' ); $route->to( return_to => $config->{return_to} // '/' ); @@ -623,7 +628,7 @@ sub register { $app->helper( 'yancy.config' => sub { return $config } ); $app->helper( 'yancy.route' => sub { return $route } ); $app->helper( 'yancy.backend' => sub { - state $backend = load_backend( $config->{backend}, $config->{collections} || $config->{openapi}{definitions} ); + state $backend = load_backend( $config->{backend}, $config->{schema} || $config->{openapi}{definitions} ); } ); $app->helper( 'yancy.plugin' => \&_helper_plugin ); @@ -701,48 +706,49 @@ sub register { action => 'index', ); - die "Cannot pass both openapi AND (collections or read_schema)" + die "Cannot pass both openapi AND (schema or read_schema)" if $config->{openapi} - and ( $config->{collections} or $config->{read_schema} ); + and ( $config->{schema} or $config->{read_schema} ); my $spec; if ( $config->{openapi} ) { $spec = $config->{openapi}; - $config->{collections} = $spec->{definitions}; # for yancy.backend etc + $config->{schema} = $spec->{definitions}; # for yancy.backend etc } else { # Merge configuration if ( $config->{read_schema} ) { my $schema = $app->yancy->backend->read_schema; for my $c ( keys %$schema ) { - _merge_schema( $config->{collections}{ $c } ||= {}, $schema->{ $c } ); + _merge_schema( $config->{schema}{ $c } ||= {}, $schema->{ $c } ); } - # ; say 'Merged Config'; - # ; use Data::Dumper; - # ; say Dumper $config; } - # read_schema on collections - for my $schema_name ( keys %{ $config->{collections} } ) { - my $schema = $config->{collections}{ $schema_name }; + # read_schema on schema + for my $schema_name ( keys %{ $config->{schema} } ) { + my $schema = $config->{schema}{ $schema_name }; if ( delete $schema->{read_schema} ) { _merge_schema( $schema, $app->yancy->backend->read_schema( $schema_name ) ); } } + # ; warn 'Merged Schema'; + # ; use Data::Dumper; + # ; warn Dumper $config->{schema}; + # Sanity check for the schema. - for my $coll ( keys %{ $config->{collections} } ) { - my $schema = $config->{collections}{ $coll }; - next if $schema->{ 'x-ignore' }; # XXX Should we just delete x-ignore collections? + for my $schema_name ( keys %{ $config->{schema} } ) { + my $schema = $config->{schema}{ $schema_name }; + next if $schema->{ 'x-ignore' }; # XXX Should we just delete x-ignore schema? $schema->{ type } //= 'object'; - my $real_coll = ( $schema->{'x-view'} || {} )->{collection} // $coll; + my $real_schema_name = ( $schema->{'x-view'} || {} )->{schema} // $schema_name; my $props = $schema->{properties} - || $config->{collections}{ $real_coll }{properties}; + || $config->{schema}{ $real_schema_name }{properties}; my $id_field = $schema->{ 'x-id-field' } // 'id'; if ( !$props->{ $id_field } ) { - die sprintf "ID field missing in properties for collection '%s', field '%s'." + die sprintf "ID field missing in properties for schema '%s', field '%s'." . " Add x-id-field to configure the correct ID field name, or" - . " add x-ignore to ignore this collection.", - $coll, $id_field; + . " add x-ignore to ignore this schema.", + $schema_name, $id_field; } } @@ -774,10 +780,10 @@ sub _ensure_json_data { decode_json $app->home->child( $data )->slurp; } -sub _openapi_find_collection_name { +sub _openapi_find_schema_name { my ( $self, $path, $pathspec ) = @_; - return $pathspec->{'x-collection'} if $pathspec->{'x-collection'}; - my $collection; + return $pathspec->{'x-schema'} if $pathspec->{'x-schema'}; + my $schema_name; for my $method ( grep !/^(parameters$|x-)/, keys %{ $pathspec } ) { my $op_spec = $pathspec->{ $method }; my $schema; @@ -799,15 +805,15 @@ sub _openapi_find_collection_name { ( $schema->{items} && $schema->{items}{'$ref'} ) || ( $schema->{properties} && $schema->{properties}{items} && $schema->{properties}{items}{'$ref'} ); next unless $this_ref =~ s:^#/definitions/::; - die "$method '$path' = $this_ref but also '$collection'" - if $this_ref and $collection and $this_ref ne $collection; - $collection = $this_ref; + die "$method '$path' = $this_ref but also '$schema_name'" + if $this_ref and $schema_name and $this_ref ne $schema_name; + $schema_name = $this_ref; } - if ( !$collection ) { - ($collection) = $path =~ m#^/([^/]+)#; - die "No collection found in '$path'" if !$collection; + if ( !$schema_name ) { + ($schema_name) = $path =~ m#^/([^/]+)#; + die "No schema found in '$path'" if !$schema_name; } - $collection; + $schema_name; } # mutates $spec @@ -815,14 +821,14 @@ sub _openapi_spec_add_mojo { my ( $self, $spec, $config ) = @_; for my $path ( keys %{ $spec->{paths} } ) { my $pathspec = $spec->{paths}{ $path }; - my $collection = $self->_openapi_find_collection_name( $path, $pathspec ); - die "Path '$path' had non-existent collection '$collection'" - if !$spec->{definitions}{$collection}; + my $schema = $self->_openapi_find_schema_name( $path, $pathspec ); + die "Path '$path' had non-existent schema '$schema'" + if !$spec->{definitions}{$schema}; for my $method ( grep !/^(parameters$|x-)/, keys %{ $pathspec } ) { my $op_spec = $pathspec->{ $method }; my $mojo = $self->_openapi_spec_infer_mojo( $path, $pathspec, $method, $op_spec ); $mojo->{controller} = $config->{api_controller}; - $mojo->{collection} = $collection; + $mojo->{schema} = $schema; my @filters = ( @{ $pathspec->{ 'x-filter' } || [] }, @{ $op_spec->{ 'x-filter' } || [] }, @@ -859,7 +865,7 @@ sub _openapi_spec_infer_mojo { }; } else { - # per-collection - GET = "list" + # per-schema - GET = "list" return { action => 'list_items', }; @@ -910,23 +916,23 @@ sub _openapi_spec_from_schema { description => 'How to sort the list. A string containing one of "asc" (to sort in ascending order) or "desc" (to sort in descending order), followed by a ":", followed by the field name to sort by.', }, ); - for my $coll ( keys %{ $config->{collections} } ) { + for my $schema_name ( keys %{ $config->{schema} } ) { # Set some defaults so users don't have to type as much - my $schema = $config->{collections}{ $coll }; + my $schema = $config->{schema}{ $schema_name }; next if $schema->{ 'x-ignore' }; my $id_field = $schema->{ 'x-id-field' } // 'id'; - my $real_coll = ( $schema->{'x-view'} || {} )->{collection} // $coll; + my $real_schema_name = ( $schema->{'x-view'} || {} )->{schema} // $schema_name; my $props = $schema->{properties} - || $config->{collections}{ $real_coll }{properties}; + || $config->{schema}{ $real_schema_name }{properties}; my %props = %$props; - $definitions{ $coll } = $schema; + $definitions{ $schema_name } = $schema; for my $prop ( keys %props ) { $props{ $prop }{ type } ||= 'string'; } - $paths{ '/' . $coll } = { + $paths{ '/' . $schema_name } = { get => { parameters => [ { '$ref' => '#/parameters/%24limit' }, @@ -954,7 +960,7 @@ sub _openapi_spec_from_schema { items => { type => 'array', description => 'This page of items', - items => { '$ref' => "#/definitions/" . url_escape $coll }, + items => { '$ref' => "#/definitions/" . url_escape $schema_name }, }, }, }, @@ -971,7 +977,7 @@ sub _openapi_spec_from_schema { name => "newItem", in => "body", required => true, - schema => { '$ref' => "#/definitions/" . url_escape $coll }, + schema => { '$ref' => "#/definitions/" . url_escape $schema_name }, }, ], responses => { @@ -979,7 +985,7 @@ sub _openapi_spec_from_schema { description => "Entry was created", schema => { '$ref' => sprintf "#/definitions/%s/properties/%s", - map { url_escape $_ } $coll, $id_field, + map { url_escape $_ } $schema_name, $id_field, }, }, default => { @@ -990,7 +996,7 @@ sub _openapi_spec_from_schema { }), }; - $paths{ sprintf '/%s/{%s}', $coll, $id_field } = { + $paths{ sprintf '/%s/{%s}', $schema_name, $id_field } = { parameters => [ { name => $id_field, @@ -1007,7 +1013,7 @@ sub _openapi_spec_from_schema { responses => { 200 => { description => "Item details", - schema => { '$ref' => "#/definitions/" . url_escape $coll }, + schema => { '$ref' => "#/definitions/" . url_escape $schema_name }, }, default => { description => "Unexpected error", @@ -1023,13 +1029,13 @@ sub _openapi_spec_from_schema { name => "newItem", in => "body", required => true, - schema => { '$ref' => "#/definitions/" . url_escape $coll }, + schema => { '$ref' => "#/definitions/" . url_escape $schema_name }, } ], responses => { 200 => { description => "Item was updated", - schema => { '$ref' => "#/definitions/" . url_escape $coll }, + schema => { '$ref' => "#/definitions/" . url_escape $schema_name }, }, default => { description => "Unexpected error", @@ -1127,21 +1133,21 @@ sub _helper_plugin { sub _helper_schema { my ( $c, $name, $schema ) = @_; if ( !$name ) { - return $c->yancy->config->{collections}; + return $c->yancy->config->{schema}; } if ( $schema ) { - $c->yancy->config->{collections}{ $name } = $schema; + $c->yancy->config->{schema}{ $name } = $schema; return; } - return copy_inline_refs( $c->yancy->config->{collections}, "/$name" ); + return copy_inline_refs( $c->yancy->config->{schema}, "/$name" ); } sub _helper_list { - my ( $c, $coll_name, @args ) = @_; - my @items = @{ $c->yancy->backend->list( $coll_name, @args )->{items} }; - my $coll = $c->yancy->schema( $coll_name ); - for my $prop_name ( keys %{ $coll->{properties} } ) { - my $prop = $coll->{properties}{ $prop_name }; + my ( $c, $schema_name, @args ) = @_; + my @items = @{ $c->yancy->backend->list( $schema_name, @args )->{items} }; + my $schema = $c->yancy->schema( $schema_name ); + for my $prop_name ( keys %{ $schema->{properties} } ) { + my $prop = $schema->{properties}{ $prop_name }; if ( $prop->{format} && $prop->{format} eq 'password' ) { delete $_->{ $prop_name } for @items; } @@ -1150,11 +1156,11 @@ sub _helper_list { } sub _helper_get { - my ( $c, $coll_name, $id, @args ) = @_; - my $item = $c->yancy->backend->get( $coll_name, $id, @args ); - my $coll = $c->yancy->schema( $coll_name ); - for my $prop_name ( keys %{ $coll->{properties} } ) { - my $prop = $coll->{properties}{ $prop_name }; + my ( $c, $schema_name, $id, @args ) = @_; + my $item = $c->yancy->backend->get( $schema_name, $id, @args ); + my $schema = $c->yancy->schema( $schema_name ); + for my $prop_name ( keys %{ $schema->{properties} } ) { + my $prop = $schema->{properties}{ $prop_name }; if ( $prop->{format} && $prop->{format} eq 'password' ) { delete $item->{ $prop_name }; } @@ -1168,25 +1174,25 @@ sub _helper_delete { } sub _helper_set { - my ( $c, $coll, $id, $item, %opt ) = @_; + my ( $c, $schema, $id, $item, %opt ) = @_; my %validate_opt = map { $_ => $opt{ $_ } } grep { exists $opt{ $_ } } qw( properties ); - if ( my @errors = $c->yancy->validate( $coll, $item, %validate_opt ) ) { + if ( my @errors = $c->yancy->validate( $schema, $item, %validate_opt ) ) { $c->app->log->error( - sprintf 'Error validating item with ID "%s" in collection "%s": %s', - $id, $coll, + sprintf 'Error validating item with ID "%s" in schema "%s": %s', + $id, $schema, join ', ', map { sprintf '%s (%s)', $_->{message}, $_->{path} // '/' } @errors ); die \@errors; } - $item = $c->yancy->filter->apply( $coll, $item ); - my $ret = eval { $c->yancy->backend->set( $coll, $id, $item ) }; + $item = $c->yancy->filter->apply( $schema, $item ); + my $ret = eval { $c->yancy->backend->set( $schema, $id, $item ) }; if ( $@ ) { $c->app->log->error( - sprintf 'Error setting item with ID "%s" in collection "%s": %s', - $id, $coll, $@, + sprintf 'Error setting item with ID "%s" in schema "%s": %s', + $id, $schema, $@, ); die $@; } @@ -1194,28 +1200,28 @@ sub _helper_set { } sub _helper_create { - my ( $c, $coll, $item ) = @_; + my ( $c, $schema, $item ) = @_; - my $props = $c->yancy->schema( $coll )->{properties}; + my $props = $c->yancy->schema( $schema )->{properties}; $item->{ $_ } = $props->{ $_ }{default} for grep !exists $item->{ $_ } && exists $props->{ $_ }{default}, keys %$props; - if ( my @errors = $c->yancy->validate( $coll, $item ) ) { + if ( my @errors = $c->yancy->validate( $schema, $item ) ) { $c->app->log->error( - sprintf 'Error validating new item in collection "%s": %s', - $coll, + sprintf 'Error validating new item in schema "%s": %s', + $schema, join ', ', map { sprintf '%s (%s)', $_->{message}, $_->{path} // '/' } @errors ); die \@errors; } - $item = $c->yancy->filter->apply( $coll, $item ); - my $ret = eval { $c->yancy->backend->create( $coll, $item ) }; + $item = $c->yancy->filter->apply( $schema, $item ); + my $ret = eval { $c->yancy->backend->create( $schema, $item ) }; if ( $@ ) { $c->app->log->error( - sprintf 'Error creating item in collection "%s": %s', - $coll, $@, + sprintf 'Error creating item in schema "%s": %s', + $schema, $@, ); die $@; } @@ -1223,10 +1229,10 @@ sub _helper_create { } sub _helper_validate { - my ( $c, $coll, $item, %opt ) = @_; + my ( $c, $schema_name, $item, %opt ) = @_; state $validator = {}; - my $schema = $c->yancy->schema( $coll ); - my $v = $validator->{ $coll } ||= _build_validator( $schema ); + my $schema = $c->yancy->schema( $schema_name ); + my $v = $validator->{ $schema } ||= _build_validator( $schema ); my @args; if ( $opt{ properties } ) { @@ -1272,28 +1278,28 @@ sub _helper_validate { } sub _helper_filter_apply { - my ( $self, $c, $coll_name, $item ) = @_; - my $coll = $c->yancy->schema( $coll_name ); + my ( $self, $c, $schema_name, $item ) = @_; + my $schema = $c->yancy->schema( $schema_name ); my $filters = $self->_filters; - for my $key ( keys %{ $coll->{properties} } ) { - next unless my $prop_filters = $coll->{properties}{ $key }{ 'x-filter' }; + for my $key ( keys %{ $schema->{properties} } ) { + next unless my $prop_filters = $schema->{properties}{ $key }{ 'x-filter' }; for my $filter ( @{ $prop_filters } ) { ( $filter, my @params ) = @$filter if ref $filter eq 'ARRAY'; my $sub = $filters->{ $filter }; - die "Unknown filter: $filter (collection: $coll_name, field: $key)" + die "Unknown filter: $filter (schema: $schema_name, field: $key)" unless $sub; $item = { %$item, $key => $sub->( - $key, $item->{ $key }, $coll->{properties}{ $key }, @params + $key, $item->{ $key }, $schema->{properties}{ $key }, @params ) }; } } - if ( my $coll_filters = $coll->{'x-filter'} ) { - for my $filter ( @{ $coll_filters } ) { + if ( my $schema_filters = $schema->{'x-filter'} ) { + for my $filter ( @{ $schema_filters } ) { ( $filter, my @params ) = @$filter if ref $filter eq 'ARRAY'; my $sub = $filters->{ $filter }; - die "Unknown filter: $filter (collection: $coll_name)" + die "Unknown filter: $filter (schema: $schema_name)" unless $sub; - $item = $sub->( $coll_name, $item, $coll, @params ); + $item = $sub->( $schema_name, $item, $schema, @params ); } } return $item; diff --git a/lib/Mojolicious/Plugin/Yancy/resources/public/yancy/app.js b/lib/Mojolicious/Plugin/Yancy/resources/public/yancy/app.js index c0897eb0..2fbe5dac 100644 --- a/lib/Mojolicious/Plugin/Yancy/resources/public/yancy/app.js +++ b/lib/Mojolicious/Plugin/Yancy/resources/public/yancy/app.js @@ -225,9 +225,9 @@ var app = new Vue({ data: function () { var current = this.parseHash(); return { - hasCollections: null, - currentCollection: current.collection || null, - collections: {}, + hasSchema: null, + currentSchemaName: current.schema || null, + schema: {}, openedRow: null, deleteIndex: null, addingItem: false, @@ -300,8 +300,8 @@ var app = new Vue({ this.fetchPage(); }, - setCollection: function ( name ) { - this.currentCollection = name; + setSchema: function ( name ) { + this.currentSchemaName = name; $( '#sidebar-collapse' ).collapse('hide'); }, @@ -316,9 +316,9 @@ var app = new Vue({ }, parseSpec: function ( spec ) { - var pathParts = [], collectionName, collection, pathObj, firstCollection; - this.collections = {}; - this.hasCollections = false; + var pathParts = [], schemaName, schema, pathObj, firstSchema; + this.schema = {}; + this.hasSchema = false; // Preprocess definitions for ( var defKey in spec.definitions ) { @@ -372,74 +372,73 @@ var app = new Vue({ pathObj = spec.paths[ pathKey ]; pathParts = pathKey.split( '/' ); - collectionName = pathParts[1]; + schemaName = pathParts[1]; - // Skip hidden collections - if ( spec.definitions[ collectionName ]['x-hidden'] ) { + // Skip hidden schemas + if ( spec.definitions[ schemaName ]['x-hidden'] ) { continue; } - collection = this.collections[ collectionName ]; - if ( !collection ) { - collection = this.collections[ collectionName ] = { + schema = this.schema[ schemaName ]; + if ( !schema ) { + schema = this.schema[ schemaName ] = { operations: { } }; } - if ( !firstCollection ) { - firstCollection = collectionName; + if ( !firstSchema ) { + firstSchema = schemaName; } - this.hasCollections = true; + this.hasSchema = true; // Array operations if ( pathParts.length == 2 ) { if ( pathObj.get ) { - collection.operations["list"] = { + schema.operations["list"] = { url: [ spec.basePath, pathKey ].join(''), - schema: spec.definitions[ collectionName ] + schema: spec.definitions[ schemaName ] }; } if ( pathObj.post ) { - collection.operations["add"] = { + schema.operations["add"] = { url: [ spec.basePath, pathKey ].join(''), - schema: spec.definitions[ collectionName ] + schema: spec.definitions[ schemaName ] }; } } // Item operations else { if ( pathObj.get ) { - collection.operations["get"] = { + schema.operations["get"] = { url: [ spec.basePath, pathKey ].join(''), - schema: spec.definitions[ collectionName ] + schema: spec.definitions[ schemaName ] }; } if ( pathObj.put ) { - collection.operations["set"] = { + schema.operations["set"] = { url: [ spec.basePath, pathKey ].join(''), - schema: spec.definitions[ collectionName ] + schema: spec.definitions[ schemaName ] }; } if ( pathObj.delete ) { - collection.operations["delete"] = { + schema.operations["delete"] = { url: [ spec.basePath, pathKey ].join(''), - schema: spec.definitions[ collectionName ] + schema: spec.definitions[ schemaName ] }; } } } - if ( this.currentCollection && this.collections[ this.currentCollection ] ) { + if ( this.currentSchemaName && this.schema[ this.currentSchemaName ] ) { this.fetchPage(); } else { - this.currentCollection = firstCollection; + this.currentSchemaName = firstSchema; } }, fetchPage: function () { if ( this.fetching ) return; - var coll = this.collections[ this.currentCollection ], - self = this, + var self = this, query = { $limit: this.perPage, $offset: this.perPage * ( this.currentPage - 1 ) @@ -455,7 +454,7 @@ var app = new Vue({ this.fetching = true; delete this.error.fetchPage; - $.get( coll.operations["list"].url, query ).done( + $.get( this.currentOperations["list"].url, query ).done( function ( data, status, jqXHR ) { if ( query.offset > data.total ) { // We somehow got to a page that doesn't exist, @@ -468,7 +467,7 @@ var app = new Vue({ self.items = data.items; self.total = data.total; - self.columns = self.getListColumns( self.currentCollection ), + self.columns = self.getListColumns( self.currentSchemaName ), self.fetching = false; self.updateHash(); } @@ -479,9 +478,8 @@ var app = new Vue({ ); }, - getListColumns: function ( collName ) { - var coll = this.collections[ collName ], - schema = coll.operations["list"].schema, + getListColumns: function ( schemaName ) { + var schema = this.schema[ schemaName ].operations["list"].schema, props = schema.properties, columns = schema['x-list-columns'] || []; return columns.map( function (c) { @@ -494,23 +492,22 @@ var app = new Vue({ parseHash: function () { var parts = location.hash.split( '/' ), - collection = parts[1], + schema = parts[1], page = parts[2]; return { - collection: collection, + schema: schema, page: page }; }, updateHash: function () { - location.hash = "/" + this.currentCollection + "/" + this.currentPage; + location.hash = "/" + this.currentSchemaName + "/" + this.currentPage; }, saveItem: function (i) { var self = this, - coll = this.collections[ this.currentCollection ], - value = this.prepareSaveItem( this.items[i], coll.operations['set'].schema ), - url = this.fillUrl( coll.operations['set'].url, this.openedOldValue ); + value = this.prepareSaveItem( this.items[i], this.currentOperations['set'].schema ), + url = this.fillUrl( this.currentOperations['set'].url, this.openedOldValue ); delete this.error.saveItem; this.$set( this, 'formError', {} ); $.ajax( @@ -541,9 +538,9 @@ var app = new Vue({ addItem: function () { var self = this, - coll = this.collections[ this.currentCollection ], - value = this.prepareSaveItem( this.newItem, coll.operations['add'].schema ), - url = coll.operations['add'].url; + schema = this.currentSchema, + value = this.prepareSaveItem( this.newItem, schema ), + url = this.currentOperations['add'].url; delete this.error.addItem; this.$set( this, 'formError', {} ); $.ajax( @@ -555,13 +552,13 @@ var app = new Vue({ } ).done( function ( data, status, jqXHR ) { - var id = coll.operations['add'].schema['x-id-field'] || 'id'; + var id = self.currentOperations['add'].schema['x-id-field'] || 'id'; var urlParams = {}; urlParams[ id ] = data; $.ajax( { - url: self.fillUrl( coll.operations['get'].url, urlParams ), + url: self.fillUrl( self.currentOperations['get'].url, urlParams ), method: 'GET', dataType: 'json' } @@ -654,7 +651,7 @@ var app = new Vue({ }, createBlankItem: function () { - var schema = this.collections[ this.currentCollection ].operations['add'].schema, + var schema = this.currentSchema, item = {}; if ( schema.example ) { return schema.example; @@ -678,9 +675,9 @@ var app = new Vue({ deleteItem: function () { var i = this.deleteIndex, self = this, - coll = this.collections[ this.currentCollection ], + schema = this.currentSchema, value = $( '#data-' + i ).val(), - url = this.fillUrl( coll.operations['delete'].url, this.items[i] ); + url = this.fillUrl( this.currentOperations['delete'].url, this.items[i] ); $.ajax( { url: url, @@ -712,8 +709,8 @@ var app = new Vue({ }, renderValue: function ( field, value ) { - var coll = this.schema; - var fieldType = coll.properties[ field ].type; + var schema = this.currentSchema; + var fieldType = schema.properties[ field ].type; var type = Array.isArray( fieldType ) ? fieldType[0] : fieldType; if ( type == 'boolean' ) { return value ? 'Yes' : 'No'; @@ -722,7 +719,7 @@ var app = new Vue({ }, rowViewUrl: function ( data ) { - return this.fillUrl( this.schema[ 'x-view-item-url' ], data ); + return this.fillUrl( this.currentSchema[ 'x-view-item-url' ], data ); }, addToast: function ( toast ) { @@ -766,15 +763,15 @@ var app = new Vue({ return pages; }, - operations: function () { - return this.collections[ this.currentCollection ] ? this.collections[ this.currentCollection ].operations : {}; + currentOperations: function () { + return this.schema[ this.currentSchemaName ] ? this.schema[ this.currentSchemaName ].operations : {}; }, - schema: function () { - return this.operations.get ? this.operations.get.schema : {}; + currentSchema: function () { + return this.currentOperations.get ? this.currentOperations.get.schema : {}; } }, watch: { - currentCollection: function () { + currentSchemaName: function () { this.data = []; this.currentPage = 1; this.openedRow = null; diff --git a/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/auth/password/register.html.ep b/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/auth/password/register.html.ep index fa61d4fc..76ecde6d 100644 --- a/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/auth/password/register.html.ep +++ b/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/auth/password/register.html.ep @@ -39,7 +39,7 @@ % } % else { - %= app->yancy->form->field_for( $plugin->collection, $field ) + %= app->yancy->form->field_for( $plugin->schema, $field ) % } % } diff --git a/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/index.html.ep b/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/index.html.ep index 0bd44509..02f3af4a 100644 --- a/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/index.html.ep +++ b/lib/Mojolicious/Plugin/Yancy/resources/templates/yancy/index.html.ep @@ -169,7 +169,7 @@
-