Introduction

1. ZetaPush Celtia

ZetaPush accelerates the creation of connected applications by providing turnkey services. The developer consumes the services needed to run his application using development kits ready to be integrated into his front end code.

ZetaPush Celtia represents a major version of ZetaPush with a major change: the developer can write business code in languages such as JavaScript and TypeScript.

The developer will focus on his business code and ZetaPush will make his life easier by providing him with all the common components such as user management, data storage, etc.

In addition, ZetaPush hosts business code and front-end code.

2. Sections

Quick Start

Tutorials

Guides

Developer Manual

Reference

If you want to start using ZetaPush quickly

If you want to learn how to use ZetaPush Celtia main features, you can begin here

If you want to go into some topics in depth

If you want to cover completely each topic

If you want to search for a particular piece of information

3. Terms and concepts

Word Definition

ZetaPush Account

A developer account on the ZetaPush platform to create and manage applications.

Credentials

A Login/Password pair of the developer ZetaPush account.

Cloud function

A single operation that has a name, well-defined behavior, that can accept parameters and can produce an output.

This is equivalent to a function in any language except that it is run remotely.

Cloud service

Several Cloud functions that are grouped together to cover a coherent set of features. You can consider this as a class (custom cloud service) with methods (cloud functions).

ZetaPush already provides ready-to-use cloud services. For example we have: Chat, Groups, Messaging…​

In this documentation, we mention Cloud Services as a shortcut of ZetaPush Cloud Services.

Custom Cloud Service

A set of features similar to ZetaPush Cloud Services but developed by you. The usage of your custom cloud services is exactly the same as ZetaPush cloud services.

Front

Web pages developed by you (HTML / CSS / JS).

Worker

A set of Custom cloud services that you have developed to mutualize your business logic.

Organization

A group of ZetaPush accounts. Each organization may have several managers and several developers. Each person uses his own credentials to interact with ZetaPush but actions are shared among the organization.

Application

A logical container designed to perform a group of coordinated tasks or activities for the benefit of the user.

An application may have a front part and a worker.

An application has at least one environment.

Environment

Your application may be deployed and executed in separate zones called Environments. Each environment is a particular zone that you can organize as you wish. For example, you can have prod, pre-prod, dev environments for the same application.

CLI

Command Line Interface: utility scripts that are usable in a terminal to run your code or deploy your application, for example.

Console

The main website used to manage your ZetaPush account and applications, read the documentation, monitor your applications…​

Quick Start

4. System requirements

To create an application with ZetaPush you only need the Node.js ecosystem:

  • Node.js version 8.11.2 (LTS) or higher

  • npm version 5.x or higher

Node.js is used to:

  • create custom cloud services

  • locally run custom cloud services

  • deploy your application on ZetaPush

Install Node.js and npm

Go to Node.js Downloads and follow the installation instructions.

You also need to install additional tools for Node.js:

Additional tools

Windows

You must install development tools on Windows (Visual Studio Build Tools and Python). Fortunately, there is a npm tool that helps you to install all required dependencies. You must run this command line using PowerShell as Administrator:

npm install --global --production windows-build-tools

Be patient! It may take a while.

Debian/Ubuntu

Install build tools:

sudo apt-get install -y build-essential

Enterprise Linux and Fedora

Install build tools:

sudo yum install gcc-c++ make
# or: sudo yum groupinstall 'Development Tools'

-

5. Create your first application

5.1. From the command line

Generate application
$ npm init @zetapush first-hello-world (1)
1 first-hello-world is the name of the folder that will be created with all the generated files

You will be prompted for a developer login and a developer password in order to create a new account.

The developer login and the developer password will be stored in the .zetarc file. By default, the .gitignore file is configured to doesn’t deploy this file.

Using npm lower than 6

npm provides npm init command since version 6.

You can either upgrade your npm version or use npx utility.

npm 5-

Upgrade npm

➊ - Upgrade npm
$ npm install -g npm@latest
➋ - Use npm init
$ npm init @zetapush first-hello-world

Use npx to launch the script

Launch with npx
$ npx @zetapush/create first-hello-world

-

See Account management to learn how to manage your account and your applications.

5.2. Generated project

result
Figure 1. Result of first-hello-world project

The generated project:

  • Provides a web page (front part)

  • The web page automatically connects to ZetaPush Cloud.

    The connection is requested with client.connect() in first-hello-world/front/index.html
  • The web page displays a button named hello().

    See first-hello-world/front/index.html
  • Provides a custom cloud service that generates a message ("hello world from JavaScript" followed by the date as a timestamp).

    The executed code is available in first-hello-world/worker/index.ts
  • When the hello() button is clicked, the custom cloud service is called and the response is displayed in the browser console. The text of the button is also updated with the count of custom cloud services responses.

    See call to api.hello() in first-hello-world/front/index.js.

The generated project files are:

Generated tree structure
{appName}
├── .zetarc
├── .gitignore
├── front
│   ├── index.css
│   ├── index.html
│   ├── index.js
│   └── logo.png
├── worker
│   └── index.ts
├── package.json
├── README.md
└── tsconfig.json
Table 1. Definition of generated files
File / Folder Definition

.zetarc

Contains the developer’s credentials. Useful to deploy or run your code. The content is auto generated if the file is empty.

.gitignore

Prevents publication of your developer credentials (on GitHub for example)

package.json

Contains the dependencies, the name of your application, the entry point of your custom cloud services (main property) and many useful other informations for your application.

README.md

Gives you some information about how to use your application.

front/

By convention, the front end code of your application is in this folder. You can change it by editing package.json.

worker/

By convention, the back end code of your application is in this folder. You can change it by editing package.json.

6. Run locally your first project

6.1. From your terminal

Start application (front and custom cloud services)
$ npm run start -- --serve-front

npm run start is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta run --serve-front

This command launches both a local web server on localhost:3000 and the custom cloud services. You can open a web browser on localhost:3000 and open the browser console.

You can click on the button and see the result of the custom cloud service call in the browser console.

6.2. How it works

run locally
Figure 2. Exchanges between web page and custom cloud services

Clicking on the button triggers a call to the ZetaPush cloud. ZetaPush routes the call to the local Node.js (the Node.js has been registered automatically by the CLI)
The custom cloud service does its job and provides a response. ZetaPush routes the response to the connected web page

7. Push your first project in the Cloud

7.1. From your terminal

Deploy application (front and custom cloud services)
$ npm run deploy

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push

This command uploads your code (front and worker) to the ZetaPush cloud. ZetaPush then handles the deployment by publishing your front and your custom cloud services.

The CLI displays the URL to access the published web page.

7.2. How it works

run in cloud
Figure 3. Exchanges between web page and custom cloud services

Clicking on the button triggers a call to the ZetaPush cloud which in turn routes directly the call to the hosted Node.js
The custom cloud service does its job and provides a response that ZetaPush routes back to the hosted web page

8. What’s next

You have created a new application, executed it locally and then published it.

Now, you are ready to code your own application:

Developer manual

9. Requirements

9.1. System requirements

To create an application with ZetaPush you only need the Node.js ecosystem:

  • Node.js version 8.11.2 (LTS) or higher

  • npm version 5.x or higher

Node.js is used to:

  • create custom cloud services

  • locally run custom cloud services

  • deploy your application on ZetaPush

Install Node.js and npm

Go to Node.js Downloads and follow the installation instructions.

You also need to install additional tools for Node.js:

Additional tools

Windows

You must install development tools on Windows (Visual Studio Build Tools and Python). Fortunately, there is a npm tool that helps you to install all required dependencies. You must run this command line using PowerShell as Administrator:

npm install --global --production windows-build-tools

Be patient! It may take a while.

Debian/Ubuntu

Install build tools:

sudo apt-get install -y build-essential

Enterprise Linux and Fedora

Install build tools:

sudo yum install gcc-c++ make
# or: sudo yum groupinstall 'Development Tools'

-

9.2. Using CLI

Once your have your application ready you can:

  • Run the backend code (Worker) locally

  • Deploy your application

To do this you need to use the CLI. To install it, launch:

Update environement variables

Debian/Ubuntu/MacOSX

You need to add it to your shell config file ~/.zshrc ~/.profile or ~/.bashrc.

$ export PATH=./node_modules/.bin:$PATH

Note that this will not automatically update your path for the remainder of the session. To do this, you should run:

$ source ~/.zshrc
$ source ~/.profile
$ source ~/.bashrc

Windows

Windows 10 and Windows 8

  • In Search, search for and then select: System (Control Panel)

  • Click the Advanced system settings link.

  • Click Environment Variables. In the section System Variables, find the PATH environment variable and select it. Click Edit. If the PATH environment variable does not exist, click New.

  • In the Edit System Variable (or New System Variable) window, specify the value of the PATH environment variable. Click OK. Close all remaining windows by clicking OK.

  • Reopen Command prompt window, and run zeta command.

Windows 7

  • From the desktop, right click the Computer icon.

  • Choose Properties from the context menu.

  • Click the Advanced system settings link.

  • Click Environment Variables. In the section System Variables, find the PATH environment variable and select it. Click Edit. If the PATH environment variable does not exist, click New.

  • In the Edit System Variable (or New System Variable) window, specify the value of the PATH environment variable. Click OK. Close all remaining windows by clicking OK.

  • Reopen Command prompt window, and run zeta command.

Windows Vista

  • From the desktop, right click the My Computer icon.

  • Choose Properties from the context menu.

  • Click the Advanced tab (Advanced system settings link in Vista).

  • Click Environment Variables. In the section System Variables, find the PATH environment variable and select it. Click Edit. If the PATH environment variable does not exist, click New.

  • In the Edit System Variable (or New System Variable) window, specify the value of the PATH environment variable. Click OK. Close all remaining windows by clicking OK.

  • Reopen Command prompt window, and run zeta command.

Windows XP

  • Select Start, select Control Panel. double click System, and select the Advanced tab.

  • Click Environment Variables. In the section System Variables, find the PATH environment variable and select it. Click Edit. If the PATH environment variable does not exist, click New.

  • In the Edit System Variable (or New System Variable) window, specify the value of the PATH environment variable. Click OK. Close all remaining windows by clicking OK.

  • Reopen Command prompt window, and run zeta command.

-

9.2.3. Use npx tool

npx is shipped with npm standard install

$ npx zeta <command>
$ npm i -g @zetapush/cli

With the CLI, you can run your worker with:

Run worker
1
$ zeta run

Or deploy your application with:

Deploy the application
1
$ zeta push

10. Account management

10.1. Create a new account and a new application

10.1.3. Using your terminal

init without account

