jtreminio/oseg

By jtreminio

Updated over 1 year ago

OSEG - OpenAPI SDK Example Generator

Image
Languages & frameworks
API management
Developer tools
0

616

jtreminio/oseg repository overview

OSEG - OpenAPI SDK Example Generator

Create (almost) ready to use SDK snippets for SDKs generated using openapi-generator using example data from your OpenAPI spec.

Examples

Several more examples can be found at jtreminio/oseg-examples.

Using the OpenAPITools/openapi-generator petstore.yaml definition, we can generate SDK examples from the addPet endpoint.

OpenAPI spec:

    post:
      tags:
        - pet
      summary: Add a new pet to the store
      description: ''
      operationId: addPet
      consumes:
        - application/json
        - application/xml
      produces:
        - application/xml
        - application/json
      parameters:
        - in: body
          name: body
          description: Pet object that needs to be added to the store
          required: true
          schema:
            $ref: '#/definitions/Pet'
      responses:
        '405':
          description: Invalid input
      security:
        - petstore_auth:
            - 'write:pets'
            - 'read:pets'

Generated SDK examples:

csharp
using System;
using System.Collections.Generic;
using System.IO;

using Org.OpenAPITools.Api;
using Org.OpenAPITools.Client;
using Org.OpenAPITools.Model;

namespace OSEG.PetStoreExamples;

public class AddPetExample
{
    public static void Run()
    {
        var config = new Configuration();
        config.AccessToken = "YOUR_ACCESS_TOKEN";

        var category = new Category(
            id: 12345,
            name: "Category_Name"
        );

        var tags1 = new Tag(
            id: 12345,
            name: "tag_1"
        );

        var tags2 = new Tag(
            id: 98765,
            name: "tag_2"
        );

        var tags = new List<Tag>
        {
            tags1,
            tags2,
        };

        var pet = new Pet(
            name: "My pet name",
            photoUrls: [
                "https://example.com/picture_1.jpg",
                "https://example.com/picture_2.jpg",
            ],
            id: 12345,
            status: Pet.StatusEnum.Available,
            category: category,
            tags: tags
        );

        try
        {
            var response = new PetApi(config).AddPet(
                pet: pet
            );

            Console.WriteLine(response);
        }
        catch (ApiException e)
        {
            Console.WriteLine("Exception when calling Pet#AddPet: " + e.Message);
            Console.WriteLine("Status Code: " + e.ErrorCode);
            Console.WriteLine(e.StackTrace);
        }
    }
}
java
package oseg.petstore_examples;

import org.openapitools.client.ApiException;
import org.openapitools.client.Configuration;
import org.openapitools.client.api.*;
import org.openapitools.client.auth.*;
import org.openapitools.client.model.*;

import java.io.File;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class AddPetExample
{
    public static void main(String[] args)
    {
        var config = Configuration.getDefaultApiClient();
        config.setAccessToken("YOUR_ACCESS_TOKEN");

        var category = new Category();
        category.id(12345L);
        category.name("Category_Name");

        var tags1 = new Tag();
        tags1.id(12345L);
        tags1.name("tag_1");

        var tags2 = new Tag();
        tags2.id(98765L);
        tags2.name("tag_2");

        var tags = new ArrayList<Tag>(List.of (
            tags1,
            tags2
        ));

        var pet = new Pet();
        pet.name("My pet name");
        pet.photoUrls(List.of (
            "https://example.com/picture_1.jpg",
            "https://example.com/picture_2.jpg"
        ));
        pet.id(12345L);
        pet.status(Pet.StatusEnum.AVAILABLE);
        pet.category(category);
        pet.tags(tags);

        try
        {
            var response = new PetApi(config).addPet(
                pet
            );

            System.out.println(response);
        } catch (ApiException e) {
            System.err.println("Exception when calling Pet#addPet");
            System.err.println("Status code: " + e.getCode());
            System.err.println("Reason: " + e.getResponseBody());
            System.err.println("Response headers: " + e.getResponseHeaders());
            e.printStackTrace();
        }
    }
}
php
<?php

namespace OSEG\PetStoreExamples;

require_once __DIR__ . '/../vendor/autoload.php';

use SplFileObject;
use OpenAPI;

