Git Product home page Git Product logo

cloudfier's Introduction

Cloudfier

Travis CI: Build Status on Travis CI

This repository contains the code for Cloudfier, a web-based environment for modeling with support for editing, testing, deploying and generating business applications based on executable models.

Just want to use Cloudfier?

If you just want to learn about Cloudfier, and how to use to develop model-based applications, stop reading this and instead head to the Cloudfier documentation.

Code organization

The components that make up Cloudfier are divided among subsystems:

  • kirra-mdd provides a Kirra compatible view over UML models.
  • codegen provides a code generation subsystem, including some code generators such as Expert4JEE.
  • runtime provides a model interpreter subsystem (containing components that are either UML-centric or Kirra-centric)
  • server provides the components required to support the Cloudfier Server environment (REST endpoints, UI generation, Orion integration, etc).
  • releng provides the components required to ship Cloudfier: product packaging, p2 repository etc.

Also, the TextUML Toolkit project, which has its own repository, provides a number of core components to Cloudfier: model repository management, front-end infrastructure and support for the primary notation (TextUML), and a number of model manipulation utilities.

Developing Cloudfier

Requirements

  • Java 8 or later
  • Maven 3.3.x
  • A git client
  • Postgres 9

You also need a database named "cloudfier" accessible (can create/drop schemas) to a user named "cloudfier" with no password.

Building

You can build and run the tests the usual way:

mvn clean install

Running

This is a work-in-progress. The instructions won't allow you yet to run a fully functional Cloudfier instance (steps for configuring the development environment integration are not included yet), but you can use this server via the cloudfier-maven-plugin (details below).

After building, you can run the tooling/runtime back-end this way (on Linux - the build is currently not generating packages for other platforms, help wanted here):

cd server/com.abstratt.kirra.server.product/
find target -name kirra-server

which will show the target platforms available, for example, on a Linux box:

target/products/com.abstratt.kirra.server.product/linux/gtk/x86_64/kirra-server

Change into the directory of choice, and run:

./kirra-server -data {path/to/workspace}

which will show:

!SESSION 2015-04-23 12:58:11.358 -----------------------------------------------
eclipse.buildId=unknown
java.version=1.8.0_31
java.vendor=Oracle Corporation
BootLoader constants: OS=linux, ARCH=x86, WS=gtk, NL=en_US
Command-line arguments:  -os linux -ws gtk -arch x86 -console -consolelog

!ENTRY com.abstratt.mdd.frontend.web 1 0 2015-04-23 12:58:12.095
!MESSAGE Started endpoint
	External: http://localhost/services/
	Internal: http://localhost/services/
osgi> 

Your local Cloudfier server is now up and running.

Using your local Cloudfier server via the cloudfier-maven-plugin

You can use those Cloudfier features exposed via the cloudfier-maven-plugin (starting with version 0.12.0 of the plugin). In order to do that, once the server is up and running, follow the instructions in the cloudfier-maven-plugin project, and make sure you always specify the -Dkirra.uri property pointing to your local instance, for example:

mvn com.abstratt:cloudfier-maven-plugin:publish -Dkirra.uri=http://localhost:8081/services

Running the Orion integration

In order to run a local development environment, you need to install Orion.

TBD

Continuous builds

Continuous builds run as GitHub actions.

Bumping the version

Example:

mvn org.eclipse.tycho:tycho-versions-plugin:2.5.0:set-version -DnewVersion=2.13.0-SNAPSHOT -DupdateVersionRangeMatchingBounds=true

Developing Cloudfier in the IDE

Requirements

  • Eclipse 2021-06 or newer(download)
  • M2E (Maven Integration for Eclipse) 1.7.x - Maven Integration for Eclipse (with Tycho support)
  • Xtext SDK 2.25.x (use their own update site)

You can easily obtain M2E, Xtext and EGit from the Eclipse Marketplace. If you don't have the Marketplace client installed (check Help > Eclipse Marketplace), install it from here: https://www.eclipse.org/mpc/

