Using scripting languages

Overview

This page provides information on using scripting languages to automate calls to the FOLIO REST API, OKAPI. It uses the snapshot server as an example.

Please improve the code examples and extend this page to cover additional languages and use cases.


Python

Python module for Okapi queries

Members of EBSCO's FSE team have published a python module for FOLIO - https://pypi.org/project/folioclient/https://github.com/folio-fse/FolioClient – that is a wrapper around common API calls.

Jupyter Notebooks

Jupyter Notebooks (https://jupyter.org/) are common, user-friendly tools that community members are learning to use to query FOLIO for data.

The advantage of Jupyter Notebooks is that you don't have to install anything on your computer to write programs, they are easy to publish and share, and their format makes it intuitive to document what you are doing as you go.

An example notebook has been shared by Lisa Sjögren (EBSCO) - https://colab.research.google.com/drive/12geGqqIEfLsEpxYlnt6m0M7ARQG64jIH?usp=sharing - this example is published on Google's Colaboratory platform, which is based on Jupyter Notebooks (https://colab.research.google.com/).

See Getting started with Jupyter Notebooks

Perl

Use the LWP::UserAgent module to simplify REST calls:

use LWP::UserAgent;


Authentication: Get OKAPI Token

This code will retrieve the token that is required for communication with the server:

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use LWP::UserAgent;
use HTTP::CookieJar::LWP;

my $ua = LWP::UserAgent->new(
cookie_jar => HTTP::CookieJar::LWP->new,
);

my $host = 'folio-snapshot-okapi.dev.folio.org';

my $x_okapi_token = '';
my $res = $ua->post(
  'https://' . $host . '/authn/login-with-expiry',
  'Accept' => 'text/plain',
  'content-type' => 'application/json',
  'x-okapi-tenant' => 'diku',
  'x-okapi-token' => '',
  'Content' => '{ "username": "diku_admin",
                  "password": "admin" }',
);
if ( $res->is_success ) {
  if ( $res->status_line =~ /^201 / ) { 
    my $ref = $ua->cookie_jar;
    my %cookies = %$ref;
    $x_okapi_token = $cookies{store}{$host}{'/'}{folioAccessToken}{value};
  }
} else {
  say STDERR 'failed:';
  say STDERR $res->status_line;
  say STDERR $res->decoded_content;
}

die 'No okapi token retrieved' unless $x_okapi_token;
say 'Auth token: ' . $x_okapi_token;

exit 0;
__END__

Get all users

This code will use the token to retrieve all active users, retrieving the records in batches of 10, until all the records are retrieved:

#!/usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use JSON;
use Data::Dumper;
use LWP::UserAgent;
use HTTP::CookieJar::LWP;

my $ua = LWP::UserAgent->new(
  cookie_jar => HTTP::CookieJar::LWP->new,
);

my $host = 'folio-snapshot-okapi.dev.folio.org';

my $x_okapi_token = '';
my $res = $ua->post(
  'https://' . $host . '/authn/login-with-expiry',
  'Accept' => 'text/plain',
  'content-type' => 'application/json',
  'x-okapi-tenant' => 'diku',
  'x-okapi-token' => '',
  'Content' => '{ "username": "diku_admin",
                  "password": "admin" }',
);
if ( $res->is_success ) {
  if ( $res->status_line =~ /^201 / ) { 
    my $ref = $ua->cookie_jar;
    my %cookies = %$ref;
    $x_okapi_token = $cookies{store}{$host}{'/'}{folioAccessToken}{value};
  }
} else {
  say STDERR 'failed:';
  say STDERR $res->status_line;
  say STDERR $res->decoded_content;
}

die 'No okapi token retrieved' unless $x_okapi_token;

my @folioUsers;
my $totalRecords = 0;
my $offset = 0;
{ 
  do {
    ( $offset, $totalRecords, @folioUsers ) = getUsers( $offset, @folioUsers );
    say $offset;
  } while ( $offset < $totalRecords );
}

say 'TotalRecords: ', $totalRecords;
say 'Records retrieved: ', scalar @folioUsers;
say Dumper @folioUsers;

exit 0;