$config = OpenAPI\Client\Configuration::getDefaultConfiguration();
$config->setAccessToken("YOUR_ACCESS_TOKEN");

$category = (new OpenAPI\Client\Model\Category())
    ->setId(12345)
    ->setName("Category_Name");

$tags_1 = (new OpenAPI\Client\Model\Tag())
    ->setId(12345)
    ->setName("tag_1");

$tags_2 = (new OpenAPI\Client\Model\Tag())
    ->setId(98765)
    ->setName("tag_2");

$tags = [
    $tags_1,
    $tags_2,
];

$pet = (new OpenAPI\Client\Model\Pet())
    ->setName("My pet name")
    ->setPhotoUrls([
        "https://example.com/picture_1.jpg",
        "https://example.com/picture_2.jpg",
    ])
    ->setId(12345)
    ->setStatus(OpenAPI\Client\Model\Pet::STATUS_AVAILABLE)
    ->setCategory($category)
    ->setTags($tags);

try {
    $response = (new OpenAPI\Client\Api\PetApi(config: $config))->addPet(
        pet: $pet,
    );

    print_r($response);
} catch (OpenAPI\Client\ApiException $e) {
    echo "Exception when calling Pet#addPet: {$e->getMessage()}";
}
python
from datetime import date, datetime
from pprint import pprint

from openapi_client import ApiClient, ApiException, Configuration, api, models

configuration = Configuration(
    access_token="YOUR_ACCESS_TOKEN",
)

with ApiClient(configuration) as api_client:
    category = models.Category(
        id=12345,
        name="Category_Name",
    )

    tags_1 = models.Tag(
        id=12345,
        name="tag_1",
    )

    tags_2 = models.Tag(
        id=98765,
        name="tag_2",
    )

    tags = [
        tags_1,
        tags_2,
    ]

    pet = models.Pet(
        name="My pet name",
        photoUrls=[
            "https://example.com/picture_1.jpg",
            "https://example.com/picture_2.jpg",
        ],
        id=12345,
        status="available",
        category=category,
        tags=tags,
    )

    try:
        response = api.PetApi(api_client).add_pet(
            pet=pet,
        )

        pprint(response)
    except ApiException as e:
        print("Exception when calling Pet#add_pet: %s\n" % e)

ruby
require "openapi_client"

OpenApiClient.configure do |config|
    config.access_token = "YOUR_ACCESS_TOKEN"
end

category = OpenApiClient::Category.new
category.id = 12345
category.name = "Category_Name"

tags_1 = OpenApiClient::Tag.new
tags_1.id = 12345
tags_1.name = "tag_1"

tags_2 = OpenApiClient::Tag.new
tags_2.id = 98765
tags_2.name = "tag_2"

tags = [
    tags_1,
    tags_2,
]

pet = OpenApiClient::Pet.new
pet.name = "My pet name"
pet.photo_urls = [
    "https://example.com/picture_1.jpg",
    "https://example.com/picture_2.jpg",
]
pet.id = 12345
pet.status = "available"
pet.category = category
pet.tags = tags

begin
    response = OpenApiClient::PetApi.new.add_pet(
        pet,
    )

    p response
rescue OpenApiClient::ApiError => e
    puts "Exception when calling Pet#add_pet: #{e}"
end
typescript-node
import * as fs from 'fs';
import api from "openapi_client"
import models from "openapi_client"

const apiCaller = new api.PetApi();
apiCaller.accessToken = "YOUR_ACCESS_TOKEN";

const category = new models.Category();
category.id = 12345;
category.name = "Category_Name";

const tags1 = new models.Tag();
tags1.id = 12345;
tags1.name = "tag_1";

const tags2 = new models.Tag();
tags2.id = 98765;
tags2.name = "tag_2";

const tags = [
  tags1,
  tags2,
];

const pet = new models.Pet();
pet.name = "My pet name";
pet.photoUrls = [
  "https://example.com/picture_1.jpg",
  "https://example.com/picture_2.jpg",
];
pet.id = 12345;
pet.status = models.Pet.StatusEnum.Available;
pet.category = category;
pet.tags = tags;

apiCaller.addPet(
  pet,
).then(response => {
  console.log(response.body);
}).catch(error => {
  console.log("Exception when calling Pet#addPet:");
  console.log(error.body);
});

Where does the example data come from?