Importing the source code into Eclipse

Use the M2E import wizard (Import... > Maven > Existing Maven Projects) and point it to the root directory for the cloudfier git workspace. It should find all Cloudfier modules inside that directory.

After the sources are imported, you should choose the target definition file cloudfier/kirra-dependencies/kirra-dependencies.target as your target platform (Window > Preferences > Plug-n Development > Target Platform > Kirra Dependencies Target, or kirra-ide-dependencies.target if you also have the TextUML Toolkit source code loaded into Eclipse). Loading the target platform may take a long time (several minutes, much of it apparently stuck at 0%), so be patient and wait until the "Load Target Platform" job completes. Once it completes, you should have no error markers in your workspace.

Running Cloudfier as an Eclipse application

  1. Open the kirra.product product definition file (find it with Ctrl-Shift-R)
  2. Launch the product from the Testing section on the Overview page. For more information, see this
  3. From there on, you can use the launch configuration that was created during the previous step for relaunching Cloudfier

Licensing

The code in this repository is licensed under one of the following licenses: EPL or AGPL. Look for the closest LICENSE file for more details.

Related repositories

This is just for your information about where other related pieces live. You should NOT need to load any of the following respositories to develop Cloudfier.

cloudfier-examples

https://github.com/abstratt/cloudfier-examples

Simple Cloudfier applications that help demonstrate and validate Cloudfier. You can clone that repo into your Cloudfier repository and play with Cloudfier's features.

codegen-examples

https://github.com/abstratt/codegen-examples

Command-line (bash-only) tools for generating code for the target platforms supported in Cloudfier.

cloudfier-maven-plugin

https://github.com/abstratt/cloudfier-maven-plugin

Plugin that exposes the functionality of a Cloudfier server to a Maven build.

TextUML

https://github.com/abstratt/textuml

The core model compilation functionality using TextUML as front-end notation.

cloudfier's People

Contributors

abstratt avatar renovate-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

cloudfier's Issues

[jee-generator] failing with NPEs for stale cross-references

Had a model referring to an operation defined elsewhere, and the referred element no longer exists (it was removed but the mmodel not recompiled), so it is an unresolved proxy. Generator fails with NPEs trying to access the parent of the operation.

[mean-generator] invariants not being enforced

Must generate code enforcing invariants, such as:

    attribute price : Double
        (* Price mustbe $50 at least. *)
        invariant above_minimum { self.price >= 50.0 }
        (* Price cannot be above $500. *)
        invariant below_maximum { self.price <= 500.0 };

