feat: initial commit

This commit is contained in:
2023-02-09 12:16:36 +01:00
commit 81dc1adfef
126 changed files with 11551 additions and 0 deletions

15
doc/README.md Normal file
View File

@ -0,0 +1,15 @@
# Documentation
## Edge Apps
Une **Edge App** est une application capable de s'exécuter dans un environnement "Edge".
### Référence
- [API Client](./apps/client-api/README.md)
- [API Serveur](./apps/server-api/README.md)
### Tutoriels
- [Créer sa première application](./apps/my-first-app.md)
- [Empaqueter une application](./apps/package-app.md)

View File

@ -0,0 +1,64 @@
# API Client
## Méthodes
### `Edge.connect(): Promise`
> `TODO`
### `Edge.disconnect(): void`
> `TODO`
### `Edge.send(message: Object): void`
> `TODO`
### `Edge.rpc(method: string, params: Object): Promise`
> `TODO`
#### Exemple
**Côté serveur**
```js
function onInit() {
rpc.register(echo);
}
function echo(ctx, params) {
return params;
}
```
**Côté client**
```js
Edge.connect().then(() => {
Edge.rpc("echo", { hello: "world!" })
.then(result => console.log(result))
.catch(err => console.error(err));
});
```
### `Edge.upload(blob: Blob, metadata: Object): Promise`
> `TODO`
### `Edge.blobUrl(bucketName: string, blobId: string): string`
> `TODO`
## Événements
### `"message"`
> `TODO`
#### Exemple
```js
Edge.addEventListener("message", evt => console.log(evt.detail));
```

103
doc/apps/my-first-app.md Normal file
View File

@ -0,0 +1,103 @@
# Créer ma première application
## 1. Télécharger le CLI
1. Se rendre à l'adresse https://forge.cadoles.com/arcad/edge/releases
2. Télécharger la dernière version du binaire `cli` disponible dans la page.
## 2. Créer l'arborescence de son application
L'arborescence d'une "Edge App" doit correspondre à une structure prédéfinie.
```bash
my-app
|-> manifest.yml # Le fichier "manifeste" décrivant votre application
|-> public # Répertoire contenant tous les fichiers accessibles publiquement
|-> server
|-> main.js # Le point d'entrée pour le code "serveur" de votre application
```
## 3. Compléter le fichier `manifest.yml`
Ce fichier est le manifeste de votre application. Il permet au serveur d'identifier celle ci et de récupérer des informations la concernant.
```yaml
---
# L'identifiant de votre application. Il doit être globalement unique.
# Un identifiant du type nom de domaine inversé est en général conseillé (ex: tld.mycompany.myapp)
id: tld.mycompany.myapp
# Le titre de votre application.
title: My App
# Les mots-clés associés à votre applications.
tags: ["chat"]
# La description de votre application.
# Vous pouvez utiliser la syntaxe Markdown pour la mettre en forme.
description: |>
A simple demo application
```
## 4. Créer la page d'accueil
Créer le fichier `my-app/public/index.html`:
```html
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>My App</h1>
<!-- Inclure le SDK -->
<script type="text/javascript" src="/edge/sdk/client.js"></script>
<script type="text/javascript">
// On utilise le SDK via la variable globale "Edge"
// pour se connecter au serveur de notre application.
Edge.connect().then(() => {
// Une fois connecté, on envoie un message au serveur.
Edge.send({ "hello": "world" });
});
// On écoute les messages en provenance du serveur.
Edge.addEventListener("message", (evt) => {
console.log("New server message", evt.detail)
});
</script>
</body>
</html>
```
## 5. Créer le fichier `server/main.js`
Ce fichier est nécessaire, même vide.
```javascript
// La fonction "onInit()" (si déclarée) est automatiquement
// exécutée au démarrage du serveur de votre application.
function onInit() {
}
// La fonction "onClientMessage(ctx, message)" est automatiquement
// exécutée quand le serveur de votre application reçoit un
// message en provenance du client.
function onClientMessage(ctx, message) {
console.log(message);
// On utilise le module "net" pour renvoyer un message au client
net.send(ctx, { "my": "message" });
}
```
## 6. Exécuter votre application en local
Utiliser le CLI téléchargé préalablement pour lancer votre nouvelle application localement.
```bash
cli app run -p ./chemin/vers/app
```
La page d'accueil devrait être accessible à l'adresse http://localhost:8080.

3
doc/apps/package-app.md Normal file
View File

@ -0,0 +1,3 @@
# Empaqueter une application
> `TODO`

View File

@ -0,0 +1,28 @@
# API Serveur
## Fonctions de rappel
### `onInit(): void`
Cette méthode est automatiquement exécutée au démarrage du serveur de l'application.
Comme son nom l'indique, elle permet d'exécuter des opérations d'initialisation de votre application.
#### Exemple
```js
function onInit() {
}
```
## Modules
Listes des modules disponibles côté serveur.
- [`console`](./console.md)
- [`context`](./context.md)
- [`net`](./net.md)
- [`rpc`](./rpc.md)
- [`store`](./store.md)
- [`blob`](./blob.md)

View File

@ -0,0 +1,73 @@
# Module `blob`
Ce module permet de manipuler des fichiers ("blobs") au sein de votre application.
## Fonctions de rappel
Pour permettre aux utilisateurs de téléverser/télécharger des fichiers ("blobs") dans votre application, il vous faudra déclarer 2 fonctions dans votre fichier `server/main.js`.
### `onBlobUpload(ctx: Context, blobId: string, blobInfo: BlobInfo, metadata: Metadata)`
> `TODO`
#### Usage
```js
function onBlobUpload(ctx, blobId, blobInfo, metadata) {
// Autoriser le téléversement et indiquer que le fichier doit être stocké dans le bucket "my-bucket"
return { allow: true, bucket: "my-bucket" };
}
```
### `onBlobDownload(ctx: Context, bucketName: string, blobId: string)`
> `TODO`
#### Usage
```js
function onBlobDownload(ctx, bucketName, blobId) {
// Autoriser le téléchargement
return { allow: true };
}
```
## Méthodes
### `blob.listBuckets(ctx: Context): string[]`
> `TODO`
### `blob.writeBlob(ctx: Context, bucketName: string, blobId: string)`
> `TODO`
### `blob.readBlob(ctx: Context, bucketName: string, blobId: string)`
> `TODO`
### `blob.listBlobs(ctx: Context, bucketName: string): BlobInfo[]`
> `TODO`
### `blob.deleteBlob(ctx: Context, bucketName: string, blobId: string)`
> `TODO`
### `blob.deleteBucket(ctx: Context, bucketName: string)`
> `TODO`
### `blob.getBlobInfo(ctx: Context, bucketName: string, blobId: string): BlobInfo`
> `TODO`
## Objets
### `Context`
Voir la documentation de l'objet [`Context`](./context.md#Context).
### `BlobInfo`
### `Metadata`

View File

@ -0,0 +1,3 @@
## Module `console`
> `TODO`

View File

@ -0,0 +1,74 @@
# Module `context`
Ce module permet de manipuler les informations de contexte liées à la réception de messages ou à l'utilisation de certains autres modules.
## Méthodes
### `context.new(): Context`
Renvoie un nouveau contexte vide.
#### Arguments
Aucun
#### Valeur de retour
Un nouvel objet de contexte.
#### Usage
```js
var ctx = context.new();
```
### `context.get(ctx: Context, key: string): any|null`
Récupère la valeur associée à la clé `key` dans le contexte si celle ci existe.
#### Arguments
- `ctx` **Context** Contexte duquel extraire la valeur souhaitée
- `key` **string** Clé associé à la valeur à récupérer
#### Valeur de retour
Valeur associée à la clé ou `null`.
#### Usage
```js
function onClientMessage(ctx, message) {
var sessionId = context.get(ctx, "mykey");
console.log(sessionId);
}
```
## Propriétés
### `context.SESSION_ID`
Clé permettant de récupérer la clé de session associé au client émetteur du message courant.
#### Usage
```js
function onClientMessage(ctx, message) {
var sessionId = context.get(ctx, context.SESSION_ID);
console.log(sessionId);
}
```
### `context.ORIGINAL_REQUEST`
Clé permettant de récupérer la requête HTTP à l'origine de la connexion du client.
_Cette propriété est utilisée par le module [`auth`](./auth.md) pour récupérer l'utilisateur associé au client._
#### Usage
```js
function onClientMessage(ctx, message) {
var request = context.get(ctx, context.ORIGINAL_REQUEST);
console.log(request);
}
```

View File

@ -0,0 +1,63 @@
# Module `net`
Ce module permet d'envoyer des messages aux clients connectés au serveur.
## Fonctions de rappel
### `onClientMessage(ctx: Context, msg: Message)`
Cette méthode est appelée pour chaque message reçu par le serveur depuis un client connecté.
> `TODO`
## Méthodes
### `net.send(ctx: string|Context, data: Object): void`
Envoie un message au client connecté au serveur.
#### Arguments
- `ctx` **string|Context** Identifiant de session du client ou contexte portant l'identifiant de session du client. Voir la documentation du module [`context`](./context.md).
- `data` **Object** Données à envoyer au client
#### Valeur de retour
Aucune
#### Usage
**Côté client**
```js
// Les données envoyées par le serveur sont accessibles
// via la propriété evt.detail.
Edge.on('message', evt => console.log(evt.detail));
Edge.connect();
```
**Côté serveur**
```js
function onInit() {
var ctx = context.background();
net.send(ctx, {"foo", "bar"});
}
```
### `net.broadcast(data: Object): void`
Envoie un message à l'ensemble des clients connectés au serveur.
#### Arguments
- `data` **object** Données à envoyer aux clients connectés
#### Valeur de retour
Aucune
#### Usage
Voir usage `net.send()`.

View File

