abstratt / cloudfier Goto Github PK
View Code? Open in Web Editor NEWCloudfier is a model-driven tool for rapid development of business applications
Home Page: http://cloudfier.com
License: Other
Cloudfier is a model-driven tool for rapid development of business applications
Home Page: http://cloudfier.com
License: Other
Since each Mongoose model class is in its own file, and they may need to have mutual references, we end up with cycles in the require graph, which results in errors that are hard to track.
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(),
...
])...
Add support for executing actions via REST API.
Fix or remove the mean automated tests. They have been disabled for the time being.
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 })
}
...
Case in point:
class Article
attribute title : String;
attribute body : Memo;
attribute tags : String[*];
end;
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...
Implement generation of "exist" queries. Example:
Customer extent.exists((c : Customer) : Boolean { c.vip })
There is no existing support for subqueries yet.
The reference to 'this' resolves to the global object in promises. Need special handling when generating async code.
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.
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', [ ]);
});
};
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.
Apostrophes are a string delimiter in Javascript, and as such must be escaped in error messages produced from constraint comments.
And that is obviously wrong.
The code generated for association traversal seems backwards.
Sometimes classes do not need instantiation, e.g. in case their only purpose is to provide an operation.
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.
Test case links are broken since move to Orion 7. Links look like this:
http://localhost/edit/edit.html#/file/demo/cloudfier-examples/timetracker/tests.tuml
but should now look like this:
http://localhost/edit/edit.html#/file/demo-OrionContent/cloudfier-examples/timetracker/tests.tuml
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.
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() };
car.model = newModel._id
;
Probably because there is only the first line, the second one is omitted due to the association being unidirectional.
An attribute may declare constraints (invariants). Need to enforce those in the generated code.
TestIdentityAction should use ObjectId(value) when passing ids in comparisons, otherwise no matching will occur.
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;
If an operation is only issuable from some state, ensure it cannot be invoked under other states.
Implement support for JAX-RS endpoints.
or else they will fail due to an invalid value being set ('' as the default for strings) or missing value for a required field.
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();
}
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];
});
});
};
Implement support for count. Right now, generated code has just the work 'count' as a placeholder.
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
A class may declare invariants. Need to enforce them in the generated code.
We support that in the models, but we are not doing that in generated code.
...so a caller can ensure after the triggering invocation returns that the state has been properly updated, it was persisted, etc.
The progress on the JEE generator is all fine and dandy but it seems we broke the JSE generator in the process.
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:
Or else we will encounter a value set when null was expected.
I guess because we don't treat readonly attributes as required. Probably a mismatch between what required means for the UI and the database.
Case in point: Rental.returned used to be declared as required.
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.
...no kidding, we disabled them, lots of green path fish to fry.
An action may declare preconditions. Those should be enforced at the beginning of the method.
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.
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.
There are a bunch of code generation plugins that are no longer used. Get rid of them.
The JAX-RS resource being currently generated does not support links yet.
Actions are often (always?) asynchronous, callers should treat them as returning promises (for sequencing etc), even though actions as modeled usually (never?) return anything.
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.
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)))
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.