and:

    attribute year : Integer
        (* Year must be later than 1990 *)
        invariant above_minimum { (self.year > 1990) }
        (* Year cannot be in the future *)
        invariant below_maximum { self.year <= Date#today().year() };

[jee-generator] support for helper query methods

One may want to reuse some query logic in a model by defining a helper operation that takes a collection as a parameter and then applies a filter based on other parameters (so it can filter the input collection - which may come from any source - according to different criteria). The JEE generator is not dealing well with this, so we changed the models so that was no longer done (by inlining the helper operations).

See:

[mean-generator] avoid wrapping promises with additional promises

In the code below, there is no need to wrap the call to Examples.car() in a function:

        return q().all([
            q().then(function() {
                return Examples.car();
            }), ...

Since it returns a promise, we could be passing the result of that expression directly:

        return q().all([
            Examples.car(),
            ...
        ])...

[mean-generator] need to inline derived boolean properties used in conditions

For instance, in this model:

readonly attribute returned : Date;
derived readonly attribute inProgress : Boolean := {
    self.returned == null
};
static query currentForCustomer(c : Customer) : Rental;
begin
    return Rental extent.\any((l : Rental) : Boolean {
        (l.customer == c) and l.inProgress
    });
end;

inProgress is a derived property, and cannot be used as is in Mongoose queries. We currently generate code for the query method as:

rentalSchema.virtual('inProgress').get(function () {
    return this['returned'] == null;
});

rentalSchema.statics.currentForCustomer = function (c) {
    var me = this;
    return Q().then(function() {
        return Q.npost(me.model('Rental').find().where({
            $and : [ 
                { customer : c },
                { 'inProgress' : true }
            ]
        }).findOne(), 'exec', [  ]);
    });
};

but should be generating something like this:

rentalSchema.statics.currentForCustomer = function (c) {
    var me = this;
    return Q().then(function() {
        return Q.npost(me.model('Rental').find().where({
            $and : [ 
                { customer : c },
                { 'returned' : null }
            ]
        }).findOne(), 'exec', [  ]);
    });
};

[mean-generator] pipeline generator is, like, totally broken

The support for asynchronous JS (MEAN) code generation from UML models is not working yet.

Exhibit A: Behavior in the model (a functional test case):

operation rentalHistory();
begin
    var car, customer;
    begin
        car := Examples#newCar();
        customer := Examples#newCustomer();
        customer.rent(car);
        Assert#areEqual(1, customer.rentals.size());
        customer.finishRental();
    end;
    begin
        customer.rent(car);
        Assert#areEqual(2, customer.rentals.size());
    end;
end;

Exhibit B: Approximate desired output (minus known pending support for count):

test('rentalHistory', function(done) {
    var behavior = function() {
        var car;
        var customer;
        return q().all([
            Examples.newCar().then(function(call_newCar) {
                car = call_newCar;
            }), 
            Examples.newCustomer().then(function(call_newCustomer) {
                customer = call_newCustomer;
            })
        ]).then(function() {
            customer.rent(car);
        }).then(function() {
            return Rental.findOne({ _id : customer.rentals }).exec();
        }).then(function(read_rentals) {
            assert.equal(1, /*TBD*/count);
        }).then(function() {
            customer.finishRental();
        }).then(function() {
            customer.rent(car);
        }).then(function() {
            return Rental.findOne({ _id : customer.rentals }).exec();
        }).then(function(read_rentals) {
            assert.equal(2, /*TBD*/count);
        });
    };
    behavior().then(done, done);
});

Exhibit C: Behavior currently generated:

test('rentalHistory', function(done) {
    var behavior = function() {
        var car;
        var customer;
        return q().all([
            q().then(function() {
                return Examples.newCar();
            }), q().then(function() {
                return Examples.newCustomer();
            }).then(function(call_newCustomer) {
                customer = call_newCustomer;
            }), q().then(function() {
                customer.rent(car);
            }), q().then(function() {
                return Rental.findOne({ _id : customer.rentals }).exec();
            }).then(function(read_rentals) {
                assert.equal(1, /*TBD*/count);
            }), q().then(function() {
                customer.finishRental();
            }), q().all([
                q().then(function() {
                    customer.rent(car);
                }), q().then(function() {
                    return Rental.findOne({ _id : customer.rentals }).exec();
                }).then(function(read_rentals) {
                    assert.equal(2, /*TBD*/count);
                })
            ]).spread(function(call_rent, call_areEqual) {
                /*sink*/;
                /*sink*/;
            })
        ]).spread(function(call_newCar, add_customer, call_rent, call_areEqual, call_finishRental, block) {
            car = call_newCar;
            /*sink*/;
            /*sink*/;
            /*sink*/;
            /*sink*/;
            /*sink*/;
        });
    };
    behavior().then(done, done);
});

IOW, still quite a bit of stuff to be worked out...

[mean-generator] must save objects created during an action

The Rental object created in the rent action below is not being saved.

customerSchema.methods.rent = function (car) {
    var rental;
    var me = this;
    return Q().then(function() {
        return Q().then(function() {
            console.log("rental = new Rental();\n");
            rental = new Rental();
        });
    }).then(function() {
        return Q().then(function() {
            console.log("rental.customer = me._id;\nme.rentals.push(rental._id);\n");
            rental.customer = me._id;
            me.rentals.push(rental._id);
        });
    }).then(function() {
        return Q().then(function() {
            console.log("rental.car = car._id;\ncar.rentals.push(rental._id);\n");
            rental.car = car._id;
            car.rentals.push(rental._id);
        });
    }).then(function() {
        return Q().then(function() {
            console.log("car.carRented();\n");
            car.carRented();
        });
    }).then(function() {
        return Q.npost(me, 'save', [  ]).then(function(saveResult) {
            return saveResult[0];
        });
    });
};

[runtime] error running tests in cities app: Error finding converter for cities::State to convert

Error running tests in the cities application. There is an assumption in RuntimeObject that will treat a related object as a simple object if there is no association, which is the case if a DataType has a slot that is a Class (entity).

 cloudfier run-tests .
✘ cities_tests.Tests.populousStates
    Cause: java.lang.RuntimeException: Error finding converter for cities::State to convert cities::State#38 = {values: {name=California, acronym=CA} - children: {} - related: {}}
✔ cities_tests.Tests.statePopulation

[jee-generator] support for global preconditions

Support for global preconditions.

Example:

    (* Book a taxi that is currently available *)
    operation book(toRent : Taxi)
        (* No taxis available *)
        precondition {
            Taxi extent.exists((t : Taxi) : Boolean { not t.full })
        }
    ...

Show UML Diagram in Orion enviroment

Validating your designs by having a graphical notation (diagram) is very helpful. Currently the only way to do it is to copy the source code to eclipse and then use the graphviz addin.

Allow singletons

Sometimes classes do not need instantiation, e.g. in case their only purpose is to provide an operation.

[jee-generator] generation of "group by" queries

Implement generation of "group by" queries. Also, need to implement at least sum/one so the "cities" example works. Example of an aggregation query using the count aggregation function:

    static query openExpenseCountPerCategory() : { category : Category, count : Integer }[*];
    begin
        return Expense extent.select((e : Expense) : Boolean {
            e.status == Status#Submitted
        }).groupBy(
            (e : Expense) : Category { e.category }
        ).groupCollect((group : Expense[*]) : { category : Category, count : Integer } {
            {
                category := group.one().category,
                count := group.size()
            }
        });
    end;

And the code to be generated:

    public Collection<Tuple> openExpenseCountPerCategory() {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Tuple> cq = cb.createTupleQuery();
        Root<Expense> expense_ = cq.from(Expense.class);
        TypedQuery<Tuple> query = entityManager.createQuery(
            cq.multiselect(
                expense_.get("category").get("id").alias("category"), 
                cb.count(expense_).alias("count")
            )
            .where(cb.equal(expense_.get("status"), Expense.Status.Submitted))
            .groupBy(expense_.get("category").get("id"))
        );
        return query.getResultList();
    }

[jee-generator] generation of filtering for grouped results (having)

Related to issue #30. Example:

model crm;
class Customer
    attribute name : String;
    attribute title : String;              
    query countByTitle() : {title : String, customerCount : Integer} [*];
    begin
        return Customer extent.groupBy((c : Customer) : String {
            c.title
        }).groupCollect((group : Customer[*]) : {title:String, customerCount : Integer} {
            { 
                title := group.one().title,
                customerCount := group.size()
            }   
        }).select((counted : {title:String, customerCount : Integer}) : Boolean {
            counted.customerCount > 100
        });
    end;
end;
end.

produces (core only):

cq
    .groupBy(customer_.get("title"))
    .multiselect(customer_.get("title"), cb.count(customer_))
    .having(cb.greaterThan(cb.count(customer_), cb.literal(100)))

[jee-generator] support projecting attributes/values in JPA queries

Examples:

    static query expenseDetails() : { reporter : Employee, category: String, expenseAmount : Double }[*];
    begin
        return Expense extent.collect((e : Expense) : { reporter : Employee, category : String, expenseAmount : Double } {
            {
                reporter := e.employee,
                category := e.category.name,
                expenseAmount := e.amount
            }
        });
    end;

[mean-generator] derived conditions not working

Derived conditions are not working. This model:

Assert#isTrue(not car.available);

is currently resulting in this:

assert.strictEqual(car['available'], true);

but 'available' is a derived property:

derived attribute available : Boolean := {
    self.status == Status#Available
};

which maps to this:

carSchema.virtual('available').get(function () {
return this['status'] == "Available";
});

but somehow that is not currently working, why is yet TBD.

Presets in dropdowns

An existing many to one relationship does not show the actual setting once you call the edit screen. So for example, if I have a crude oil from a specified region (e.g. terra nova from europe) it doesn't necessarly show europe in the edit screen (e.g. asia pacific instead of europe in the dropdown). Instead it takes "the first one" for whatever reason.

building is failing

For two weeks now, due to a partial commit...

ERROR] 
ERROR:  SubQueryActionGenerator.xtend - 
41: The method generateTraverseRelationshipAction(InputPin, Property) of type SubQueryActionGenerator must override a superclass method.

[mean-generator] instance query operations not being generated

    derived attribute population  : Integer := { self.computePopulation() };
    query computePopulation() : Integer;
    begin
        return (self.cities.sum((c : City) : Integer { c.population }) as Integer);
    end;

produces this:

/*************************** DERIVED PROPERTIES ****************/

stateSchema.methods.getPopulation = function () {
    var me = this;
    return Q().then(function() {
        return me.computePopulation();
    }).then(function(computePopulationResult) {
        return computePopulationResult;
    });
};

computePopulation is nowhere to be seen though.

[runtime] renaming tuple slot names leads to missing data

Anonymous tuples with omitted/mismatched names pass type verification but data that changed name is not available downstream.

For instance, this query:

        return City extent.groupBy((c : City) : State {
            c.cityState
        }).groupCollect((cities : City[*]) : { : String,  : Integer} {
            {
                cityState := cities.one().cityState.abbreviation, 
                statePopulation := cities.sum((c : City) : Integer {
                    c.population
                })
            }
        }).select((aggregated : { cityState : String, statePopulation : Integer}) : Boolean {
            aggregated.statePopulation > threshold
        }).collect((aggregated : { acronym : String, statePopulation : Integer}) : String {
            aggregated.acronym
        });

would result in a collection with a an empty string (probabaly a sanitized null). For it to work, I had to change the final collect to:

        }).collect((aggregated : { cityState : String, statePopulation : Integer}) : String {
            aggregated.cityState
        });

