Skip to content
This repository has been archived by the owner on Dec 24, 2019. It is now read-only.

REST API Genertation

Gergő Balogh edited this page May 3, 2018 · 2 revisions

Generator

REST API Tutorial files

Install

First of all, I installed a new copy of Eclipse (Committers), just to make sure I start with a clean install.

Execute the following steps.

  1. Clone repository: https://github.com/patrickneubauer/crossminer-workflow
  2. Import projects from the cloned repo to empty Eclipse workspace:
    1. Import root via Maven > Existing Maven Projects,
    2. then the rest via General > Existing projects into Workspace without checking Search for nested projects.
  3. Install new software packages:
    1. Install Graphical Modeling Framework (GMF): http://marketplace.eclipse.org/content/graphical-modeling-framework-gmf-tooling [web page]
    2. Install Epsilon (Stable): http://download.eclipse.org/epsilon/updates/ [update site]
    3. Update Epsilon (Interim): http://download.eclipse.org/epsilon/interim/ [update site]
    4. Install Emfatic: http://download.eclipse.org/emfatic/update/ [update site]
      Note: I didn't see the the feature until Group items by category was unchecked.
    5. Install GMF tooling: http://download.eclipse.org/modeling/gmp/gmf-tooling/updates/releases/ [update site]
  4. Import the given json projects (org.eclipse.epsilon.emc.json and org.eclipse.epsilon.emc.json.dt) into the workspace via General > Existing projects into Workspace.
  5. Now right-click on org.eclipse.crossmeter.workflow project and select Maven > Update Project....
  6. Copy the give M2M_Environment_oxygen.launch (for Windows 10) to org.eclipse.crossmeter.workflow\org.eclipse.crossmeter.workflow.restmule.generator and overwrite the old one.
  7. Refresh project org.eclipse.crossmeter.workflow.restmule.generator in Eclipse.
  8. Before running the .launch file, right-click on it, Run as > Run configurations.... Here go to Plug-ins tab and click on Add Required Plug-ins.
  9. Now you can run the .launch file TBA:How?
  10. In the Runtime Eclipse import the org.eclipse.crossmeter.workflow.restmule.generator project as Maven Project.
  11. Run generateFromOAS.launch as you can see in the videos https://youtu.be/BJXuozHJPeg.
  12. Now, you should see it generating the project.
  13. For further steps, watch Patrick's videos .

Example

I've made a simple example for the generator. I've created an OpenAPI specification and then built it with the generator. You will see a minimalistic configuration: it contains a single path specification, which requires one number input parameter and then gives back the parameters square. The number input parameter is provided via the path, so it looks like the following: /square/{number}. You can replace the {number} with any number. The repsonse for this request is a JSON object, which looks like this:

{
    "squared": {squaredValue}
}

It contains only one field, named squared, which has the value of the square of the given number. From the specification the generator will provide us a Java Project which will implement the use of the specified API. It will provide us an interface to easily access the functionalities of the API. To use this interface, after the import of the proper dependencies, we only need to write a few lines:

ITestAPIApi api = TestAPIApi.createDefault(); //create an instance of the api interface (with default settings).
int number = 7; //The number to be squared.
IData<NumberValue> squared = api.getSquareNumberValueByNumber(number); //the actual use of the API descibed in the specification. It sends a request to the server.
NumberValue numberValue; //NumberValue is the class which represents the object received from the server
numberValue = squared.observe().blockingSingle(); //Get the actually received object from the response.
System.out.println("Squared:" + numberValue.getSquared()); //Print the received answer.

Everything, including the IEntityApi interface and the NumberValue class is generated by the generator. The former is a complete set of the functions provided by the API and the latter is an example of the container classes, which has the purpose of letting access through Java to the objects/field inside of the received JSON object. And once again, they are all generated from the OpenAPI specification.

So, to generate the mentioned Java Project, you have to put your OpenAPI specification file into the schemas folder inside the org.eclipse.crossmeter.workflow.restmule.generator project. I'll call mine as TestAPI.json, and it's made up of the following:

{
  "swagger": "2.0",
  "schemes": [
    "http"
  ],
  "host": "localhost:8080",
  "basePath": "/",
  "info": {
    "description": "This is a test API, only for demonstration of the generator.",
    "termsOfService": "",
    "title": "TestAPI",
    "version": "v1"
  },
  "consumes": [
    "application/json"
  ],
  "produces": [
    "application/json"
  ],
  "securityDefinitions": {
    
  },
  "paths": {
    "/square/{number}": {
      "get": {
        "description": "Square a number.",
        "parameters": [
          {
            "description": "The number to be squared",
            "in": "path",
            "name": "number",
            "required": true,
            "type": "integer"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "schema": {
              "$ref": "#/definitions/NumberValue"
            }
          }
        }
      }
    }
  },
  "definitions": {
    "NumberValue": {
      "properties": {
        "squared": {
          "type": "integer"
        }
      },
      "type": "object"
    }
  }
}

