# PODNAME: Yancy::Help::Config # ABSTRACT: How to configure Yancy __END__ =pod =head1 NAME Yancy::Help::Config - How to configure Yancy =head1 VERSION version 1.069 =head1 SYNOPSIS use Mojolicious::Lite; plugin Yancy => { backend => 'pg://localhost/myapp', read_schema => 1, schema => { users => { title => 'Users', description => 'The authorized user accounts', }, }, }; =head1 DESCRIPTION This document describes all of the configuration available for Yancy. When using the Yancy L plugin, these values are given as a hash reference argument to the plugin. See L for some plugin-specific configuration values. When using the Yancy standalone mode, these values are defined in a C file which is parsed as a Perl hash reference. See L for more information about running the standalone app. =head1 Database Backend The C URL defines what database to use and how to connect to it. Each backend has its own format of URL, and some examples are shown below. See your backend's documentation for more information. =over =item L # Backend URL backend => 'pg://user@example.com/mydb', # Backend hash backend => { Pg => { dsn => 'dbi:Pg:dbname', username => 'fry', password => 'b3nd3r1sgr34t', }, } =item L # Backend URL backend => 'mysql://user@localhost/mydb', # Backend hash backend => { Mysql => { dsn => 'dbi:mysql:mydb', username => 'fry', password => 'b3nd3r1sgr34t', }, } =item L # Backend URL backend => 'sqlite:filename.db', # Backend hash backend => { Sqlite => { dsn => 'sqlite:data.db', }, } =item L # Backend URL backend => 'dbic://My::Schema/dbi:SQLite:file.db', # Backend arrayref (passed to Schema->connect() method) backend => { Dbic => [ 'My::Schema', 'dbi:SQLite:mysql.db', undef, undef, { PrintError => 1 }, ], } =back =head1 Schema The C data structure defines what data is in the database. Each key in this structure refers to the name of a schema, and the value describe the fields for items inside the schema. Each backend may define a schema differently. For a relational database like Postgres or MySQL, a schema is a table, and the fields are columns. For an ORM like DBIx::Class, the schemas are ResultSet objects. For a document store like MongoDB, the schemas are collections. See your backend's documentation for more information. Schemas are configured using L. The JSON Schema defines what fields (properties) an item has, and what type of data those field have. The JSON Schema also can define constraints like required fields or validate strings with regular expressions. The schema can also contain metadata like a C, C<description>, and even an C<example> value. For more information on what can be defined, see L<the docs on JSON Schema|http://json-schema.org>. For a schema named C<people> that has 3 fields (an integer C<id> and two strings, C<name> and C<email>), a minimal JSON schema will look like this: schema => { people => { properties => { id => { type => 'integer', readOnly => 1, }, name => { type => 'string', }, email => { type => 'string', }, }, }, }, By default, Yancy will read your database to fill in as much schema information as it can. This includes the field C<type>, field order (C<x-order>), enumerated values (C<enum>), required fields (C<required>), ID fields (C<x-id-field>), foreign keys (C<x-foreign-key>), and some C<format> (date/time mostly). You can (and should) add your own annotations and corrections while configuring Yancy. The C<schema> configuration will be merged with the information Yancy reads from the database, with the configuration overriding the defaults from the database. =head2 Types Yancy generates input elements based on the C<type>, and C<format> of the object's properties. =over =item * C<< type => "boolean" >> - A Yes/No field. Boolean fields support input values C<0>, C<1>, C<"true">, and C<"false">. They will be stored as C<0>, and C<1> in the database. =item * C<< type => "integer" >> - A number field (C<< <input type="number" > >>) =item * C<< type => "number" >> - A number field (C<< <input type="number" > >>) =item * C<< type => "string", format => "date" >> - A date field (C<< <input type="date"> >>) =item * C<< type => "string", format => "date-time" >> - A date/time field (C<< <input type="datetime-local"> >>) Date/time fields can have a special C<default> value: C<now>. This will be replaced with the current date/time in the database. =item * C<< type => "string", format => "email" >> - A e-mail address (C<< <input type="email"> >>) =item * C<< type => "string", format => "url" >> - A URL input (C<< <input type="url"> >>) =item * C<< type => "string", format => "tel" >> - A telephone number (C<< <input type="tel"> >>) =item * C<< type => "string", format => "textarea" >> - A multiline text field (C<< <textarea> >>) =item * C<< type => "string", format => "markdown" >> - A Markdown field that shows a live preview of the rendered HTML. The Markdown can be saved as HTML in another field by adding C<< x-html-field => $field_name >> to that field. =item * C<< enum => [...], type => "..." >> - A C<< <select> >> element. This can be of any type. =item * C<< type => "string", format => "filepath" >> - A file upload field (C<< <input type="file"> >>). See L<Yancy::Plugin::File> for more information. =item * C<< type => "string", format => "binary" >> - A field containing binary data. This currently does not generate any input field, but it may become another way to upload files in the future. =back JSON schemas allow specifying multiple types for a field using an array. If a field has multiple types, the generated form will use the first type to decide what kind of field to display. =head2 Field Configuration Other schema attributes will be translated as necessary to the HTML input fields: =over =item * C<title> will be used to label the input field =item * C<description> will be placed near the input field to explain it =item * C<readOnly> will set the input field as read-only =item * C<pattern> for string fields, a string that can be used as a regex, like C<< pattern => '^foo-\d+$' >>. =item * C<minimum> for numeric fields, the minimum value =item * C<maximum> for numeric fields, the maximum value =item * C<minLength> for string fields, the minimum length =item * C<maxLength> for string fields, the maximum length =back =head2 Required Values JSON Schema allows marking properties as required using the C<required> property, which must be an array of property names. schema => { people => { required => [ 'name', 'email' ], properties => { id => { type => 'integer', readOnly => 1, }, name => { type => 'string', }, email => { type => 'string', }, }, }, }, Required values will be marked as such in the HTML. =head2 Nullable Values If a value can be C<null> (C<undef> in Perl terms) in addition to its declared type (C<string>, C<integer>, etc...), you must add it to the C<type> field by using an array of types: schema => { people => { required => [ 'name' ], properties => { id => { type => 'integer', readOnly => 1, }, name => { type => 'string', # Required and must be a string }, email => { type => [ 'string', 'null' ], # Can be null }, }, }, }, If you don't do this, and still include the field in an object, you will get an error: C<Expected string - Got null.>. The correct way to fix this error is to add C<null> as an option for the field's type. =head2 Extended Collection Configuration There are some extended fields you can add to your schema definition to control how it is treated by Yancy. =over =item title A friendly title for the schema =item description A description of the schema. Markdown will be parsed into HTML. You can use the C<trim> and C<unindent> functions from L<Mojo::Util> to allow indenting your schema description: use Mojolicious::Lite; use Mojo::Util qw( unindent trim ); plugin Yancy => { schema => { employees => { description => unindent( trim q{ The employees of Planet Express. * [View the employee health plan](/decapod-life) * [Latest Good News](/news) } ), }, }, }; =item x-hidden If this is true, the schema will be hidden from the list in the Yancy web app. This does not prevent using the API to edit this data. =item x-ignore Ignore this schema: Do not add it to the API, do not show it in the rich editing form. This is for schema that should not be edited from the Yancy form or the Yancy API. This allows for removing schema when using L</read_schema>. =item x-id-field This key sets the name of the schema's ID field to use to uniquely identify individual items. By default, Yancy assumes the ID field is named C<id>. If your schema uses some other identifier (e-mail address or username for example), you should set this configuration key. people => { 'x-id-field' => 'email', properties => { ... }, }, This field can be any unique identifier, but it will be the ID that Yancy uses for all of its operations. =item x-list-columns This key should be an array of columns to display on the list view, in order. This helps put useful information on the list page. people => { 'x-list-columns' => [ 'name', 'email' ], properties => { ... }, }, Instead of field names, columns can also be made out of templates using a hash with C<title> and C<template> keys. Inside the template key, use fields from the row with C<{field}>, like so: people => { 'x-list-columns' => [ { title => "Person", template => '{name} <{email}>' }, ], }, =item x-filter This key is an array of filter names to run when setting or creating an item. Filters can allow for hashing passwords, for example. Filters are added by plugins or during configuration of L<Mojolicious::Plugin::Yancy>. See L<Mojolicious::Plugin::Yancy/yancy.filter.add> for how to create a filter in your app. Instead of a filter name, you can provide an array. The first member will be the name, and any further members will be passed to the filter code-ref as parameters after the mandatory three. =item x-view-url A URL to view the schema in the application. Will be shown as a button in the editor. =item x-view-item-url A URL to view the items in the schema. Will be shown as an icon next to the item row. Add data from the row in the url using C<{field}>, like so: # /people/1 /people/{id} # /user/preaction /user/{username} =back =head2 Extended Field Configuration There are some extended fields you can add to a field configuration to control how it is treated by Yancy. =over =item title A friendly title for the field =item description A description of the field. Markdown will be parsed into HTML. =item default The default value for the field, if the field is missing or C<undef> (C<null> in JavaScript). =item x-foreign-key If provided, this field is a foreign key linking to the given schema. This field must link to the ID field of the other schema. schema => { user => { 'x-id-field' => 'username', properties => { username => { type => 'string', }, }, }, comment => { properties => { username => { type => 'string', 'x-foreign-key' => 'user', }, }, }, }, By default, the target schema's first list column (if C<x-list-columns> is defined) or the schema's ID field is used to show the current value of the relationship. This can be changed by setting C<x-display-field> to the field in the target schema you want to use. By default, the target schema's ID field (C<x-id-field> or C<id>) will be used as the value for the foreign key. This can be changed by setting C<x-value-field> to the field in the target schema you want to use. B<NOTE:> This support is experimental and will need further development to support more possibilities of foreign key linkages. Patches appreciated! =item x-hidden If true, thie field will be hidden from the rich editing form. This is for schema that you want to use from the API but do not want to edit from the Yancy application. =item x-order Set the order of the fields in the edit form by assigning a number to the C<x-order> property. Fields in the form are be sorted by their C<x-order>, and then by their name (alphabetically). Fields that do not have C<x-order> set will be sorted after fields that do. =item x-filter This key is an array of filter names to run on the field when setting or creating an item. Filters can allow for hashing passwords, for example. Filters are added by plugins or during configuration of L<Mojolicious::Plugin::Yancy>. See L<Mojolicious::Plugin::Yancy/yancy.filter.add> for how to create a filter in your app. Instead of a filter name, you can provide an array. The first member will be the name, and any further members will be passed to the filter code-ref as parameters after the mandatory three. =item x-view # to get a data-light "view" of users when listing comments end of blogpost usermini => { type => 'object', 'x-view' => { schema => 'user' }, properties => { id => { 'x-order' => 1, type => 'integer' }, username => { 'x-order' => 2, type => 'string' }, }, }, This key means the schema is not a real one that exists in the backend, but a strict subset of a real one. It is an object with keys: =over =item schema Mandatory. Names the "real" schema. B<NB> This is the schema's text name, not a JSON pointer. =back All the properties' types will need to be the same as on the "real" one since the datasource will be the real one. If no properties are given, the "real" schema's ones will be used. The generated OpenAPI spec will only have read functionality for the "view" schema, not mutations. =back =head1 OpenAPI specification =head2 Generation An OpenAPI spec will be generated from the C<schema> specified as above, by using the C<schema> value as the C</definitions> of the spec. A default type called C<_Error> will be added, to act as the return type in case of error. From this, the C</paths> will be generated by adding one for each of CRUD (create, read, update, delete) plus a "list", to each schema. Finally, for each operation generated under C</paths>, an C<x-mojo-to> will be added, to connect it to the right controller. See L<Mojolicious::Plugin::OpenAPI::Guides::Tutorial> for more information. =head2 Passing in complete As an alternative to supplying only the C<schema> (and/or a true value for C<read_schema>), you can pass a complete OpenAPI spec as C<openapi>. It is an error to pass both C<schema> and C<openapi>. The spec will then have C<x-mojo-to> added to each operation, using inferences based on the HTTP method. The C</definitions> of the spec will be used as the C<schema> property of the L<backend|Yancy::Backend>. All of the operations on each path under C</paths> of the spec must, in line with REST convention, refer to only a single "schema". If any path has operations referring to more than one schema, that is an error. To derive which schema, these things are considered: =over =item * a key C<x-schema> with string-value under the path =item * within the path's operations, looks up the C<$ref> of either the C<body> parameter (for mutations) or either the first 2XX, or default, response =item * the first path-component, so C</user> would operate on C<user> =back Each operation infers from the HTTP method plus other information which method of L<Yancy::Controller::Yancy> it should connect to. The C<id_field> stash parameter is either the value of the C<x-id-field> key in the operation or path spec, or if not given, the C<name> of the last C<in: "path"> parameter specified in the operation's spec. These methods need an C<id_field> parameter: C<get> in "read" mode, C<put>, and C<delete>. This is what the controller will pass as the C<id> to the relevant L<Yancy::Backend> method. =head1 Editor Configuration =head2 Authentication / Authorization To configure authentication for the editor, first set up an Auth plugin like L<Yancy::Plugin::Auth> or L<Yancy::Plugin::Auth::Password> (L<Yancy::Plugin::Auth::Basic> is deprecated). With an authentication plugin configured, the editor will require a logged-in user. To further limit which users can use the editor, set the C<editor.require_user> configuration with a hashref to match against the current user (a L<SQL::Abstract/WHERE CLAUSES> matched using L<Yancy::Util/match>). use Mojolicious::Lite; plugin Yancy => { ... editor => { require_user => { # Users must have "is_admin" set to "1" to use the editor is_admin => 1, }, }, }; =head2 Custom Editor API To customize how Yancy responds to API requests with data, you can create a custom controller and set the class name as the C<default_controller>. For details how to create a custom controller, see L<Yancy::Controller::Yancy>. use Mojolicious::Lite; plugin Yancy => { ... editor => { default_controller => 'MyController', }, }; This allows you to alter how the editor reads and writes data. For example, you could use it to add authorization to individual rows, or require an approval workflow before displaying content. =head1 Additional Configuration There are additional configuration keys to alter how Yancy works. =head2 C<read_schema> By default, Yancy will read your backend to fill in as much data as possible about your schema. To disable this, you can set C<read_schema> to false (C<0>). =head1 SEE ALSO L<Yancy>, L<Mojolicious::Plugin::Yancy> =head1 AUTHOR Doug Bell <preaction@cpan.org> =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2020 by Doug Bell. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut