Clean documentation

This commit is contained in:
Romaric Mourgues
2023-04-17 17:00:03 +02:00
parent a46c6ca0a8
commit 88bd3b2a32
80 changed files with 138 additions and 2492 deletions
@@ -1,11 +1,11 @@
---
description: Get started with Twake service development
description: Get started with TDrive service development
---
# 🛠 Twake service development
# 🛠 TDrive service development
[start-working-into-a-service](start-working-into-a-service.md)
[create-a-new-twake-service](create-a-new-twake-service.md)
[create-a-new-tdrive-service](create-a-new-tdrive-service.md)
[platform/](platform/README.md))
@@ -1,7 +1,7 @@
---
description: >-
If you are here, you probably have a very great idea for Twake, like adding a
brand new feature into Twake, maybe a coffee maker service ? ☕️
If you are here, you probably have a very great idea for TDrive, like adding a
brand new feature into TDrive, maybe a coffee maker service ? ☕️
---
# Create a new service
@@ -19,10 +19,10 @@ In order to illustrate how to create a component, let's create a fake Notificati
```javascript
// File src/services/notification/index.ts
import { TwakeService } from "../../core/platform/framework";
import { TDriveService } from "../../core/platform/framework";
import NotificationServiceAPI from "./api.ts";
export default class NotificationService extends TwakeService<NotificationServiceAPI> {
export default class NotificationService extends TDriveService<NotificationServiceAPI> {
version = "1";
name = "notification";
service: NotificationServiceAPI;
@@ -33,15 +33,15 @@ export default class NotificationService extends TwakeService<NotificationServic
}
```
1. Our `NotificationService` class extends the generic `TwakeService` class and we defined the `NotificationServiceAPI` as its generic type parameter. It means that in the platform, the other components will be able to retrieve the component from its name and then consume the API defined in the `NotificationServiceAPI` interface and exposed by the `api` method.
1. Our `NotificationService` class extends the generic `TDriveService` class and we defined the `NotificationServiceAPI` as its generic type parameter. It means that in the platform, the other components will be able to retrieve the component from its name and then consume the API defined in the `NotificationServiceAPI` interface and exposed by the `api` method.
We need to create this `NotificationServiceAPI` interface which must extend the `TwakeServiceProvider` from the platform like:
We need to create this `NotificationServiceAPI` interface which must extend the `TDriveServiceProvider` from the platform like:
```javascript
// File src/services/notification/api.ts
import { TwakeServiceProvider } from "../../core/platform/framework/api";
import { TDriveServiceProvider } from "../../core/platform/framework/api";
export default interface NotificationServiceAPI extends TwakeServiceProvider {
export default interface NotificationServiceAPI extends TDriveServiceProvider {
/**
* Send a message to a list of recipients
@@ -65,17 +65,17 @@ export class NotificationServiceImpl implements NotificationServiceAPI {
}
```
1. `NotificationServiceImpl` now needs to be instanciated from the `NotificationService` class since this is where we choose to keep its reference and expose it. There are several places which can be used to instanciate it, in the constructor itself, or in one of the `TwakeService` lifecycle hooks. The `TwakeService` abstract class has several lifecycle hooks which can be extended by the service implementation for customization pusposes:
1. `NotificationServiceImpl` now needs to be instanciated from the `NotificationService` class since this is where we choose to keep its reference and expose it. There are several places which can be used to instanciate it, in the constructor itself, or in one of the `TDriveService` lifecycle hooks. The `TDriveService` abstract class has several lifecycle hooks which can be extended by the service implementation for customization pusposes:
2. `public async doInit(): Promise<this>;` Customize the `init` step of the component. This is generally the place where services are instanciated. From this step, you can retrieve services consumed by the current component which have been already initialized by the platform.
3. `public async doStart(): Promise<this>;` Customize the `start` step of the component. You have access to all other services which are already started.
```javascript
// File src/services/notification/index.ts
import { TwakeService } from "../../core/platform/framework";
import { TDriveService } from "../../core/platform/framework";
import NotificationServiceAPI from "./api.ts";
import NotificationServiceImpl from "./services/api.ts";
export default class NotificationService extends TwakeService<NotificationServiceAPI> {
export default class NotificationService extends TDriveService<NotificationServiceAPI> {
version = "1";
name = "notification";
service: NotificationServiceAPI;
@@ -95,12 +95,12 @@ export default class NotificationService extends TwakeService<NotificationServic
1. Now that the service is fully created, we can consume it from any other service in the platform. To do this, we rely on Typescript decorators to define the links between components. For example, let's say that the a `MessageService` needs to call the `NotificationServiceAPI`, we can create the link with the help of the `@Consumes` decorator and get a reference to the `NotificationServiceAPI` by calling the `getProvider` on the component context like:
```javascript
import { TwakeService, Consumes } from "../../core/platform/framework";
import { TDriveService, Consumes } from "../../core/platform/framework";
import MessageServiceAPI from "./providapier";
import NotificationServiceAPI from "../notification/api";
@Consumes(["notification"])
export default class MessageService extends TwakeService<MessageServiceAPI> {
export default class MessageService extends TDriveService<MessageServiceAPI> {
public async doInit(): Promise<this> {
const notificationService = this.context.getProvider<NotificationServiceAPI>("notification");
@@ -143,7 +143,7 @@ Then each service can have its own configuration block which is accessible from
On the component class side, the configuration object is directly accessible from the `configuration` property like:
```javascript
export default class WebSocket extends TwakeService<WebSocketAPI> {
export default class WebSocket extends TDriveService<WebSocketAPI> {
async doInit(): Promise<this> {
// get the "path" value, defaults to "/socket" if not defined
const path = this.configuration.get < string > ("path", "/socket");
@@ -166,7 +166,7 @@ After creating a new service, you can add controllers, business services and ent
## Create a new technical service
Now you are bringing things a step further, you are going to add new core services in Twake, like for instance a new database connector or encryption system.
Now you are bringing things a step further, you are going to add new core services in TDrive, like for instance a new database connector or encryption system.
Creating a new core service is as easy as creating a functional service. But it must be in `src/core/platform/services` .
@@ -1,6 +1,6 @@
---
description: >-
List of core shared components in Twake backend, available in
List of core shared components in TDrive backend, available in
src/core/platform/services
---
@@ -8,7 +8,7 @@ description: >-
## **Database Technical Service**
Twake uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
TDrive uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
::: info
This paragraph is not ready yet, you can contribute to this documentation on our Github!
@@ -108,7 +108,7 @@ Services annotated as described above automatically publish events to WebSockets
```javascript
const io = require("socket.io-client");
// Get a JWT token from the Twake API first
// Get a JWT token from the TDrive API first
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
@@ -150,7 +150,7 @@ socket.on("connect", () => {
.emit("authenticate", { token })
.on("authenticated", () => {
// join the /channels room
socket.emit("realtime:join", { name: "/channels", token: "twake" });
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
socket.on("realtime:join:error", (message) => {
// will fire when join does not provide a valid token
console.log("Error on join", message);
@@ -231,7 +231,7 @@ socket.on("connect", () => {
.emit("authenticate", { token })
.on("authenticated", () => {
// join the "/channels" room
socket.emit("realtime:join", { name: "/channels", token: "twake" });
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
// will only occur when an action occured on a resource
// and if and only if the client joined the room
@@ -1,6 +1,6 @@
---
description: >-
List of core shared components in Twake backend, available in
List of core shared components in TDrive backend, available in
src/core/platform/services
---
@@ -8,7 +8,7 @@ description: >-
## **Database Technical Service**
Twake uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
TDrive uses a custom ORM to work with both MongoDB and CassandraDB/ScyllaDB.
[database-orm-platform-service](database-orm-platform-service.md)
@@ -106,7 +106,7 @@ Services annotated as described above automatically publish events to WebSockets
```javascript
const io = require("socket.io-client");
// Get a JWT token from the Twake API first
// Get a JWT token from the TDrive API first
const token =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJfaWQiOjEsImlhdCI6MTYwMzE5ODkzMn0.NvQoV9KeWuTNzRvzqbJ5uZCQ8Nmi2rCYQzcKk-WsJJ8";
const socket = io.connect("http://localhost:3000", { path: "/socket" });
@@ -148,7 +148,7 @@ socket.on("connect", () => {
.emit("authenticate", { token })
.on("authenticated", () => {
// join the /channels room
socket.emit("realtime:join", { name: "/channels", token: "twake" });
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
socket.on("realtime:join:error", (message) => {
// will fire when join does not provide a valid token
console.log("Error on join", message);
@@ -229,7 +229,7 @@ socket.on("connect", () => {
.emit("authenticate", { token })
.on("authenticated", () => {
// join the "/channels" room
socket.emit("realtime:join", { name: "/channels", token: "twake" });
socket.emit("realtime:join", { name: "/channels", token: "tdrive" });
// will only occur when an action occured on a resource
// and if and only if the client joined the room
@@ -5,7 +5,10 @@
A. Create an entity and put it anywhere in the code
```typescript
import { Entity, Column } from "../../../core/platform/services/database/services/orm/decorators";
import {
Entity,
Column,
} from "../../../core/platform/services/database/services/orm/decorators";
@Entity("my_entity", {
primaryKey: [["company_id"], "id"], //Primary key, see Cassandra documentation for more details
@@ -45,25 +48,24 @@ await repository.remove({company_id: "", id: ""});
#### I set a column to a type but I get an other type on code. Why for two identical definitions it created fields of different types?
It depends on what database you use \(mongo or scylladb\) for development. Here is the process for each:
It depends on what database you use \(mongo or scylladb\) for development. Here is the process for each:
Scylla:
* on startup it creates the tables with the requested types, in this case twake\_boolean =&gt; tinyint on scylla side
* on save entity it will convert the node type \(boolean\) to the good cql request: "{bool: false}" =&gt; "SET bool = 0", it happens in the transformValueToDbString method
* on find entity it will convert the database raw value \(a tinyint\) to the nodejs type \(boolean\): 1 =&gt; true, 0 =&gt; false.
- on startup it creates the tables with the requested types, in this case tdrive_boolean =&gt; tinyint on scylla side
- on save entity it will convert the node type \(boolean\) to the good cql request: "{bool: false}" =&gt; "SET bool = 0", it happens in the transformValueToDbString method
- on find entity it will convert the database raw value \(a tinyint\) to the nodejs type \(boolean\): 1 =&gt; true, 0 =&gt; false.
Mongo:
* on startup it does nothing \(mongo don't need to initialise columns
* on save entity it will create a document, it means in mongo we just store json for each entity, there is no really a column concept.
* on find entity we just get back the saved json and map it to the entity in node.
- on startup it does nothing \(mongo don't need to initialise columns
- on save entity it will create a document, it means in mongo we just store json for each entity, there is no really a column concept.
- on find entity we just get back the saved json and map it to the entity in node.
Even if mongo just store json directly from mongo, we sometime do some changes to the data before to save in mongo, it will also be in the typeTransforms.ts file.
So what could have happened in you case ?
* \(1\) if you use mongodb and we did not enforce the type before to save to mongo, then maybe you used a string instead of a boolean at some point in time while working and mongo just saved it as it was \(without checking the requested type on entity\)
* \(2\) other possibility is that we incorrectly get the information from the database on the typeTransforms.ts file, from cassandra for instance I think we don't convert tinyint back to clean boolean, so you could get 0 and 1 instead of false and true. And maybe instead of 0 and 1 sometime undefined values can convert to ''.
* To fix all this just enforce the types in typeTransforms.ts for the twake\_boolean type.
- \(1\) if you use mongodb and we did not enforce the type before to save to mongo, then maybe you used a string instead of a boolean at some point in time while working and mongo just saved it as it was \(without checking the requested type on entity\)
- \(2\) other possibility is that we incorrectly get the information from the database on the typeTransforms.ts file, from cassandra for instance I think we don't convert tinyint back to clean boolean, so you could get 0 and 1 instead of false and true. And maybe instead of 0 and 1 sometime undefined values can convert to ''.
- To fix all this just enforce the types in typeTransforms.ts for the tdrive_boolean type.
@@ -4,7 +4,7 @@ description: >-
to our channel service ? You are in the right place !
---
# What is a service in Twake ?
# What is a service in TDrive ?
The backend is developed using a **software component approach** in order to compose and adapt the platform based on needs and constraints. The current section describes this approach, and how to extend it by creating new components.
@@ -25,9 +25,9 @@ The platform currently have some limitations:
## Discover what is in a service
To unfold the internal ways of services in Twake, we will follow a simple request journey into our framework.
To unfold the internal ways of services in TDrive, we will follow a simple request journey into our framework.
1. The requests starts from Twake Frontend or Postman for instance,
1. The requests starts from TDrive Frontend or Postman for instance,
2. it then goes to a controller which validate the request parameters and extract them for the services,
3. the services uses the given parameters to get/set entities in database and returns a proper reply.