because cityState is how the slot was named when the tuple data was projected (in groupCollect). We must either forbid those renames when consuming tuples (type clash), or we restructure the internal data so we can use the new names.

[mean-generator] accessing an entity instance may need to re-fetch it first

Consider the case:

    operation availableUponReturn();
    begin
        var car, customer;
        begin
            car := Examples#newCar();
            customer := Examples#newCustomer();
        end;
        begin
            Assert#isTrue(car.available);
            customer.rent(car);
        end;
        begin
            Assert#isTrue(not car.available);
            customer.finishRental();
        end;
        begin
            Assert#isTrue(car.available);
        end;
    end;

Reading the car variable in that last block should trigger a re-fetch - the state we had loaded before is from a different block/transaction/session and no longer current.

[jee-generator] query generation does not deal well with inheritance

For instance, in carserv:

    public Collection<Customer>  findByName(String firstName, String lastName) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<Customer> cq = cb.createQuery(Customer.class);
        Root<Customer> customer_ = cq.from(Customer.class);
        return entityManager.createQuery(
            cq.select(customer_).distinct(true).where(
                cb.or(
                    cb.equal(
                        cb.parameter(String.class, "lastName"),
                        person_.get("lastName")
                    ),
                    cb.equal(
                        cb.parameter(String.class, "firstName"),
                        person_.get("firstName")
                    )
                )
            )
        ).setParameter("firstName", firstName).setParameter("lastName", lastName).getResultList();
    }

The person_ alias is bogus, and results from the attributes being inherited from a Person class.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.