Giter Site home page Giter Site logo

ngs-doo / revenj Goto Github PK

View Code? Open in Web Editor NEW
268.0 20.0 44.0 24.28 MB

DSL Platform compatible backend

Home Page: https://dsl-platform.com

License: BSD 3-Clause "New" or "Revised" License

C# 71.89% JavaScript 1.25% Java 12.96% Scala 6.04% HTML 0.23% ASL 0.51% ASP.NET 0.01% TypeScript 6.78% CSS 0.33%

revenj's Introduction

Revenj

Revenj is a fast framework for .NET/JVM with advanced LINQ support for Postgres and Oracle databases. It's ideal to use as a REST-like service, but can also be used as a library from other frameworks such as ASP.NET, Spring...

DSL Platform will use Invasive software composition to integrate with Revenj, so developers can focus on rich modeling (NoSQL/ER hybrid on top of ORDBMS). DSL Platform will take care of various boilerplate and model evolution while Revenj will expose:

  • persistence
  • reporting
  • notifications
  • cache invalidation
  • event sourcing
  • security
  • serialization

and various other simple and complex features captured in the model. With Revenj you will stop writing PO*O and instead focus on domain model/business value.

How it works

Domain is described using various modeling building blocks in a DSL, for example:

module REST {
  aggregate Document(title) {
    string title;
    timestamp createdOn;
    Set<string(10)> tags { index; }
    Document? *parent;
    List<Part> parts;
    Properties metadata;
    persistence { history; }
  }
  value Part {
    Type type;
    string content;
    Queue<string> footnotes;
  }
  enum Type {
    Documentation;
    Specification;
    Requirement;
  }
  snowflake<Document> DocumentList {
    title;
    createdOn;
    tags;
    order by createdOn desc;
  }
}

Which gives you a DTO/POCO/POJO/POPO/POSO... across different languages, tables, types, views, functions specialized for supported ORDBMS, repositories, converters, serialization and various other boilerplate required by the supported frameworks. There are also a lot more modeling concepts which go beyond basic persistence features and cover reporting/customization/high performance aspects of the application.

The biggest benefit shows when you start changing your model and DSL compiler gives you not only the new DLL/jar, but also a SQL migration file which tries to preserve data if possible. SQL migration is created by the compiler analyzing differences between models, so you don't need to write manual migration scripts.

DSL compiler acts as a developer in your team which does all the boring work you would need to do, while providing high quality and high performance parts of the system.

Why to use it?

There are mostly two reasons to use it:

  • ease of development - but this is hard to explain since you need to experience it for yourself, but in a nutshell there are several benefits:
    • automatic migrations - when the model changes, SQL migration script will be provided. This means that developers can utilize typesafety of relational SQL database and get ease of use from JSON NoSQL solution
    • less manual boilerplate - most of the boilerplate (DTOs, repositories, ...) will be automatically maintained. This will speed up development and reduce the cost of changing the model
    • evolution - reduced cost of changing the model should allow for both fast prototyping and low maintenance cost later in the project
  • performance - DSL Platform will provide libraries for Revenj which are faster than anything you could write by hand or by picking some other libraries/frameworks

Benchmarks:

DAL bench

JSON bench

Framework bench

Getting started:

Setup a Postgres or Oracle instance.

Go through the tutorials:

Revenj features

Revenj is basically only a thin access layer to the domain captured in the DSL.

Some of the features/approaches available in the framework or precompiled library and just exposed through the framework:

  • advanced LINQ/JINQ driver - no impedance mismatch between database objects and .NET/JVM classes
  • event sourcing - capture events in the system. Bulk process events. Replay events to reconstruct aggregate roots
  • notification infrastructure - integrate with SignalR for push notification or invalidate cache to keep data up-to-date
  • plugin based architecture - almost everything is a plugin, from REST commands to LINQ/JINQ converters
  • signature based implementations - no need for convention or configuration, just add a new implementation or plugin and let it be picked up by the system
  • NoSQL modeling in relational database - let system automatically decompose and aggregate complex object hierarchies across several tables and data sources
  • AOP support - hot-patch your system with aspects when such scenario is most convenient
  • IOC/DI support - let container inject correct dependencies into your plugins or just provide alternative service implementation
  • permissions/security - inject security directly into DAL or handle it manually when appropriate
  • various serialization support - JSON/XML/Protobuf - others can be easily added
  • fast JSON serialization - let DSL Platform bake serialization directly into the model when JSON library is not fast enough
  • cache infrastructure - various caching features to provide excellent performance
  • WCF compatible REST API running on top of HttpListener
  • transactional mailer - queue mails while in transaction - rely on ACID offered by the databases
  • complex reporting features - provide several different data sources through a single DB call or a single REST call
  • integrate with S3 - store binary data outside of DB when required
  • and many others...

Usage examples:

DSL model:

module DAL {
  root ComplexObject {
    string name;
    Child[] children;
    timestamp modifiedAt { versioning; index; }
    List<VersionInfo> versions;
  }
  entity Child {
    int version;
    long? uncut;
    ip address;
    VersionInfo? info;        
  }
  value VersionInfo {
    Properties dictionary;
    Stack<Date> dates;
    decimal(3) quantity;
    set<decimal(2)?>? numbers;
  }
  SQL Legacy "SELECT id, name FROM legacy_table" {
    int id;
    string name;
  }
  event CapturedAction {
    ComplexObject pointInTimeSnapshot;
    Set<int> points { index; }
    list<location>? locations;
  }
  report Aggregation {
    int[] inputs;
    int? maxActions;
    CapturedAction[] actionsMatchingInputs 'a => a.points.Overlaps(inputs)' limit maxActions;
    List<ComplexObject> last5objects 'co => true' order by modifiedAt desc limit 5;
    Legacy[] matchingLegacy 'l => inputs.Contains(l.id)';
  }
}