Example data used to generate the code snippets can come from a number of places:

OSEG currently generates one code snippet file per requestBody definition, but only a single example per parameters definition.

In other words, you can generate multiple examples of the same operation/endpoint by having multiple requestBody definitions. Each generate example will have different data embedded within it. However, since parameters is tied to a single operation, the same data will apply to any and all requestBody definitions.

If you use a custom JSON file with examples you can define as many examples per endpoint as you want, since you can define each data source separately (query, path, header, cookie, and body) per endpoint.

Adding more SDKs

Examples are generated using (fairly) simple Jinja templates. Adding a new SDK requires a few steps:

  1. Create the Generator
  2. Create the Jinja template (Python example)

How to run

The entrypoint to this project is run.py. It currently supports two commands:

config-help

Prints all config options available to a given generator.

Run python3 run.py config-help --help for more details.

Or, with Docker: docker container run --rm jtreminio/oseg:latest config-help --help

Show config options for the python generator:

python3 run.py config-help -g python

Or, with Docker:

docker container run --rm jtreminio/oseg:latest config-help -g python
generate

Generates the SDK snippets and writes to specified location.

Run python3 run.py generate --help for more details.

Run with a local config file:

python3 run.py generate \
    -i openapi.yaml \
    -o python/src \
    --config-file config-python.yaml \
    --example-data-file example_data.json

Or, with Docker:

docker container run --rm -v $PWD:/app jtreminio/oseg:latest generate \
    -i openapi.yaml \
    -o python/src \
    --config-file config-python.yaml \
    --example-data-file example_data.json

Run with inline config values. These config values can be seen by running python3 run.py config-help -g {generator}:

python3 run.py generate \
    -i openapi.yaml \
    -o python/src \
    --generator-name python \
    --config '{
      "packageName": "openapi_client",
      "oseg.propertyNamingConvention": "camel_case",
      "oseg.security.api_key.api_key": "YOUR_API_KEY",
      "oseg.security.petstore_auth.access_token": "YOUR_ACCESS_TOKEN"
    }' \
    --example-data-file example_data.json

Or, with Docker:

docker container run --rm -v $PWD:/app jtreminio/oseg:latest generate \
    -i openapi.yaml \
    -o python/src \
    --generator-name python \
    --config '{
      "packageName": "openapi_client",
      "oseg.propertyNamingConvention": "camel_case",
      "oseg.security.api_key.api_key": "YOUR_API_KEY",
      "oseg.security.petstore_auth.access_token": "YOUR_ACCESS_TOKEN"
    }' \
    --example-data-file example_data.json

Tests

See tests directory.

Feature support

The aim of this project is to cover the most common use-cases in an OpenAPI spec.

Supported
  • Requests with and without formdata: openapi-generator will create a different interface if an endpoint's content-type has formdata (multipart/form-data or application/x-www-form-urlencoded) and everything else
  • Discriminators with allOf
Issues

The following are issues with the SDK code generated by openapi-generator.

  • allOf without a discriminator: Depending on the openapi-generator generator used, the generated SDK may be completely broken
    • typescript-node SDK code is generated broken by openapi-generator
    • Other SDKs currently supported by OSEG will usually just use a generic object type
  • anyOf
    • typescript-node generated SDK does not set data
    • php generated SDK does not set data
    • Other SDKs currently supported by OSEG generate a class
  • oneOf
    • typescript-node generated SDK does not set data
    • php generated SDK does not set data
    • Other SDKs currently supported by OSEG generate a class
  • Multiple values in type definition. New in OpenAPI 3.1 type can now be an array of supported types
    • c#, java, ruby Generate a class
    • python generates a class but does not use it, types property as the first value in type
    • php generates a class, data not set
    • typescript-node generates a class, data not set
  • openapi-generator generates a class for inline schema with type=object. If two or more Operations share identical inline schema definitions, openapi-generator will only generate a class for one of the Operations, and all Operations will reference this class. The name of the class depends on which Operation is read first by openapi-generator, but it may not reflect the first definition within the OAS file and thus is impossible for OSEG to know the name of the class ahead of time.

Tag summary

Content type

Image

Digest

sha256:3620663fc

Size

38.4 MB

Last updated

over 1 year ago

docker pull jtreminio/oseg