sealious
A declarative framework for fast & easy app development.
Last updated a year ago by groovy354 .
BSD-2-Clause · Repository · Bugs · Original npm · Tarball · package.json
$ cnpm install sealious 
SYNC missed versions from official npm registry.

Sealious Logo

Sealious

Sealious is a declarative node.js framework

The App class

This is the class that holds all the logic and configuration of your, well, app ;)

The basic constructor is:

const newApp = new Sealious.App(config, manifest);

config and manifest both influence how the app will be set up.

config

config is considered secret, and contains information on the infrastructure, as well as SMTP passwords and the like.

It's best to be kept in a separate json/yml file, and imported with

const config = require("./config.json");

in your app.

The default config is:

{
  "core": {
    "environment": "dev"
  },
  "logger": {
    "level": "info",
    "color": true,
    "file": null,
    "dirname": ".",
    "to_json": false,
    "rotation": ".yyyy-MM-Tdd"
  },
  "www-server": {
    "port": 8080,
    "api-base": "/api/v1",
    "session-cookie-name": "sealious-session",
    "anonymous-cookie-name": "sealious-anon",
    "max-payload-bytes": 10485760
  },
  "datastore_mongo": {
    "embedded": false,
    "host": "localhost",
    "port": 27017,
    "db_name": "sealious"
  }
}

manifest

manifest is public, and can be safely required by a front-end script. It contains information on branding and version of your app. It must include the following fields:

  • name (string) - the name of your app
  • logo (string) - path to an image with the logo of your app
  • version (string) - the version of your app
  • colors.primary (string) - the primary color of your brand
  • default_language (string) - the default language for your app. Email templates use this
  • admin_email (string) - the email address of the admin. It might be publicly revealed within the app. Used to create the initial admin account. Whenever the app starts and there's no user with that email, a registration intent is created, causing an email to be sent to this address.

You can also include your own fields/values, so they can be easily shared across different modules on both back-end and front-end.

Configuring your app

Every Sealious application has an App.ConfigManager interface through which you can configure settings available throughout all the various components of your application. It comes with some sane defaults.

Changing the app settings

To run the app with specific settings, use an argument to app.run method:

const myApp = new Sealious.App();
myApp.run({http: {port: 8070}}, manifest);

Alternatively, you can use the ConfigManager.set method - which is especially useful when you want to use the dot notation to change just a single value:

const myApp = new Sealious.App();
myApp.ConfigManager.set("http.port", 8070);

Default values

If you're creating your own Sealious module and want to make it globally configurable, use the .setDefault to set the default value for your setting.

NOTE: Sometimes it's better to just parametrize your module so it can be used multiple times with different configuration. ConfigManager is only useful for app-wide configurations.

app.setDefault("my-module", {port: 8080});

You can also use dot notation when setting single field values:

app.setDefault("my-module.port", 8080);

Sending emails

This synopsis is self-explanatory:

const message = await TestApp.EmailTemplates.Simple(TestApp, {
  to: "test@example.com",
  subject: "Congratulations!",
  text: "Enlarge your 'seal' with herbal supplements",
});
await message.send(TestApp);

To send emails via smtp, set the following config:

email: {
  from_name: "Sealious app",
  from_address: "sealious@example.com",
},

Filtering resources

When reading a list of resources in a collection, you can use filtering to limit the list to resources that match certain criteria.

Let's say you have a Users collection with an additional "age" field. Now, to get all the users with age set to 42, you can call:

app.run_action(context, ["collections", "users"], "show", {
  filter: { age: 42 },
});

or, via http:

GET /api/v1/collections/users?filter[age]=42

Some field-types support advanced filters. int, for example, lets you define a range of numbers like so:

app.run_action(context, ["collections", "users"], "show", {
  filter: { age: { ">": 50 } },
});

Or, via HTTP:

GET /api/v1/collections/users?filter[age][>]=50

The above requests would return only those users with age set to above 50.

You can specify multiple filtering parameters:

app.run_action(context, ["collections", "users"], "show", {
  filter: { age: { ">": 50 }, country: "Poland" },
});
GET /api/v1/collections/users?filter[age][>]=50&filter[country]=Poland

Implementation

Each field can implement its own filtering logic, by the means of the filter_to_query method. It's goal is to transform user input (like {">": 52}) into a Mongo $match operator (like {$gt: 52}). It should be an async function.

AND and OR access strategy optimization

Sealious communicates with mongo using mainly MongoDB Pipelines, which are represented as arrays of stages. Two main pipeline stages are lookups and matches (equivalents of SQL joins and wheres). Lookups are quite expensive in comparison to matches; thus, we would like to do them as lately as possible. However, we cannot just place them in the end because some matches can be dependent on some lookups, as they use fields fetched by lookups. In addition, some lookups can also be done only after another lookup(s) takes place. Hence, we have to build a dependency graph and run a kind of priority-first search algorithm on it.

The construction of dependency graph is straightforward. Firstly, a new node, which is an equivalent of a single pipeline stage, is just inserted to the graph as a seperate node. If it represents match and queries more than one field, it will be split. For instance:

{
  $match: {
    weight: {$gt: 200},
    date_of_birth: {$lt : ISODate("2005-01-01T00:00:00Z")},
  }
}

will create two seperate nodes. The split is done because of optimization reasons - some fields within single match may be dependent on other nodes, while other are dependency free. Then, if node has dependencies an edge from the direct dependency is added. Note that it is enough because any additional dependencies are also undoubtly parents for the direct dependency (it simply means that a field required a few lookups to access it).

The order of visiting the nodes depends on two sets: the front, denoted by F, and the candidates, denoted by C. F embraces the nodes, which have already been visited, but at least one of their children is still to be visited. To simplify the notation we can distinguish dummy node Ø, which is a parent to orphans. Consequently, C embraces all direct children of nodes in F. Thus, while traversing the graph we evaluate next step from the perspective of the whole front instead of single node.

Let's run our algorithm on a simple example.

1.                                            2.
 +-------Ø--------+                            +-------Ø--------+
 |       |        |                            |       |        |
 |       |        |                            |       |        |
 v       v        v                            v       v        v
L1       M3* +----L4----+                     L1*      M3  +----L4----+
 +           |          |                      +           |          |
 |           |          |                      |           |          |
 v           v          v                      v           v          v
M2           L5        M6                     M2           L5        M6
                        +                                             +
                        |                                             |
                        v                                             v
                       M7                                            M7
F: Ø                                           F: Ø
C: L1 M3 L4                                    C: L1 L4

For the first two steps F only embraces our dummy node. First visitee is obviously M3, as matches have the highest priority. It doesn't become a part of front because it has no children. The second visitee is determined by calculating the additional fitness measure which is average priority of children of each candidate. The fitness of single match is definetely better than the average of lookup and match, so L1 is our choice.

3.                                            4.
 +-------Ø--------+                            +-------Ø--------+
 |       |        |                            |       |        |
 |       |        |                            |       |        |
 v       v        v                            v       v        v
L1       M3  +----L4----+                     L1       M3  +----L4*---+
 +           |          |                      +           |          |
 |           |          |                      |           |          |
 v           v          v                      v           v          v
M2*          L5        M6                     M2           L5        M6
                        +                                             +
                        |                                             |
                        v                                             v
                       M7                                            M7
F: Ø L1                                        F: Ø
C: M2 L4                                       C: L4

Again, steps 3 and 4 are not complicated. M2 is picked up first and then it's time for L4.

5.                                            6.
 +-------Ø--------+                            +-------Ø--------+
 |       |        |                            |       |        |
 |       |        |                            |       |        |
 v       v        v                            v       v        v
L1       M3  +----L4----+                     L1       M3  +----L4----+
 +           |          |                      +           |          |
 |           |          |                      |           |          |
 v           v          v                      v           v          v
M2           L5        M6*                    M2           L5        M6
                        +                                             +
                        |                                             |
                        v                                             v
                       M7                                            M7*
F: L4                                        F: L4 M6
C: L5 M6                                       C: L5 M7

At last Ø leaves F. The front moves down the right subtree of L4.

7.
 +-------Ø--------+
 |       |        |
 |       |        |
 v       v        v
L1       M3  +----L4----+
 +           |          |
 |           |          |
 v           v          v
M2           L5*       M6
                        +
                        |
                        v
                       M7
F:
C: L5

The only node left in candidates is L5, so algorithm picks it up. We traversed the whole graph, so that's it.

Query class

Whenever possible we try to use Query class instead of raw MongoDB queries. The following classes extend Query class (their names are rather self-explanatory):

  • Query.And
  • Query.Or
  • Query.Not
  • Query.DenyAll
  • Query.AllowAll

Every class which belongs to Query group has to expose the functions below. The usage examples can be find in lib/datastore/query.test.js.

lookup(body)

Adds lookup to the query.

Returns hexadecimal hash of passed lookup.

match(body)

Adds match to the query.

dump()

Usually other queries are supplied with its return value. It is rather used internally by classes implementing Query.

Returns the inner representation of the query.

toPipeline()

Returns the MongoDB aggregation pipeline.

fromSingleMatch(body)

Returns the query object on which match(body) has been called.

fromCustomPipeline(pipeline)

Returns the query object equivalent to the given pipeline.


Classes that implement operators requiring multiple subqueries expose also the following:

addQuery(query)

Adds argument as the another parameter of the operator connected with base query (and, or, etc.)

Current Tags

  • 0.4.6                                ...           beta (5 years ago)
  • 0.9.5                                ...           latest (a year ago)
  • 0.7.11                                ...           next (4 years ago)

