This commit is contained in:
Matheus
2026-09-23 12:29:08 -03:00
commit f5d231ab8f
59 changed files with 16658 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
# Endpoints
Endpoints are REST WebServices written in ES5 JavaScript, executed on the Rhino engine. Each file must export a single `WebService` instance. Vitruvio maps incoming HTTP requests to the corresponding handler method.
---
## vitruvio.json Registration
```json
{
"key": "my-endpoint",
"name": "My Endpoint",
"description": "What this endpoint does.",
"language": "javascript",
"authMode": "PUBLIC",
"active": true,
"source": "endpoints/my-endpoint.js"
}
```
- `key` — kebab-case, stable. Changing it breaks any external integrations pointing to the URL.
- `authMode` — one of `"PUBLIC"`, `"STATIC_TOKEN"`, `"VITRUVIO_WS_USER_AUTH"`, `"HTTP_BASIC_AUTH"`. Default to `"PUBLIC"`.
- `active` — must be `true` for the endpoint to be reachable. Set to `false` to disable without removing.
### URL structure
The `authMode` determines the URL segment used to reach the endpoint:
| authMode | URL |
|---|---|
| `PUBLIC` | `/api/integration/public/{key}` |
| `STATIC_TOKEN` | `/api/integration/tokenauth/{key}` |
| `VITRUVIO_WS_USER_AUTH` | `/api/integration/bearerauth/{key}` |
| `HTTP_BASIC_AUTH` | `/api/integration/bauth/{key}` |
### STATIC_TOKEN authentication
The token can be sent in either of two ways:
- Query parameter: `_x_token_auth=<token>`
- Request header: `X-WS-TOKEN-AUTH: <token>`
---
## File Structure
```javascript
function WebService() {
this.onGet = function(params) { ... }
this.onPost = function(params) { ... }
this.onPut = function(params) { ... }
this.onPatch = function(params) { ... }
this.onDelete = function(params) { ... }
}
module.exports = new WebService();
```
- All methods are **optional**. Only implement the verbs your endpoint actually handles.
- Remove unused verb stubs entirely — don't leave placeholder `return JSON.stringify({ prop: value })` bodies in production code.
- `module.exports` must be `new WebService()` — Vitruvio instantiates and reads from this exported object.
- If a caller sends a verb that is not implemented in the script, Vitruvio throws `EndpointMethodNotImplementedException` — it is not a silent 404.
---
## The `params` Object
Each handler receives a single `params` argument:
```javascript
// GET / DELETE
params = {
headers: {}, // request headers as key/value
query: {} // query string params as key/value e.g. params.query["hub.mode"]
}
// POST / PUT / PATCH
params = {
requestBody: '', // raw request body string — always JSON.parse() before use
headers: {},
query: {}
}
```
**Always parse the body explicitly:**
```javascript
this.onPost = function(params) {
var body = JSON.parse(params.requestBody);
// use body.*
}
```
---
## Return Values
| Return value | HTTP response |
|---|---|
| A non-empty string | `200 OK` with that string as body |
| `null`, no return, or empty string `""` | `204 No Content` |
| `JSON.stringify(obj)` | `200 OK` with JSON body — set Content-Type accordingly |
Always return strings. Returning a raw object will not serialize correctly.
```javascript
// Correct
return JSON.stringify({ status: 'ok', data: result });
// Wrong — object will not be serialized properly
return { status: 'ok' };
```
---
## Error Handling
Wrap handler logic in `try/catch` and return a JSON error body. Don't let uncaught exceptions bubble up.
```javascript
this.onPost = function(params) {
try {
var body = JSON.parse(params.requestBody);
if (!body.id) throw "Missing required field: id";
// ...
return JSON.stringify({ success: true });
} catch (e) {
return JSON.stringify({ error: e.toString() });
}
};
```
- Use `e.toString()` when serializing caught exceptions — raw exception objects may not stringify correctly under Rhino.
- Validate required fields early and throw descriptive messages.
---
## DELETE Convention
By convention, DELETE requests should not receive a body and should not return data. If nothing is returned, Vitruvio responds with `204 No Content`.
```javascript
this.onDelete = function(params) {
// perform deletion
// no return needed — 204 is implied
}
```
---
## Loading Scripts and Libraries
Use `libService.loadScript(key)` to load scripts registered in `vitruvio.json`. The `key` is the script's `key` field in the manifest.
```javascript
// Load inside the handler — safe, always fresh
this.onPost = function(params) {
var lib = libService.loadScript('my-lib');
lib.doSomething();
}
// Load in the constructor — shared across all calls to this endpoint instance
function WebService() {
var lib = libService.loadScript('my-lib');
this.onPost = function(params) {
lib.doSomething();
}
}
```
**Prefer constructor-level loading** when the same library is used in multiple handlers — avoids redundant loads. Use handler-level loading when the script key is dynamic or determined at runtime.
---
## Database Access
Load the `db` library and instantiate with a datasource name:
```javascript
var db = libService.loadScript('db');
var banco = new db(db.VITRUVIO_DATASOURCE); // default Vitruvio datasource
// or
var banco = new db('named-datasource');
```
Common operations:
```javascript
// Single row
var row = banco.queryRow("SELECT NAME FROM TB_CONFIG WHERE ID = :id", { id: 1 });
// Multiple rows
var rows = banco.query("SELECT * FROM TB_ITEMS WHERE ACTIVE = 1");
// Update / Insert
banco.update("UPDATE TB_ITEMS SET STATUS = :status WHERE ID = :id", {
status: 'DONE',
id: itemId
});
```
- Prefer named queries from `queries/*.sql` for reusable or complex SQL. Use inline SQL only for simple, endpoint-specific queries.
- Use named bind parameters (`:paramName`) — never concatenate user input into SQL strings.
---
## Logged User (Authenticated Modes)
For `VITRUVIO_WS_USER_AUTH` and `HTTP_BASIC_AUTH`, Vitruvio resolves the caller's identity and injects a `loggedUser` object into the script context. This gives the endpoint access to who is making the call.
`HTTP_BASIC_AUTH` additionally validates that the authenticated user belongs to the roles configured for that endpoint in Vitruvio's admin.
---
## Common Patterns
### Webhook with verification (e.g. Meta/WhatsApp)
```javascript
this.onGet = function(params) {
var mode = params.query["hub.mode"];
var token = params.query["hub.verify_token"];
var challenge = params.query["hub.challenge"];
if (mode === "subscribe" && token === EXPECTED_TOKEN) {
return challenge;
}
return JSON.stringify({ error: "Forbidden" });
};
```
### Delegating to a script
```javascript
this.onPost = function(params) {
try {
var body = JSON.parse(params.requestBody);
var lib = libService.loadScript('my-processing-script');
var result = lib.run(body);
return JSON.stringify(result);
} catch (e) {
return JSON.stringify({ error: e.toString() });
}
};
```
---
## What NOT to do
- Don't leave placeholder bodies (`return JSON.stringify({ prop: value })`) in unused verb stubs — remove the whole method instead.
- Don't return raw exception objects — always call `.toString()` or wrap in a message.
- Don't hardcode datasource names or tokens in the script — read them from `vConfigService` or from a DB config table.
- Don't put heavy business logic directly in the endpoint file — delegate to a script via `libService.loadScript`.