Tech Blog

Building Client SDKs with Swagger Codegen

In recent blog posts, we discussed newly-announced support for the OpenAPI standard for Alma APIs. Another benefit of OpenAPI is the ability to generate client class libraries or server stubs for testing.

For many developers, “thin” clients for calling REST APIs are sufficient for their needs. A “thin” client encapsulates basic HTTP functionality, such as authorization, content type handling, and body serialization. Examples of “thin” client include these Alma REST API clients for Ruby and Node. These clients expose methods for the basic HTTP requests- GET, POST, PUT and DELETE.

Other developers prefer a full SDK when interacting with a set of REST APIs. Full SDKs provide classes and methods for the APIs and the objects that are accepted and returned from those APIs. The difference between a “thin” client and a full SDK is highlighted in the pseudocode below:

# "Thin" client:
user = alma_rest_client.get("/users/joshw") # specify URI in generic method
pprint(user['first_name']) # user is a standard Hash

# Full SDK:
user = alma_user_api.get_user("joshw") # specific method on a custom class
pprint(user.first_name) # user is a User object

There are several tools which can generate full client SDKs from an OpenAPI specification, including:

We will use Swagger CodeGen to generate client SDKs for the Alma User API. As we described in a previous blog post, each Alma API area includes a section with OpenAPI links and resources. On the panel for Swagger Codegen, select the desired client language and click Download SDK.

The process for each language is similar:

  • Unzip the downloaded package
  • Check out the README.md file for installation instructions
  • Use the provided samples and documentation to learn how to use the SDK

In the remainder of this post, we will go through this process for PHP, Python, and Java. Each example will call the same API requests:

  • Get User Requests
  • Create User Request
  • Delete User Requests

PHP

After unzipping the SDK package, change into the client directory. Use composer install to install the required dependencies.

cd php-client-generated/SwaggerClient-php/
composer install

The example code includes the SDK, configures the API key, creates a new API client object, and calls the generated method.

<?php
require_once(__DIR__ . '/php-client-generated/SwaggerClient-php/vendor/autoload.php');

// Configure API key authorization: ApiKeyAuth
$config = Swagger\Client\Configuration::getDefaultConfiguration()->setApiKey('apikey', $_ENV['ALMA_APIKEY']);

$requestsApi = new Swagger\Client\Api\RequestsApi(
    new GuzzleHttp\Client(),
    $config
);

/* GET USER REQUESTS */
if (count($argv) < 3) exit("Usage: php SwaggerClientTest.php user_id mms_id\n");

$user_id = $argv[1];
$limit = 5; 
$request_type = 'HOLD';
try {
    $result = $requestsApi->getalmawsv1usersuserIdrequests($user_id, $request_type, $user_id_type, $limit, $offset, $status);
    print_r($result);
} catch (Exception $e) {
    echo 'Exception when calling DepositsApi->getalmawsv1usersuserIddeposits: ', $e->getMessage(), PHP_EOL;
}

Python

The process for Python is very similar. After unzipping the SDK package, change into the client directory. Use pip install to install the required dependencies.

cd python-client-generated/
pip3 install .

The example code includes the SDK, configures the API key, creates a new API client object, and calls the generated method.

import swagger_client
from swagger_client.rest import ApiException
from pprint import pprint
import os
import sys

# Configure API key authorization: ApiKeyAuth
configuration = swagger_client.Configuration()
configuration.api_key['apikey'] = os.environ['ALMA_APIKEY']

# create an instance of the API class
api_instance = swagger_client.RequestsApi(swagger_client.ApiClient(configuration))

# GET USER REQUESTS
if len(sys.argv) < 3:
    exit("Usage: python SwaggerTestClient.py user_id mms_id")

user_id = sys.argv[1]
limit = 5 
request_type = 'HOLD'

try:
    # Requests by user id
    api_response = api_instance.getalmawsv1usersuser_idrequests(user_id, request_type, limit=limit)
    pprint(api_response)
except ApiException as e:
    print("Exception when calling RequestsApi->getalmawsv1usersuser_idrequests: %s\n" % e)

Java

The Java example is a bit different since we need to compile the Java file with the proper classpath. After unzipping the SDK package, change into the client directory. Use mvn clean package to install the required dependencies and package the SDK into a jar file.

cd java-client-generated
mvn clean package

Then we can take our Java file and compile it with the generated jar and all of its dependencies:

javac -cp ".:java-client-generated/target/swagger-java-client-1.0.0.jar:java-client-generated/target/lib/*" SwaggerClientTest.java

