apigee2openapi - A Node.Js command line tool to generate a OpenAPI 2.0 spec from Apigee API proxies

Hello Everyone,

We have a new tool that will help you connect a missing piece in the Apigee developer ecosystem. I am sure many of you love SmartDocs in the Apigee Developer Portal. SmartDocs, which allow you to create documentation for your APIs using a beautiful Swagger-like UI, are generated from either a WADL or a OpenAPI 2.0 specification. One thing that was missing, though, was out-of-the-box support for generating SmartDocs directly from your Apigee Edge API proxies.

The apigee2openapi tool will help you bridge that gap to some extent. In this article, I’ll describe apigee2openapi and show you how to use it.

apigee2openapi

apigee2openapi is a Node.JS command line tool that will help you convert an Apigee API proxy to a Swagger 2.0 spec that can be used to generate SmartDocs. (It does the opposite of what the swagger2api tool does: lets you generate Apigee API proxies from openapi 2.0 specs.)

Installation

Installing apigee2openapi is as simple as any other node module from NPM. Requirements to use this tool are Node.JS and NPM. You can install this tool by running the following command after you have Node.JS and NPM installed on your local machine.

$ sudo npm install -g apigee2openapi

Once you have apigee2openapi installed, you are ready to generate a OpenAPI 2.0 spec from API proxies. In this article, I’ll use the popular Swagger 2.0 Petstore API to first create an API proxy, then generate a OpenAPI 2.0 spec from that proxy.

Here’s what apigee2openapi does:

  • Queries for Apigee Edge API proxy details.
  • Downloads the API proxy bundle.
  • Unzips the bundle and reads the API proxy XML files.
  • Generates OpenAPI JSON files. (It currently supports only JSON. You can generate YAML from JSON using API Studio as described later in this article.)
  • apigee2openapi supports multiple proxy endpoints.

How to use apigee2openapi (Earlier called Apigee2Swagger)

First, create an Apigee API proxy that will later be converted to a OpenAPI 2.0 spec.

Create the API proxy

Attached to this article is the petStore sample API proxy that you can import into your own Apigee Edge environment. The following image shows this being done in the Edge management UI. (The process is described here in the Apigee documentation.)

You can also generate the API proxy from a OpenAPI 2.0 spec using the swagger2api tool using the following command.

$ swagger2api generateApi petStore -s <a href="http://petstore.swagger.io/v2/swagger.json">http://petstore.swagger.io/v2/swagger.json</a> -D -d /Users/Anil/Desktop/

Your petStore API proxy should look like the following once you have successfully imported it into your Edge environment.

Generate the OpenAPI spec

Now generate the OpenAPI 2.0 spec from the API Proxy using apigee2openapi

Run the following command in a terminal. Change the the path in option -d to the destination directory on your local machine where you want to download the API proxy and generated Swagger files.

$ apigee2openapi -d /Users/Anil/Documents

When you run the command, you’ll be prompted for the following:

  • Base URI ? https://api.enterprise.apigee.com
  • Org Name ? [your Edge organization name]
  • User ID ? [your Edge organization administrator email address]
  • Password ? [your Edge organization administrator password]
  • API Proxy Name ? [the name of the API proxy. In our case, it’s petStore]
  • Revision Number ? [the API proxy revision number]
  • API Proxy End Point ? [the API proxy target endpoint URL]

The OpenAPI 2.0 spec is generated from the Apigee API proxy and stored in the local directory specified; for example, /Users/Anil/Documents/petStore/petStore.json. The contents of petStore.json should look like this:

{
  "swagger": "2.0",
  "info": {
    "description": "Swagger Petstore",
    "version": "1.0.0",
    "title": "petStore",
    "contact": {
      "email": "asagar.nodeapp@gmail.com"
    }
  },
  "host": "asagarnodeapp-test.apigee.net",
  "schemes": [
    "https"
  ],
  "basePath": "/petStore",
  "paths": {
    "/pet": {
      "put": {
        "operationId": "updatePet",
        "summary": "Update an existing pet",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/pet/findByStatus": {
      "get": {
        "operationId": "findPetsByStatus",
        "summary": "Finds Pets by status",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/pet/findByTags": {
      "get": {
        "operationId": "findPetsByTags",
        "summary": "Finds Pets by tags",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/pet/{petId}": {
      "delete": {
        "operationId": "deletePet",
        "summary": "Deletes a pet",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        },
        "parameters": [
          {
            "name": "petId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ]
      }
    },
    "/pet/{petId}/uploadImage": {
      "post": {
        "operationId": "uploadFile",
        "summary": "uploads an image",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        },
        "parameters": [
          {
            "name": "petId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ]
      }
    },
    "/store/inventory": {
      "get": {
        "operationId": "getInventory",
        "summary": "Returns pet inventories by status",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/store/order": {
      "post": {
        "operationId": "placeOrder",
        "summary": "Place an order for a pet",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/store/order/{orderId}": {
      "delete": {
        "operationId": "deleteOrder",
        "summary": "Delete purchase order by ID",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        },
        "parameters": [
          {
            "name": "orderId",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ]
      }
    },
    "/user": {
      "post": {
        "operationId": "createUser",
        "summary": "Create user",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/user/createWithArray": {
      "post": {
        "operationId": "createUsersWithArrayInput",
        "summary": "Creates list of users with given input array",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/user/createWithList": {
      "post": {
        "operationId": "createUsersWithListInput",
        "summary": "Creates list of users with given input array",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/user/login": {
      "get": {
        "operationId": "loginUser",
        "summary": "Logs user into the system",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/user/logout": {
      "get": {
        "operationId": "logoutUser",
        "summary": "Logs out current logged in user session",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        }
      }
    },
    "/user/{username}": {
      "delete": {
        "operationId": "deleteUser",
        "summary": "Delete user",
        "responses": {
          "200": {
            "description": "successful operation"
          }
        },
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "required": true,
            "type": "string"
          }
        ]
      }
    }
  }
}

Now copy that JSON and use apistudio.io to test the generated OpenAPI 2.0 spec.

Navigate to http://apistudio.io/ and click Get Started to open the editor. In the menu, select File > Paste JSON, paste the JSON into the window (there’s currently a bug that won’t let you see the pasted text), and click Import. You’ll see the petStore Swagger doc in the right pane.

That’s it!

To view the doc in the Swagger UI, select File > View SwaggerUI, then click the Show/Hide link. The doc should look like the following. You can also go here and click Show/Hide to see a sample.


Please keep us posted with your comments / suggestions about apigee2openapi

Cheers,

Anil Sagar

5 Likes

This is awesome, @Anil Sagar! What a great tool. Please move it to whichever forum you want. View revisions to see the detailed edits. I added a couple of navigation tips to the Swagger UI section and updated the link to the example output. Ping me if you have any questions. (This comment is visible only to you and Apigeek moderators.)

Thank you @Floyd Jones :slight_smile:

Getting a segmentation fault for on-premises installation. Any idea what the issue could be?

Getting a segmentation fault. On-premises installation.

➜ apigee2swagger apigee2swagger -d .
? Base URI? [http://hub-646.hub.com:8080](http://hub-646.hub.com:8080)
? Organization? hub
? User Id? hubteam@hub.com
? Password? ************************
? API Proxy Name ? Eligibility
? Revision Number ? 1
? API Proxy End Point ? [http://hub-644.com:9001/rhfapi-1/v1](http://hub-644.com:9001/rhfapi-1/v1)
Downloaded Bundle from Apigee: ./Eligibility/Eligibility.zip
[Error: invalid signature: 0x8080014]
{ [Error: invalid code lengths set] errno: -3, code: 'Z_DATA_ERROR' }
[Error: invalid signature: 0x8080014]
{ [Error: invalid block type] errno: -3, code: 'Z_DATA_ERROR' }
[Error: invalid signature: 0x8080014]
{ [Error: invalid stored block lengths] errno: -3, code: 'Z_DATA_ERROR' }

[1] 88521 segmentation fault apigee2swagger -d .

@Michael Zatko , Which operating system are you using ? I haven’t tested it on windows.. I have tested only on mac, linux should work too…

Seems like bundle got downloaded, can you verify bundle contents are vaild ? It might be zip extraction error..

On a Mac.

I ran the script against another proxy and it worked. There must be something specific to the other proxy’s zip that is causing the error… not sure what that would be.

Also, I tried running the script against another local edge environment which has a locally signed SSL certificate on it and get the following:

{ [Error: unable to verify the first certificate] code: ‘UNABLE_TO_VERIFY_LEAF_SIGNATURE’ }

I have the certs trusted in my local keychain.

Relevant:

http://stackoverflow.com/questions/20082893/unable-to-verify-leaf-signature

I have an APIGEE service which I tried to create the swagger json. It was successful but the paths were empty.

Should the APIGEE proxy be created with Swagger docs for apigee2swagger to create the json with all the paths?

@Sudheendra.Singh , It creates paths only if conditional flows exist in your API proxy, otherwise there is no way to read paths information since paths data is only available during API runtime not in the proxy bundle.

I have added condition and it seems to be working.

I am now able to make the path dynamic via condition and enter data on SmartDoc request page.

I am able to edit the swagger json and laod it on SmartDocs and it works.

How can I make the header also dynamic such that I should be able to enter header data while submitting request?

Hi @Anil Sagar

Does this tool works on windows now? I tried testing this on windows for one of the proxy and it gave me error like - [Error: Received error 404 when fetching proxy: [object Object]]

Thanks,

Santosh

@santosh_ghalsasi , I haven’t tested it on windows. Above error seems like proxy not found. Are you sure you have given right proxy name ?

@Anil Sagar - Yes, I tried this with different proxies but still got same error.

Hello,

I tried to run the tool on a Windows environment using the petStore sample but received error 400. Is there a way to find out what went wrong?

C:\Users\xxx>apigee2openapi -d c:\users\xxx
? Base URI? [http://x.x.x.89:9001](http://x.x.x.89:9001)
? Organization? xxx-org
? User Id? xxx-user
? Password? **********
? API Proxy Name ? petStore
? Revision Number ? 1
? API Proxy End Point ? [http://x.x.x.89:9001](http://x.x.x.89:9001)
[Error: Received error 400 when fetching proxy: [object Object]]

@Joseph Chung

Welcome to Apigee Community. Are you sure Base URI is your Management API server URI ? Above tool failed to find the proxy using management API. Also, Are you using org admin credentials ?

Can you verify below curl call gives success response ? Update the base64 encode.

curl -X GET -H "Authorization: Basic XXXXXX" -H "Cache-Control: no-cache" "http://10.89.89.89:9001/v1/organizations/jaganr1000"

Hello @Anil Sagar,

Thanks for the guidance. I have re-executed the command with the correct Base URI, using an org admin credentials. Kindly refer to the output below:

C:\Users\xxx>apigee2openapi -d c:\users\xxx
? Base URI? [http://x.x.x.88:9000](http://x.x.x.88:9000)
? Organization? xxx-org
? User Id? xxx-user
? Password? **********
? API Proxy Name ? petStore
? Revision Number ? 1
? API Proxy End Point ? [http://x.x.x.89:9001](http://x.x.x.89:9001)
[Error: Received error 404 when fetching proxy:

<!DOCTYPE html>
<html>
<head>

...HTML 404 page, too long to be listed here...

Here is the curl response:

C:\Users\xxx>curl -X GET -H "Authorization: Basic xxx" -H "Cache-Control: no-cache" "http://x.x.x.88:9000/v1/organizations/xxx"

<!DOCTYPE html>
<html>
<head>

...HTML 404 page, too long to be listed here...<br>

Note: Actual Base64 encoded value removed from the above.

Appreciate if you can help to point out what went wrong. Will gladly provide more information if needed.

Thanks!

@Joseph Chung , Management UI runs on port 9000, Management API by default should be running on port 8080 , can you please try Base URI as “http://10.89.20.88:8080” ?

1 Like

Thank you @Anil Sagar! It seems to work now:

C:\Users\xxx>apigee2openapi -d c:\users\xxx
? Base URI? [http://x.x.x.88:8080](http://x.x.x.88:8080)
? Organization? xxx-org
? User Id? xxx-user
? Password? **********
? API Proxy Name ? petStore
? Revision Number ? 1
? API Proxy End Point ? [http://x.x.x.89:9001](http://x.x.x.89:9001)
Downloaded Bundle from Apigee: c:\users\xxx/petStore/petStore.zip
openapi JSON File successfully generated in : c:\users\xxx/petStore/petStore.json<br>
1 Like

Cool, Glad issue is resolved @Joseph Chung , Keep us posted moving forward if any.

I really liked this tool but when I try to use it i’m receiving a

[Error: invalid signature: 0x6a626f5b]

I tried it on multiple computers(windows and macs) and I get the same error. Has anybody else seen this issue?

@Ben Rodriguez , I haven’t seen this error. Looks like unzipping the bundle issue. Do you see proxy getting downloaded into your machine ? Do you see proxy bundle folder / zip file in your machine ?

BTB, On a separate note, Did you get a chance to play with http://specgen.apistudio.io/ ? It also helps to generate OpenAPI spec from existing API. Keep us posted.