Browse Source

Add Script Actions docs and dry-run details

Add a new Script Actions reference (docs/en/low-code/script-actions.md) and wire it into the docs navigation. Update low-code docs (custom-endpoints.md, designer.md, index.md, model-json.md, scripting-api.md) to document the Designer code editor, autocomplete (including fileFields/imageFields and enum registries), action types (event handlers, background jobs, workers), and the built-in Test JavaScript dry-run behavior. The dry-run tables describe captured side effects, HTTP mock behavior, and rollback semantics to help authors safely test scripts in the Designer.
pull/25610/head
SALİH ÖZKARA 4 months ago
parent
commit
bc8d87b0b2
  1. 4
      docs/en/docs-nav.json
  2. 30
      docs/en/low-code/custom-endpoints.md
  3. 4
      docs/en/low-code/designer.md
  4. 3
      docs/en/low-code/index.md
  5. 3
      docs/en/low-code/model-json.md
  6. 259
      docs/en/low-code/script-actions.md
  7. 129
      docs/en/low-code/scripting-api.md

4
docs/en/docs-nav.json

@ -486,6 +486,10 @@
"text": "Custom Endpoints",
"path": "low-code/custom-endpoints.md"
},
{
"text": "Script Actions",
"path": "low-code/script-actions.md"
},
{
"text": "Scripting API",
"path": "low-code/scripting-api.md"

30
docs/en/low-code/custom-endpoints.md

@ -161,6 +161,35 @@ Custom endpoint scripts use the same common [Scripting API](scripting-api.md) se
}
```
## Testing Endpoint Scripts
The Low-Code Designer endpoint editor includes **Test JavaScript**. Use it to run the current editor content without saving it.
The dry-run request editor lets you provide:
* HTTP method
* Request path
* Route values
* Query values
* Headers
* Body JSON
* Outbound HTTP mocks
Dry-run execution evaluates the endpoint descriptor, request context, script, authentication metadata, and required permissions against the current user. It returns the same response shape that a real endpoint execution would return.
Side effects are captured instead of being sent to external systems:
| Operation | Dry-run behavior |
|-----------|------------------|
| Database writes | Rolled back |
| Email send or queue | Captured under **Captured Side Effects** |
| Event publish | Captured under **Captured Side Effects** |
| Background job enqueue | Captured under **Captured Side Effects** |
| Outbound HTTP | Matched against HTTP mocks |
| File, image, and attachment operations | Captured without persisting files |
If a script calls the `http` helper and no mock matches the method and URL, the result contains a mock miss instead of sending a real HTTP request.
## Response Policy
Dynamic endpoint responses are validated by `LowCode:Scripting:EndpointResponse`.
@ -203,5 +232,6 @@ Default blocked headers also include hop-by-hop headers such as `Connection`, `T
## See Also
* [Scripting API](scripting-api.md)
* [Script Actions](script-actions.md)
* [Interceptors](interceptors.md)
* [model.json Structure](model-json.md)

4
docs/en/low-code/designer.md

@ -122,6 +122,10 @@ Generated pages and menus are permission-aware. If a user cannot access a page,
Use **Actions** only when model metadata and standard CRUD behavior are not enough. The scripting surface can define custom HTTP endpoints, distributed event handlers, background jobs, and scheduled background workers. Scripts run server-side and use the [Scripting API](scripting-api.md).
JavaScript editors in the Designer provide syntax highlighting, service autocomplete, entity name autocomplete, entity property autocomplete, enum autocomplete, and an **Available context** list. The context list reflects the services enabled for the selected script type. The `fileFields` and `imageFields` context items are selector trees for `File` and `Image` properties used by the file and image helpers; they are not lists of every entity property.
Endpoint and event handler editors include **Test JavaScript**. Dry-run execution runs the current editor content without saving it, rolls back database writes, captures email/event/job/file side effects, and resolves outbound HTTP calls through test mocks. See [Script Actions](script-actions.md) for action descriptors and dry-run behavior.
## Health
Use **Health** before shipping changes. It helps catch missing display properties, invalid relation targets, form/page references, script problems, and other model issues that would otherwise surface at runtime.

3
docs/en/low-code/index.md