# -------------- subroutines ----------------------#
sub getUsers {
  my ( $offset, @folioUsers ) = @_;
  say 'offset: ', $offset;
  my $jsonString;
  $res = $ua->get(
    'https://folio-snapshot-okapi.dev.folio.org/users?limit=10&query=(active=="true")&offset=' . $offset,
    'Accept' => 'text/plain',
    'content-type' => 'application/json',
    'x-okapi-tenant' => 'diku',
    'x-okapi-token' => "$x_okapi_token"
  );
  if ( $res->is_success ) {
    if ( $res->status_line ne '200 OK' ) { 
      say STDERR 'not ok:';
      say STDERR $res->status_line;
      say STDERR $res->decoded_content;
      return;
    }
    $jsonString = $res->decoded_content;
  } else {
    say STDERR 'failed:';
    say STDERR $res->status_line;
    say STDERR $res->decoded_content;
    return;
  }
  my $ref = JSON->new->decode( $jsonString );
  my %hash = %$ref;
  my $totalRecords = $hash{'totalRecords'};
  my @users = $hash{users};
  foreach ( @users ) {
    my @records = @$_;
    foreach my $ref ( @records ) {
      push @folioUsers, JSON->new->encode( $ref );
      $offset++;
    }
  }
  return $offset, $totalRecords, @folioUsers;
}
__END__


Postman

Matt Weaver provided this code on Slack:

const xOkapiTenant = pm.variables.get("x-okapi-tenant");
const username = pm.variables.get("username");
const password = pm.variables.get("password");
const baseUrl = pm.variables.get("baseUrl");
const okapiUrl = pm.variables.get("x-okapi-url");

const loginRequest= {
url: `${okapiUrl}/bl-users/login-with-expiry`,
method: 'POST',
header: [
"Content-type: application/json",
`x-okapi-tenant: ${xOkapiTenant}`
],
body: {
mode: 'raw',
raw: JSON.stringify({ "username": username,"password": password })
}
};

await new Promise((reqDone) => 
pm.sendRequest(loginRequest, async (err, res) => {
const userId = res.json().user.id;
// const tenantId = res.json().tenant;
pm.collectionVariables.set("x-okapi-user-id", userId);
// pm.collectionVariables.set("x-okapi-tenant", tenantId);
console.log("res raw", res);
console.log("error", err ? err : res.json());
console.log("res json", res.json());

await new Promise((success) => pm.cookies.jar().get(loginRequest.url, "folioAccessToken", (error, cookieValue) => {
console.log("Setting cookie", cookieValue);
console.log("cookie error", error);
pm.collectionVariables.set("x-okapi-token-value", cookieValue);
success();
}));

console.log("Done");
reqDone();
}));

pm.request.headers.upsert({key: 'x-okapi-tenant', value: pm.variables.get("x-okapi-tenant") });
// pm.request.headers.upsert({ key: 'x-okapi-token', value: pm.variables.get("x-okapi-token-value") });
pm.request.headers.upsert({ key: 'x-okapi-user-id', value: pm.variables.get("x-okapi-user-id") });
pm.request.headers.upsert({ key: 'x-okapi-url', value: pm.variables.get("x-okapi-url") });


NodeJS

Use the https module:

const https = require(‘https’);


Authentication: Get OKAPI token

This code can be used within a NodeJS app to retrieve the token that is required for communication with the server:

const https = require('https');

function getPromise_token() {
  return new Promise(( resolve, reject ) => {
    if ( x_okapi_token == '' ) {
      const data = JSON.stringify ({
        username: 'diku_admin',
        password: 'admin'
      });
      const options = {
        hostname: 'folio-snapshot-okapi.dev.folio.org',
        port: '443',
        path: '/authn/login',
        method: 'POST',
        headers: {
          'Content-type': 'application/json',
          'Content-Length': data.length,
          'x-okapi-tenant': 'diku',
          'x-okapi-token': ''
        }
      }
      const req = https.request(options, res => {
        res.on('data', d => {
          process.stdout.write(d);
        })
        x_okapi_token = res.headers['x-okapi-token'];
        resolve(x_okapi_token);
      })
      req.on('error', error => {
        reject(error);
      })
      req.write(data);
      req.end();
    } else {
      resolve(x_okapi_token);
    }
  });
}
async function get_token() {
  try {
    let http_promise = getPromise_token();
    let x_okapi_token = await http_promise;
    return x_okapi_token;
  }
  catch(error) {
    console.log(error);
  }
}