Skip to content

Example Perl Client

Magali Ruffier edited this page Jul 29, 2016 · 6 revisions

cpanfile

#Used by the basic clients
requires 'JSON';
requires 'JSON::XS';
requires 'HTTP::Tiny';

# Used by the mojo client
requires 'Mojo';

HTTP::Tiny

#!/usr/bin/env perl

use strict;
use warnings;
use HTTP::Tiny;
use Time::HiRes;
use JSON;

my $http = HTTP::Tiny->new();
my $server = 'http://rest.ensembl.org';
my $global_headers = { 'Content-Type' => 'application/json' };
my $last_request_time = Time::HiRes::time();
my $request_count = 0;

run();

sub run {
  my ($species, $symbol) = @ARGV;
  $species ||= 'human';
  $symbol ||= 'BRAF';
  my $variants = get_variants($species, $symbol);
  foreach my $v (@{$variants}) {
    printf("%s:%d-%d:%d ==> %s (%s)\n", $v->{seq_region_name}, $v->{start}, $v->{end}, $v->{strand}, $v->{id}, $v->{consequence_type});
  }
  return;
}

sub get_variants {
  my ($species, $symbol) = @_;
  my $genes = perform_json_action("/xrefs/symbol/${species}/${symbol}", {object_type => 'gene'});
  if(! @{$genes}) {
    die "No genes found for symbol $symbol";
  }
  my $gene_stable_id = $genes->[0]->{id};
  my $variants = perform_json_action("/overlap/id/${gene_stable_id}", {feature => 'variation'});
  return $variants;
}

sub perform_json_action {
  my ($endpoint, $parameters) = @_;
  my $headers = $global_headers;
  my $content = perform_rest_action($endpoint, $parameters, $headers);
  return {} unless $content;
  my $json = decode_json($content);
  return $json;
}

sub perform_rest_action {
  my ($endpoint, $parameters, $headers) = @_;
  $parameters ||= {};
  $headers ||= {};
  $headers->{'Content-Type'} = 'application/json' unless exists $headers->{'Content-Type'};
  if($request_count == 15) { # check every 15
    my $current_time = Time::HiRes::time();
    my $diff = $current_time - $last_request_time;
    # if less than a second then sleep for the remainder of the second
    if($diff < 1) {
      Time::HiRes::sleep(1-$diff);
    }
    # reset
    $last_request_time = Time::HiRes::time();
    $request_count = 0;
  }
  
  my $url = $server.$endpoint;
  
  if(%{$parameters}) {
    my @params;
    foreach my $key (keys %{$parameters}) {
      my $value = $parameters->{$key};
      push(@params, "$key=$value");
    }
    my $param_string = join(';', @params);
    $url.= '?'.$param_string;
  }
  my $response = $http->get($url, {headers => $headers});
  my $status = $response->{status};
  if(!$response->{success}) {
    # Quickly check for rate limit exceeded & Retry-After (lowercase due to our client)
    if($status == 429 && exists $response->{headers}->{'retry-after'}) {
      my $retry = $response->{headers}->{'retry-after'};
      Time::HiRes::sleep($retry);
      # After sleeping see that we re-request
      return perform_rest_action($endpoint, $parameters, $headers);
    }
    else {
      my ($status, $reason) = ($response->{status}, $response->{reason});
      die "Failed for $endpoint! Status code: ${status}. Reason: ${reason}\n";
    }
  }
  $request_count++;
  if(length $response->{content}) {
    return $response->{content};
  }
  return;
}

Mojo Client

#!/usr/bin/env perl

use strict;
use warnings;
use Mojo::UserAgent;
use Mojo::URL;
use JSON;

my $ua = Mojo::UserAgent->new();
# $ua->proxy->detect;
my $server = 'http://rest.ensembl.org';
my $last_request_time = Time::HiRes::time();
my $request_count = 0;

run();

sub run {
  my ($species, $symbol) = @ARGV;
  $species ||= 'human';
  $symbol ||= 'BRAF';
  my $variants = get_variants($species, $symbol);
  foreach my $v (@{$variants}) {
    printf("%s:%d-%d:%d ==> %s (%s)\n", $v->{seq_region_name}, $v->{start}, $v->{end}, $v->{strand}, $v->{id}, $v->{consequence_type});
  }
  return;
}

sub get_variants {
  my ($species, $symbol) = @_;
  my $genes = perform_json_action("/xrefs/symbol/${species}/${symbol}", {object_type => 'gene'});
  if(! @{$genes}) {
    die "No genes found for symbol $symbol";
  }
  my $gene_stable_id = $genes->[0]->{id};
  my $variants = perform_json_action("/overlap/id/${gene_stable_id}", {feature => 'variation'});
  return $variants;
}

sub perform_json_action {
  my ($endpoint, $parameters, $headers) = @_;
  $parameters ||= {};
  $headers ||= {};
    
  if($request_count == 15) { # check every 15
    my $current_time = Time::HiRes::time();
    my $diff = $current_time - $last_request_time;
    # if less than a second then sleep for the remainder of the second
    if($diff < 1) {
      Time::HiRes::sleep(1-$diff);
    }
    # reset
    $last_request_time = Time::HiRes::time();
    $request_count = 0;
  }
  
  # Build the URL
  my $url = Mojo::URL->new();
  $url->scheme('http');
  $url->host($server);
  $url->path($endpoint);
  $url->query($parameters);
  my $url_string = $url->to_string();
  
  # Do the get
  $headers->{'Content-Type'} = 'application/json' unless exists $headers->{'Content-Type'};
  my $tx = $ua->get($url_string => $headers);
  if (my $res = $tx->success) { 
    # you can use mojo's internal JSON parser but JSON::XS::decode_json() is faster
    # return $res->json;
    return decode_json($res->body);
  }
  my ($err, $code) = $tx->error; #Codes mean HTTP errors
  if($code && $code == 429 && $tx->res->headers->header('Retry-After')) {
    my $retry = $tx->res->headers->header('Retry-After');
    Time::HiRes::sleep($retry);
    return perform_json_action($endpoint, $parameters, $headers);
  }
  warn $code ? "$code response: $err" : "Connection error: $err";
  return {};
}