API-Drip-Request

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

Revision history for API-Drip

0.05    8/29/17
        Added Github information to meta and updated bug reporting location.
        Please submit an issue via Github.

0.04    8/29/17
        Fixed tests

0.03    8/24/17
        Fixed bug when the endpoint contains slashes.
        Added more options to drip.pl

0.02    8/23/17
        Adding dependencies in Build.PL.   We might need those.

bin/drip_client.pl  view on Meta::CPAN


Each configuration item may also be overriden by setting an environment
variable with the same name.

=back

=head2 Operations

=over

=item B<getsub>

Get a list of all subscribers.

=item B<addsub>

Add a subscriber.   At a minimum, must also specify -email, or -id.

Accepts: -email, -id, -new_email, -user_id, -time_zone, -lifetime_value, -ip_address

=item B<delsub>

Delete a subscriber.   Must specify -email, or -id.

=item B<getwork>

Get a list of all workflows.

=item B<startwork>

Start a user on a workflow

Requires: -workflow

bin/drip_client.pl  view on Meta::CPAN

use Readonly;

my %OPT = ( # Add defaults here.
);

GetOptions( \%OPT,
    'help|h|?',
    'man',
    'verbose|verb|v+',
    'conf=s',
    'addsub', 'getsub', 'delsub', 'getwork', 'startwork', 'event',
    'workflow=i', 'action=s',
    'email=s', 'id=s', 'new_email=s', 'user_id=s', 'time_zone=s', 'lifetime_vaule=i', 'ip_address=s', 'prospect'
) or pod2usage(2);
pod2usage(1) if $OPT{help};
pod2usage(-exitval => 0, -verbose => 2) if $OPT{man};
p %OPT if $OPT{verbose};

use API::Drip::Request;
my $client = API::Drip::Request->new( $OPT{conf} ? ( DRIP_CLIENT_CONF => $OPT{conf} ) : () );

eval {
    if ( $OPT{getsub} ) {  get_subscribers() and exit }
    if ( $OPT{addsub} ) {  add_subscribers( %OPT ) and exit }
    if ( $OPT{delsub} ) {  delete_subscribers( %OPT ) and exit }
    if ( $OPT{getwork} ) { get_workflows( %OPT ) and exit }
    if ( $OPT{startwork} ) { start_workflow( %OPT ) and exit }
    if ( $OPT{event} ) { record_event( %OPT ) and exit }
};
if ( $@ ) {
    warn "Request failed:";
    p $@;
}

exit;

sub record_event {
    my %OPT = @_;

    $OPT{email} or $OPT{id} or pod2usage( "Either -email or -id is required for -event" );
    $OPT{action} or pod2usage( "-action is required for -event" );

    my $content = _build_hash( %OPT, keys => [qw(email id action prospect)] );
    my $result = $client->do_request( POST => 'events', { events => [ $content ] } );
    p $result;
}


sub get_workflows {
    my %OPT = @_;

    my $result = $client->do_request( GET => 'workflows' );
    p $result;
}


sub start_workflow {
    my %OPT = @_;
    $OPT{workflow} or pod2usage( "Required parameter -workflow missing for -startwork" );

    my $subscriber = _build_hash( %OPT, keys => [qw( email id user_id time_zone prospect )] );

    my $endpoint = 'workflows/' . $OPT{workflow} . '/subscribers';

    my $result = $client->do_request( POST => $endpoint, { subscribers => [ $subscriber ] } );
    p $result;
}


sub delete_subscribers {
    my %OPT = @_;
    my $id = $OPT{email} || $OPT{id};

    die "email or id required in delete subscriber" unless $id;

    my $result = $client->do_request( DELETE => "subscribers/$id" );
    p $result;

}

sub add_subscribers {
    my %OPT = @_;
    my $subscriber = _build_hash( %OPT, keys => [qw( email id new_email user_id time_zone lifetime_value ip_address )] );
    die "email or id required in add subscriber" unless $subscriber->{email} or $subscriber->{id};

    my $result = $client->do_request( POST => 'subscribers', { subscribers => [ $subscriber ]});
    p $result;
}

sub _build_hash {
    my %OPT = @_;
    my $build = {};
    foreach my $key ( @{$OPT{keys}} ) {
        next unless defined $OPT{$key};
        $build->{$key} = $OPT{$key};
    }
    return $build;
}

sub get_subscribers {
    my $result = $client->do_request( GET => 'subscribers' );
    p $result;
}

lib/API/Drip/Request.pm  view on Meta::CPAN

=cut

our $VERSION = '0.05';

=head1 SYNOPSIS

    use API::Drip::Request;

    my $drip = API::Drip::Request->new();

    $drip->do_request( POST => 'subscribers', { subscribers => [ { email => 'foo@example.com', ... }] } );

=head1 DESCRIPTION

Low-level perl interface to the Drip API as specified at https://www.getdrip.com/docs/rest-api