@ -0,0 +1,43 @@
# Module `rpc`
Ce module permet de déclarer des méthodes côté serveur qui seront "invoquable" côté client via la méthode [`Edge.rpc(method: string, params: Object): Promise`](../client-api/README.md#edgerpcmethod-string-params-object-promise).
## Méthodes
### `rpc.register(name: string, cb?: Function): void`
Marque une fonction comme étant appelable par le client.
#### Arguments
- `name` **string** Le nom de la fonction telle qu'elle sera appelable par le client. Si `cb` n'est pas spécifié, la fonction portant le même nom est utilisée.
- `cb` **Function** Référence de la fonction à exécuter.
#### Valeur de retour
Aucune
#### Usage
```js
function onInit() {
rpc.register("echo", echo);
}
function echo(ctx, params) {
return params;
}
```
**Côté client**
```js
Edge.connect().then(() => {
Edge.rpc("echo", { hello: "world!" })
.then(result => console.log(result))
.catch(err => console.error(err));
});
```
### `rpc.unregister(name: string): void`
> `TODO`

View File

@ -0,0 +1,183 @@
# Module `store`
Ce module permet de stocker et récupérer des données structurées ("documents") sur le serveur.
## Méthodes
### `store.upsert(ctx: Context, collection: string, doc: Object)`
Enregistre un document dans une collection.
Si le document a une propriété `_id` celle ci est utilisée comme identifiant. Dans le cas contraire elle sera autogénérée par le moteur de stockage.
#### Arguments
- `ctx` **Context** Le contexte d'exécution. Voir la documentation du module [`context`](./context.md)
- `collection` **string** Nom de la collection dans laquelle retrouver le document
- `doc` **Object** Le document à enregistrer
#### Valeur de retour
Le document dans sa forme après enregistrement.
#### Usage
```js
var ctx = context.new();
var obj = store.upsert(ctx, "myCollection", {"foo": "bar"});
```
### `store.get(ctx: Context, collection: string, docId: string)`
Retourne le document associé à l'identifiant `docId` ou `null` si celui ci n'est pas trouvé.
#### Arguments
- `ctx` **Context** Le contexte d'exécution. Voir la documentation du module [`context`](./context.md)
- `collection` **string** Nom de la collection dans laquelle retrouver le document
- `docId` **string** Identifiant du document à récupérer
#### Valeur de retour
le document stocké si il existe, `null` sinon.
#### Usage
```js
function onInit() {
var ctx = context.new();
var obj = store.get(ctx, "myCollection", "doc-id");
}
```
### `store.delete(ctx: Context, collection: string, docId: string)`
Supprime le document associé à l'identifiant dans la collection.
#### Arguments
- `ctx` **Context** Le contexte d'exécution. Voir la documentation du module [`context`](./context.md)
- `collection` **string** Nom de la collection dans laquelle retrouver le document
- `docId` **string** Identifiant de le document à supprime
#### Valeur de retour
Aucune
#### Usage
```js
var ctx = context.new();
store.delete(ctx, "myCollection", "my-item-id");
```
### `store.query(ctx: Context, collection: string, doc: Object, filter: Filter?, options: QueryOptions?)`
Filtre la collection et récupère les documents associés à la requête.
#### Arguments
- `ctx` **Context** Le contexte d'exécution. Voir la documentation du module [`context`](./context.md)
- `collection` **string** Nom de la collection dans laquelle retrouver le document
- `filter` **Filter?** Filtre à appliquer à la collection, voir "Filtres".
- `options` **QueryOptions?** Options de filtrage, voir "Options".
## Propriétés
### `store.DIRECTION_ASC`
> `TODO`
### `store.DIRECTION_DESC`
> `TODO`
## Objets
### `Filter`
Un filtre se construit à partir d'une hiérarchie d'opérateurs sous la forme d'un document.
On distingue deux types d'opérateurs:
- Les opérateurs de combinaison logique;
- Les opérateurs de filtrage.
Les opérateurs d'combinaison logique prennent la forme suivante:
```
{
"<comb_op>": [
<filter_op>,
<filter_op>,
etc
]
}
```
**Exemple**
```json
{
"and": [
{ "eq": { "myAttr1": "foo" } },
{ "neq": { "myAttr2": "bar" } }
]
}
```
Ce filtre serait traduit en syntaxe SQL en `myAttr1 = "foo" AND myAttr2 != "bar"`.
Voici la liste des opérateurs de combinaison logique:
- `and` - "ET" logique
- `or` - "OU" logique
- `not` - Négation logique
Les opérateurs de filtrage prennent la forme suivantes:
```
{
<filter_op>: {
"key1": "val1",
"key2": "val2",
"key3": "val3",
etc
}
}
```
**Exemple**
```json
{ "gt": { "foo": "bar" } },
```
Voici la liste des opérateurs de filtrage:
- `eq` - Comparaison `==`
- `neq` - Comparaison `!=`
- `gt` - Comparaison `>`
- `gte` - Comparaison `>=`
- `lt` - Comparaison `<`
- `lte` - Comparaison `<=`
- `like` - Comparaison type `LIKE` MySQL/SQLite
- `in` - Comparaison type `IN` MySQL/SQLite
### QueryOptions
#### Usage
```js
var ctx = context.new();
var results = store.query(ctx, "myCollection", {
eq: {
"foo": "bar",
}
}, {
limit: 10,
offset: 5,
orderBy: "foo",
orderDirection: store.ASC,
});
```