You can see here the path and the NumberValue object. They are well specified in this file, so the generator can create our project by on its own. Then we have to set the generator to use our new schema. To do this, open the build.xml in the generator project and modify the api and json.model.file property to testapi and schemas/TestAPI.json, respectively.

<!--API Variables -->
<property name="api" value="testapi" />
<property name="json.model.file" value="schemas/TestAPI.json" />

We are almost done, but we have to take one more small step. We have to provide an .eol file in the epsilon/util/fix/ folder in the generator project. Its name must be the same as the value of the api property in the previous step, so for this example we use the testapi.eol. The content of this file:

import "../restmule.eol";

var api = RestMule!API.all.first();

// RATE LIMITS

var search = new RestMule!RatePolicyScope;
search.scope = "Search";
var entity = new RestMule!RatePolicyScope;
entity.scope = "Entity";

// RATE POLICY

var policy = new RestMule!RatePolicy;
policy.scopes.add(entity);
policy.scopes.add(search);

var reset = new RestMule!ResponseHeader;
var resetInt = new RestMule!TInteger;
resetInt.label = "X-RateLimit-Reset";
reset.type = resetInt;
policy.reset = reset;

var limit = new RestMule!ResponseHeader;
var limitInt = new RestMule!TInteger;
limitInt.label = "X-RateLimit-Limit";
limit.type = limitInt;
policy.limit = limit;

var remaining = new RestMule!ResponseHeader;
var remainingInt = new RestMule!TInteger;
remainingInt.label = "X-RateLimit-Remaining";
remaining.type = remainingInt;
policy.remaining = remaining;

api.ratePolicy = policy;

// PAGINATION

var pagination= new RestMule!PaginationPolicy;
pagination.start = 1;
pagination.max = 10;
pagination.increment = 1;
pagination.maxPerIteration = 100;

var perIteration = new RestMule!Query; 
perIteration.description = "Items per page";
perIteration.required = false;
var type = new RestMule!TInteger;
type.label = "per_page";
type.name = type.label;
perIteration.type = type;
pagination.perIteration = perIteration;

var page = new RestMule!Query;
page.description = "Page identifier";
page.required = false;
var type1 = new RestMule!TInteger;
type1.label = "page";
type1.name = type1.label;
page.type = type1;
pagination.page = page;

var link = new RestMule!ResponseHeader;
link.description = "Page links";
var format = new RestMule!TFormattedString;
format.label = "Link";
format.name = format.label;
link.type = format;
pagination.links = link;

api.pagination = pagination; 

// WRAPPER

var wrapper = new RestMule!Wrapper;
wrapper.name = "Wrapper";
var items = new RestMule!ListType;
items.label = "items";
wrapper.items = items;
wrapper.totalLabel= "total_count";
wrapper.incompleteLabel = "incomplete_results";
api.pageWrapper = wrapper;

// ADD RATE & WRAPPER TO REQUESTS (FIXME)

for (r in RestMule!Request.all){
	if (r.parent.path.startsWith("/search")){
		r.scope = search;
	} else {
		r.scope = entity;
	}
	r.parameters.removeAll(r.parameters.select(p|p.instanceOf(RequestHeader)));
	for (resp in r.responses.select(s|s.responseType <> null)){
		resp.unwrap();
	}
}

/*  //////////
 * OPERATIONS
 *//////////
operation RestMule!ObjectType hasWrapper() : Boolean {
	var wrapper = RestMule!Wrapper.all.first;
	var lists = self.listFields.collect(a|a.label);
	return (not lists.isEmpty()) and lists
		.includes(wrapper.items.label);
}

operation RestMule!Response unwrap() : RestMule!ObjectType{
	if (self.responseType.instanceOf(ObjectType)){
		if (self.responseType.hasWrapper()){
			("Unwrapping : "+ self.responseType.name).println;
			var wrapper = RestMule!Wrapper.all.first;
			var name = self.responseType.name.println;
			self.responseType = self.responseType.println.listFields
				.select(b| b.label == wrapper.items.label).first.elements.first;			
			self.responseType.name = name;
			self.pageWrapped = true;
			self.responseType.description = "UNWRAPPED: " + self.responseType.description;
		}
	}
}

After this, we can run the generator and it will generate our Java Project from the specification.

In the attached zip you can find all of the releated projects and files. There is a spring-boot server, which can serve the request, a java client, which uses the generated project, an eclipse plug-in project which also uses the generated project, the generated project and the additional files for the generator. And there is one more project, which provides the proper dependecies for the plug-in project.