@ -77,7 +77,7 @@ The designer is the day-to-day entry point.
2. Use **Pages** to choose a page type, menu placement, fields, default sorting, filters, dashboards, and linked forms.
3. Use **Forms** to arrange create and edit forms with tabs, groups, controls, validations, and actions.
4. Use **Permissions** to review generated permissions and control access.
5. Use **Actions** and **Interceptors** when the standard CRUD flow needs custom logic.
5. Use **Actions** and **Interceptors** when the standard CRUD flow needs custom logic, endpoints, event handlers, jobs, or workers.
6. Use **Health** to review model issues before publishing changes.
![Entity properties in the designer](images/designer-properties.png)
@ -152,6 +152,7 @@ The designer stores and reads the same model metadata described in the reference
| [Foreign Access](foreign-access.md) | Access to related dynamic entities through relations |
| [Interceptors](interceptors.md) | JavaScript lifecycle logic for CRUD operations |
| [Custom Endpoints](custom-endpoints.md) | JavaScript-backed REST endpoints |
| [Script Actions](script-actions.md) | Event handlers, background jobs, background workers, script editor, and dry-run testing |
| [Scripting API](scripting-api.md) | Server-side script context and helpers |
## Runtime Internals

3
docs/en/low-code/model-json.md

@ -427,7 +427,7 @@ See [Custom Endpoints](custom-endpoints.md).
}
```
Background workers require either `period` in milliseconds or `cronExpression`.
Background workers require either `period` in milliseconds or `cronExpression`. See [Script Actions](script-actions.md) for event handler, background job, background worker, code editor, and dry-run testing details.
## Complete Example
@ -537,4 +537,5 @@ In ABP Studio, run the generated migration task for the solution. If you run the
* [Attributes & Fluent API](fluent-api.md)
* [Interceptors](interceptors.md)
* [Custom Endpoints](custom-endpoints.md)
* [Script Actions](script-actions.md)
* [Scripting API](scripting-api.md)

259
docs/en/low-code/script-actions.md

@ -0,0 +1,259 @@
```json
//[doc-seo]
{
"Description": "Define event handlers, background jobs, background workers, script code editing, autocomplete, and dry-run testing in the ABP Low-Code Designer."
}
```
# Script Actions
> **Preview:** Script actions are part of the preview Low-Code System. Descriptor fields, designer screens, and script context members may change before general availability.
Use **Actions** in the Low-Code Designer when model metadata and generated CRUD behavior are not enough. Actions can add JavaScript-backed HTTP endpoints, distributed event handlers, background jobs, and scheduled background workers.
Custom HTTP endpoints are documented separately in [Custom Endpoints](custom-endpoints.md). This page focuses on the shared action designer experience and the event handler, background job, and background worker action types.
## Action Types
| Type | Use it for | Runtime trigger |
|------|------------|-----------------|
| Custom endpoint | Expose a small model-owned REST API | HTTP request to the configured route |
| Event handler | React to a named distributed event | `events.publishAsync(...)` or any compatible distributed event publisher |
| Background job | Run named JavaScript work asynchronously | `jobs.enqueueAsync(...)` |
| Background worker | Run recurring JavaScript work | `period` or `cronExpression` |
All action scripts run server-side and use the shared [Scripting API](scripting-api.md). Available services can be enabled or disabled per action type with scripting capability profiles.
## Script Code Editor
The Designer uses a code editor for JavaScript fields in custom endpoints, event handlers, background jobs, background workers, and interceptors.
The editor provides:
* Syntax highlighting for JavaScript
* Type-aware completions for low-code services
* Entity name completions for `db`, file, image, and attachment helpers
* Entity property completions for query lambda parameters and query results
* Enum completions through `enums` and `enumValues`
* File and image field selector completions through `fileFields` and `imageFields`
* An **Available context** list for the services enabled for the selected script type
The `fileFields` and `imageFields` globals are not lists of every entity property. They are selector trees for `File` and `Image` properties used by `files.save(...)`, `files.get(...)`, `images.save(...)`, and `images.get(...)`.
```javascript
await files.save(fileFields.Acme.Campaigns.Campaign.Document, {
fileName: 'brief.pdf',
contentType: 'application/pdf',
base64: base64Content
});
await images.save(imageFields.Acme.Campaigns.Campaign.BannerImage, {
fileName: 'banner.png',
contentType: 'image/png',
base64: base64Image
});
```
Regular entity fields autocomplete from entity records and query lambda parameters:
```javascript
var campaignQuery = await db.query('Acme.Campaigns.Campaign');
var rows = await campaignQuery
.where(campaign => campaign.Name.includes('Spring'))
.select(campaign => ({
id: campaign.Id,
name: campaign.Name
}))
.toList();
```
If the selected model layer is read-only, the Designer shows the JavaScript in a read-only editor. Switch to a writable layer before editing.
## Event Handlers
Event handlers run when a distributed event with the configured name is published.
### Descriptor
| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Unique event handler name |
| `eventName` | string | Distributed event name to handle |
| `javascript` | string | JavaScript handler body |
| `description` | string | Optional documentation text |
### Context
| Global | Description |
|--------|-------------|
| `handler` | Handler runtime metadata |
| `event` | Runtime event metadata with `name` and `data` |
| `eventName` | Event name string |
| `eventData` | Event payload |
### Example
```json
{
"eventHandlers": [
{
"name": "NotifyCampaignCompleted",
"eventName": "Acme.Campaigns.CampaignCompleted",
"description": "Logs and notifies when a campaign is completed",
"javascript": "log('Campaign completed: ' + eventData.id);\nawait email.queueAsync('ops@example.com', 'Campaign completed', eventData.id);"
}
]
}
```
Publish the event from another script:
```javascript
await events.publishAsync('Acme.Campaigns.CampaignCompleted', {
id: campaignId,
completedAt: new Date().toISOString()
});
```
## Background Jobs
Background jobs define named JavaScript handlers that can be enqueued from scripts.
### Descriptor
| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Unique job name used by `jobs.enqueueAsync(...)` |
| `javascript` | string | JavaScript job body |
| `description` | string | Optional documentation text |
### Context
| Global | Description |
|--------|-------------|
| `job` | Job runtime metadata |
| `jobName` | Job name string |
| `jobData` | Parsed job payload |
| `jobJsonData` | Raw JSON payload |
### Example
```json
{
"backgroundJobs": [
{
"name": "SendCampaignSummary",
"description": "Sends a summary for one campaign",
"javascript": "var campaign = await db.get('Acme.Campaigns.Campaign', jobData.campaignId);\nif (!campaign) { userFriendlyError('Campaign not found.'); }\nawait email.queueAsync(jobData.to, 'Campaign summary', campaign.Name);"
}
]
}
```
Enqueue the job from another script:
```javascript
var jobId = await jobs.enqueueAsync('SendCampaignSummary', {
campaignId: campaignId,
to: 'ops@example.com'
}, {
priority: 'Normal',
delayMs: 60000
});
```
## Background Workers
Background workers run recurring JavaScript work on a schedule.
### Descriptor
| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Unique worker name |
| `period` | number | Period in milliseconds |
| `cronExpression` | string | Cron expression for scheduled execution |
| `javascript` | string | JavaScript worker body |
| `description` | string | Optional documentation text |
Configure either `period` or `cronExpression`.
### Context
| Global | Description |
|--------|-------------|
| `worker` | Worker runtime metadata |
| `workerName` | Worker name string |
### Example
```json
{
"backgroundWorkers": [
{
"name": "CampaignCleanup",
"period": 3600000,
"description": "Runs every hour",
"javascript": "var query = await db.query('Acme.Campaigns.Campaign');\nvar stale = await query.where(c => c.Status === 0).take(100).toList();\nlog('Stale draft count: ' + stale.length);"
}
]
}
```
## Test JavaScript
Where the Designer shows **Test JavaScript**, you can run the current editor content without saving it. The built-in dry-run panel is available for custom endpoints, interceptors, event handlers, background jobs, and background workers.
For custom endpoints, provide request data:
* Method
* Path
* Route values
* Query values
* Headers
* Body JSON
For interceptors, provide the command name, entity name, command data, and an optional record id. For event handlers, provide event data JSON. For background jobs and background workers, provide the job or worker input JSON that the script expects.
When the script uses the HTTP API, you can also define outbound HTTP mocks. If a script calls `http.getAsync(...)`, `http.postAsync(...)`, or another HTTP helper, the dry-run engine returns the matching mock response instead of calling the real URL. If no mock matches, the result includes an HTTP mock miss.
Dry-run behavior:
| Operation | Dry-run behavior |
|-----------|------------------|
| Database writes | Executed in a transaction and rolled back |
| Low-code file/image/attachment operations | Captured as side effects without persisting files |
| Email send or queue | Captured as an `email` side effect; no email is sent |
| Event publish | Captured as an `event` side effect; no event is published |
| Background job enqueue | Captured as a `job` side effect; no job is enqueued |
| Outbound HTTP | Matched against HTTP mocks; no real HTTP call is made |
| Logs | Returned in the test result |
| Errors | Returned with type, message, and diagnostics when available |
Dry-run results can include:
* Endpoint response data
* Execution status and duration
* Logs
* Captured side effects
* Error details
The endpoint dry-run still evaluates the endpoint authentication and permission metadata against the current user. If the test user is not authenticated or does not have the required permission, the dry-run returns the corresponding `401` or `403` endpoint response.
## Operational Guidance
* Prefer metadata and generated CRUD behavior before adding scripts.
* Keep scripts small and focused.
* Use explicit permissions for custom endpoints.
* Use `take()` and specific filters for database queries.
* Treat public unauthenticated endpoints as public API surface.
* Keep outbound HTTP, email, event, job, file, and blob limits enabled for tenant-authored scripts.
* Use capability profiles to disable services that a script type does not need.
## See Also
* [Low-Code Designer](designer.md)
* [Custom Endpoints](custom-endpoints.md)
* [Interceptors](interceptors.md)
* [Scripting API](scripting-api.md)
* [model.json Structure](model-json.md)

129
docs/en/low-code/scripting-api.md

@ -15,6 +15,31 @@ The Low-Code System provides a server-side JavaScript scripting engine for execu
Scripts are wrapped in an async function, so `await` and top-level `return` are supported.
## Designer Code Editor
JavaScript fields in the Low-Code Designer use a code editor with syntax highlighting and low-code-aware autocomplete. The editor is available for interceptors, custom endpoints, event handlers, background jobs, and background workers.
The **Available context** list shows the globals enabled for the current script type. The list is based on the scripting capability profile, so an application can disable services such as HTTP, email, files, or background jobs for a specific script type.
Autocomplete covers:
* Common globals such as `db`, `currentUser`, `emailSender`, `http`, `events`, and `jobs`
* Endpoint, event handler, background job, background worker, and interceptor context variables
* Dynamic entity names in `db.query(...)`, `db.get(...)`, file, image, and attachment helpers
* Dynamic entity properties inside query lambda parameters and query results
* Enum names and values through `enums` and `enumValues`
* File and image field selectors through `fileFields` and `imageFields`
`fileFields` is intentionally limited to `File` properties and `imageFields` is limited to `Image` properties. They are safe selector trees for file and image helpers, not lists of every entity property.
```javascript
await files.save(fileFields.Acme.Campaigns.Campaign.Document, {
fileName: 'brief.pdf',
contentType: 'application/pdf',
base64: base64Content
});
```
## Unified Database API (`db`)
The `db` object is the main entry point for all data operations.
@ -356,7 +381,9 @@ Scripts receive a `context` object and common global shortcuts. Available servic
| `files` | `context.files` | Low-code file field helper |
| `images` | `context.images` | Low-code image field helper |
| `attachments` | `context.attachments` | Record attachment helper |
| `fields` | `context.fields` | Field selector helpers for file operations |
| `fileFields` | `context.fileFields` | File field selector tree |
| `imageFields` | `context.imageFields` | Image field selector tree |
| `enums`, `enumValues` | enum registry | Low-code enum value registry |
| `log`, `logWarning`, `logError` | logging methods | Script logging |
Global helpers are also available:
@ -410,6 +437,42 @@ Custom endpoints add request globals and response helpers. See [Custom Endpoints
| Background job | `job`, `jobName`, `jobData`, `jobJsonData` |
| Background worker | `worker`, `workerName` |
Event handlers, background jobs, and background workers are configured in the Designer **Actions** section or in `model.json`. See [Script Actions](script-actions.md) for descriptors, examples, and dry-run testing.
Event handler example:
```javascript
log('Received event ' + eventName);
if (eventData && eventData.campaignId) {
await jobs.enqueueAsync('SendCampaignSummary', {
campaignId: eventData.campaignId
});
}
```
Background job example:
```javascript
var campaign = await db.get('Acme.Campaigns.Campaign', jobData.campaignId);
if (!campaign) {
userFriendlyError('Campaign not found.');
}
await email.queueAsync(jobData.to, 'Campaign summary', campaign.Name);
```
Background worker example:
```javascript
var campaignQuery = await db.query('Acme.Campaigns.Campaign');
var staleCount = await campaignQuery
.where(campaign => campaign.Status === 0)
.count();
log('Stale draft campaigns: ' + staleCount);
```
## Service Helpers
### HTTP
@ -466,6 +529,69 @@ The file helpers use low-code page services so permissions, file validation, lin
File content is passed as base64 data. File operations are subject to configured read/write size limits.
Use `fileFields` and `imageFields` when you want typed selectors for file or image properties:
```javascript
await files.save(fileFields.Acme.Campaigns.Campaign.Document, {
fileName: 'brief.pdf',
contentType: 'application/pdf',
base64: base64Content
});
var content = await images.get(
imageFields.Acme.Campaigns.Campaign.BannerImage,
campaignId
);
```
The selector path is based on the full entity name and the `File` or `Image` property name. Record-level attachments are entity-level, not property-level, so they use the `attachments` helper instead of field selectors.
### Email
The `email` and `emailSender` globals use the configured ABP `IEmailSender`.
| Method | Description |
|--------|-------------|
| `email.sendAsync(to, subject, body)` | Send plain text email |
| `email.sendAsync(from, to, subject, body)` | Send plain text email with explicit sender |
| `email.sendHtmlAsync(to, subject, htmlBody)` | Send HTML email |
| `email.sendHtmlAsync(from, to, subject, htmlBody)` | Send HTML email with explicit sender |
| `email.queueAsync(to, subject, body)` | Queue plain text email |
| `email.queueAsync(from, to, subject, body)` | Queue plain text email with explicit sender |
| `email.queueHtmlAsync(to, subject, htmlBody)` | Queue HTML email |
| `email.queueHtmlAsync(from, to, subject, htmlBody)` | Queue HTML email with explicit sender |
Email operations validate the recipient address, apply allowed or blocked domain rules when configured, and enforce the per-execution email limit.
```javascript
if (email.isAvailable) {
await email.queueAsync(
'ops@example.com',
'Campaign completed',
'Campaign ' + campaignId + ' completed.'
);
}
```
### Test JavaScript Dry Run
The Designer can run JavaScript without saving it where the **Test JavaScript** panel is available. The built-in dry-run panel supports custom endpoints, interceptors, event handlers, background jobs, and background workers.
Dry-run execution returns the endpoint response or script status, logs, captured side effects, duration, and error diagnostics.
| Operation | Dry-run behavior |
|-----------|------------------|
| Database writes | Executed in a transaction and rolled back |
| File, image, and attachment operations | Captured as side effects without persisting files |
| Email send or queue | Captured as an `email` side effect; no email is sent |
| Event publish | Captured as an `event` side effect; no event is published |
| Background job enqueue | Captured as a `job` side effect; no job is enqueued |
| Outbound HTTP | Resolved from configured HTTP mocks; no real HTTP request is sent |
| Logs | Returned in the result |
| Errors | Returned with type, message, and diagnostics when available |
For endpoint dry runs, the request method, path, route values, query values, headers, and body are supplied by the test panel. Endpoint authentication and permission metadata are checked against the current user. For interceptor dry runs, the test panel supplies command metadata and command data. For event handler dry runs, it supplies `eventData`. For background job and worker dry runs, it supplies the job or worker input JSON.
## Configuration
Configure scripting limits with the `LowCode:Scripting` configuration section or `AbpLowCodeScriptingOptions`.
@ -658,4 +784,5 @@ return ok({
* [Interceptors](interceptors.md)
* [Custom Endpoints](custom-endpoints.md)
* [Script Actions](script-actions.md)
* [model.json Structure](model-json.md)

Loading…
Cancel
Save