All of the methods in this module will throw exceptions on error. 

=head1 SUBROUTINES/METHODS

=head2 new()

lib/API/Drip/Request.pm  view on Meta::CPAN

=cut

my $config_validator = validation_for(
    params => {
        DRIP_CLIENT_CONF => { type => Str(), optional => 1 },
        map { $_ => { type => Str(), optional => 1 } } keys %DEFAULTS,
        debugger => { type => CodeRef(), optional => 1 },
    }
);

sub new {
    my $class = shift;
    my %OPT = $config_validator->(@_);

    my $self = _load_conf( \%OPT );

    # At this point, all configuration values should be set
    foreach my $key ( keys %DEFAULTS ) {
        confess "Missing configuration $key" unless defined $self->{$key};
    }

    $self->{debugger} = _mk_debugger( $self, %OPT );

    bless $self, $class;
    return $self;
}

sub _mk_debugger {
    my ($self, %OPT)  = @_;

    unless ($self->{DRIP_DEBUG}) { return sub {}; }
    if ( $OPT{debugger} ) { return $OPT{debugger} }

    return sub { warn join "\n", map { ref($_) ? np $_ : $_ } @_ };
}

=head2 do_request

Accepts the following positional parameters:

=over

=item HTTP Method (required)

May be 'GET', 'POST', 'DELETE', 'PATCH', etc..

=item Endpoint (requird)

Specifies the path of the REST enpoint you want to query.   Include everything after the account ID.   For example, "subscribers", "subscribers/$subscriber_id/campaign_subscriptions", etc...

=item Content (optional)

Perl hashref of data that will be sent along with the request.   

=back

On success, returns a Perl data structure corresponding to the data returned
from the server.    Some operations (DELETE), do not return any data and may
return undef on success.  On error, this method will die() with the
HTTP::Response object.

=cut

my $request_validator = validation_for( params => [ {type => Str()}, {type => Str()}, {type => HashRef(), optional => 1} ] );
sub do_request {
    my $self = shift;
    my ($method, $endpoint, $content) = $request_validator->(@_);

    my $uri = URI->new($self->{DRIP_URI});
    $uri->path_segments( $uri->path_segments, $self->{DRIP_ID}, split( '/', $endpoint) );

    $self->{debugger}->( 'Requesting: ' . $uri->as_string );
    my $request = HTTP::Request->new( $method => $uri->as_string, );
    if ( ref($content) ) {
        $request->content_type('application/vnd.api+json');

lib/API/Drip/Request.pm  view on Meta::CPAN

=item * DRIP_AGENT (optional)

Defaults to "API::Drip".   Specifies the HTTP Agent header.

=item * DRIP_DEBUG (optional)

Defaults to 0.   Set to a true value to enable debugging.

=cut

sub _load_conf {
    my $OPT = shift();
    my $conf = {};

    KEY:
    foreach my $key ( keys %DEFAULTS ) {
        next KEY if defined $OPT->{$key};

        if ( defined $ENV{$key} ) { $conf->{$key} = $ENV{$key};  next KEY; }

        state $YAML_CONF //= _load_yaml_conf( $OPT );
        if ( defined $YAML_CONF->{$key} ) { $conf->{$key} = $YAML_CONF->{$key}; next KEY; }

        $conf->{$key} = $DEFAULTS{$key};
    }
    return $conf;
}

sub _load_yaml_conf {
    my $OPT = shift();

    FILE:
    foreach my $location( $OPT->{DRIP_CLIENT_CONF}, $ENV{DRIP_CLIENT_CONF}, File::Spec->catfile( File::HomeDir->my_home, '.drip.conf' )) {
        no warnings 'uninitialized';
        next FILE unless -f $location && -r _; 
        return YAML::LoadFile $location;
    }
}

xt/boilerplate.t  view on Meta::CPAN

#!perl -T
use v5.14;
use strict;
use warnings;
use Test::More;

plan tests => 3;

sub not_in_file_ok {
    my ($filename, %regex) = @_;
    open( my $fh, '<', $filename )
        or die "couldn't open $filename for reading: $!";

    my %violated;

    while (my $line = <$fh>) {
        while (my ($desc, $regex) = each %regex) {
            if ($line =~ $regex) {
                push @{$violated{$desc}||=[]}, $.;

xt/boilerplate.t  view on Meta::CPAN

    }

    if (%violated) {
        fail("$filename contains boilerplate text");
        diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
    } else {
        pass("$filename contains no boilerplate text");
    }
}

sub module_boilerplate_ok {
    my ($module) = @_;
    not_in_file_ok($module =>
        'the great new $MODULENAME'   => qr/ - The great new /,
        'boilerplate description'     => qr/Quick summary of what the module/,
        'stub function definition'    => qr/function[12]/,
    );
}

{
  not_in_file_ok(README =>



( run in 1.401 second using v1.01-cache-2.11-cpan-88abd93f124 )