API-Client

 view release on metacpan or  search on metacpan

lib/API/Client.pm  view on Meta::CPAN

    );
  }

  my $object = ref($self)->new(
    %{$self->serialize}, ($url ? ('url', $url) : ())
  );

  return $object;
}

method serialize() {

  return {
    debug => $self->debug,
    fatal => $self->fatal,
    name => $self->name,
    retries => $self->retries,
    timeout => $self->timeout,
    url => $self->url->to_string,
  };
}

method set_auth($ua, $tx, %args) {
  if ($self->can('auth')) {
    $tx->req->url->userinfo(join ':', @{$self->auth});
  }

  return $self;
}

method set_headers($ua, $tx, %args) {
  if ($self->can('headers')) {
    $tx->req->headers->header(@$_) for @{$self->headers};
  } else {
    $tx->req->headers->header('Content-Type' => 'application/json');
  }

  return $self;
}

method set_identity($ua, $tx, %args) {
  $tx->req->headers->header('User-Agent' => $self->name);

  return $self;
}

method execute(Str :$method = 'get', Str :$path = '', Any %args) {
  delete $args{method};

  my $ua = $self->user_agent;
  my $url = $self->url->clone;

  my $query = $args{query} || {};
  my $headers = $args{headers} || {};

  $url->path(join '/', $url->path, $path) if $path;
  $url->query($url->query->merge(%$query)) if keys %$query;

  my @args;

  # data handlers
  for my $type (sort keys %{$ua->transactor->generators}) {
    push @args, $type, delete $args{$type} if $args{$type};
  }

  # handle raw body value
  push @args, delete $args{body} if exists $args{body};

  # transaction prepare hook
  $ua->on(prepare => fun ($ua, $tx) {
    $self->prepare($ua, $tx, %args);
  });

  # client timeouts
  $ua->max_redirects(0);
  $ua->connect_timeout($self->timeout);
  $ua->request_timeout($self->timeout);

  # transaction
  my ($ok, $tx, $req, $res);

  # times to retry failures
  my $retries = $self->retries;

  # transaction retry loop
  for (my $i = 0; $i < ($retries || 1); $i++) {
    # execute transaction
    $tx = $ua->start($ua->build_tx($method, $url, $headers, @args));
    $self->process($ua, $tx, %args);

    # transaction objects
    $req = $tx->req;
    $res = $tx->res;

    # determine success/failure
    $ok = $res->code ? $res->code !~ /(4|5)\d\d/ : 0;

    # log activity
    if ($req && $res) {
      my $log = $self->logger;
      my $msg = join " ", "attempt", ("#".($i+1)), ": $method", $url->to_string;

      $log->debug("req: $msg")->data({
        request => $req->to_string =~ s/\s*$/\n\n\n/r
      });

      $log->debug("res: $msg")->data({
        response => $res->to_string =~ s/\s*$/\n\n\n/r
      });

      # output to the console where applicable
      $log->info("res: $msg [@{[$res->code]}]");
      $log->output if $self->debug;
    }

    # no retry necessary
    last if $ok;
  }

  # throw exception if fatal is truthy
  if ($req && $res && $self->fatal && !$ok) {
    my $code = $res->code;

    $self->stash(tx => $tx);
    $self->throw([$code, uc "${code}_http_response"]);
  }

  # return transaction
  return $tx;
}

1;

=encoding utf8

=head1 NAME

API::Client

=cut

=head1 ABSTRACT

HTTP API Thin-Client Abstraction

=cut

=head1 SYNOPSIS

  package main;

  use API::Client;

  my $client = API::Client->new(url => 'https://httpbin.org');

  # $client->resource('post');

  # $client->update(json => {...});

=cut

=head1 DESCRIPTION

This package provides an abstraction and method for rapidly developing HTTP API
clients. While this module can be used to interact with APIs directly,
API::Client was designed to be consumed (subclassed) by higher-level
purpose-specific API clients.



( run in 1.890 second using v1.01-cache-2.11-cpan-f6376fbd888 )