NAME
DBIx::Class::Sims::REST
SYNOPSIS
In your REST API class:
package My::Sims::REST
use base 'DBIx::Class::Sims::REST::MySQL';
sub get_schema_class { return 'My::Schema::Class' }
1;
In a rest.cgi file (somewhere):
use Module::Runtime qw(use_module);
my $app = use_module('My::Sims::REST')->to_psgi_app;
use Plack::Builder;
builder {
# Or whatever other middleware you want.
enable "Runtime"; # Adds the X-Runtime header
$app
}
Then later:
plackup <path/to/your/rest.cgi> -p <PORT>
And, finally, in your test (or some library your tests use):
my $data = {
databases => [
{
database => {
name => 'database name',
username => 'some username',
password => 'some password',
},
spec => <DBIx::Class::Sims specification>,
},
],
};
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new(POST => 'http://<URL>:<PORT>/sims');
$req->content(encode_json($data));
my $res = $ua->request($req);
return decode_json($res->content);
PURPOSE
DBIx::Class::Sims provides an easy way to create and populate test data. But, sometimes, your application isn't built on DBIx::Class. Or Perl. These issues shouldn't get in the way of using good tools. (Even if your application or test suite aren't in Perl.)
Assumption: Everything can issue an HTTP request.
Conclusion: The Sims should be available via an HTTP request.
DESCRIPTION
This is a skeleton base class that provides the basic functionality for a REST API around DBIx::Class::Sims. By itself, it only takes the request, parses it out, and invokes a series of methods that have empty implementations. You are supposed to subclass this class and provide the meat of these methods.
You will have to create a DBIx::Class description of your schema (or, at the least, the bits you want to be able to sim in your tests). It really isn't that difficult - there are examples in the test suite for this module.
Once you have all of that, you will need to host this REST API somewhere. Since its purpose is to aid in testing, a good place for it is in your developers' Vagrant VMs, and also in the VM you use to run CI tests on.
THIS SHOULD NEVER BE MADE AVAILABLE IN PRODUCTION. If you do so, the problems you will have are on your head and your head alone. I explicitly and categorically disavow any and all responsibility for your idiocy if this ends up in your production environment. Please, do not be stupid.
REQUEST
The full data structure to be passed via a request is as follows:
{
defaults => {
database => {
username => '',
password => '',
root => {
username => '',
password => '',
},
},
create => 1,
deploy => 1,
},
databases => [
{
database => {
username => 'username',
password => 'password',
name => 'name',
root => {
username => 'root_username',
password => 'root_password',
},
},
create => <0|1>,
deploy => <0|1>,
spec => < First parameter to load_sims() >
options => < Second parameter to load_sims() >
},
...
],
}
databases
Each entry in this array represents the setting up of a single database. You may want to set up multiple databases at the same time, hence the ability to specify multiple databases. Each entry will be executed in the order they are specified, if that matters. Each entry can decide whether or not to create or deploy, as appropriate to your purpose.
create
This will drop and recreate the database. This defaults to true. If this is set, then deploy will also be set.
This requires the root username, password, and whatever create commands are required to be set. Please see the appropriate sections of the documentation for further information.
deploy
This will deploy the schema per
<$schema-
deploy(add_drop_tables => 1)>>.spec
This is the meat and potatoes of this module - the reason why you're here.
Please see "load_sims" in DBIx::Class::Sims for further information.
options
"load_sims" in DBIx::Class::Sims has an optional second parameter of options. This allows you to set them. Please see that documentation for further information.
You can set a default set of options by overriding "sims_options".
RESPONSE
If all goes well, the response will be an array of the return values from "load_sims" in DBIx::Class::Sims. The array will be in the same order as the databases element of the request.
If nothing happens, the response will be exactly:
{ "error": "No actions taken" }
REQUIRED METHODS
You have to override the following methods for anything to work.
get_schema_class( $item, $defaults )
This method should return a string containing the package name for the schema. It can use anything in the entry and the defaults.
If it returns nothing, then this entry will be skipped.
You MUST provide an implementation of this method - the base implementation throws an error.
get_connect_string( $item, $defaults )
This method should return the first parameter in the connect() method. It can use anything in the entry and the defaults.
If it returns nothing, then this entry will be skipped.
This method has implementations in the SQLite and MySQL subclasses. Otherwise, you MUST provide an implementation of this method - the base implementation throws an error.
OPTIONAL METHODS
get_defaults()
This method should return a hashref that provides the defaults for all calls.
The base implementation returns the following:
{
database => {
username => '',
password => '',
root => {
username => '',
password => '',
},
},
create => 1,
deploy => 1,
}
get_username( $item, $defaults )
This method should return a string to be used as the username in the connect string.
The base implementation returns the username from either the $item
or the $defaults
.
get_password( $item, $defaults )
This method should return a string to be used as the password in the connect string.
The base implementation returns the password from either the $item
or the $defaults
.
get_root_connection( $item, $defaults )
This method should return a DBI $dbh
that has a root connection to the database. This is what the return value of get_create_commands()
will be passed to.
The base implementation retrieves the root username and password from the database entry in the $item
or $defaults
, then uses the value from get_connect_string()
to connect to the database. This should be sufficient for most purposes.
get_create_commands( $item, $defaults )
This method should return an array of SQL commands to be executed via a root connection when a database is dropped and created. These commands will be executed when <create =
1>>.
The base implementation returns an empty array.
populate_default_data( $schema, $item, $defaults )
This method is called after create/deploy is processed, but before <$schema-
load_sims()>> is invoked. You can use this method to do any default data population. For example, to invoke DBIx::Class::Fixtures or to call "populate" in DBIx::Class::Schema.
sims_options( $schema, $item, $defaults )
This method should return a hashref appropriate to pass as the second parameter to load_sims()
. It will be merged with the whatever is passed in for the specific item.
The base implementation returns an empty hashref.
SUGGESTIONS
Additional Keys
The keys listed thus far in $item
are only those DBIx::Class::Sims uses internally. You are more than welcome to add additional keys as you need. Then, in the various methods that are passed $item
, you can pull those out.
Multiple databases
Your tests may need multiple databases to be populated. This is why the data specification requires a databases array. The $item
s will be processed in the order they are specified, in case that matters.
You can combine this with adding an additional key to specify which database this $item
is dealing with. That way, you can have a different schema class, different default data, etc.
TODO
Chef/Puppet recipes for auto-launching the REST API
Figure out how to pass the class into plackup without an envvar.
BUGS/SUGGESTIONS
This module is hosted on Github at https://github.com/robkinyon/dbix-class-sims-rest. Pull requests are strongly encouraged.
SEE ALSO
DBIx::Class::Sims, Web::Simple
AUTHOR
Rob Kinyon <rob.kinyon@gmail.com>
LICENSE
Copyright (c) 2013 Rob Kinyon. All Rights Reserved. This is free software, you may use it and distribute it under the same terms as Perl itself.