Alex Bowen - August 23, 2016

We are pleased to introduce another DreamFactory community member, Phil Schuler. You may have read one of his blog posts about DreamFactory. In this tutorial, he will be demonstrating how to generate SDK’s based on API service Swagger definitions in DreamFactory.

Phil is a Platform Architect and DevOps Engineer has a background in both development as well as IT operations. He is experienced with a wide range of modern programming languages for web and native development as well as infrastructure/platform stacks. Nowadays he is focusing on platform architecture and DevOps and IT security for startups and enterprise clients. This has spurred his interest in everything around Docker, microservices and IT automation, which led him to DreamFactory while evaluating technologies for a recent project. It’s now been successfully integrated as a vital part of that platform for 4 months.

DreamFactory uses Swagger to generate live API documentation for services. You can then use them instantly to try out your services without writing client application code. We also provide plenty of open-source server and client side resources already available to help with doc generation. This empowers developers developers to get up to speed fast with their API, and get a clear picture of how the API responds to requests with various parameters and options.

But, there is more you can do with a Swagger definition which you might not be aware of, such as generating self-contained client SDKs for almost any programming language you might think of. With this, we are excited to pass the reigns to Phil to walk you through how to save time and call generated functions for your API endpoints, rather than implement all the HTTP request/response and parameter parsing yourself.

Doesn’t that sound like an intriguing time-saver to you? Let’s get started.

Prerequisites

If you are on DF 2.2 or 2.2.1, make sure to run “composer update dreamfactory/df-core” on the command-line in your DreamFactory installation directory, so you have df-core 0.3.3 installed. You can verify under “Config->System Info”, right side, “Installed Packages”.

Create a custom PHP scripting service called “math” and use the following example code.

$verb = $event['request']['method'];

if($verb !== 'GET'){
    throw new DreamFactoryCoreExceptionsBadRequestException('Only HTTP GET is allowed on this endpoint.');
}

// get resource, /math —> "", /math/add —> "add"
$resource = $event['resource'];

// get query params from request
$params = $event['request']['parameters'];

$required = ['n1', 'n2'];

if(!empty($resource)){
	foreach($required as $element){
    	if(!isset($params[$element])){
        	throw new DreamFactoryCoreExceptionsBadRequestException('Missing '.$element.' in params.');
    	}
	}
	$n1 = $params['n1'];
	$n2 = $params['n2'];
}

switch ($resource) {
	case "":
    	// /math means return all supported resources
    	$result = ['resource' => ['add', 'subtract', 'multiply', 'divide']];
    	break;
	case "add":
    	$result = ['result' => ($n1 + $n2)];
    	break;
	case "subtract":
    	$result = ['result' => ($n1 - $n2)];
    	break;
	case "multiply":
    	$result = ['result' => ($n1 * $n2)];
    	break;
	case "divide":
    	if (!$n2) {
        	throw new DreamFactoryCoreExceptionsBadRequestException('Divide by zero error.');
    	}
    	$result = ['result' => ($n1 / $n2)];
    	break;
	default:
    	throw new DreamFactoryCoreExceptionsBadRequestException('Invalid or missing resource name.');
    	break;
}

return $result;

Then, add the following Swagger service definition. Since version 2.1.2 you can directly use this YAML format. For earlier versions you need to convert it to “Swagger JSON”. See DreamFactory’s blog post on how to do this.

swagger: '2.0'

info:
  version: "2.0"
  title: "Math Service"
  
parameters:
  n1:
    name: n1
    in: query
    description: First number
    required: true
    type: number
    format: integer
  n2:
    name: n2
    in: query
    description: Second number
    required: true
    type: number
    format: integer
  
paths:
  /math:
    get:
      tags:
      - math
      description: Resources available to this service.
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/MathResourceList'
        default:
          description: Error
          schema:
            $ref: '#/definitions/MathError'
  /math/add:
    get:
      tags:
        - math
      description: Add two numbers.
      parameters:
        - $ref: "#/parameters/n1"
        - $ref: "#/parameters/n2"
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/MathSuccess'
        default:
          description: Error
          schema:
            $ref: '#/definitions/MathError'
  /math/subtract:
    get:
      tags:
        - math
      description: Subtract two numbers.
      parameters:
        - $ref: "#/parameters/n1"
        - $ref: "#/parameters/n2"
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/MathSuccess'
        default:
          description: Error
          schema:
            $ref: '#/definitions/MathError'
  /math/multiply:
    get:
      tags:
        - math
      description: Multiply two numbers.
      parameters:
        - $ref: "#/parameters/n1"
        - $ref: "#/parameters/n2"
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/MathSuccess'
        default:
          description: Error
          schema:
            $ref: '#/definitions/MathError'
  /math/divide:
    get:
      tags:
        - math
      description: Divide two numbers.
      parameters:
        - $ref: "#/parameters/n1"
        - $ref: "#/parameters/n2"
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/MathSuccess'
        default:
          description: Error
          schema:
            $ref: '#/definitions/MathError'