results in same objects which can be consumed through IDataContext:

LINQ/JINQ query:

C#
IDataContext context = ...
string matchingKey = ...
var matchingObjects = context.Query<ComplexObject>().Where(co => co.versions.Any(v => v.dictionary.ContainsKey(matchingKey))).ToList();
var legacyObjects = context.Query<Legacy>().Where(l => l.name.StartsWith(matchingKey)).ToArray();
...
context.Update(matchingObjects);
Java
DataContext context = ...
String matchingKey = ...
List<ComplexObject> matchingObjects = context.query(ComplexObject.class).filter(co -> co.getVersions().anyMatch(v -> v.getDictionary().containsKey(matchingKey))).list();
Stream<Legacy> legacyObjects = context.query(Legacy.class).filter(l -> l.name.startsWith(matchingKey)).stream();
...
context.update(matchingObjects);

Lookup by identity:

ComplexObject is an aggregate root which is one of the objects identified by unique identity: URI. URI is a string version of primary key; which mostly differs on composite primary keys.

C#
IDataContext context = ...
string[] uris = ...
var foundObjects = context.Find<ComplexObject>(uris);
Java
DataContext context = ...
String[] uris = ...
List<ComplexObject> foundObjects = context.find(ComplexObject.class, uris);

Listening for change:

LISTEN/NOTIFY from Postgres and Advanced Queueing in Oracle are utilized to provide on commit information about data change.

C#
IDataContext context = ...
context.Track<CapturedAction>().Select(ca => ...);
Java
DataContext context = ...
context.track(CapturedAction.class).doOnNext(ca -> ...);

Populating report object:

Report can be used to capture various data sources at once and provide it as a single object.

C#
var report = new Aggregation { inputs = new [] { 1, 2, 3}, maxActions = 100 };
var result = report.Populate(locator); //provide access to various dependencies
Java
Aggregation report = new Aggregation().setInputs(new int[] { 1, 2, 3}).setMaxActions(100);
Aggregation.Result result = report.populate(locator); //provide access to dependencies

No abstractions, using ADO.NET:

IDatabaseQuery query = ...
string rawSql = ...
query.Execute(rawSql, params);

Adding event handler:

Event handlers are picked up by their signatures during system initialization in appropriate aspect. This means it's enough to write an implementation class and place DLL alongside others.

class CapturedActionHandler : IDomainEventHandler<CapturedAction[]> {
  private readonly IDataContext context;
  public CapturedActionHandler(IDataContext context) { this.context = context; }
  public void Handle(CapturedAction[] inputs) {
    ...
  }
}

Exposing simple custom REST service:

To add a custom REST service it's enough to implement specialized typesafe signature.

public class MyCustomService : IServerService<int[], List<ComplexObject>> {
  ...
  public MyCustomService(...) { ... }
  public List<ComplexObject> Execute(int[] arguments) { ... }
}

Registering custom access permissions:

By default permissions are checked against the singleton IPermissionManager. Custom permissions can be registered by hand if they don't really belong to the DSL.

C#
IPermissionManager permissions = ...
permissions.RegisterFilter<CapturedAction>(it => false, "Admin", false); //return empty results for everybody who are not in Admin role
Java
PermissionManager permissions = ...
permissions.registerFilter(CapturedAction.class, it -> false, "Admin", false); //return empty results for everybody who are not in Admin role

External tools and libraries

DSL can be written in Visual studio with the help of DDD for DSL plugin. There is also syntax highlighting plugin for IntelliJ IDEA

Revenj can be also used as a NoSQL database through a REST API and consumed from other languages:

Various tools can be used to setup environment/compilation:

revenj's People

Contributors

hperadin avatar kobus-smit avatar kzaher avatar melezov avatar mladens avatar mskalnik avatar nutrija avatar ogmzh avatar rpetrano avatar tindzk avatar zapov avatar zrajnis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

revenj's Issues

Default values

Does DSL-Platform / Revenj support default values for aggregate properties?

For example when I create a new create a new Customer I want it's IsActive property to default to true.

DomainEventHandler failed resolution may display incorrect message

When submitting an event, following error can display:
Can't find domain event store for Some.Event. Is Some.Event a domain event and does it have registered store

This message is incorrect when any of event handler's dependencies fail to instantiate. Further, the message can mask true error/exception message. For example:

