#!/usr/bin/env perl
use strict;
use warnings;
use Mojolicious::Commands;
Mojolicious::Commands->start_app('WebService::Fake');

__END__

=pod

=encoding utf8

=head1 NAME

wsf - Web Service Faker

=head1 VERSION

This document describes wsf version 0.003.

=begin html

<a href="https://travis-ci.org/polettix/WebService-Fake">
<img alt="Build Status" src="https://travis-ci.org/polettix/WebService-Fake.svg?branch=master">
</a>
<a href="https://www.perl.org/">
<img alt="Perl Version" src="https://img.shields.io/badge/perl-5.10+-brightgreen.svg">
</a>
<a href="https://badge.fury.io/pl/WebService-Fake">
<img alt="Current CPAN version" src="https://badge.fury.io/pl/WebService-Fake.svg">
</a>
<a href="http://cpants.cpanauthors.org/dist/WebService-Fake">
<img alt="Kwalitee" src="http://cpants.cpanauthors.org/dist/WebService-Fake.png">
</a>
<img alt="CPAN Testers" src="https://img.shields.io/badge/cpan-testers-blue.svg">
</a>
<a href="http://matrix.cpantesters.org/?dist=WebService-Fake">
<img alt="CPAN Testers Matrix" src="https://img.shields.io/badge/matrix-@testers-blue.svg">
</a>

=end html

=head1 USAGE

   shell$ WEBSERVICE_FAKE=/path/to/fake.yml wsf daemon

=head1 DESCRIPTION

This application allows building fake web services. Well, they might be
real... but don't trust the apps you will generate to be too powerful.

=head2 Configuration File Definition

The input definition file is YAML-formatted and its path is taken from
environment variable C<WEBSERVICE_FAKE>. By default, file
C<webservice-fake.yml> in the current directory is used. This file will be
called I<configuration> file in the following.

The highest level is a key/value pairs hash. The following keys have
a special meaning:

=over

=item C<defaults>

key/value pairs that will be taken as default values for some elements in
the L</routes>. You can set the following items, see L</routes> for
details on each one:

=over

=item *

L</body_wrapper>, a possible wrapper to be applied to each body generated
by L</routes>. This can come handy to factor most of your response in
a single place, and concentrate only on the I<parts that change>;

=item *

L</code>, defaulting to C<200>;

=item *

L</headers> - note that in this case the values specified here are
I<added> to the ones in each specification in L</routes>, so be sure to
only put the ones that have to appear in I<every> response;

=item *

L</template_start> for L<Template::Perlish>, defaulting to C<[%>;

=item *

L</template_stop> for L<Template::Perlish>, defaulting to C<%]>;

=back

=item C<routes>

an array of route specifications. Each specification is an hash with the
following possible keys:

=over

=item C<body>

a L<Template::Perlish> text that will be used to generate the body for the
response (but see also L</body_wrapper>);

=item C<body_wrapper>

a L<Template::Perlish> text that, if defined, will be used to wrap
whatever is generated by L</body>. For example, in the following
definition:

   # ...
   body: 'id -> [% stash.id %]'
   body_wrapper: >
      Hello, [% recipient %]. Here is what we have:
      [% content %]

So, whatever is generated by L</body> can then be wrapped in
C<body_wrapper> using the new variable C<content> for espanding its text;

=item C<code>

the code to return for the call

=item C<headers>

array of key/value pairs for defining headers in the response. Each
I<value> is treated as a L<Template::Perlish> template;

=item C<method>

the HTTP method name. See L</methods> if you want to specify more than
one;

=item C<methods>

an array with the list of HTTP methods;

=item C<path>

the path of the route, anything accepted by L<Mojolicious> will do,
including placeholders and other amenities (e.g. C</> or C</foo/:bar>).

=back

All L<Template::Perlish> templates have access to the following variables:

=over

=item *

C<body_params>: all parameters in the body of a C<POST> request;

=item *

C<config>: the configuration file contents

=item *

C<controller>: the L<Mojolicious::Controller> object that catched the
request;

=item *

C<headers>: headers in the request, as L<Mojo::Headers>;

=item *

C<params>: all parameters from the request (both C<GET> and C<POST>);

=item *

C<query_params>: all parameters in the query (mostly for a C<GET>
request);

=item *

C<spec>: the full specification that originated a specific route;

=item *

C<stash>: the stash values for the request;

=item *

C<v>: a shortcut to sub-item C<v> inside the C<config>, to ease your life
for tracking your own variables.

=back

In addition, L</body_wrapper> can also access whatever is generated by
L</body> through the key C<content>.

=back

=head2 Example

The following commented example should get you started.

   # vim: ts=2 sw=2 expandtab
   defaults:
     body_wrapper: |
       {
         "status": true,
         "data": [% content %]}
     headers:
       - X-Whatever: hello
         X-Hey: "You [%= join '/', sort keys %{V('headers')} %] rock"
   somestuff: &somestuff >
     {"hey":"joe"}
   v:
     some_array:
       - one
       - two
       - three
     x: starter
   routes:
     # this route gets the same behaviour for GETs and POSTs.
     # Default body_wrapper applies here because there's no overriding
     - path: '/'
       methods: [ GET, post ]
       headers:
         - Server: 'My::Server'
       body: '{"message":"ciao [% query_params.name %]"}'
     # this route gets a custom wrapping and a single method
     - path: '/simple'
       method: get
       headers:
         - Content-Type: text/plain
       body: 'hullo'
       body_wrapper: "I say: [% content %]\n"
     # this route does not get and wrapping at all
     - path: '/nowrap'
       method: get
       headers:
         - Content-Type: text/plain
       body: "LOOK MA', NO WRAP!\n"
       body_wrapper: ~
     # this leverages upon YAML to get stuff around in this file
     - path: '/somestuff'
       body: *somestuff
     # this modifies a variable that can be reused in following requests
     - path: '/add'
       method: post
       code: 201
       headers:
         - Content-Type: text/plain
       body: |
         [%= push @{V "v.some_array"}, time(); "ok" %]
       body_wrapper: ~
     # this prints out the list in v.some_array (see above). It can be
     # used to check that /add actually works
     - path: '/visit-config'
       body: >
         [[%= join ", ", map { qq{"$_"} } A "v.some_array" %]]
     # these two team up. The first one prepares the answer that the second
     # will give out
     - path: '/prepare/:id'
       method: post
       body: '[% V("v")->{x} = (A("v.some_array"))[V "stash.id"]; %]'
       code: 204
       body_wrapper: ~
     - path: '/whatnow'
       method: get
       body: '[% v.x %]'
       body_wrapper: ~


=head1 BUGS AND LIMITATIONS

Report bugs through GitHub (patches welcome).

=head1 SEE ALSO

L<WebService::Fake>.

=head1 AUTHOR

Flavio Poletti <polettix@cpan.org>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2016 by Flavio Poletti <polettix@cpan.org>

This module is free software. You can redistribute it and/or modify it
under the terms of the Artistic License 2.0.

This program is distributed in the hope that it will be useful, but
without any warranty; without even the implied warranty of
merchantability or fitness for a particular purpose.

=cut