➊ I launch npm init @zetapush myApp without parameters
➋ You type your email and a password to create your ZetaPush account
➌ The application is created in the myApp folder
➍ The .zetarc file looks like:

1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": "nameOfAppAutoGenerated"
}
You need to confirm your ZetaPush account to run or deploy your application.

10.2. Add a new application to your account

10.2.2. Using your terminal

init with account without app

➊ I launch npm init @zetapush myApp with the --developer-login and --developer-password parameters
➋ The application is created in the myApp folder
➌ The .zetarc file looks like:

1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": ""
}
If the appName property in the .zetarc is empty or missing, ZetaPush deploys or executes the code in a new application.

10.3. Configure your project to use an existing application

10.3.1. Using your terminal

init with account and app

➊ I launch npm init @zetapush myApp with the --developer-login, --developer-password and --appName parameters
➋ The application is created in the myApp folder
➌ The .zetarc file looks like:

1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": "nameOfAppAutoGenerated"
}

10.3.2. Configure your project manually

Here the basic source tree you need to create:

├── .zetarc
├── .gitignore
├── front
│   ├── index.html
│   └── index.js
├── worker
│   └── index.ts
└── package.json

You will see what you need to do for each file in the following section.

10.3.2.1. package.json

The package.json is the most important file in the application. It contains all the information needed to deploy or run your application on the ZetaPush platform (Except the credentials of the ZetaPush account in the .zetarc).

The minimal package.json is below:

package.json
1
2
3
4
5
6
7
8
{
  "name": "myApp",
  "main": "worker/index.ts", (1)
  "dependencies": {
    "@zetapush/cli": "^0.33.4" (2)
    "@zetapush/core": "^0.33.4", (3)
  }
}
1 Entry point of your worker. In this file you will expose your custom cloud services
2 Dependency to the CLI (Command Line Interface). Use it to run your worker or deploy your code.
3 Dependency to the ZetaPush Core to write some custom cloud services
Path to the code
By convention, ZetaPush Celtia considers that your front code is in the directory front and your back end code (worker) in worker. You can change it by setting the path in the package.json.

Here is an example of application where the front code is in the directory front/dist and the back code in server:

package.json
1
2
3
4
5
6
7
8
9
10
11
12
{
  "name": "myApp",
  "main": "worker/index.ts",
  "dependencies": {
    "@zetapush/core": "^0.33.4",
    "@zetapush/cli": "^0.33.4"
  },
  "zetapush": {
    "front": "./front/dist", (1)
    "worker": "./server" (2)
  }
}
1 The front code to deploy is in the directory front/dist
2 The back code to deploy is in the directory server
Install dependencies

You need to install some dependencies in your application. To do this, you can run:

$ npm install --save @zetapush/core @zetapush/cli
10.3.2.2. .zetarc

To specify which account (on the ZetaPush platform) you use for an application, you fill the file .zetarc with the account information.

This file is in JSON format. The possible properties are below:

  • developerLogin: Login of the developer account on the ZetaPush platform

  • developerPassword: Password of the developer account on the ZetaPush platform

  • appName: The name of your application

.zetarc
1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": "myAppName"
}
If you want to create a new application, you can leave the appName property empty, this will create a new application and fill your .zetarc when you will run or deploy your code.
10.3.2.3. front

By convention, the directory contains the front code. For example you can have this tree structure:

└── front
    ├── index.html
    └── index.js
Basic example of Hello World (calling custom cloud service)
front/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>ZetaPush SDK Examples - Worker</title>
  <style>
    body {
      font-family: Arial, Helvetica, sans-serif;
    }
    body::before {
      display: block;
      content: "Open Your Console";
      background: #000;
      color: #FFF;
      position: fixed;
      transform: rotate(45deg) translate(60px, 0px);
      right: 0;
      top: 0;
      height: 25px;
      padding: 5px 30px;
      line-height: 25px;
      font-family: Calibri, Helvetica, Arial;
      font-weight: bold;
    }
    nav {
      display: flex;
      flex-direction: column;
    }
    button,
    label {
      margin: 0.5rem 1rem;
      height: 5vh;
      font-size: calc(5vh / 2);
      background: linear-gradient(rgb(216, 216, 216), rgb(186, 186, 186));
      border-color: rgb(117, 117, 117) rgb(107, 107, 107) rgb(97, 97, 97);
      border-style: solid;
      border-width: 1px;
      padding: 1px 7px 2px;
      text-align: center;
      cursor: pointer;
      box-sizing: border-box;
      align-items: flex-start;
    }
    button[disabled],
    label[disabled] {
      opacity: 0.9;
      pointer-events: none;
      cursor: not-allowed;
      color: graytext;
    }
  </style>
</head>

<body>
  <nav>
    <button disabled class="js-Hello">hello</button>
  </nav>
  <main>
    <ul></ul>
  </main>
  <script src="https://unpkg.com/@zetapush/client"></script>
  <script src="https://unpkg.com/@zetapush/platform-legacy"></script>
  <script src="./index.js"></script>
</body>
</html>
front/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Create new ZetaPush Client
const client = new ZetaPushClient.WeakClient();
// Create a proxy to invoked worker methods
const api = client.createProxyTaskService();
// Handle connection
client.connect().then(() => (
  console.debug('onConnectionEstablished'),
  [...document.querySelectorAll('button')].forEach((node) =>
    node.removeAttribute('disabled'),
  )
));
// Handle DOM events
document.querySelector('.js-Hello').addEventListener('click', async () => {
  console.log(await api.hello());
});
10.3.2.4. worker

By convention, the directory contains the back code. For example you can have this tree structure:

└── worker
    └── index.ts
Basic example of Hello World (custom cloud service)
worker/index.ts
1
2
3
4
5
6
7
8
export default class Api {
  /**
   * Get an Hello World string with server side timestamp value
   */
  hello() {
    return `Hello World from JavaScript ${Date.now()}`;
  }
}

11. Develop your front with ZetaPush

You may want to quickly provide a web application by just focusing on the visible part and the user experience instead of wasting time on technical concerns.

ZetaPush provides built-in cloud services to increase your productivity that are directly usable by your front.

11.1. Use built-in ZetaPush cloud services

On the front-side, you will want to use the cloud services provided by ZetaPush or your custom cloud services. The usage of a cloud service or a custom cloud service is exactly the same process.

11.1.1. Requirements

To use services (custom or not), you need to install the ZetaPush Core dependency (@zetapush/client).

So you can install it with:

Install @zetapush/client via npm
1
$ npm install @zetapush/client

or

Install @zetapush/client via unpkg
<script src="https://unpkg.com/@zetapush/client"></script>

11.1.2. Use a built-in cloud service

The usage of a custom cloud service and a cloud service provided by ZetaPush is exactly the same process.

An example is worth a thousand words, so here is an example of usage of services.

When you call a cloud function, the result is always a Promise.

In our example we use this custom cloud service named HelloService:

worker/index.ts
1
2
3
4
5
export default class HelloService { (1)
  hello() {
    return `Hello World`;
  }
}
1 The service is exported

Now we create the HTML page with a button to display the HelloWorld message in the console:

front/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Celtia</title>
</head>

<body>
    <button onclick="hello()">hello</button> (1)
    <ul></ul>

    <script src="https://unpkg.com/@zetapush/client"></script> (2)
    <script src="./index.js"></script>
</body>

</html>
1 We call hello(), which in turns calls our custom cloud service
2 Dependency to ZetaPush Core

Finally, we have the front-side code call our services:

front/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Create new ZetaPush Client
const client = new ZetaPushClient.WeakClient({ (1)
  appName: <yourAppName>,
  platformUrl: <optionalUrl>
});
// Create a proxy to invoked worker methods
const api = client.createProxyTaskService(); (2)
// Handle connection
client.connect().then(() => ( (3)
  console.debug('onConnectionEstablished'),
  [...document.querySelectorAll('button')].forEach((node) =>
    node.removeAttribute('disabled'),
  )
));
// Handle DOM events
document.querySelector('.js-Hello').addEventListener('click', async () => {
  console.log(await api.hello()); (4)
});
1 Create the ZetaPush client with a specific appName and optional URL
2 API to access our custom service(s)
3 Connection to the ZetaPush platform
4 Call our cloud function and display the result

Now when we click the button, "Hello World" is displayed on the page.

List of available built-in services

Unresolved directive in developer-manual.adoc - include::/mnt/harddisk/workspace/celtia/documentation/src/docs/asciidoc/common/cloud-services/summary.adoc[leveloffset=+3]

12. Develop your business logic

12.1. What is a custom cloud service

A custom cloud service combines many cloud functions like the cloud services exposed by ZetaPush. The only difference is that you create the cloud functions. Generally, you will want to put the business logic of your application in the custom cloud services.

12.1.1. Architecture

12.1.1.1. Development phase
custom cloud service dev
Figure 4. Custom Cloud Service in development phase

The worker is running "locally" and interacts with the existing cloud services on the ZetaPush platform and the front code interacts only with the custom cloud services that are running "locally"

12.1.1.2. Production phase
custom cloud service prod
Figure 5. Custom Cloud Service in production phase

The front code interacts only with the custom cloud services that are running "remotely"

12.2. Define a custom cloud service

First, a custom cloud service is a class and its methods are the cloud functions. So the most basic example of a custom cloud service is below:

12.2.1. Create a cloud function

Basic custom cloud service
1
2
3
4
5
6
7
8
class HelloWorldAsCustomCloudService {

    constructor() {} (1)

    helloWorld() { (2)
        return "Hello World";
    }
}
1 You always need to add a constructor in a custom cloud service
2 helloWorld is your first cloud function in your custom cloud service named HelloWorldAsCustomCloudService
A cloud function is always asynchronous (with the async keyword or not)

Each cloud function in a custom cloud service must follow a specific format. Only the first parameter is passed, the second is the context of the call. For example:

1
2
3
4
5
6
7
class myCustomCloudService {
    constructor() {}

    myCloudFunction(parameters, context) { (1)
        ...
    }
}
1 parameters is the first argument passed, it can be an object. context is the context of the call for this cloud function. For example you can get the ID of the caller with context.owner.

On the front side, you can use this cloud function like this:

1
2
this.api.myCloudFunction({ 'name': 'toto'});
// Or this.api.myCloudFunction('titi');

12.2.2. Expose a custom cloud service

When you create a custom cloud service that you want to expose to the client, you need to declare it. You have 2 cases:

  • Only one custom cloud service exposed

  • Many custom cloud services exposed

The package.json has a property named main. It defines the file where the custom cloud services are exposed. The following examples define them in worker/index.ts.
12.2.2.1. Namespace

When you call a cloud function from a custom cloud service, you can specify a namespace (if you have 2 cloud functions on 2 differents custom cloud services).

