-
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 Workspace
without 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 category
was 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.json
andorg.eclipse.epsilon.emc.json.dt
) into the workspace viaGeneral > Existing projects into Workspace
. - Now right-click on
org.eclipse.crossmeter.workflow project
and selectMaven > Update Project....
- Copy the give
M2M_Environment_oxygen.launch
(for Windows 10) toorg.eclipse.crossmeter.workflow\org.eclipse.crossmeter.workflow.restmule.generator
and overwrite the old one. - Refresh project
org.eclipse.crossmeter.workflow.restmule.generator
in Eclipse. - Before running the
.launch
file, right-click on it,Run as > Run configurations....
Here go toPlug-ins
tab and click onAdd Required Plug-ins
. - Now you can run the
.launch
file TBA:How? - In the Runtime Eclipse import the
org.eclipse.crossmeter.workflow.restmule.generator
project as Maven Project. - Run
generateFromOAS.launch
as 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.