public class SomeEventProcessor : IDomainEventHandler<Some.Event>
{
    public SomeEventProcessor(Mailer mailer) {
        ...

public class Mailer
{
    public Mailer() {
        throw new ConfigurationsErrorException("I'm missing some setting");

In this case, a "Can't find domain event..." message will be displayed, instead of Mailer's message.

Default date/timestamp values

When persisting an object through Crud.svc by POSTing an empty JSON with 0 properties, a

Date of 0001-01-01

and

Timestamp of 0001-01-01T00:00:00 (no timezone!)

should be persisted.

As it is now, current Date and current Timestamp are persisted instead.

Disable DSL Compile

is it possible to only compile the dsl only when we update the dsl file? right now it always compiling dsl and creating automigrate like this everytime i run the spring boot even though i did not update the dsl file

[INFO] --- dsl-platform-maven-plugin:1.1.0:sql-migration (default) @ revenj-boot ---
[info] Creating SQL migration for Postgres ...
[info] Migration saved to /home/aldo/IdeaProjects/revenjboot/sql/postgres-sql-migration-1483433536783.sql
[info] Applying migration...
[info] Database migrated and script renamed to: applied-postgres-sql-migration-1483433536783.sql
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ revenj-boot ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 3 resources
[INFO] Copying 11 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.3:compile (default-compile) @ revenj-boot ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 87 source files to /home/aldo/IdeaProjects/revenjboot/target/classes

Auditing

What is the most conscious way to implement auditing in revenj? Ideally 'envers' like, but any idea would be welcome.

Fix issues with Dryioc

Dryioc is added as alternative container, but it still doesn't work correctly.
Go though the issues and make it work.

Check if aggregate object is New?

Using the Client library objects, how do I know if an aggregate is "New"?

I see the Update() method uses the internal __locator field to determine if it is new:

public global::Module1.Aggr1 Update()
{
  if (__locator == null) throw new ArgumentException("Can't update new aggregate");
  ...

returnInstance command parameter

Add additional parameter to Crud/Update/... etc commands so client can control if he wants a full instance returned.

By default this can be true (compatible with current behavior and makes sense)

Cast between objects from generated models and preloading aggregates

Hi Team

I have a situation where I want to call a "Load" IServerService method from my service plugin and it should return to the client a custom object containing an IAggregateRoot object.

The client can then update the aggregate root and send it back by calling the "Save" service plugin method.

The custom object's class is in an assembly shared between the client and the server plugin.

So my shared assembly references the "POCO library" dll
And my client references the "Client library" generated by DSL Platform and also the "POCO library".
And my service plugin references the "Revenj for Postgres" dll and also the "POCO library".

I had to use "extern alias" to use those same types that resides in different assemblies.

  1. How do I cast/copy between a POCO object and a Client object
    or between a POCO object and Revenj for Postgress object?
    Do I need to manually copy the properties or use Automapper or something?

  2. It is probably not correct to use 3 different model dlls? What is best practise?
    But if I for example use the "Revenj for Postgres" dll in my service plugin and also in my WinForms client then I "loose" nice functionality like .Search and .FindAll?

  3. How do I send a "preloaded" aggregate object as parameter? My aggregate object has reference properties so it will only lazy load them. I want to send the fully loaded aggregate object to the client to prevent the additional lazy loading server round trips that could be slow because of a bad latency network.
    So I call all the reference properties (server side) and then the aggregate object is "preloaded"/"fully hydrated". But it does not seem to serialize the reference properties (because they do not have DataMember attributes?) so I do not receive the full object client side.

Snowflakes and reports work great but they are readonly, so I can't use them as DTOs.

Can you please advise me?

If my questions are not clear enough I can prepare a sample project and send it to you.
Much appreciated.

Regards

BoolConverter java issue with nullables

Simple example dsl:

module Test {
  root TestIt{
    Bool? nesto; 
  }
}

When compiling java revenj, I get following error:

Error during Revenj.Java library compilation.
com/example/Test/converters/TestItConverter.java:88: error: cannot find symbol
        items[__index___nesto] = org.revenj.postgres.converters.BoolConverter.toTupleNullable(instance.getNesto());
                                                                             ^
  symbol:   method toTupleNullable(Boolean)
  location: class BoolConverter
com/example/Test/converters/TestItConverter.java:98: error: cannot find symbol
        items[__index__extended_nesto] = org.revenj.postgres.converters.BoolConverter.toTupleNullable(instance.getNesto());
                                                                                     ^
  symbol:   method toTupleNullable(Boolean)
  location: class BoolConverter
2 errors

Left join in dsl

Given the model:

module Test {
  
  aggregate Place {
    string Name { unique; }
  }  

  aggregate User {
    string Username { unique; }
  }

  aggregate UserFavouritePlace(UserID, PlaceID) {
    User *User;
    Place *Place;
  }
}

I'd like to return the all places, showing a specific user's favourites first:

select p."Name", f."UserID" is not null "IsFavourite"
from "Test"."Place" p
left join "Test"."UserFavouritePlace" f on f."PlaceID" = p."ID" and f."UserID" = 1001
order by 2 desc, 1

Is it possible to define the above in the dsl? Or should I write a service side LINQ join query?

Mixin error compiler in revenj java

snippet:

module trading{
    mixin AlertScenario {
        Int partnerID;
    }

    value Alert1 {
        has mixin AlertScenario;
        String prop1;
    }

    value Alert2 {
        has mixin AlertScenario;
        String prop2;
    }

    event AlertEventLog {
        AlertScenario alert;
    }
}

This fails to compile in revenj java.

Unhandled Exception:
Revenj.Common.FrameworkException: Unresolved tokens in xc.converters.AlertScenarioConverter ---> System.FormatException: /*
* Created by DSL Platform
* v1.5.5948.20559 
*/

package xc.converters;



import java.io.*;
import java.util.*;
import java.util.stream.*;
import org.revenj.postgres.*;
import org.revenj.postgres.converters.*;

public class AlertScenarioConverter implements ObjectConverter<xc.AlertScenario> {

    @SuppressWarnings("unchecked")
    public AlertScenarioConverter(List<ObjectConverter.ColumnInfo> allColumns) throws java.io.IOException {
        Optional<ObjectConverter.ColumnInfo> column;


        final java.util.List<ObjectConverter.ColumnInfo> columns =
                allColumns.stream().filter(it -> "xc".equals(it.typeSchema) && "AlertScenario".equals(it.typeName))
                .collect(Collectors.toList());
        columnCount = columns.size();

        readers = new ObjectConverter.Reader[columnCount];
        for (int i = 0; i < readers.length; i++) {
            readers[i] = (instance, rdr, ctx) -> { StringConverter.skip(rdr, ctx); return instance; };
        }

        final java.util.List<ObjectConverter.ColumnInfo> columnsExtended =
                allColumns.stream().filter(it -> "xc".equals(it.typeSchema) && "-ngs_AlertScenario_type-".equals(it.typeName))
                .collect(Collectors.toList());
        columnCountExtended = columnsExtended.size();

        readersExtended = new ObjectConverter.Reader[columnCountExtended];
        for (int i = 0; i < readersExtended.length; i++) {
            readersExtended[i] = (instance, rdr, ctx) -> { StringConverter.skip(rdr, ctx); return instance; };
        }

        column = columns.stream().filter(it -> "partnerID".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'partnerID' column in xc AlertScenario. Check if DB is in sync");
        __index___partnerID = (int)column.get().order - 1;

        column = columnsExtended.stream().filter(it -> "partnerID".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'partnerID' column in xc AlertScenario. Check if DB is in sync");
        __index__extended_partnerID = (int)column.get().order - 1;

        column = columns.stream().filter(it -> "xc.Alert1".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert1' column in xc AlertScenario. Check if DB is in sync");
        __obect_index__xc_Alert1 = (int)column.get().order - 1;

        column = columnsExtended.stream().filter(it -> "xc.Alert1".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert1' column in xc AlertScenario. Check if DB is in sync");
        __obect_index_extended_xc_Alert1 = (int)column.get().order - 1;

        column = columns.stream().filter(it -> "xc.Alert2".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert2' column in xc AlertScenario. Check if DB is in sync");
        __obect_index__xc_Alert2 = (int)column.get().order - 1;

        column = columnsExtended.stream().filter(it -> "xc.Alert2".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert2' column in xc AlertScenario. Check if DB is in sync");
        __obect_index_extended_xc_Alert2 = (int)column.get().order - 1;/* factory initialize */
    }

    public void configure(org.revenj.patterns.ServiceLocator locator) {
        /* factory setup */


        readers[__index___partnerID] = (item, reader, context) -> { org.revenj.postgres.converters.IntConverter.parse(reader)(reader, context); return item; };
        readers[__index__extended_partnerID] = (item, reader, context) -> { org.revenj.postgres.converters.IntConverter.parse(reader)(reader, context); return item; };
            __converter_xc$Alert1 = locator.resolve(xc.converters.Alert1Converter.class);
            readers[__obect_index__xc_Alert1] = (item, reader, context) -> { xc.Alert1 inst = __converter_xc$Alert1.from(reader, context); return item != null ? item : inst; };
            readersExtended[__obect_index_extended_xc_Alert1] = (item, reader, context) -> { xc.Alert1 inst = __converter_xc$Alert1.fromExtended(reader, context); return item != null ? item : inst; };
            __converter_xc$Alert2 = locator.resolve(xc.converters.Alert2Converter.class);
            readers[__obect_index__xc_Alert2] = (item, reader, context) -> { xc.Alert2 inst = __converter_xc$Alert2.from(reader, context); return item != null ? item : inst; };
            readersExtended[__obect_index_extended_xc_Alert2] = (item, reader, context) -> { xc.Alert2 inst = __converter_xc$Alert2.fromExtended(reader, context); return item != null ? item : inst; };/* factory configure */
    }

    @Override
    public String getDbName() {
        return "\"xc\".\"AlertScenario\"";
    }

    @Override
    public xc.AlertScenario from(PostgresReader reader) throws java.io.IOException {
        return from(reader, 0);
    }

    private xc.AlertScenario from(PostgresReader reader, int outerContext, int context, ObjectConverter.Reader<xc.AlertScenario>[] readers) throws java.io.IOException {
        reader.read(outerContext);
        xc.AlertScenario instance = null;
        for (ObjectConverter.Reader<xc.AlertScenario> rdr : readers) {
            instance = rdr.read(instance, reader, context);
        }
        /* object finalized */
        reader.read(outerContext);
        return instance;
    }

    @Override
    public PostgresTuple to(xc.AlertScenario instance) {
        if (instance == null) return null;
        PostgresTuple[] items = new PostgresTuple[columnCount];

        items[__index___partnerID] = org.revenj.postgres.converters.IntConverter.toTuple(instance.getPartnerID());
        if (instance instanceof xc.Alert1)items[__obect_index__xc_Alert1] = __converter_xc$Alert1.to((xc.Alert1)instance);
        if (instance instanceof xc.Alert2)items[__obect_index__xc_Alert2] = __converter_xc$Alert2.to((xc.Alert2)instance);/* factory get properties */
        return RecordTuple.from(items);
    }

    public PostgresTuple toExtended(xc.AlertScenario instance) {
        if (instance == null) return null;
        PostgresTuple[] items = new PostgresTuple[columnCountExtended];

        items[__index__extended_partnerID] = org.revenj.postgres.converters.IntConverter.toTuple(instance.getPartnerID());
        if (instance instanceof xc.Alert1)items[__obect_index_extended_xc_Alert1] = __converter_xc$Alert1.toExtended((xc.Alert1)instance);
        if (instance instanceof xc.Alert2)items[__obect_index_extended_xc_Alert2] = __converter_xc$Alert2.toExtended((xc.Alert2)instance);/* factory get extended properties */
        return RecordTuple.from(items);
    }



    private final int columnCount;
    private final ObjectConverter.Reader<xc.AlertScenario>[] readers;

    public xc.AlertScenario from(PostgresReader reader, int context) throws java.io.IOException {
        int cur = reader.read();
        if (cur == ',' || cur == ')') return null;
        xc.AlertScenario instance = from(reader, context, context == 0 ? 1 : context << 1, readers);
        reader.read();
        return instance;
    }

    public xc.AlertScenario from(PostgresReader reader, int outerContext, int context) throws java.io.IOException {
        return from(reader, outerContext, context, readers);
    }
    private final int columnCountExtended;
    private final ObjectConverter.Reader<xc.AlertScenario>[] readersExtended;

    public xc.AlertScenario fromExtended(PostgresReader reader, int context) throws java.io.IOException {
        int cur = reader.read();
        if (cur == ',' || cur == ')') return null;
        xc.AlertScenario instance = from(reader, context, context == 0 ? 1 : context << 1, readersExtended);
        reader.read();
        return instance;
    }

    public xc.AlertScenario fromExtended(PostgresReader reader, int outerContext, int context) throws java.io.IOException {
        return from(reader, outerContext, context, readersExtended);
    }
    private final int __index___partnerID;
    private final int __index__extended_partnerID;
    private final int __obect_index__xc_Alert1;
    private xc.converters.Alert1Converter __converter_xc$Alert1;
    private final int __obect_index_extended_xc_Alert1;
    private final int __obect_index__xc_Alert2;
    private xc.converters.Alert2Converter __converter_xc$Alert2;
    private final int __obect_index_extended_xc_Alert2;/* factory body */
}
 ---> Revenj.Common.FrameworkException: Could not replace all tokens! 
Missing tokens: 
/* postgres ctor */
/* postgres extended ctor */
  at s.an () <0x41a637a0 + 0x001f7> in <filename unknown>:0 
  at x.a (w A_0) <0x41a63690 + 0x0002d> in <filename unknown>:0 
  at t.y () <0x41a633f0 + 0x000e7> in <filename unknown>:0 
  --- End of inner exception stack trace ---
  --- End of inner exception stack trace ---
  at t.y () <0x41a633f0 + 0x00217> in <filename unknown>:0 
  at h+c.y () <0x41a633c0 + 0x0001b> in <filename unknown>:0 
  at j.a (Boolean A_0, a A_1, bpf A_2, IEnumerable`1 A_3, System.Action`1 A_4, System.Func`1 A_5) <0x419ca740 + 0x003f7> in <filename unknown>:0 
  at j.a (bpf A_0, a A_1, System.String[] A_2, System.Action`1 A_3, System.Func`1 A_4) <0x41502770 + 0x002e3> in <filename unknown>:0 
  at h.a (System.String[] A_0) <0x41469d80 + 0x0070b> in <filename unknown>:0 
[ERROR] FATAL UNHANDLED EXCEPTION: Revenj.Common.FrameworkException: Unresolved tokens in xc.converters.AlertScenarioConverter ---> System.FormatException: /*
* Created by DSL Platform
* v1.5.5948.20559 
*/

package xc.converters;



import java.io.*;
import java.util.*;
import java.util.stream.*;
import org.revenj.postgres.*;
import org.revenj.postgres.converters.*;

public class AlertScenarioConverter implements ObjectConverter<xc.AlertScenario> {

    @SuppressWarnings("unchecked")
    public AlertScenarioConverter(List<ObjectConverter.ColumnInfo> allColumns) throws java.io.IOException {
        Optional<ObjectConverter.ColumnInfo> column;


        final java.util.List<ObjectConverter.ColumnInfo> columns =
                allColumns.stream().filter(it -> "xc".equals(it.typeSchema) && "AlertScenario".equals(it.typeName))
                .collect(Collectors.toList());
        columnCount = columns.size();

        readers = new ObjectConverter.Reader[columnCount];
        for (int i = 0; i < readers.length; i++) {
            readers[i] = (instance, rdr, ctx) -> { StringConverter.skip(rdr, ctx); return instance; };
        }

        final java.util.List<ObjectConverter.ColumnInfo> columnsExtended =
                allColumns.stream().filter(it -> "xc".equals(it.typeSchema) && "-ngs_AlertScenario_type-".equals(it.typeName))
                .collect(Collectors.toList());
        columnCountExtended = columnsExtended.size();

        readersExtended = new ObjectConverter.Reader[columnCountExtended];
        for (int i = 0; i < readersExtended.length; i++) {
            readersExtended[i] = (instance, rdr, ctx) -> { StringConverter.skip(rdr, ctx); return instance; };
        }

        column = columns.stream().filter(it -> "partnerID".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'partnerID' column in xc AlertScenario. Check if DB is in sync");
        __index___partnerID = (int)column.get().order - 1;

        column = columnsExtended.stream().filter(it -> "partnerID".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'partnerID' column in xc AlertScenario. Check if DB is in sync");
        __index__extended_partnerID = (int)column.get().order - 1;

        column = columns.stream().filter(it -> "xc.Alert1".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert1' column in xc AlertScenario. Check if DB is in sync");
        __obect_index__xc_Alert1 = (int)column.get().order - 1;

        column = columnsExtended.stream().filter(it -> "xc.Alert1".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert1' column in xc AlertScenario. Check if DB is in sync");
        __obect_index_extended_xc_Alert1 = (int)column.get().order - 1;

        column = columns.stream().filter(it -> "xc.Alert2".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert2' column in xc AlertScenario. Check if DB is in sync");
        __obect_index__xc_Alert2 = (int)column.get().order - 1;

        column = columnsExtended.stream().filter(it -> "xc.Alert2".equals(it.columnName)).findAny();
        if (!column.isPresent()) throw new java.io.IOException("Unable to find 'xc.Alert2' column in xc AlertScenario. Check if DB is in sync");
        __obect_index_extended_xc_Alert2 = (int)column.get().order - 1;/* factory initialize */
    }

    public void configure(org.revenj.patterns.ServiceLocator locator) {
        /* factory setup */


        readers[__index___partnerID] = (item, reader, context) -> { org.revenj.postgres.converters.IntConverter.parse(reader)(reader, context); return item; };
        readers[__index__extended_partnerID] = (item, reader, context) -> { org.revenj.postgres.converters.IntConverter.parse(reader)(reader, context); return item; };
            __converter_xc$Alert1 = locator.resolve(xc.converters.Alert1Converter.class);
            readers[__obect_index__xc_Alert1] = (item, reader, context) -> { xc.Alert1 inst = __converter_xc$Alert1.from(reader, context); return item != null ? item : inst; };
            readersExtended[__obect_index_extended_xc_Alert1] = (item, reader, context) -> { xc.Alert1 inst = __converter_xc$Alert1.fromExtended(reader, context); return item != null ? item : inst; };
            __converter_xc$Alert2 = locator.resolve(xc.converters.Alert2Converter.class);
            readers[__obect_index__xc_Alert2] = (item, reader, context) -> { xc.Alert2 inst = __converter_xc$Alert2.from(reader, context); return item != null ? item : inst; };
            readersExtended[__obect_index_extended_xc_Alert2] = (item, reader, context) -> { xc.Alert2 inst = __converter_xc$Alert2.fromExtended(reader, context); return item != null ? item : inst; };/* factory configure */
    }

    @Override
    public String getDbName() {
        return "\"xc\".\"AlertScenario\"";
    }

    @Override
    public xc.AlertScenario from(PostgresReader reader) throws java.io.IOException {
        return from(reader, 0);
    }

    private xc.AlertScenario from(PostgresReader reader, int outerContext, int context, ObjectConverter.Reader<xc.AlertScenario>[] readers) throws java.io.IOException {
        reader.read(outerContext);
        xc.AlertScenario instance = null;
        for (ObjectConverter.Reader<xc.AlertScenario> rdr : readers) {
            instance = rdr.read(instance, reader, context);
        }
        /* object finalized */
        reader.read(outerContext);
        return instance;
    }

    @Override
    public PostgresTuple to(xc.AlertScenario instance) {
        if (instance == null) return null;
        PostgresTuple[] items = new PostgresTuple[columnCount];

        items[__index___partnerID] = org.revenj.postgres.converters.IntConverter.toTuple(instance.getPartnerID());
        if (instance instanceof xc.Alert1)items[__obect_index__xc_Alert1] = __converter_xc$Alert1.to((xc.Alert1)instance);
        if (instance instanceof xc.Alert2)items[__obect_index__xc_Alert2] = __converter_xc$Alert2.to((xc.Alert2)instance);/* factory get properties */
        return RecordTuple.from(items);
    }

    public PostgresTuple toExtended(xc.AlertScenario instance) {
        if (instance == null) return null;
        PostgresTuple[] items = new PostgresTuple[columnCountExtended];

        items[__index__extended_partnerID] = org.revenj.postgres.converters.IntConverter.toTuple(instance.getPartnerID());
        if (instance instanceof xc.Alert1)items[__obect_index_extended_xc_Alert1] = __converter_xc$Alert1.toExtended((xc.Alert1)instance);
        if (instance instanceof xc.Alert2)items[__obect_index_extended_xc_Alert2] = __converter_xc$Alert2.toExtended((xc.Alert2)instance);/* factory get extended properties */
        return RecordTuple.from(items);
    }



    private final int columnCount;
    private final ObjectConverter.Reader<xc.AlertScenario>[] readers;

    public xc.AlertScenario from(PostgresReader reader, int context) throws java.io.IOException {
        int cur = reader.read();
        if (cur == ',' || cur == ')') return null;
        xc.AlertScenario instance = from(reader, context, context == 0 ? 1 : context << 1, readers);
        reader.read();
        return instance;
    }

    public xc.AlertScenario from(PostgresReader reader, int outerContext, int context) throws java.io.IOException {
        return from(reader, outerContext, context, readers);
    }
    private final int columnCountExtended;
    private final ObjectConverter.Reader<xc.AlertScenario>[] readersExtended;

    public xc.AlertScenario fromExtended(PostgresReader reader, int context) throws java.io.IOException {
        int cur = reader.read();
        if (cur == ',' || cur == ')') return null;
        xc.AlertScenario instance = from(reader, context, context == 0 ? 1 : context << 1, readersExtended);
        reader.read();
        return instance;
    }

    public xc.AlertScenario fromExtended(PostgresReader reader, int outerContext, int context) throws java.io.IOException {
        return from(reader, outerContext, context, readersExtended);
    }
    private final int __index___partnerID;
    private final int __index__extended_partnerID;
    private final int __obect_index__xc_Alert1;
    private xc.converters.Alert1Converter __converter_xc$Alert1;
    private final int __obect_index_extended_xc_Alert1;
    private final int __obect_index__xc_Alert2;
    private xc.converters.Alert2Converter __converter_xc$Alert2;
    private final int __obect_index_extended_xc_Alert2;/* factory body */
}
 ---> Revenj.Common.FrameworkException: Could not replace all tokens! 
Missing tokens: 
/* postgres ctor */
/* postgres extended ctor */
  at s.an () <0x41a637a0 + 0x001f7> in <filename unknown>:0 
  at x.a (w A_0) <0x41a63690 + 0x0002d> in <filename unknown>:0 
  at t.y () <0x41a633f0 + 0x000e7> in <filename unknown>:0 
  --- End of inner exception stack trace ---
  --- End of inner exception stack trace ---
  at t.y () <0x41a633f0 + 0x00217> in <filename unknown>:0 
  at h+c.y () <0x41a633c0 + 0x0001b> in <filename unknown>:0 
  at j.a (Boolean A_0, a A_1, bpf A_2, IEnumerable`1 A_3, System.Action`1 A_4, System.Func`1 A_5) <0x419ca740 + 0x003f7> in <filename unknown>:0 
  at j.a (bpf A_0, a A_1, System.String[] A_2, System.Action`1 A_3, System.Func`1 A_4) <0x41502770 + 0x002e3> in <filename unknown>:0 
  at h.a (System.String[] A_0) <0x41469d80 + 0x0070b> in <filename unknown>:0 

Mono: Limiting a Domain search throws a 500 error

Foo.Bar is an aggregate root.

GET http://revenj/Domain.svc/search/Foo.Bar?limit=1

results in

HTTP/1.1 500 Internal Server Error
Server: Mono-HTTPAPI/1.0

Object reference not set to an instance of an object

Download dsl-compiler every compile

Why it always download dsl-compiler every time compile the project:
Checking for latest compiler version due to download option
[info] dsl-compiler.exe at latest version (2016-12-22)
is there any way to just compile the code?

Problem opening the solution in VS2013

Some projects contain a reference to:

(...)\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets

Which prevents the loading and building of some projects when using a different version of Visual Studio. Manually changing the path to the locally installed version temporarily fixes the issue.

Compile problem - aggregate root, primary key with more than 5 fields

A dsl with >5 fields primary keys does not compile.

Clicking the compile button shows the Diffing Postgres DSL... for a few ms and then nothing happens.

module Test {

aggregate XYZ(A, B, C, D, E, F) {
  int A;
  int B;
  int C;
  int D;
  int E;
  int F;
}

}

Using the DDD plugin in Visual Studio 2017 v15.5.7
Compiler v1.9.6592.23874
PostgreSQL v10

Snowflake from Event

 event CoverageUpdate {
    String(4) supplierID;
    List<Coverage> coverages;
  }

  // zanima me lista timestampova ažuriranja za nekog suppliera
  snowflake<CoverageUpdate> CoverageUpdateView {
    supplierID;
    processedAt;
  }

Compiler javlja
Object CoverageUpdate in module trading is of invalid type. Check required type for specified concept

sample project

it would be nice if you create a sample servlet project that is ready to use

Event

can you give me example how to submit event to this system and handle the event?
Thanks

call function in the database

sorry may be this is out of the context but i found this function in schema:

CREATE OR REPLACE FUNCTION university."insert_University"(_inserted university."University_entity"[])
  RETURNS void AS
$BODY$
BEGIN
	INSERT INTO "university"."University" ("ID", "name", "ownerUserID") VALUES(_inserted[1]."ID", _inserted[1]."name", _inserted[1]."ownerUserID");
	
	PERFORM pg_notify('aggregate_roots', 'university.University:Insert:' || array["URI"(_inserted[1])]::TEXT);
END

How do i call this function from sql ?

insert initial data

Is it possible to insert initial data from dsl file? if yes please give me some examples

use -source 8 or higher to enable lambda expressions)

I got this error:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project revenj-boot: Compilation failure: Compilation failure:
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/repositories/WorldRepository.java:[79,21] try-with-resources is not supported in -source 1.6
[ERROR] (use -source 7 or higher to enable try-with-resources)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/repositories/WorldRepository.java:[93,91] lambda expressions are not supported in -source 1.6
[ERROR] (use -source 8 or higher to enable lambda expressions)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/repositories/WorldRepository.java:[122,86] diamond operator is not supported in -source 1.6
[ERROR] (use -source 7 or higher to enable diamond operator)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/repositories/WorldRepository.java:[123,56] multi-catch statement is not supported in -source 1.6
[ERROR] (use -source 7 or higher to enable multi-catch statement)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/repositories/WorldRepository.java:[161,146] method references are not supported in -source 1.6
[ERROR] (use -source 8 or higher to enable method references)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/converters/WorldConverter.java:[24,63] lambda expressions are not supported in -source 1.6
[ERROR] (use -source 8 or higher to enable lambda expressions)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/converters/WorldConverter.java:[46,166] method references are not supported in -source 1.6
[ERROR] (use -source 8 or higher to enable method references)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/Boot.java:[52,123] diamond operator is not supported in -source 1.6
[ERROR] (use -source 7 or higher to enable diamond operator)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/Boot.java:[53,21] try-with-resources is not supported in -source 1.6
[ERROR] (use -source 7 or higher to enable try-with-resources)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/World.java:[125,90] lambda expressions are not supported in -source 1.6
[ERROR] (use -source 8 or higher to enable lambda expressions)
[ERROR] /home/aldo/IdeaProjects/revenjboot/target/generated-sources/hello/World.java:[126,29] try-with-resources is not supported in -source 1.6
[ERROR] (use -source 7 or higher to enable try-with-resources)
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

when running mvn install
how to fix this?

Principal aware connection strings

Allow usage of authorized user in connection string.
Either via custom connection string or with something along the line of Application Name

SQL specification missing implements specification

Generated specification from SQL type doesn't implement Specification

  sql TransportUserVersion
  <# SELECT uri as username,at,snapshot FROM "trading"."-ngs_TransportUser_history-" ORDER BY at DESC #> {
    String username;
    Timestamp at;
    String snapshot;

    specification findUserVersions 'it => it.username == username && it.at <= timeRequested'
    {
      String username;
      Timestamp timeRequested;
    }
  }

Shouldn't this one:
public static class findUserVersions implements java.io.Serializable, com.dslplatform.json.JsonObject
be like this:
public static class findUserVersions implements java.io.Serializable, org.revenj.patterns.Specification<TransportUserVersion>?

Report compile error - revenj java

E.g.

  report TransportUserSnapshotReport {
    // String networkShortName;
    // String supplierID;
    String username;
    Timestamp timeRequested;

    TransportUserVersion transportUserSnapshot 'it => it.username == username && it.at <= timeRequested';
  }

  sql TransportUserVersion
  <# SELECT uri as username,at,snapshot FROM "trading"."-ngs_TransportUser_history-" ORDER BY at DESC #> {
    String username;
    Timestamp at;
    String snapshot;
  }

When it compiles, I get a stack trace from method populate:

TransportUserSnapshotReport.java:365: error: cannot find symbol
            return new Result(_transportUserSnapshot_);

method snippet:

    public Result populate(java.sql.Connection connection, org.revenj.patterns.ServiceLocator locator) {throw new UnsupportedOperationException();
        try (java.sql.PreparedStatement ps = connection.prepareStatement("SELECT \"trading\".\"TransportUserSnapshotReport\"(?, ?)");
            org.revenj.postgres.PostgresReader reader = org.revenj.postgres.PostgresReader.create(locator)) {
            int index = 1;

            ps.setString(index, this.username);
            index++;

            org.revenj.postgres.converters.TimestampConverter.setParameter(reader, ps, index, this.timeRequested);
            index++;

            java.sql.ResultSet rs = ps.executeQuery();
            rs.next();
            reader.process(rs.getString(1));
            rs.close();
            reader.read(2);

            return new Result(_transportUserSnapshot_);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

generate class diagram

Is it possible to generate class diagram from this dsl file? what is the tools to do this?
Thanks

Unique field

how to set the field to unique
for example
root User() {
string(256) email;
string(256) name;
//Revenj supports Postgres arrays and this will be mapped to varchar[] in the database
Set roles;
binary password;
bool isAllowed;
}
i want to make the email unique but not as a primary key
Thanks

where the list stored?

I have this model:
root Curriculum{
University *University;
Program *Program;
int year;
string(256) code;
string(256) desc;
int totalCredits;
int maxSemester;

	List<CourseType>* courseTypes;
	List<CourseOffered>* courses;
}

where did courseTypes and courses stored in the database? i cant find this column in Curriculum table
Thanks

Make JinqQueryProvider public

Change org.revenj.spring.JinqQueryProvider class visibility to public to support custom configuration scenarios for revenj.spring

Can not download dsl-compiler

[ERROR] connect timed outRetrying download... from https://compiler.dsl-platform.com:8443/platform/download/dsl-compiler.zip
[ERROR] connect timed outRetrying download... from https://compiler.dsl-platform.com:8443/platform/download/dsl-compiler.zip
[ERROR] connect timed outError downloading compiler from https://dsl-platform.com
[ERROR] Try specifying alternative compiler location.
[ERROR] connect timed out

https://compiler.dsl-platform.com:8443/platform/download/dsl-compiler.zip url is not available

reference to self

how to reference to self entity?
aggregate CourseOffered{
List prerequisiteCourses;
}
entity CoursePrerequisite{
CourseOffered *CourseOffered;
int minValue;
}

right now it generates
CREATE TABLE university."CoursePrerequisite"
(
"CourseOfferedID" integer NOT NULL, -- NGS generated
"minValue" integer NOT NULL, -- NGS generated
"Index" integer NOT NULL, -- NGS generated
)
I want to be able to add another reference to courseOffered in CoursePrerequisite. how to do that?
Thanks

Non-nullable binary default

When a null bytea record inside a value is retrieved it is not converted into an empty array of bytes, but rather returned as null instead.

change the name column

is it possible to set the column from dsl file?
right now this dsl
root University{
string(256) name {unique;}
security.User *ownerUser;
}
will generate this table:
CREATE TABLE university."University"
(
"ID" integer NOT NULL DEFAULT nextval('university."University_ID_seq"'::regclass), -- NGS generated
name character varying(256) NOT NULL, -- NGS generated
"ownerUserID" integer NOT NULL, -- NGS generated
...
}

its hard to use column with double quote

Static helper methods for Revenj.NET server libraries

The client libraries have the option to generate static helper methods. Would it be possible to do the same for the server libraries?

public partial class MyAggregate: global::System.IEquatable<MyAggregate>, global::System.ICloneable, global::Revenj.DomainPatterns.IEntity, global::Revenj.DomainPatterns.ICacheable, global::Revenj.DomainPatterns.IAggregateRoot, global::Revenj.DomainPatterns.IChangeTracking<MyAggregate>
{
  public MyAggregate Create(IDataContext context)
  {
    context.Create(this);
    return this;
  }
}

Usage will result in less code and would feel consistent with client libraries:

var ma = new MyAggregate { Abc = 1, Xyz= "Hello" }.Create(context);

//versus

var ma = new MyAggregate { Abc = 1, Xyz= "Hello" };
context.Create(ma);

Revenj.Java: Sum lambda does not compile

aggregate Account {
    Vector<CashFlow> cashFlaw;
    calculated Decimal(2) insum from 'it => it.cashFlaw.Sum(cf => cf.inflow)';
}

value CashFlow {
    Decimal(2) inflow;
}

Produres:

Unable to convert calculated property: insum in bank.Account into Java lambda expression: Java operator not supported: Sum()

Submit an Event

I have this event
module Inheritance
{
event ServeTable
{
string Table;
}
}
and i have tried to submit event using CURL:
curl -X POST -H "X-Requested-With: XMLHttpRequest" -H "Content-T
ype: application/json" -H "Cache-Control: no-cache" -d '{
"Table": "Table1",
}' "http://localhost:8080/Domain.svc/submit/Inheritance.ServeTable?result=instance"
but i got this result:
{"timestamp":1482557832179,"status":403,"error":"Forbidden","message":"Could not verify the provided CSRF token because your session was not found.","path":"/Domain.svc/submit/Inheritance.ServeTable"}
How do i get this CSRF token?
Thanks

migration detection

If app is migrated while Revenj is running, some stuff can stop working.

Error example: "cached plan must not change result type" due view recompilation

Connections should be restarted on such event.
setup listen/notify for migration

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.