The namespace is never used inside the custom cloud service. The namespace is defined when you expose your custom cloud service.

For example we have this service:

1
2
3
4
5
6
7
8
9
10
11
class Api {
    constructor() {}

    hello(name, context) { (1)
        return `Hello ${name} called by ${context.owner}.`;
    }
}

module.exports = {
    api: Api (2)
}
1 The namespace is never passed here
2 We export this custom cloud service with the namespace 'api'

You can call hello() like this:

1
2
// api is an instance of Api
this.api.hello('Toto', 'api'); (1)

We use the method hello() from the namespace api.

12.2.2.2. Only one custom cloud service exposed

In this example, we have 2 custom cloud services: One is private and the second is exposed:

worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Injectable } from '@zetapush/core';

class Calendar { (1)
  getNow() {
    return new Date().toLocaleDateString();
  }
}

@Injectable()
export default class MyPublicCustomCloudService { (2)
  constructor(
    private calendar: Calendar
  ) {}
  hello() {
    return `Hello World at ${this.calendar.getNow()}`;
  }
}
1 Calendar is my private custom cloud service
2 We expose only MyPublicCustomCloudService to the client side
12.2.2.3. Many custom cloud services exposed

In this example, we want to expose 2 custom cloud services. In this case, we need to specify the namespace of each custom cloud service:

worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Injectable } from '@zetapush/core';

@Injectable()
class Calendar {
  getNow() {
    return new Date().toLocaleDateString();
  }
}

@Injectable()
class HelloService {
  constructor(
    private calendar: Calendar
  ) {}
  hello() {
    return `Hello World at ${this.calendar.getNow()}`;
  }
}

export default  { (1)
  helloService: HelloService,
  calendar: Calendar
};
1 We expose the 2 custom cloud services to the client side with the syntax: { namespace1: Service1, namespace2: Service2 }

12.2.3. Dependency injection

First of all, if you want to include some services (custom or not), you need to use the dependency injection of ZetaPush. This will ease the injection of your dependencies.

You need to import the package @zetapush/core in order to use Injectable. This object will resolve the dependency injection of cloud services and custom cloud services.

Install @zetapush/core
1
$ npm install --save @zetapush/core
The dependency injection of ZetaPush uses Angular injection-js library.

You need to import the package @zetapush/platform-legacy in order to use Stack cloud services.

Install @zetapush/platform-legacy
1
$ npm install --save @zetapush/platform-legacy

For example, to import an other service, you simply need to write:

Import service
1
2
3
4
5
6
7
8
9
10
import { Injectable } from '@zetapush/core';
import { Stack } = require('@zetapush/platform-legacy'); (1)
import { MyOtherService } from './services'; (2)

@Injectable()
class MyOwnService {
    constructor(private myOtherService: MyOtherService, private stack: Stack) { (3)
        ...
    }
}
1 We import the Inject object and a service (Stack) provided by ZetaPush
2 We import an other custom cloud service
3 We use instances of our injected services in our service throught the constructor

If you import a custom cloud service or a cloud service provided by ZetaPush, you use both similarily

12.2.4. Use built-in cloud service

Once you have imported the services, you can use them.

In this example, we will use the Stack cloud service provided by ZetaPush. In our use-case, we want to put some data associated with the current timestamp and be able to list all stored data.

For this the Stack service provides some methods:

  • push({ stack: string, data: object });

  • list({ stack: string });

The Stack service has asynchronous methods
MyStorageService
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { Injectable } from '@zetapush/core';
import { Stack } from '@zetapush/platform-legacy';

@Injectable()
export default class MyStorageService {
  private stackName = 'stack-example';
  constructor((2)
    private stack: Stack
  ) {}
  /**
   *  Store data with associated timestamp
   */
  storeWithTimestamp(value: any) {
    return this.stack.push({
      stack: this.stackName,
      data: {
        value,
        timestamp: Date.now()
      }
    });
  }

  /**
   * List all stored data
   */
  getAllData() {
    return this.stack.list({
      stack: this.stackName
    });
  }
}
1 We import the Stack service
2 stack is an instance of Stack

12.2.5. Use another custom cloud service

Once you have imported the services, you can use them.

In this example, we will have 2 custom cloud services:

  • Calendar: Utils function to return the current date

  • HelloWorldService: Basic example using Calendar cloud function

The first custom cloud service (Calendar) is in the file: "worker/calendar.js".

worker/calendar.ts
1
2
3
4
5
export class Calendar {
  getNow() { (1)
    return new Date().toLocalDateString('fr-FR');
  }
}
1 The only cloud function of the Calendar service

Then, we have the HelloWorldService that use our Calendar service. It is in the file: "./worker/index.ts".

worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Injectable } from '@zetapush/core';
import { Calendar } from './calendar'; (1)

@Injectable()
class HelloWorldService {
  constructor( (2)
    private calendar: Calendar
  ) {}
  /**
   * Return 'Hello World' with current date
   */
  helloWorldWithDate() {
    return `Hello world at ${this.calendar.getNow()}`;
 }
}
1 We import the Calendar service
2 calendar is an instance of Calendar

12.3. Run a custom cloud service

Start custom cloud services locally
$ npm run start

npm run start is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta run

12.4. Use a custom cloud service in your front

The usage of a custom cloud service and a cloud service provided by ZetaPush is exactly the same process.

An example is worth a thousand words, so here is an example of usage of services.

When you call a cloud function, the result is always a Promise.

In our example we use this custom cloud service named HelloService:

worker/index.ts
1
2
3
4
5
export default class HelloService { (1)
  hello() {
    return `Hello World`;
  }
}
1 The service is exported

Now we create the HTML page with a button to display the HelloWorld message in the console:

front/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Celtia</title>
</head>

<body>
    <button onclick="hello()">hello</button> (1)
    <ul></ul>

    <script src="https://unpkg.com/@zetapush/client"></script> (2)
    <script src="./index.js"></script>
</body>

</html>
1 We call hello(), which in turns calls our custom cloud service
2 Dependency to ZetaPush Core

Finally, we have the front-side code call our services:

front/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Create new ZetaPush Client
const client = new ZetaPushClient.WeakClient({ (1)
  appName: <yourAppName>,
  platformUrl: <optionalUrl>
});
// Create a proxy to invoked worker methods
const api = client.createProxyTaskService(); (2)
// Handle connection
client.connect().then(() => ( (3)
  console.debug('onConnectionEstablished'),
  [...document.querySelectorAll('button')].forEach((node) =>
    node.removeAttribute('disabled'),
  )
));
// Handle DOM events
document.querySelector('.js-Hello').addEventListener('click', async () => {
  console.log(await api.hello()); (4)
});
1 Create the ZetaPush client with a specific appName and optional URL
2 API to access our custom service(s)
3 Connection to the ZetaPush platform
4 Call our cloud function and display the result

Now when we click the button, "Hello World" is displayed on the page.

13. Deploy/publish your application

Once you have developed your application and tested it, you may want to deploy it on the ZetaPush cloud.

13.1. What will be deployed ?

In the deployment with ZetaPush, you have 2 parts:

  • Hosting and scalability of the worker (custom cloud services)

  • Hosting of the front end

13.1.1. Worker

When you deploy the worker (custom cloud services) of your application, we take care of the hosting of your code. In addition, we manage the scability of your application.

13.1.2. Front end

For the front end part, we host your application and return you an unique URL to access to your application. We also link your front end logic with the differents services you need (custom cloud services or cloud services provided by ZetaPush).

13.2. Deploy both front and custom cloud services on the ZetaPush Cloud

The deployment is very easy, to do this you only need to launch this command in your application directory:

Deploy the application
$ npm run deploy

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push

The command will read the .zetarc file. If there is no developerLogin and developerPassword properties or if the ZetaPush account is not validated, you will not be able to push your application.

About the appName property, if it is empty, the CLI will deploy your code on a new application.

When you deploy your application (Front + Worker), the CLI returns you an unique URL to use it.

13.3. Deploy your front on the ZetaPush Cloud

If you want to only deploy the front part of your application on the ZetaPush platform, you can launch:

Deploy the worker
$ npm run deploy -- --front

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push --front

In this case your worker is not deployed at all.

13.4. Deploy your custom cloud services on the ZetaPush Cloud

The Worker is the back end of your application. Generally you put your business logic inside it.

If you want to deploy your worker only on the ZetaPush platform, you can launch:

Deploy the worker
$ npm run deploy -- --worker

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push --worker

In this case you only deploy your back end code, so no URL will be returned.

14. Debug

14.1. Debug your application with VSCode

VSCode provide a powerfull debugging feature https://code.visualstudio.com/docs/editor/debugging.

Add a launch configuration with the following structure.

Configure .vscode/launch.json file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "zeta run", (1)
      "type": "node",
      "request": "launch",
      "program": "${workspaceRoot}/node_modules/.bin/zeta",
      "stopOnEntry": false,
      "sourceMaps": true,
      "args": [ (2)
        "run",
        "--serve-front"
      ],
      "cwd": "${workspaceRoot}",
      "console": "internalConsole"
    }
  ]
}
1 Launch configuration name
2 zeta command arguments

Run VSCode debugger

Run VSCode debugger

You can run Debugger by following the official VSCode documentation: https://code.visualstudio.com/docs/editor/debugging.

14.2. Debug your application with IntelliJ based IDE

Intellij’s WebStorm provide a powerfull debugging feature https://blog.jetbrains.com/webstorm/2018/01/how-to-debug-with-webstorm/.

On the top menu, select Run → Edit Configurations

then select a new NodeJs Template :

  • Node interpreter : path to nodeJS executable.

  • Node parameters : must be "node_modules/.bin/zeta".

  • Working directory : path to your project.

  • Javascript file : should be blank.

  • Application parameters : zeta command to launch, with optionals flags.

Configure WebStorm Debug

You can now use Intellij in debug mode with breakpoints and all the debugs features.

15. Testing

16. Configuration of cloud services

16.1. Standard User Workflow

Configuration of Standard User Worflow.

17. Extension of cloud services

17.1. Standard User Workflow

Extension of Standard User Worflow.

Tutorials

In this tutorial, you will create an application with ZetaPush, from the initialization to the deployment. You will learn to:

  • Add business logic with ZetaPush

  • Use cloud services provided by ZetaPush

  • Run your code locally

  • Deploy your application

18. Use case

You will create a realtime chat application, but it will be particular: This will be an Avengers Chat !

At the beginning, the user of the chat can choose an Avenger. Each character has many skills and they can use them on the chat to attack the others Avengers.

Table 2. List of skills by Avenger
Avenger Skill

Captain America

Shoot of shield / Cure

Falcon

