# PODNAME: Yancy::Help::Config
# ABSTRACT: How to configure Yancy

__END__

=pod

=head1 NAME

Yancy::Help::Config - How to configure Yancy

=head1 VERSION

version 1.023

=head1 SYNOPSIS

    use Mojolicious::Lite;
    plugin Yancy => {
        backend => 'pg://localhost/myapp',
        read_schema => 1,
        collections => {
            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<Mojolicious> plugin, these values are given as
a hash reference argument to the plugin. See
L<Mojolicious::Plugin::Yancy/CONFIGURATION> for some plugin-specific
configuration values.

When using the Yancy standalone mode, these values are defined in
a C<yancy.conf> file which is parsed as a Perl hash reference. See
L<Yancy::Help::Standalone> for more information about running the
standalone app.

=head1 Database Backend

The C<backend> 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<Postgres backend|Yancy::Backend::Pg>

    # Backend URL
    backend => 'pg://user@example.com/mydb',

    # Backend hash
    backend => {
        Pg => {
            dsn => 'dbi:Pg:dbname',
            username => 'fry',
            password => 'b3nd3r1sgr34t',
        },
    }

=item L<MySQL backend|Yancy::Backend::Mysql>

    # Backend URL
    backend => 'mysql://user@localhost/mydb',

    # Backend hash
    backend => {
        Mysql => {
            dsn => 'dbi:mysql:mydb',
            username => 'fry',
            password => 'b3nd3r1sgr34t',
        },
    }

=item L<SQLite backend|Yancy::Backend::Sqlite>

    # Backend URL
    backend => 'sqlite:filename.db',

    # Backend hash
    backend => {
        Sqlite => {
            dsn => 'sqlite:data.db',
        },
    }

=item L<DBIx::Class backend|Yancy::Backend::Dbic>

    # 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 Data Collections

The C<collections> data structure defines what data is in the database.
Each key in this structure refers to the name of a collection, and the
value describe the fields for items inside the collection.

Each backend may define a collection differently. For a relational
database like Postgres or MySQL, a collection is a table, and the fields
are columns. For an ORM like DBIx::Class, the collections are ResultSet
objects. For a document store like MongoDB, the collections are
collections. See your backend's documentation for more information.

Collections are configured using L<JSON Schema|http://json-schema.org>.
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<title>,
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 collection 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:

    collections => {
        people => {
            properties => {
                id => {
                    type => 'integer',
                    readOnly => 1,
                },
                name => {
                    type => 'string',
                },
                email => {
                    type => 'string',
                },
            },
        },
    },

Instead of (or in addition to) defining collections yourself, you can
tell Yancy to read your database to try to determine your collection
configuration. See L<read_schema|/read_schema> for more information.

=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"> >>)

=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.

=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.

    collections => {
        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:

    collections => {
        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 Example Values

Setting an example value makes it easier to add new data. When a user
tries to add a new item, Yancy will fill in the data from the C<example>
key of the collection. This key holds an example object using fake data.
As an example of our C<people> collection:

    people => {
        example => {
            name => 'Philip J. Fry',
            email => 'fry@aol.com',
        },
        properties => { ... },
    },

=head2 Extended Collection Configuration

There are some extended fields you can add to your collection definition
to control how it is treated by Yancy.

=over

=item title

A friendly title for the collection

=item description

A description of the collection. Markdown will be parsed into HTML.

You can use the C<trim> and C<unindent> functions from L<Mojo::Util> to
allow indenting your collection description:

    use Mojolicious::Lite;
    use Mojo::Util qw( unindent trim );
    plugin Yancy => {
        collections => {
            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 collection 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 collection: Do not add it to the API, do not show it in the
rich editing form. This is for collections that should not be edited
from the Yancy form or the Yancy API.

This allows for removing collections when using L</read_schema>.

=item x-id-field

This key sets the name of the collection's ID field to use to uniquely
identify individual items. By default, Yancy assumes the ID field is
named C<id>. If your collection 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 collection 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 collection. 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 x-hidden

If true, thie field will be hidden from the rich editing form. This is
for collections 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.

=back

=head1 OpenAPI specification

=head2 Generation

An OpenAPI spec will be generated from the C<collections> specified as
above, by using the C<collections> 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 collection.

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<collections> (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<collections> 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<collections>
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 "collection". If
any path has operations referring to more than one collection, that is
an error. To derive which collection, these things are considered:

=over

=item *

a key C<x-collection> 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::API> 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 Additional Configuration

There are additional configuration keys to alter how Yancy works.

=head2 C<read_schema>

When this is set, Yancy will read your backend to see what collections
you have available. Any collections and fields that you do not configure
will be assigned default configuration from your database schema. You
can use the configuration to override information that Yancy gets
incorrect, or to set information that Yancy can't (like the C<title> and
C<description> of collections and properties).

You can also set this on a per-collection basis to read only certain
collections from the database. This way you can include only the data
you want in your editor.

B<NOTE:> This exposes all the data in your schema to Yancy's API, which allows
anyone to edit the data. If you do not want anyone to edit the data, use
L<the plugin's route configuration|Mojolicious::Plugin::Yancy/CONFIGURATION> to
add authentication to Yancy.

    # All collections
    use Mojolicious::Lite;
    plugin Yancy => {
        backend => 'pg://localhost/myapp',
        read_schema => 1,
    };

    # Only the notes and events
    use Mojolicious::Lite;
    plugin Yancy => {
        backend => 'pg://localhost/myapp',
        collections => {
            notes => { read_schema => 1 },
            events => { read_schema => 1 },
        },
    };

=head2 C<api_controller>

To customize how Yancy responds to API requests with data, you can
create a custom controller and set the class name here. For details how
to create a custom API controller, see L<Yancy::Controller::Yancy::API>.

=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) 2018 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