Finally, we can call the compiled Java class, again with the proper classpath:

java -cp ".:java-client-generated/target/swagger-java-client-1.0.0.jar:java-client-generated/target/lib/*" SwaggerClientTest joshw 9963757400561

Again, the example code imports the SDK, configures the API key, creates a new API client object, and calls the generated method.

public static void main(String[] args) {
      ApiClient defaultClient = Configuration.getDefaultApiClient();

      // Configure API key authorization: ApiKeyAuth
      ApiKeyAuth ApiKeyAuth = (ApiKeyAuth) defaultClient.getAuthentication("ApiKeyAuth");
      ApiKeyAuth.setApiKey(System.getenv("ALMA_APIKEY"));
              
      /* GET USER REQUESTS */
      if (args.length<2) {
          System.out.println("Missing parameters: user_id mms_id");
          System.exit(1);
      }
      String userId = args[0];        
      Integer limit = 5;
      String requestType = "HOLD";
      try {
          Object result = apiInstance.getalmawsv1usersuserIdrequests(userId, requestType, null, limit, null, null);
          System.out.println(result);
      } catch (ApiException e) {
          System.err.println("Exception when calling RequestsApi#getalmawsv1usersuserIdrequests");
          e.printStackTrace();
      }

There are some additional complexities in the Java code due to how Java handles the serialization and deserialization from the underling JSON to the strongly typed classes, especially around dates. The full code sample includes the required configuration of the date serializers.

Next Steps

To reiterate, if you’re like the many developers who are happy using a “thin” client, you should continue with your current practice. However, if working with a full SDK or strongly-typed objects interests you, you’re welcome to download the SDKs from the relevant Alma REST API homepage. The full samples referenced in this post are available in this Github gist.

And regardless of your interest in code generation, you can benefit from the OpenAPI support in any of the following ways:

  • Power users can leverage the evolving toolset which supports the OpenAPI standard for their development workflow
  • Inquisitive users benefit from the newest API console powered by OpenAPI & Swagger
  • Casual users enjoy up-to-date documentation and accurate and comprehensive JSON and XML examples

Note that this is an emerging area and the underlying code of the generators is quite complex. There are a great many parameters that you can use to control that code that is generated. If you want to go further and work with the code generator itself, please note that the Alma SDKs are generated with a forked version of the Swagger Codegen engine which includes some bug fixes required to process the OpenAPI provided for Alma APIs. Until the submitted pull requests are processed, you’ll need to take that into consideration.

6 Replies to “Building Client SDKs with Swagger Codegen”

  1. Just in case you feel that you’re talking into the void, there are people who are very interested in, and greatly appreciate, what is being done here.

  2. Thanks a lot, Josh.

    I had some success working with this and it was really exciting until it wasn’t.

    As of right now, it would seem that ExLibris’ code generator is broken.

    https://exl-swagger-generator.herokuapp.com/api/types?types=client&version=V3

    Gonna put in a ticket, but it seems to me that the .json and .yaml description files don’t seem to work with ANY current implementation of swagger/openapi code generators.

    I was working with conf.[yaml|json], I haven’t tried many others:
    https://developers.exlibrisgroup.com/wp-content/uploads/alma/openapi/

    I’ve tried the most recent and official builds for swagger_codegen and openapi_codegen, as well as building your own forked version and I can’t seem to get any of them to spit out the python code I was able to download from the bottom of the API doc pages.

    Do you have any idea when this spec will be fully rolled out (and working)?

    Thanks again!!

    1. Hi Rob. Thanks for your feedback. This is definitely cutting edge stuff so I expect it will take some tweaking to get it to work.

      I’ve followed the example in the blog for Python after downloading the package from the Users APIs page. I then run the test script from the Gist. That seems to work fine for me.

      I’m sure that other scenarios might not work perfectly though. Can you let me know if the example here works for you? And if so, which one doesn’t work?

      Thanks!

  3. Thanks much, Josh!

    I’m pretty sure the examples in the gist were working for me. The problem was that the package wasn’t available for download at the bottom of the API page. The dropdown where you select the language just said “Loading.”

    In addition, trying to generate the code on my own (see example below) was failing. That, too, seems to be generating code for me now, so things are looking much better!

    This was failing with an error, but not anymore:

    > docker run –rm -v ${PWD}:/local openapitools/openapi-generator-cli generate -i https://developers.exlibrisgroup.com/wp-content/uploads/alma/openapi/conf.yaml -g python -o /local/out/python

    I think I’ll stick to the ExLibris-generated code for now anyway:)

    Thanks again!!

Leave a Reply