Archery / Super vision

Iron Man

Flight / Missile launcher

Hulk

Punch / Regeneration

Thor

Hammer hit / Lightning strike

Spider Man

Shoot of cobweb / Jump with cobweb

Wolverine

Scratch / Regeneration

You will create this application in few steps:

We recommend to use TypeScript so in this tutorial all of your code will be in this language.

18.1. Initialize your application

In the first step, you need to create your application. To do this you can run:

1
npm init @zetapush myApp

This command will ask you your developer login and password.

This command will create you an account on the ZetaPush platform and will create your first application in the myApp directory. An email will be sent to confirm your account.

To use your application, you need to validate your account. To do this, click on the link in the received email

In this tutorial, we are focusing on the ZetaPush development. So, to begin, get all resources and add them to your application (css, assets, …​):

1
2
3
4
$ cd myApp/front/
$ rm -r ./*
$ curl -X GET 'http://github-download-subfolder.zpush.io/?owner=zetapush&repository=zetapush-tutorials&path=avengersChat/front' --output front.zip
$ unzip front.zip && rm front.zip
If you are using Windows, you can download the file with all resources and unzip it in the front directory. Link: http://github-download-subfolder.zpush.io/?owner=zetapush&repository=zetapush-tutorials&path=avengersChat/front

So you already have the style and the frontend logic in the folders assets and utils. You can browse these files to understand this code.

In this tutorial, you need to:

  1. Create the business logic with a custom cloud service

  2. Implement the interaction with the custom cloud service (front side)

  3. Deploy your application

18.2. Create your business logic

Now, you will create the business logic of your application. To do this, you will use a custom cloud service.

A custom cloud service is a class where you develop a business logic. You can create several custom cloud services inside one application. Each custom cloud service can use the cloud services provided by ZetaPush.

In your existing application you already have a custom cloud service on the file worker/index.ts (The entry point of the service is defined in the package.json with the property main).

To use your custom cloud services you need to export them. To do this, the property main in your package.json define the path of the file where you export all of your custom cloud services.

You can delete the content of the file worker/index.ts and fill it with the following content step by step.

18.2.1. Import cloud services and define constants

First, you need to import the cloud services provided by ZetaPush, used in this application:

  • Stack (Save data in a stack)

  • Messaging (Send and receive messages)

  • Groups (Manage group of users)

You also need to import Injectable that is useful for the dependencies injection.

Imports and constants
1
2
3
4
5
import { Injectable } from '@zetapush/core';
import { Stack, Messaging, Groups } from '@zetapush/platform-legacy';

const CONVERSATION_ID = 'avengersChat';
const CHANNEL_MESSAGING = 'avengersChannel'; (1)
1 Channel on which the users send and listen to the messages

18.2.2. Create the class with its constructor

A custom cloud service is a class, so you need to create it.

Class and constructor
1
2
3
4
5
6
7
8
9
10
11
12
@Injectable() (1)
export default class AvengersApi {
  /**
   * Constructor of our API
   */
  constructor(
    private stack: Stack,
    private messaging: Messaging,
    private groups: Groups
  ) {
  }
}
1 We expose our custom cloud service with only one annotation
The dependencies injection of ZetaPush use injection-js.

18.2.3. Create the chat

To create our chat, we use the Groups cloud service provided by ZetaPush. The created group includes all users of the conversation. So we add a method to our class to create this conversation.

Create conversation
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * Create the conversation of the chat, if doesn't already exists
 */
async createConversation() {
  const { exists } = await this.groups.exists({
    group: CONVERSATION_ID
  });
  if (!exists) {
    await this.groups.createGroup({
      group: CONVERSATION_ID
    });
  }
}

18.2.4. Add user to the conversation

When an user comes to the chat, we need to add it to the conversation. So we create a method in our class to add the current user to the conversation.

1
2
3
4
5
6
7
8
9
/**
* Add the current user to the conversation
*/
addMeToConversation(parameters: any, context: any) { (1)
  return this.groups.addUser({
    group: CONVERSATION_ID,
      user: context.owner (2)
  });
}
1 Each custom cloud function takes only one (client-provided) parameter. The second is the context, injected by the SDK
2 We get the caller (owner) of the cloud function from the context.

18.2.5. Send a message

No we want to send a message on our chat. To do this, we need to follow 3 steps:

  1. Get all users in the conversation

  2. Send the message of all users in the conversation

  3. Store the message in a stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* Send a message on the chat
* @param {Object} message
*/
async sendMessage(message = {}) {
  // Get all users inside the conversation
  const group = await this.groups.groupUsers({
    group: CONVERSATION_ID
  });
  const users = group.users || [];

  // Send the message to each user in the conversation
  this.messaging.send({
    target: users,
    channel: CHANNEL_MESSAGING,
    data: { message }
  });

  // Store the message in a stack
  await this.stack.push({
    stack: CONVERSATION_ID,
    data: message
  });

  return group;
}
There is no specific method to launch an attack, to do this, we only send a specific message throught sendMessage.

18.2.6. Get all messages

To complete our business logic, we need to have an other method in our class to get all messages (when we enter the chat).

1
2
3
4
5
6
7
8
9
/**
* Get all messages in the conversation
*/
async getMessages() {
  const { result } = await this.stack.list({
    stack: CONVERSATION_ID
  });
  return result;
}

Now your custom cloud service is ready with this following content:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import { Injectable } from '@zetapush/core';
import { Stack, Messaging, Groups } from "@zetapush/platform-legacy";

const CONVERSATION_ID = "avengersChat";
const CHANNEL_MESSAGING = "avengersChannel";

@Injectable()
export default class AvengersApi {
  /**
   * Constructor of our API
   */
  constructor(
    private stack: Stack,
    private messaging: Messaging,
    private groups: Groups
  ) {}

  /**
   * Create the conversation of the chat, if doesn't already exists
   */
  async createConversation() {
    const { exists } = await this.groups.exists({
      group: CONVERSATION_ID
    });
    if (!exists) {
      await this.groups.createGroup({
        group: CONVERSATION_ID
      });
    }
  }

  /**
   * Add the current user in the conversation
   */
  addMeToConversation(parameters: any, context: any) {
    return this.groups.addUser({
      group: CONVERSATION_ID,
      user: context.owner
    });
  }

  /**
   * Send a message on the chat
   * @param {Object} message
   */
  async sendMessage(message = {}) {
    // Get all users inside the conversation
    const group = await this.groups.groupUsers({
      group: CONVERSATION_ID
    });
    const users = group.users || [];

    // Send the message to each user in the conversation
    this.messaging.send({
      target: users,
      channel: CHANNEL_MESSAGING,
      data: { message }
    });

    // Store the message in a stack
    await this.stack.push({
      stack: CONVERSATION_ID,
      data: message
    });

    return group;
  }

  /**
   * Get all messages in the conversation
   */
  async getMessages() {
    const { result } = await this.stack.list({
      stack: CONVERSATION_ID
    });
    return result;
  }
}

18.3. Use your custom cloud services

Once we have our custom cloud service, we need to create the frontend of our application.

18.3.1. Launch the worker locally

When we work on the frontend part, we often want to test our interaction with the backend logic. With ZetaPush, you can launch your worker locally to test this interaction. To do this you need to use the CLI and run: zeta run. But in this project, the CLI is imported and a script is created with npm, so you just need to launch:

Run worker locally
// In the root of the application
$ npm run start -- --serve-front
If you want to install the ZetaPush CLI you can run npm install -g @zetapush/cli.

Then you have access to your application on http://localhost:3000.

18.3.2. Interaction with worker

Now you just need to add the interaction with the custom cloud service. To do this, fill the file front/index.js:

Interaction with worker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import AvengersController from './utils/controller.js'; (1)

// Create the ZetaPush Client
const client = new ZetaPushClient.WeakClient();
/**
 * Create service to listen incoming messages
 */
const service = client.createService({
  Type: ZetaPushPlatform.Messaging,
  listener: {
    // Listen incoming messages from the channel 'avengersChannel'
    avengersChannel: ({ data }) => controller.onAvengersMessage(data), (2)
  },
});

const controller = new AvengersController(client);

(async function main() {
  // Expose to template
  window.$controller = controller;
  // Connect to ZetaPush
  await client.connect();
  // Notify client connected
  controller.onConnectionEstablished();
})();
1 Import the frontend logic (in the controller)
2 Cloud service provided by ZetaPush to listen to incoming messages

18.4. Deploy your application

Your application is working but only locally. To deploy your application you just need to launch zeta push. In this application you already have a npm script so you can run:

Deploy the application
$ npm run deploy

This command deploys all in once (Front and Worker). It will return you an unique URL to access to your application.

Enjoy ! Your Avengers Chat is working and deployed !

Guides

FAQ

  1. Do I have to develop custom cloud services ?

    No. Developing custom cloud services is not required. You can use cloud services that are already provided by ZetaPush. You may need to develop custom cloud services in different situations:

    • You have several devices so you want to mutualize your business code

    • ZetaPush services are not fully compatible with your business needs

Reference

19. Available cloud services

19.1. Standard User Workflow

19.1.1. Presentation

In a software application, we (almost) always need to manage users. The Standard User Workflow helps you to do this, in the most common use case. Currently, it only handles the registration of a user. In the following versions, this cloud service will help you to:

  • Log in a user

  • Log out a user

  • Manage a forgotten password

  • Manage permissions of users

  • etc…​

At this moment, the connection and the disconnection of a user is done through the ZetaPush SDK (We will see below how to do this).

For us, the most common use case is when a user can create an account using a login and a password and needs to confirm his account via a link in an email.

Sign up:

standarduserworkflow signup
1 The user creates his account using a login, a password and an email. When the user submits the form, the account is created on the ZetaPush platform. In order to ensure that the user is not a bot, the account has to be confirmed before the user is authorized to login.
2 An email is sent to the user in order to confirm its account. The email contains the confirmation link. He needs to confirm his account before connection.
3 When the user clicks on the lonk in the email, he is authorized to login.

Log in:

standarduserworkflow login
1 When the user account is confirmed, he can log in the application using the his login and his password.
If the user tries to log into the application before he confirmed his account, an error will be returned to explain that the account needs to be confirmed via the link in the confirmation email.

19.1.2. Quick start

In this part we will explain how to use the Standard User Workflow in the default behavior, i.e. using the default implementations of each parts of the workflow (For example, the default implementation to send the confirmation email use the Mailjet API).

To use the Standard User Workflow, you just need 4 steps :

  • Create a ZetaPush application as usual

  • Import and use the Standard User Workflow in your worker

  • Configure some properties

  • Develop your front by calling the Standard User Workflow API

19.1.2.1. Create a ZetaPush application as usual

