NAME

Net::HTTP::Knork - Lightweight implementation of Spore specification

VERSION

version 0.20

SYNOPSIS

use strict;
use warnings;
use Net::HTTP::Knork;
use JSON::MaybeXS;
my $spec = encode_json(
    {   version => 1,
        format  => [ "json", ],
        methods => {
            test => {
                method => 'GET',
                path   => '/test/:foo',
                required_params => [ "foo" ],
            }
        }
        base_url => 'http://example.com',
    }
);

my $knork = Net::HTTP::Knork->new(spec => $spec);

# make a GET to 'http://example.com/test/bar'
my $resp = $knork->test({ foo => 'bar'});

DESCRIPTION

Net::HTTP::Knork is a module that was inspired by <the Spore specification|https://github.com/SPORE/specifications/blob/master/spore_description.pod>. So it is like Net::HTTP::Spore but with some differences.

JSON or Perl hash specifications

Specifications can be written either in a JSON file, string, or as a Perl hash (no YAML support). On top of that, every specification passed is validated against a Spore specification embedded, using Data::Rx.

No middleware modules

This module does not provide middlewares as in Net::HTTP::Spore, but you can alter the default behaviour on requests/responses. See Middlewares below

Behaviour of requests with payloads (POST/PUT/PATCH)

The original behaviour of Net::HTTP::Spore with POST/PATCH/PUT was not clear enough, so I made some modifications:

  • I changed the meaning of required_payload and optional_payload: now they contain the fields required or optional in the payload

  • I removed altogether the support for form-data and payload

  • Consequently I removed the { payload => ... } that was passed to methods when making a request with a payload

  • The default encoding on payload requests is now set to 'application/x-www-form-urlencoded'. I don't plan on support 'multipart/form-data' right now, but if needed, it can be added by using 'encoding' and 'decoding' in the constructor.

HTTP::Response compliant

All the responses returned by Knork are objects from a class extending HTTP::Response. This means that you can basically manipulate any response returned by a Knork client as an HTTP::Response.

Always check your HTTP codes !

No assumptions are made regarding the responses you may receive from an API. It means that, contrary to Net::HTTP::Spore, the code won't just die if the API returns a 4XX. This also implies that you should always check the responses returned.

Moo !

When I was working with Net::HTTP::Spore, I found it hard to get around all the magic done with Moose. So this implementation tries to be lightweight.

METHODS

new

Creates a new Knork object.

my $client = Net::HTTP::Knork->new(spec => '/some/file.json');
# or
my $client = Net::HTTP::Knork->new(spec => $a_perl_hash);
# or
my $client = Net::HTTP::Knork->new($spec => $a_json_object);

Other constructor options:

  • default_params: hash specifying default parameters to pass on every requests.

    # pass foo=bar on every request
    my $client = Net::HTTP::Knork->new(spec => 'some_file.json', default_params => {foo => 'bar'});
  • client: LWP::UserAgent HTTP client. Automatically created if not passed.

  • lax_optionals: a string parameter that can either be set to 'payload' or 'params'. When this parameter is set, the remaining parameters passed to any method will be either put in the URL or in the payload. The default behaviour is to ignore any parameter that is not explicitly put in the spec.

  • encoding/decoding: those parameters allow you to apply an encoding/decoding on the requests/responses. Encoding or decoding is applied BEFORE the middlewares and can be changed on the fly (attributes are rw).

    • encoding: a subref that will be applied before the 'middlewares' to encode a request in a certain way. Takes a Net::HTTP::Knork::Request object as an argument and MUST return a Net::HTTP::Knork::Request object. When using the encoding attribute, the content is set to a Perl hash containing the keys/values needed for the POST request.

      use JSON::MaybeXS;
      my $client = Net::HTTP::Knork->new($spec => 'some_spec.json', 
          encoding => sub {  
              my $req = shift; 
              my $content = $req->content; 
              $req->content(encode_json ($content));
              return $req;
          }
      );
    • decoding: a subref that will be applied before the 'middlewares' to decode a request in a certain way. Takes a Net::HTTP::Knork::Response object as an argument and MUST return a Net::HTTP::Knork object.

      use JSON::MaybeXS;
      my $client = Net::HTTP::Knork->new($spec => 'some_spec.json', 
          decoding => sub {  
              my $req = shift; 
              my $content = $req->content; 
              $req->content(decode_json ($content));
              return $req;
          }
      );
make_sub_from_spec

Creates a new Knork sub from a snippet of spec. You might want to do that if you want to create new subs with parameters you can get on runtime, while maintaining all the benefits of using Knork.

my $client = Net::HTTP::Knork->new(spec => '/some/file.json');
my $response = $client->get_foo_url();
my $foo_url = $response->body->{foo_url};
my $post_foo = $client->make_sub_from_spec({method => 'POST', path => $foo_url});
$client->$post_foo(payload => { bar => 'baz' });

MIDDLEWARES

Usage

use strict;
use warnings;
use Net::HTTP::Knork;
my $client = Net::HTTP::Knork->new(spec => '/path/to/spec.json');
$client->add_middleware(
    {   on_request => sub {
            my $self = shift;
            my $req = shift;
            # alter the request in some way
            return $req;
        },
        on_response => sub {
            my $self = shift;
            my $resp = shift;
            # alter the response in some way
            return $resp;
          }
    }
);

Although middlewares cannot be created as in Net::HTTP::Spore, there is still the possibility to declare subs that will be executed either before the request is sent or before the response is processed. To accomplish this, it installs modifiers around some core functions in Net::HTTP::Knork, using Class::Method::Modifiers.

Limitations

  • Basic : The system is kind of rough on edges. It should match simple needs, but for complex middlewares it would need a lot of code.

  • Order of application : The last middleware applicated will always be the first executed.

TESTING

As a HTTP client can be specified as a parameter when building a Net::HTTP::Knork client, this means that you can use Test::LWP::UserAgent to test your client. This is also how tests for Net::HTTP::Knork are implemented.

use Test::More;
use Test::LWP::UserAgent;
use Net::HTTP::Knork;
use Net::HTTP::Knork::Response;
my $tua = Test::LWP::UserAgent->new;
$tua->map_response(
    sub {
        my $req = shift;
        my $uri_path = $req->uri->path;
        if ( $req->method eq 'GET' ) {
            return ( $uri_path eq '/show/foo' );
        }
    },
    Net::HTTP::Knork::Response->new('200','OK')
);
my $client = Net::HTTP::Knork->new(
    spec => {
        base_url => 'http://example.com',
        name     => 'test',
        methods  => [
            {   get_user_info => { method => 'GET', path => '/show/:user' }
            }
        ]
    },
    client => $tua
);


my $resp = $client->get_user_info( { user => 'foo' } );
is( $resp->code, '200', 'our user is correctly set to foo' );

TODO

This is still early alpha code but there are still some things missing :

  • debug mode

  • more tests

  • a real life usage

BUGS

This code is early alpha, so there will be a whole bucket of bugs.

ACKNOWLEDGEMENTS

Many thanks to Franck Cuny, the originator of Net::HTTP::Spore. Some parts of this module borrow code from his module.

SEE ALSO

Net::HTTP::Spore

AUTHOR

Emmanuel Peroumalnaïk

COPYRIGHT AND LICENSE

This software is copyright (c) 2014 by E. Peroumalnaik.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.

CONTRIBUTORS

  • Daniel Gempesaw - <gempesaw@cpan.org>

  • Fabrice Gabolde - <fabrice.gabolde@gmail.com>