From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/env perl
use strict;
Mojolicious::Commands->start_app('WebService::Fake');
__END__
=pod
=encoding utf8
=head1 NAME
wsf - Web Service Faker
=head1 VERSION
This document describes wsf version 0.006.
=begin html
</a>
<img alt="Perl Version" src="https://img.shields.io/badge/perl-5.10+-brightgreen.svg">
</a>
<img alt="Current CPAN version" src="https://badge.fury.io/pl/WebService-Fake.svg">
</a>
</a>
</a>
<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