In this Quickstart, we begin with a generated application using the CLI. So you can run:

$ npm init @zetapush myApp

We import the library @zetapush/user-management that provides the Standard User Workflow using NPM:

$ npm install --save @zetapush/user-management
19.1.2.2. Import and use the Standard User Workflow in your worker

Your worker contains just this code :

worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
import { Module } from '@zetapush/core';
import { StandardUserManagementModule, StandardUserWorkflow, ConfirmationUrlHttpHandler } from '@zetapush/user-management';

@Module({ (1)
  imports: [StandardUserManagementModule], (2)
  expose: {
    user: StandardUserWorkflow, (3)
    http: ConfirmationUrlHttpHandler (4)
  }
})
export default class Api {}
1 As usual, the worker code exports a class (maned Api here). As we import another module (@zetapush/user-management) we need to use @module annotation.
2 First you need to export a class (named Api here) that is a module (configured with the @Module() annotation). You import the StandardUserManagementModule to use it into your application.
3 The @zetapush/user-management library/cloud service provides the StandardUserWorkflow class. The front needs to call methods of the StandardUserWorkflow class that’s why StandardUserWorkflow class is exposed. In this example, the StandardUserWorflow methods are exposed in user namespace.
4 We also expose ConfirmationUrlHttpHandler to instanciate a web server in your worker that will listen the account confirmation event (When the user clicks on the link in the email).

That all for the worker !

19.1.2.3. Configure some properties

Some configuration properties are required (like Mailjet API keys for example) to make the whole workflow work. To configure those properties, you need to create a file named application.json at the root of your application:

application.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
  "email": {
    "from": "celtia@zetapush.com" (1)
  },
  "registration": {
    "confirmation": {
      "base-url": "http://localhost:2999", (2)
      "email": {
        "subject": "Please confirm your account registration"
      },
      "success-url": "http://localhost:3000#login", (3)
      "failure-url": "http://localhost:3000#confirmation-failed" (4)
    }
  },
  "mailjet": {  (5)
    "apikey-public": "",
    "apikey-private": ""
  }
}
1 We configure the email sender
2 the base-url property is necessary to run the application in local. This property will soon be optional.
3 Link of your application where the user will be redirected when he confirms his account.
4 Link of your application where the user will be redirected when the confirmation failed.
5 Your Mailjet properties.
19.1.2.4. Develop your front by calling the Standard User Workflow API

Finally, we just need to call the cloud functions to create a user account, log in and log out from the client part. You can replace the front/index.js file by the following content :

front/index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
// Create the ZetaPush API
const client = new ZetaPushClient.SmartClient();
const userApi = client.createProxyTaskService({ (1)
  namespace: 'user'
});

// The user is hard-coded because we only show the front -> worker interaction
const user = {
  login: 'test',
  password: 'password',
  email: 'test-zetapush@yopmail.com'
};

async function signUpUser() { (2)
  // First connection as anonymous user to allow us to create a user
  await client.connect();

  // Creation of the user account
  try {
    await userApi.signup({
      credentials: {
        login: user.login,
        password: user.password
      },
      profile: {
        email: user.email
      }
    });
    console.log('User account created');
  } catch (err) {
    console.error('Failed to create user account : ', err);
  }
}

/**
 * Connection of a user
 */
async function connectUser() { (3)
  await client.setCredentials({ login: user.login, password: user.password });
  client
    .connect()
    .then(() => {
      console.log('User connected');
    })
    .catch(err => {
      console.error('Failed to connect user : ', err);
    });
}

/**
 * Disconnection of a user
 */
async function disconnectUser() { (4)
  await client.disconnect();
  console.log('User disconnected');
}
1 We created a ZetaPush client and his API to call our cloud functions (We use the namespace 'user' to call the Standard User Workflow).
2 Function to create an account of the ZetaPush platform for this application.
3 Function to connect a user.
4 Function to disconnect a user.

Here is a very basic HTML page to see the result in the console :

front/index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Standard User Workflow</title>
</head>

<body>
  <button onclick="signUpUser()">Sign up</button>
  <button onclick="connectUser()">Connect</button>

  <script src="https://unpkg.com/@zetapush/client"></script>
  <script src="./index.js"></script>
</body>

</html>

Run the following command to run your application in local and serve the front part :

Run the application
$ npm run start -- --serve-front
For a more real-world sample with a complete UI (signup form and login form) you can go to GitHub ZetaPush Examples.

20. Legacy cloud services

20.1. User Management

20.1.1. Simple

End-user API for the simple local authentication

These API verbs allow end-users to manage their account(s).

20.1.1.1. createUser

Creates a new account in this 'simple' authentication realm. Returns a map of account fields, including a field named <i>zetapushKey</i> containing the global user key of the user (value of the <b>__userKey</b> pseudo-constant when this new account will be used)

createUser(parameters: BasicAuthenticatedUser): Promise<-object>.adoc#,Map<String,Object>>
1
const response = await this.simple.createUser(parameters);
20.1.1.2. credentials

Returns the list of account credentials in this service for the asking user. Might return an empty list.

credentials(parameters: ImpersonatedTraceableRequest): Promise<AllCredentials>
1
const response = await this.simple.credentials(parameters);
20.1.1.3. updateKey

Updates an existing account primary key (login, NOT <b>__userKey</b>) in this 'simple' authentication realm. The updated account MUST belong to the user making the call. The configured login field MUST be given, as a user (identified by his zetapush userKey) might possess several accounts. Returns a map of account fields

updateKey(parameters: UserLoginchange): Promise<-object>.adoc#,Map<String,Object>>
1
const response = await this.simple.updateKey(parameters);
20.1.1.4. updateUser

Updates an existing account in this 'simple' authentication realm. The updated account MUST belong to the user making the call. The configured login field MUST be given, as a user (identified by his zetapush userKey) might possess several accounts. Returns a map of account fields

updateUser(parameters: BasicAuthenticatedUser): Promise<-object>.adoc#,Map<String,Object>>
1
const response = await this.simple.updateUser(parameters);
20.1.1.5. deleteUser

Deletes an existing account in this 'simple' authentication realm.

deleteUser(parameters: ExistenceCheck): Promise<ExistenceCheck>
1
const response = await this.simple.deleteUser(parameters);
20.1.1.6. checkUser

Checks whether the given account already exists in this 'simple' authentication realm. This verb returns all the information about the user, including non public fields.

checkUser(parameters: ExistenceCheck): Promise<-object>.adoc#,Map<String,Object>>
1
const response = await this.simple.checkUser(parameters);
20.1.1.7. requestReset

Requests a password reset for the given unique account key. The account key must exist and must be given, as it cannot obviously be deduced from the currently logged in user. The returned token needs to be sent to the intended recipient only. The typical use case is to define a macro that requests a reset, generates a email template and emails the user. The macro can then be safely called by a weakly authenticated user. Requesting a reset does not invalidate the password. Requesting a reset again invalidates previous reset requests (only the last token is usable)

requestReset(parameters: ResetRequest): Promise<ResetInfo>
1
const response = await this.simple.requestReset(parameters);
20.1.1.8. changePassword

Changes a user password for this authentication realm. The user can be either explicit, implicit (one of the current user’s accounts) or deduced from the token. You should provide at least one of 'key' and 'token'. If you do not, the server will try and find any key for the current user. The change is effective immediately. However, already logged in users might stay connected. The password and token fields are always null in the output.

changePassword(parameters: ChangePasswordRequest): Promise<ChangePasswordRequest>
1
const response = await this.simple.changePassword(parameters);
20.1.1.9. checkPassword
checkPassword(parameters: CheckPasswordRequest): Promise<CheckPasswordResult>
1
const response = await this.simple.checkPassword(parameters);
20.1.1.10. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Simple, BasicAuthenticatedUser } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private simple: Simple (2)
  ) {}
  async doStuff(parameters: BasicAuthenticatedUser) {
    const response = await this.simple.createUser(parameters); (3)
    return response;
  }
}
1 Import Simple from platform
2 Declare injected service
3 Call injected service

20.1.2. Weak

User API for weak devices control

User API for control and release of weakly authenticated user sessions.

20.1.2.1. control

Takes control of a weak user session, identified by the given public token. The public token has been previously made available by the controlled device, for example by displaying a QRCode. Upon control notification, the client SDK of the controlled session is expected to re-handshake.

control(parameters: UserControlRequest): Promise<UserControlStatus>
1
const response = await this.weak.control(parameters);
20.1.2.2. release

Releases control of a weak user session, identified by the given public token. The weak user session must have been previously controlled by a call to 'control'.

release(parameters: UserControlRequest): Promise<UserControlStatus>
1
const response = await this.weak.release(parameters);
20.1.2.3. provision

Provisions an arbitrary number of accounts. The maximum number of accounts that you can create in one single call is configured per server.

provision(parameters: ProvisioningRequest): Promise<ProvisioningResult>
1
const response = await this.weak.provision(parameters);
20.1.2.4. getToken

Returns your current session’s private token. The token field may be null, if you did not log in with this authentication. The token can be used to log in as the same weak user another time.

getToken(parameters: undefined): Promise<UserToken>
1
const response = await this.weak.getToken(parameters);
20.1.2.5. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Weak, UserControlRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private weak: Weak (2)
  ) {}
  async doStuff(parameters: UserControlRequest) {
    const response = await this.weak.control(parameters); (3)
    return response;
  }
}
1 Import Weak from platform
2 Declare injected service
3 Call injected service

20.1.3. Userdir

User API for user information

20.1.3.1. userInfo
userInfo(parameters: UserInfoRequest): Promise<UserInfoResponse>
1
const response = await this.userdir.userInfo(parameters);
search(parameters: UserSearchRequest): Promise<UserSearchResponse>
1
const response = await this.userdir.search(parameters);
20.1.3.3. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Userdir, UserInfoRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private userdir: Userdir (2)
  ) {}
  async doStuff(parameters: UserInfoRequest) {
    const response = await this.userdir.userInfo(parameters); (3)
    return response;
  }
}
1 Import Userdir from platform
2 Declare injected service
3 Call injected service

20.1.4. Groups

User API for groups and rights.

Groups are stored per user. This means that two users can own a group with the same identifier. A couple (owner, group) is needed to uniquely identify a group inside a group management service. The triplet (deploymentId, owner, group) is actually needed to fully qualify a group outside of the scope of this service.

20.1.4.1. grant

The granting API does not do any check when storing permissions. In particular when granting rights on a verb and resource of another API, the existence of said verb and resource is not checked.

grant(parameters: Grant): Promise<Grant>
1
const response = await this.groups.grant(parameters);
20.1.4.2. createGroup