definitions:
  MathResourceList:
    type: object
    properties:
      resource:
        type: array
        description: Array of accessible resources available to this service.
        items:
          type: string
  MathSuccess:
    type: object
    properties:
      result:
        type: integer
  MathError:
    type: object
    properties:
      code:
        type: integer
        format: int32
        description: Error code.
      message:
        type: string
        description: String description of the error.

By now, you should be able to go to API Docs and try out your custom service already. This is what you might have had before for your custom services as well as the generated ones such as MySQL DB, or AWS S3 service.

Now, we are going to utilize the Swagger definition further and leverage the full power of Swagger with DreamFactory.

This tutorial assumes you use the API key of an app which has a default role assigned. This role needs to grant access to the “math” service we just created. You can have a look at DreamFactory Tutorials: Authentication and Authorization if you want a detailed guide.

Generating the SDK

Once you have a Swagger definition ready, you can easily generate SDKs by going to https://editor.Swagger.io and pasting your definition there. Make sure it doesn’t show any errors at the top of the right pane. For our tutorial, copy & paste the definition provided above.

Now, go ahead and select “Generate Client” from the menu and you will be prompted with a whole bunch of possible languages/frameworks. Pick the one that suits you best and save the generated SDK.
For this tutorial, we will be using PHP. The approach stays the same no matter what language you will be using.

Screen_Shot_2016-08-23_at_6.03.43_PM.png

Advanced users can also use a CLI version of Swagger-codegen from the Swagger GitHub repo. That project relies on contributions for all those language generators. So pay them a visit if you find a bug or have something to add!

Making it work with DreamFactory

Now you have a SDK that’s almost ready to be used. There’s only one slight catch: Swagger doesn’t know about DreamFactory and therefore doesn’t know it has to send an X-DreamFactory-Api-Key Header and a session token along every request.

Here is a small sample script, using the SDK we just generated:

As you can see, we have to set the host of our DreamFactory API, which is quite obvious, but we also have to add a custom header for providing our API key.

If you don’t have a default role assigned to your app, you will first have to programmatically perform a login request and then add another custom header “X-DreamFactory-Session-Token” with the resulting JWT token right after adding the X-DreamFactory-Api-Key. The Login process is described here DreamFactory Tutorials: Logging in.

After you’ve completed the above steps successfully, run this example script, you should see:

30 + 12 equals 42 according to Math API
30 * 12 equals 360 according to Math API

Swagger-codegen will generate SDK functions for all your service endpoints + HTTP verbs. So, if you have a service named “foobar” with an endpoint of “baz” where you have to POST to, you will have an SDK function “foobarBazPost(..)” available. It will also generate Models for all non-primitive parameter/response objects you might define in you Swagger service specification.

Depending on the language and how the SDK template got implemented, there might not be a convenient config option for adding default headers, but most do. However, you can still fairly easily fix it up.

Find the class/function that’s responsible for actually doing the HTTP request. For e.g. PHP, it would be callApi(..) in ApiClient.php . Then you would want to add the header in there.

Conclusion

As a platform architect and DevOps, I successfully introduced this approach into a project which uses DreamFactory as BaaS. The API has two different services with a lot endpoints for two different types of users. For both services, a Swagger definition is maintained which enables the backend to offer “Live API Docs” for these services. The same definition is used to generate SDKs with Swagger-codegen for the mobile app, as well as other internal applications.

We encountered a few bugs in the Swagger-codegen templates for our languages which were fairly easy to fix. For instance, the Qt5/C++ SDK was missing datatype handlers for binary data (e.g. images) and DateTime. But, it was trivial to fix the SDK and also the upstream templates of the generator.

Overall, using the SDK still saves developers a lot time and reduces potential sources of error. If you come across a bug in the SDK, it’s a one time fix, generic for all your existing and future services.
If you have more than a handful of service endpoints, it’s worth trying out the SDK approach.

In the near future, you will also have the possibility to view the swagger definitions which DreamFactory generates in background for built-in service types like MySQL, MongoDB and all others. You could then also generate SDKs for accessing those services.

As you can see, Swagger is not only a way for having API Docs so you can quickly try them them out. Swagger definitions in DreamFactory are a powerful way of generating SDKs for your auto-generated API, which will save you a lot time during development.