-
Notifications
You must be signed in to change notification settings - Fork 13
REST API Genertation
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.
- Clone repository:
https://github.com/patrickneubauer/crossminer-workflow - Import projects from the cloned repo to empty Eclipse workspace:
- Import root via
Maven > Existing Maven Projects, - then the rest via
General > Existing projects into Workspacewithout checkingSearch for nested projects.
- Import root via
- Install new software packages:
- Install Graphical Modeling Framework (GMF): http://marketplace.eclipse.org/content/graphical-modeling-framework-gmf-tooling [web page]
- Install Epsilon (Stable): http://download.eclipse.org/epsilon/updates/ [update site]
- Update Epsilon (Interim): http://download.eclipse.org/epsilon/interim/ [update site]
- Install Emfatic: http://download.eclipse.org/emfatic/update/ [update site]
Note: I didn't see the the feature untilGroup items by categorywas unchecked. - Install GMF tooling: http://download.eclipse.org/modeling/gmp/gmf-tooling/updates/releases/ [update site]
- Import the given json projects (
org.eclipse.epsilon.emc.jsonandorg.eclipse.epsilon.emc.json.dt) into the workspace viaGeneral > Existing projects into Workspace. - Now right-click on
org.eclipse.crossmeter.workflow projectand selectMaven > Update Project.... - Copy the give
M2M_Environment_oxygen.launch(for Windows 10) toorg.eclipse.crossmeter.workflow\org.eclipse.crossmeter.workflow.restmule.generatorand overwrite the old one. - Refresh project
org.eclipse.crossmeter.workflow.restmule.generatorin Eclipse. - Before running the
.launchfile, right-click on it,Run as > Run configurations....Here go toPlug-instab and click onAdd Required Plug-ins. - Now you can run the
.launchfile TBA:How? - In the Runtime Eclipse import the
org.eclipse.crossmeter.workflow.restmule.generatorproject as Maven Project. - Run
generateFromOAS.launchas you can see in the videos https://youtu.be/BJXuozHJPeg. - Now, you should see it generating the project.
- For further steps, watch Patrick's videos .
- github client generation and execution example — https://youtu.be/BJXuozHJPeg
- github client generation example 2 — https://youtu.be/CIebrgRE4zI
- github client generation example — https://youtu.be/ltSNnSZRETA
- kafka twitter default example — https://youtu.be/3XZMMQyURVc
- kafka twitter workflow example 2 — https://youtu.be/Udqd-gaH4O4
- kafka twitter workflow example — https://youtu.be/DB03Rtfa5ZA
- MDEPopularityExample — https://youtu.be/PBeuOaqHngk
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.