Creates a group owned by the current user. Group creation may fail if the group already exists.

createGroup(parameters: GroupInfo): Promise<GroupInfo>
1
const response = await this.groups.createGroup(parameters);
20.1.4.3. listOwnedGroups

Returns the whole list of groups owned by the current user

listOwnedGroups(parameters: TraceablePaginatedImpersonatedRequest): Promise<OwnedGroups>
1
const response = await this.groups.listOwnedGroups(parameters);
20.1.4.4. listDetailedOwnedGroups

Returns the whole list of groups owned by the current user, with their members

listDetailedOwnedGroups(parameters: TraceablePaginatedImpersonatedRequest): Promise<OwnedGroupsWithDetails>
1
const response = await this.groups.listDetailedOwnedGroups(parameters);
20.1.4.5. myGroups

Returns the whole list of groups the current user is part of. Groups may be owned by anyone, including the current user.

myGroups(parameters: ImpersonatedRequest): Promise<GroupInfo[]>
1
const response = await this.groups.myGroups(parameters);
20.1.4.6. listJoinedGroups

Returns the whole list of groups the current user is part of. Groups may be owned by anyone, including the current user.

listJoinedGroups(parameters: TraceablePaginatedImpersonatedRequest): Promise<JoinedGroups>
1
const response = await this.groups.listJoinedGroups(parameters);
20.1.4.7. delGroup

Removes the given group owned by the current user or the given owner. Also removes all grants to that group.

delGroup(parameters: GroupRelated): Promise<GroupRelated>
1
const response = await this.groups.delGroup(parameters);
20.1.4.8. addMe

Adds me (the caller) to a group. This verb exists so that group owners may grant the right to join their groups without granting the right to add other users to those groups. The 'user' field is implicitly set to the current user’s key.

addMe(parameters: UserGroup): Promise<UserGroup>
1
const response = await this.groups.addMe(parameters);
20.1.4.9. memberOf

Tests whether I (the caller) am a member of the given group. This verb exists so that users can determine if they are part of a group without being granted particular rights. The 'user' field is implicitly set to the current user’s key.

memberOf(parameters: UserMembership): Promise<UserGroupMembership>
1
const response = await this.groups.memberOf(parameters);
20.1.4.10. addUsers

Users are processed in the given order In case of failure in the middle of a user list, this verb may have succeeded to add the first users, but will not continue processing the end of the list.

addUsers(parameters: GroupUsers): Promise<void>
1
const response = await this.groups.addUsers(parameters);
20.1.4.11. delUsers
delUsers(parameters: GroupUsers): Promise<void>
1
const response = await this.groups.delUsers(parameters);
20.1.4.12. mgrant

Grant several rights at once.

mgrant(parameters: Grants): Promise<Grants>
1
const response = await this.groups.mgrant(parameters);
20.1.4.13. mrevoke
mrevoke(parameters: Grants): Promise<Grants>
1
const response = await this.groups.mrevoke(parameters);
20.1.4.14. listGrants

This API lists explicitly configured rights. Effective rights include configured rights, implicit rights and inherited rights.

listGrants(parameters: GroupRelated): Promise<GrantList>
1
const response = await this.groups.listGrants(parameters);
20.1.4.15. listGroupGrants

This API lists explicitly configured rights. Effective rights include configured rights, implicit rights and inherited rights.

listGroupGrants(parameters: GroupRelatedAndPaged): Promise<PagedGrantList>
1
const response = await this.groups.listGroupGrants(parameters);
20.1.4.16. listPresences

Returns the list of members of the given groups, along with their actual and current presence on the zetapush server. The current implementation does not include information about the particular devices users are connected with. If a user is connected twice with two different devices, two identical entries will be returned.

listPresences(parameters: GroupRelated): Promise<GroupPresence>
1
const response = await this.groups.listPresences(parameters);
20.1.4.17. listGroupPresences

Returns the list of members of the given groups, along with their actual and current presence on the zetapush server. The current implementation does not include information about the particular devices users are connected with. If a user is connected twice with two different devices, two identical entries will be returned.

listGroupPresences(parameters: GroupRelatedAndPaged): Promise<PagedGroupPresence>
1
const response = await this.groups.listGroupPresences(parameters);
20.1.4.18. addUser

Adds the given user to the given group. Addition may fail if the given group does not already exist.

addUser(parameters: UserGroup): Promise<UserGroup>
1
const response = await this.groups.addUser(parameters);
20.1.4.19. delUser
delUser(parameters: UserGroup): Promise<UserGroup>
1
const response = await this.groups.delUser(parameters);
20.1.4.20. groupUsers

Returns the whole list of users configured inside the given group.

groupUsers(parameters: GroupRelated): Promise<GroupUsers>
1
const response = await this.groups.groupUsers(parameters);
20.1.4.21. revoke
revoke(parameters: Grant): Promise<Grant>
1
const response = await this.groups.revoke(parameters);
20.1.4.22. allGroups

Returns the whole list of groups owned by the current user, with their members

allGroups(parameters: ImpersonatedRequest): Promise<GroupUsers[]>
1
const response = await this.groups.allGroups(parameters);
20.1.4.23. groups

Returns the whole list of groups owned by the current user

groups(parameters: ImpersonatedRequest): Promise<GroupInfo[]>
1
const response = await this.groups.groups(parameters);
20.1.4.24. check

This API checks if the given user has the proper authorizations to perform the given action on the owner’s resource. If you give the same value for 'user' and 'owner', the check always passes.

check(parameters: GrantCheckRequest): Promise<GrantCheckResult>
1
const response = await this.groups.check(parameters);
20.1.4.25. exists

Returns whether a group exists or not.

exists(parameters: GroupRelated): Promise<GroupExistence>
1
const response = await this.groups.exists(parameters);
20.1.4.26. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Groups, Grant } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private groups: Groups (2)
  ) {}
  async doStuff(parameters: Grant) {
    const response = await this.groups.grant(parameters); (3)
    return response;
  }
}
1 Import Groups from platform
2 Declare injected service
3 Call injected service

20.2. Data Management

20.2.1. Gda

GDA User API

User API for Generic Data Access. The data are stored on a per-user basis. Users can put, get, list their data.

20.2.1.1. puts

Creates or replaces the (maybe partial) contents of a collection of rows. This method only creates or replaces cells for non-null input values.

puts(parameters: GdaPuts): Promise<GdaPutsResult>
1
const response = await this.gda.puts(parameters);
20.2.1.2. inc

Increments a cell 64-bit signed integer value and returns the result in the data field. The increment is atomic : if you concurrently increment 10 times a value by 1, the final result will be the initial value plus 10. The actual individual resulting values seen by the 10 concurrent callers may vary discontinuously, with duplicates : at least one of them will see the final (+10) result.

inc(parameters: GdaPut): Promise<GdaPut>
1
const response = await this.gda.inc(parameters);
20.2.1.3. removeRow

Removes all columns of the given row from the given table.

removeRow(parameters: GdaRowRequest): Promise<GdaRowRequest>
1
const response = await this.gda.removeRow(parameters);
20.2.1.4. removeColumn

Removes all cells of the given column of the given row from the given table.

removeColumn(parameters: GdaColumnRequest): Promise<GdaColumnRequest>
1
const response = await this.gda.removeColumn(parameters);
20.2.1.5. removeCell

Removes only one cell of the given column of the given row from the given table.

removeCell(parameters: GdaCellRequest): Promise<GdaCellRequest>
1
const response = await this.gda.removeCell(parameters);
20.2.1.6. mget

Returns full data rows, in the order they were asked.

mget(parameters: GdaMultiGetRequest): Promise<GdaMultiGetResult>
1
const response = await this.gda.mget(parameters);
20.2.1.7. getCells

Returns a precise list of cells from a column in a data row.

getCells(parameters: GdaCellsRequest): Promise<GdaCellsResult>
1
const response = await this.gda.getCells(parameters);
20.2.1.8. get

Returns a full data row.

get(parameters: GdaGet): Promise<GdaGetResult>
1
const response = await this.gda.get(parameters);
20.2.1.9. put

Creates or replaces the contents of a particular cell.

put(parameters: GdaPut): Promise<GdaPut>
1
const response = await this.gda.put(parameters);
20.2.1.10. list

Returns a paginated list of rows from the given table.

list(parameters: GdaList): Promise<GdaListResult>
1
const response = await this.gda.list(parameters);
20.2.1.11. removeRange

Removes the specified columns of the given range of rows from the given table.

removeRange(parameters: GdaRemoveRange): Promise<GdaRemoveRange>
1
const response = await this.gda.removeRange(parameters);
20.2.1.12. filter

Similar to range, but rows can be filtered out according to a developer-supplied predicate. A range consists of consecutive rows from the start key (inclusive) to the stop key (exclusive). You can specify partial keys for the start and stop fields.

filter(parameters: GdaFilterRequest): Promise<GdaFilterResult>
1
const response = await this.gda.filter(parameters);
20.2.1.13. range

Returns a paginated range of rows from the given table. A range consists of consecutive rows from the start key (inclusive) to the stop key (exclusive). You can specify partial keys for the start and stop fields.

range(parameters: GdaRange): Promise<GdaRangeResult>
1
const response = await this.gda.range(parameters);
20.2.1.14. reduce

Returns a computed single reduced result from a range of rows from the given table. A range consists of consecutive rows from the start key (inclusive) to the stop key (exclusive). You can specify partial keys for the start and stop fields.

reduce(parameters: GdaReduceRequest): Promise<GdaReduceResult>
1
const response = await this.gda.reduce(parameters);
20.2.1.15. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Gda, GdaPuts } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private gda: Gda (2)
  ) {}
  async doStuff(parameters: GdaPuts) {
    const response = await this.gda.puts(parameters); (3)
    return response;
  }
}
1 Import Gda from platform
2 Declare injected service
3 Call injected service

20.2.2. Stack

Data stack user API

Data is stored on a per user basis. However, notifications can be sent to a configurable set of listeners. Stack names are arbitrary and do not need to be explicitly initialized.

20.2.2.1. purge

Removes all items from the given stack.

purge(parameters: StackRequest): Promise<StackRequest>
1
const response = await this.stack.purge(parameters);
20.2.2.2. getListeners

Returns the whole list of listeners for the given stack.

getListeners(parameters: StackRequest): Promise<StackListeners>
1
const response = await this.stack.getListeners(parameters);
20.2.2.3. setListeners

Sets the listeners for the given stack.

setListeners(parameters: StackListeners): Promise<StackListeners>
1
const response = await this.stack.setListeners(parameters);
20.2.2.4. remove

Removes the item with the given guid from the given stack.