110 Versions

  • 0.9.5                                ...           a year ago
  • 0.9.4                                ...           a year ago
  • 0.6.24                                ...           4 years ago
  • 0.6.23                                ...           4 years ago
  • 0.6.22                                ...           4 years ago
  • 0.6.21                                ...           4 years ago
  • 0.6.20                                ...           4 years ago
  • 0.6.19                                ...           4 years ago
  • 0.6.18                                ...           4 years ago
  • 0.7.11                                ...           4 years ago
  • 0.7.10                                ...           4 years ago
  • 0.6.17                                ...           4 years ago
  • 0.7.9                                ...           4 years ago
  • 0.7.8                                ...           4 years ago
  • 0.7.7                                ...           4 years ago
  • 0.7.6                                ...           4 years ago
  • 0.7.5                                ...           4 years ago
  • 0.7.4                                ...           4 years ago
  • 0.7.3                                ...           4 years ago
  • 0.6.16                                ...           4 years ago
  • 0.6.15                                ...           4 years ago
  • 0.6.14                                ...           4 years ago
  • 0.6.13                                ...           4 years ago
  • 0.6.12                                ...           4 years ago
  • 0.6.11                                ...           5 years ago
  • 0.6.9                                ...           5 years ago
  • 0.6.10                                ...           5 years ago
  • 0.6.8                                ...           5 years ago
  • 0.6.7                                ...           5 years ago
  • 0.6.6                                ...           5 years ago
  • 0.6.5                                ...           5 years ago
  • 0.6.4                                ...           5 years ago
  • 0.6.3                                ...           5 years ago
  • 0.6.2                                ...           5 years ago
  • 0.6.1                                ...           5 years ago
  • 0.6.0                                ...           5 years ago
  • 0.5.13                                ...           5 years ago
  • 0.5.12                                ...           5 years ago
  • 0.5.11                                ...           5 years ago
  • 0.5.10                                ...           5 years ago
  • 0.5.9                                ...           5 years ago
  • 0.5.8                                ...           5 years ago
  • 0.5.7                                ...           5 years ago
  • 0.5.6                                ...           5 years ago
  • 0.5.5                                ...           5 years ago
  • 0.5.4                                ...           5 years ago
  • 0.5.3                                ...           5 years ago
  • 0.5.2                                ...           5 years ago
  • 0.5.1                                ...           5 years ago
  • 0.5.0                                ...           5 years ago
  • 0.4.9                                ...           5 years ago
  • 0.4.7                                ...           5 years ago
  • 0.4.6                                ...           5 years ago
  • 0.4.4                                ...           5 years ago
  • 0.4.2                                ...           5 years ago
  • 0.4.1                                ...           5 years ago
  • 0.4.0                                ...           5 years ago
  • 0.3.9                                ...           5 years ago
  • 0.3.5                                ...           5 years ago
  • 0.3.6                                ...           5 years ago
  • 0.3.4                                ...           5 years ago
  • 0.3.3                                ...           5 years ago
  • 0.3.2                                ...           5 years ago
  • 0.3.1                                ...           5 years ago
  • 0.3.0                                ...           5 years ago
  • 0.2.13                                ...           5 years ago
  • 0.2.11                                ...           5 years ago
  • 0.2.10                                ...           5 years ago
  • 0.2.8                                ...           5 years ago
  • 0.2.7                                ...           5 years ago
  • 0.2.5                                ...           5 years ago
  • 0.2.4                                ...           5 years ago
  • 0.2.3                                ...           5 years ago
  • 0.2.2                                ...           5 years ago
  • 0.2.1                                ...           5 years ago
  • 0.2.0                                ...           5 years ago
  • 0.1.23                                ...           5 years ago
  • 0.1.21                                ...           5 years ago
  • 0.1.20                                ...           5 years ago
  • 0.1.19                                ...           5 years ago
  • 0.1.18                                ...           5 years ago
  • 0.1.17                                ...           5 years ago
  • 0.1.15                                ...           5 years ago
  • 0.1.14                                ...           5 years ago
  • 0.1.13                                ...           5 years ago
  • 0.1.12                                ...           5 years ago
  • 0.1.11                                ...           5 years ago
  • 0.1.10                                ...           5 years ago
  • 0.1.9                                ...           5 years ago
  • 0.1.8                                ...           5 years ago
  • 0.1.7                                ...           5 years ago
  • 0.1.6                                ...           5 years ago
  • 0.1.5                                ...           5 years ago
  • 0.1.4                                ...           5 years ago
  • 0.1.3                                ...           5 years ago
  • 0.1.2                                ...           5 years ago
  • 0.1.1                                ...           5 years ago
  • 0.0.14                                ...           5 years ago
  • 0.0.13                                ...           5 years ago
  • 0.0.12                                ...           5 years ago
  • 0.0.11                                ...           5 years ago
  • 0.0.10                                ...           5 years ago
  • 0.0.9                                ...           5 years ago
  • 0.0.8                                ...           5 years ago
  • 0.0.7                                ...           5 years ago
  • 0.0.6                                ...           5 years ago
  • 0.0.5                                ...           5 years ago
  • 0.0.4                                ...           5 years ago
  • 0.0.3                                ...           5 years ago
  • 0.0.2                                ...           5 years ago
Downloads
Today 0
This Week 0
This Month 1
Last Day 0
Last Week 1
Last Month 9
Dependencies (32)
Dev Dependencies (7)

Copyright 2014 - 2016 © taobao.org |