The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

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 My::Sims::REST;
  use Web::Simple 'My::Sims::REST';

  My::Sims::REST->run_if_script;

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 $items 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.