remove(parameters: StackItemRemove): Promise<StackItemRemove>
1
const response = await this.stack.remove(parameters);
20.2.2.5. update

Updates an existing item of the given stack. The item MUST exist prior to the call.

update(parameters: StackItemAdd): Promise<StackItemAdd>
1
const response = await this.stack.update(parameters);
20.2.2.6. push

Pushes an item onto the given stack. The stack does not need to be created.

push(parameters: StackItemAdd): Promise<StackItemAdd>
1
const response = await this.stack.push(parameters);
20.2.2.7. list

Returns a paginated list of contents for the given stack. Content is sorted according to the statically configured order.

list(parameters: StackListRequest): Promise<StackListResponse>
1
const response = await this.stack.list(parameters);
20.2.2.8. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Stack, StackRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private stack: Stack (2)
  ) {}
  async doStuff(parameters: StackRequest) {
    const response = await this.stack.purge(parameters); (3)
    return response;
  }
}
1 Import Stack from platform
2 Declare injected service
3 Call injected service

20.2.3. Search

ElasticSearch Service

This API is a very thin wrapper around ElasticSearch’s API.

20.2.3.1. index

Inserts or updates a document into the elasticsearch engine.

index(parameters: SearchData): Promise<void>
1
const response = await this.search.index(parameters);
20.2.3.2. get

Retrieves a document from the elasticsearch engine by id.

get(parameters: SearchDocumentId): Promise<SearchData>
1
const response = await this.search.get(parameters);
20.2.3.3. delete

Deletes a document from the elasticsearch engine by id.

delete(parameters: SearchDocumentId): Promise<void>
1
const response = await this.search.delete(parameters);
20.2.3.4. search
search(parameters: SearchRequest): Promise<SearchResults>
1
const response = await this.search.search(parameters);
20.2.3.5. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Search, SearchData } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private search: Search (2)
  ) {}
  async doStuff(parameters: SearchData) {
    const response = await this.search.index(parameters); (3)
    return response;
  }
}
1 Import Search from platform
2 Declare injected service
3 Call injected service

20.3. File Management

20.3.1. Zpfs_hdfs

User API for local file management

User API for file content manipulation User API for virtual file management and http file upload This API contains all the verbs needed to browse, upload and remove files. Files are stored on a per-user basis: each user has his or her own whole virtual filesystem. Uploading a file is a 3-step process : request an upload URL, upload via HTTP, notify this service of completion.

20.3.1.1. readToken

Requests a token. This token can be used to retrieve a compressed folder via HTTP.

readToken(parameters: ZpfsRequest): Promise<ZpfsToken>
1
const response = await this.zpfs_hdfs.readToken(parameters);
20.3.1.2. create

Creates a file, writes content and closes the file as a single operation. Calling this verb is functionnally equivalent to successively calling 'newUploadUrl', posting the content and calling 'newFile'

create(parameters: FileCreationRequest): Promise<ListingEntryInfo>
1
const response = await this.zpfs_hdfs.create(parameters);
20.3.1.3. open

Opens a file for reading.

open(parameters: ZpfsRequest): Promise<ZpfsFileHandler>
1
const response = await this.zpfs_hdfs.open(parameters);

Links a file or folder to another location. May fail if the target location is not empty.

link(parameters: CreatedFile): Promise<CreatedFile>
1
const response = await this.zpfs_hdfs.link(parameters);
20.3.1.5. stat

Returns information about a single file. The entry field will be null if the path does not exist

stat(parameters: FileStatRequest): Promise<FileStatResult>
1
const response = await this.zpfs_hdfs.stat(parameters);
20.3.1.6. cp

Copies a file or folder (recursively) to a new location. May fail if the target location is not empty.

cp(parameters: CreatedFile): Promise<CreatedFile>
1
const response = await this.zpfs_hdfs.cp(parameters);
20.3.1.7. ls

Returns a paginated list of the folder’s content.

ls(parameters: FolderListingRequest): Promise<FolderListing>
1
const response = await this.zpfs_hdfs.ls(parameters);
20.3.1.8. mv

Moves a file or folder (recursively) to a new location. May fail if the target location is not empty.

mv(parameters: CreatedFile): Promise<CreatedFile>
1
const response = await this.zpfs_hdfs.mv(parameters);
20.3.1.9. snapshot

Creates a new folder and then copies the given files inside

snapshot(parameters: SnapshotCreationRequest): Promise<CreatedFile>
1
const response = await this.zpfs_hdfs.snapshot(parameters);
20.3.1.10. du

Returns an recursively aggregated number of used bytes, starting at the given path.

du(parameters: ZpfsRequest): Promise<ZpfsDiskUsage>
1
const response = await this.zpfs_hdfs.du(parameters);
20.3.1.11. rm

Removes a file or folder (recursively).

rm(parameters: FileRemoval): Promise<FileRemoval>
1
const response = await this.zpfs_hdfs.rm(parameters);
20.3.1.12. freeUploadUrl
freeUploadUrl(parameters: FileUploadRequest): Promise<FileUploadLocation>
1
const response = await this.zpfs_hdfs.freeUploadUrl(parameters);
20.3.1.13. newUploadUrl

Requests an HTTP upload URL. The URL contains temporary credentials (typically valid for a few minutes) and is meant for immediate use.

newUploadUrl(parameters: FileUploadRequest): Promise<FileUploadLocation>
1
const response = await this.zpfs_hdfs.newUploadUrl(parameters);
20.3.1.14. updateMeta
updateMeta(parameters: FileMetaUpdate): Promise<ListingEntryInfo>
1
const response = await this.zpfs_hdfs.updateMeta(parameters);
20.3.1.15. newFile

The client application calls this verb to notify that it’s done uploading to the cloud. Calling that verb MAY trigger additional events such as thumbnail/metadata creation.

newFile(parameters: FileUploadComplete): Promise<ListingEntryInfo>
1
const response = await this.zpfs_hdfs.newFile(parameters);
20.3.1.16. mkdir

Creates a new folder. May fail if the target location is not empty.

mkdir(parameters: FolderCreationRequest): Promise<CreatedFile>
1
const response = await this.zpfs_hdfs.mkdir(parameters);
20.3.1.17. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Zpfs_hdfs, ZpfsRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private zpfs_hdfs: Zpfs_hdfs (2)
  ) {}
  async doStuff(parameters: ZpfsRequest) {
    const response = await this.zpfs_hdfs.readToken(parameters); (3)
    return response;
  }
}
1 Import Zpfs_hdfs from platform
2 Declare injected service
3 Call injected service

20.3.2. Template

User API for templates

Users use this API to evaluate pre-configured templates.

20.3.2.1. evaluate

Evaluates the given template and returns the result as a string. Templates are parsed the first time they are evaluated. Evaluation may fail early due to a parsing error.

evaluate(parameters: TemplateRequest): Promise<TemplateResult>
1
const response = await this.template.evaluate(parameters);
20.3.2.2. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Template, TemplateRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private template: Template (2)
  ) {}
  async doStuff(parameters: TemplateRequest) {
    const response = await this.template.evaluate(parameters); (3)
    return response;
  }
}
1 Import Template from platform
2 Declare injected service
3 Call injected service

20.4. Communication

20.4.1. Messaging

Messaging service

Simple and flexible user-to-user or user-to-group messaging service.

20.4.1.1. send

Sends the given message to the specified target on the given (optional) channel. The administratively given default channel name is used when none is provided in the message itself.

send(parameters: Message): Promise<void>
1
const response = await this.messaging.send(parameters);
20.4.1.2. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Messaging, Message } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private messaging: Messaging (2)
  ) {}
  async doStuff(parameters: Message) {
    const response = await this.messaging.send(parameters); (3)
    return response;
  }
}
1 Import Messaging from platform
2 Declare injected service
3 Call injected service

20.4.2. Notif

Notification User API

User API for notifications. For notifications to work properly, it is imperative that the resource name of a device remain constant over time.

20.4.2.1. send

Sends a native push notification to the target.

send(parameters: NotificationMessage): Promise<NotificationSendStatus>
1
const response = await this.notif.send(parameters);
20.4.2.2. unregister

Unregisters the device for the current user and resource. This verb does not need any parameters.

unregister(parameters: undefined): Promise<void>
1
const response = await this.notif.unregister(parameters);
20.4.2.3. register

Registers the device for the current user and resource. This service maintains a mapping of userkey/resource to device registration IDs. You MUST NOT re-use the same resource name from one device to another if you want to target specific devices with 'send'. Only one registration can be active for a given userKey/resource pair in a notification service. Device registration can be <b>neither impersonated nor called indirectly</b> (from a scheduled job).

register(parameters: NotifiableDeviceRegistration): Promise<void>
1
const response = await this.notif.register(parameters);
20.4.2.4. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Notif, NotificationMessage } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private notif: Notif (2)
  ) {}
  async doStuff(parameters: NotificationMessage) {
    const response = await this.notif.send(parameters); (3)
    return response;
  }
}
1 Import Notif from platform
2 Declare injected service
3 Call injected service

20.4.3. Sendmail

Mail service user API

This service is statically configured with an outgoing SMTP server. Users call the API here to actually send emails.

20.4.3.1. send

Sends an email with the given body to the intended recipients.

send(parameters: Email): Promise<void>
1
const response = await this.sendmail.send(parameters);
20.4.3.2. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Sendmail, Email } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private sendmail: Sendmail (2)
  ) {}
  async doStuff(parameters: Email) {
    const response = await this.sendmail.send(parameters); (3)
    return response;
  }
}
1 Import Sendmail from platform
2 Declare injected service
3 Call injected service

20.4.4. Sms_ovh

SMS service

User API for SMS.

20.4.4.1. send

Sends the given message to the given recipients.

send(parameters: SmsMessage): Promise<void>
1
const response = await this.sms_ovh.send(parameters);
20.4.4.2. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Sms_ovh, SmsMessage } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private sms_ovh: Sms_ovh (2)
  ) {}
  async doStuff(parameters: SmsMessage) {
    const response = await this.sms_ovh.send(parameters); (3)
    return response;
  }
}
1 Import Sms_ovh from platform
2 Declare injected service
3 Call injected service

20.5. Utilities Management

20.5.1. Cron

User API for the Scheduler

User endpoints for scheduling : users can schedule, list and delete tasks. Tasks are stored on a per-user basis: a task will run with the priviledges of the user who stored it. Tasks are run on the server and thus can call api verbs marked as server-only.

20.5.1.1. schedule

Schedules a task for later execution. Tasks are executed asynchronously with the identity of the calling user. Tasks will be executed at a fixed moment in time in the future, or repeatedly, with minute precision. If a task already exists with the same cronName (a cronName is unique for a given user), this new task completely replaces it. A task can be scheduled with a cron-like syntax for repetitive or one-shot execution. Wildcards are not allowed for minutes and hours. When scheduling for one-shot execution, the time must be at least two minutes into the future.

schedule(parameters: CronTaskRequest): Promise<CronTaskRequest>
1
const response = await this.cron.schedule(parameters);
20.5.1.2. setTimeout

Schedules a task for later execution. Tasks are executed asynchronously with the identity of the calling user. Tasks will be executed with second precision in the near future (120 seconds delay max).

setTimeout(parameters: TimerRequest): Promise<TimerResult>
1
const response = await this.cron.setTimeout(parameters);
20.5.1.3. unschedule

Removes a previously scheduled task. Does absolutely nothing if asked to remove a non-existent task.

unschedule(parameters: CronTaskDeletion): Promise<CronTaskDeletion>
1
const response = await this.cron.unschedule(parameters);
20.5.1.4. list

Returns a paginated list of the asking user’s tasks.

list(parameters: CronTaskListRequest): Promise<CronPlanning>
1
const response = await this.cron.list(parameters);
20.5.1.5. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Cron, CronTaskRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private cron: Cron (2)
  ) {}
  async doStuff(parameters: CronTaskRequest) {
    const response = await this.cron.schedule(parameters); (3)
    return response;
  }
}
1 Import Cron from platform
2 Declare injected service
3 Call injected service

20.5.2. Logs

Log API

User API for logging.

20.5.2.1. log

Adds some server generated data and stores the entry into the sink defined by configuration.

log(parameters: LogRequest): Promise<void>
1
const response = await this.logs.log(parameters);
20.5.2.2. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Logs, LogRequest } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private logs: Logs (2)
  ) {}
  async doStuff(parameters: LogRequest) {
    const response = await this.logs.log(parameters); (3)
    return response;
  }
}
1 Import Logs from platform
2 Declare injected service
3 Call injected service

20.5.3. Trigger

Trigger service

Register listeners and trigger events.

20.5.3.1. trigger

Triggers an event. All listeners previously registered for that event will be called, in no particular order.

trigger(parameters: EventTrigger): Promise<void>
1
const response = await this.trigger.trigger(parameters);
20.5.3.2. How to use it ?
worker/index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Injectable } from '@zetapush/core';
import { Trigger, EventTrigger } from '@zetapush/platform-legacy'; (1)

@Injectable()
export default class Api {
  constructor(
    private trigger: Trigger (2)
  ) {}
  async doStuff(parameters: EventTrigger) {
    const response = await this.trigger.trigger(parameters); (3)
    return response;
  }
}
1 Import Trigger from platform
2 Declare injected service
3 Call injected service

21. Command Line Interface

21.1. Initialize an application

The command to initialize an application with the CLI is:

$ npm init @zetapush myApp (1)
1 myApp is the name of the generated directory

This command can take the following parameters:

  • --developer-login

This login is the email used by the developer when logging in to the ZetaPush console. It does not concern the applications developed by the developer. It is only used to identify the developer account.

  • --developer-password

Like the property --developer-login, this password is only used for the ZetaPush developer account. It does not concern the applications developed by the developer.

  • --app-name

A developer can develop many applications. Hence the need to identify each of them. The appName property is the unique name of an application on a ZetaPush developer account.

  • --platform-url

By default, the developer uses the production environment of ZetaPush to develop and deploy its applications. In most cases, the developer doesn’t need to set this property. This property if useful when another (on premise, or dedicated) ZetaPush platform is to be used.

  • --env-name

An application created by a developer can have many environments. The default environment is prod but you can create your own (dev, preprod, …​). An environment is one stage in the development of the application.

  • --javascript

This option allow developer to bootstrap project with a pure JavaScript stack. Default strategy is TypeScript.

21.1.1. Create a new project and/or account

21.1.1.1. Initialization workflows

With or without account

Don’t have ZetaPush account

init without account
Figure 6. I want to create an account

➊ I launch npm init @zetapush myApp without parameters
➋ You type your email and a password to create your ZetaPush account
➌ The application is created in the myApp folder
➍ The .zetarc file looks like:

1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": "nameOfAppAutoGenerated"
}
You need to confirm your ZetaPush account to run or deploy your application.

Have an account and an app

init with account and app
Figure 7. I want to use an existing application

➊ I launch npm init @zetapush myApp with the --developer-login, --developer-password and --appName parameters
➋ The application is created in the myApp folder
➌ The .zetarc file looks like:

1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": "nameOfAppAutoGenerated"
}

Have an account and want new app

init with account without app
Figure 8. I want to create a new application

➊ I launch npm init @zetapush myApp with the --developer-login and --developer-password parameters
➋ The application is created in the myApp folder
➌ The .zetarc file looks like:

1
2
3
4
5
{
    "developerLogin": "user@gmail.com",
    "developerPassword": "password",
    "appName": ""
}
If the appName property in the .zetarc is empty or missing, ZetaPush deploys or executes the code in a new application.

-

21.1.1.2. Create a new account and a new application
Generate application
$ npm init @zetapush first-hello-world (1)
1 first-hello-world is the name of the folder that will be created with all the generated files

You will be prompted for a developer login and a developer password in order to create a new account.

The developer login and the developer password will be stored in the .zetarc file. By default, the .gitignore file is configured to doesn’t deploy this file.

Using npm lower than 6

npm provides npm init command since version 6.

You can either upgrade your npm version or use npx utility.

npm 5-

Upgrade npm

➊ - Upgrade npm
$ npm install -g npm@latest
➋ - Use npm init
$ npm init @zetapush first-hello-world

Use npx to launch the script

Launch with npx
$ npx @zetapush/create first-hello-world

-

21.1.1.3. Use existing account and application
Generate application
$ npm init @zetapush first-hello-world (1)
           --developer-login <your email address> (2)
           --app-name <your application name> (3)
1 first-hello-world is the name of the folder that will be created with all the generated files
2 <your email address> is the email you used when you registered on ZetaPush
3 <your application name> is the application name registered on ZetaPush

You will be prompted for a password in order to log in to ZetaPush.

Using npm lower than 6

npm provides npm init command since version 6.

You can either upgrade your npm version or use npx utility.

npm 5-

Upgrade npm

➊ - Upgrade npm
$ npm install -g npm@latest
➋ - Use npm init
$ npm init @zetapush first-hello-world

Use npx to launch the script

Launch with npx
$ npx @zetapush/create first-hello-world

-

21.1.1.4. Add an application to my account
Generate application
$ npm init @zetapush first-hello-world (1)
           --developer-login <your email address> (2)
1 first-hello-world is the name of the folder that will be created with all the generated files
2 <your email address> is the email you used when you registered on ZetaPush

You will be prompted for a password in order to log in to ZetaPush.

Using npm lower than 6

npm provides npm init command since version 6.

You can either upgrade your npm version or use npx utility.

npm 5-

Upgrade npm

➊ - Upgrade npm
$ npm install -g npm@latest
➋ - Use npm init
$ npm init @zetapush first-hello-world

Use npx to launch the script

Launch with npx
$ npx @zetapush/create first-hello-world

-

21.2. Run project locally

21.2.1. Run front and custom cloud services

Start application (front and custom cloud services)
$ npm run start -- --serve-front

npm run start is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta run --serve-front

21.2.2. Run only custom cloud services

Start custom cloud services locally
$ npm run start

npm run start is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta run

21.3. Deploy project on the ZetaPush Cloud

21.3.1. Deploy front and custom cloud services

Deploy application (front and custom cloud services)
$ npm run deploy

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push

21.3.2. Deploy only custom cloud services

Deploy the worker
$ npm run deploy -- --worker

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push --worker

21.3.3. Deploy only front

Deploy the worker
$ npm run deploy -- --front

npm run deploy is a script alias defined in the package.json file. The command that is executed under the hood is:

$ zeta push --front

22. Configuration

ZetaPush provides many variables to configure your applications. There are 2 types: the account configuration variables and the application configuration variables.

22.1. Account configuration

To use ZetaPush Celtia, you need to have an account and an optional application. You specify this properties in the .zetarc file. Here is the list of them:

Table 3. Account variables
Name of the property Definition environment variable Name in the .zetarc CLI parameter

Developer login

This login is the email used by the developer when logging in to the ZetaPush console. It does not concern the applications developed by the developer. It is only used to identify the developer account.

ZP_DEVELOPER_LOGIN

developerLogin

--developer-login <login>

Developer password

Like the property --developer-login, this password is only used for the ZetaPush developer account. It does not concern the applications developed by the developer.

ZP_DEVELOPER_PASSWORD

developerLogin

--developer-password <password>

Application name

A developer can develop many applications. Hence the need to identify each of them. The appName property is the unique name of an application on a ZetaPush developer account.

ZP_APP_NAME

appName

--app-name <name>

Environment name

An application created by a developer can have many environments. The default environment is prod but you can create your own (dev, preprod, …​). An environment is one stage in the development of the application.

ZP_ENV_NAME

envName

--env-name <env>

Platform URL

By default, the developer uses the production environment of ZetaPush to develop and deploy its applications. In most cases, the developer doesn’t need to set this property. This property if useful when another (on premise, or dedicated) ZetaPush platform is to be used.

ZP_PLATFORM_URL

platformUrl

--platform-url <platform>

22.2. Application configuration

You can configure your applications with several variables. Here is the list of them:

Table 4. Application variables
Name of the property Definition package.json CLI parameter Default value

Front code

In an application, you need to define where you put your front end part of your application. ZetaPush uses this property to host and expose it.

1
2
3
"zetapush": {
    "front": "./path/of/front"
}

--front <path>

./front/

Worker code

When you create your business logic in custom cloud services, ZetaPush need to know his location to deploy it.

1
2
3
"zetapush": {
    "worker": "./path/of/worker"
}

--worker <path>

./worker/

Worker entry point

To use your custom cloud services from the front part you need to export them. So the exportation of services need to be in only one file.

"main": "./path/of/entry"

None

./worker/index.ts

22.3. Configuration override

You can put many properties in several locations (environment variables, CLI…​). ZetaPush includes and overrides this properties in a specific order:

22.3.1. Account properties

override account properties

So if you use the same property in several locations, the environment variable is overwritten by the .zetarc value and this last value is overwritten by the CLI parameter.

22.3.2. Application properties

override application properties

So if you use the same property in several locations, the default value is overwritten by the package.json value and this last value is overwritten by the CLI parameter.

22.4. User Management

Unresolved directive in reference.adoc - include::/mnt/harddisk/workspace/celtia/documentation/src/docs/asciidoc/common/cloud-services/user-management.adoc[leveloffset=+2]