@xano/developer-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +261 -0
- package/api_docs/addon.md +193 -0
- package/api_docs/agent.md +154 -0
- package/api_docs/api_group.md +236 -0
- package/api_docs/authentication.md +68 -0
- package/api_docs/file.md +190 -0
- package/api_docs/function.md +217 -0
- package/api_docs/history.md +263 -0
- package/api_docs/index.md +104 -0
- package/api_docs/mcp_server.md +139 -0
- package/api_docs/middleware.md +205 -0
- package/api_docs/realtime.md +153 -0
- package/api_docs/table.md +151 -0
- package/api_docs/task.md +191 -0
- package/api_docs/tool.md +216 -0
- package/api_docs/triggers.md +344 -0
- package/api_docs/workspace.md +246 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +495 -0
- package/package.json +49 -0
- package/xanoscript_docs/README.md +1 -0
- package/xanoscript_docs/api_query_examples.md +1255 -0
- package/xanoscript_docs/api_query_guideline.md +129 -0
- package/xanoscript_docs/build_from_lovable.md +715 -0
- package/xanoscript_docs/db_query_guideline.md +427 -0
- package/xanoscript_docs/ephemeral_environment_guideline.md +529 -0
- package/xanoscript_docs/expression_guideline.md +1086 -0
- package/xanoscript_docs/frontend_guideline.md +67 -0
- package/xanoscript_docs/function_examples.md +1406 -0
- package/xanoscript_docs/function_guideline.md +130 -0
- package/xanoscript_docs/functions.md +2155 -0
- package/xanoscript_docs/input_guideline.md +227 -0
- package/xanoscript_docs/mcp_server_examples.md +36 -0
- package/xanoscript_docs/mcp_server_guideline.md +69 -0
- package/xanoscript_docs/query_filter.md +489 -0
- package/xanoscript_docs/table_examples.md +586 -0
- package/xanoscript_docs/table_guideline.md +137 -0
- package/xanoscript_docs/task_examples.md +511 -0
- package/xanoscript_docs/task_guideline.md +103 -0
- package/xanoscript_docs/tips_and_tricks.md +144 -0
- package/xanoscript_docs/tool_examples.md +69 -0
- package/xanoscript_docs/tool_guideline.md +139 -0
- package/xanoscript_docs/unit_testing_guideline.md +328 -0
- package/xanoscript_docs/version.json +3 -0
- package/xanoscript_docs/workspace.md +17 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Tips and Tricks
|
|
6
|
+
|
|
7
|
+
## Variable definition
|
|
8
|
+
|
|
9
|
+
Xano doesn't have a concept of variable scope, you can define a variable anywhere in the stack and use it anywhere else.
|
|
10
|
+
|
|
11
|
+
```xs
|
|
12
|
+
var $greeting {
|
|
13
|
+
value = "Hello, World!"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
if defining an object or array, use `{}` or `[]`:
|
|
18
|
+
|
|
19
|
+
```xs
|
|
20
|
+
var $user {
|
|
21
|
+
value = {
|
|
22
|
+
name: "John Doe"
|
|
23
|
+
email: "john.doe@example.com"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```xs
|
|
29
|
+
var $numbers {
|
|
30
|
+
value = [1, 2, 3, 4, 5]
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## External API requests
|
|
35
|
+
|
|
36
|
+
When making an external API request, you'll want to use `params` for the request body, there is `body` keyword
|
|
37
|
+
|
|
38
|
+
## Comments
|
|
39
|
+
|
|
40
|
+
Use description argument on the statement or a comment `//` above it. Do not use description on responses, they do not support it. A comment can only be placed above a statement, not inside it.
|
|
41
|
+
|
|
42
|
+
````xs
|
|
43
|
+
// A simple hello world function
|
|
44
|
+
function "hello_world" {
|
|
45
|
+
input {
|
|
46
|
+
// The name of the person to greet
|
|
47
|
+
text name
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
stack {
|
|
51
|
+
// A simple greeting message
|
|
52
|
+
var $greeting {
|
|
53
|
+
value = "Hello, World!"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
response = $greeting
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
## Environment Variables
|
|
61
|
+
|
|
62
|
+
Always assume secrets and other configuration values are stored in the $env.<variable_name>, it's the responsibility of the user to create the values. Do not create a `.env` file. When adding a new environment variable, make sure to document it clearly and provide instructions for how to set it up.
|
|
63
|
+
|
|
64
|
+
## Conditionals
|
|
65
|
+
|
|
66
|
+
Conditionals accepts only one `if` and one `else` but you can add many `elseif` blocks.
|
|
67
|
+
|
|
68
|
+
## Existing logic and data schema
|
|
69
|
+
|
|
70
|
+
Try to reuse existing logic and data schema whenever possible. This includes using existing functions, API groups, data models, and API endpoints to avoid duplication and ensure consistency across your application.
|
|
71
|
+
|
|
72
|
+
## Large text
|
|
73
|
+
|
|
74
|
+
When dealing with large body of text (like a webpage, email body, AI prompt) it's best practice to use the `util.template_engine` function to manage and format the text efficiently. This templating system uses TWIG syntax.
|
|
75
|
+
|
|
76
|
+
```xs
|
|
77
|
+
util.template_engine {
|
|
78
|
+
description = "Create HTML table from event data"
|
|
79
|
+
value = """
|
|
80
|
+
<table>
|
|
81
|
+
<thead>
|
|
82
|
+
<tr>
|
|
83
|
+
<th>ID</th>
|
|
84
|
+
<th>Created At</th>
|
|
85
|
+
<th>Type</th>
|
|
86
|
+
<th>User ID</th>
|
|
87
|
+
<th>Metadata</th>
|
|
88
|
+
<th>Source</th>
|
|
89
|
+
<th>IP Address</th>
|
|
90
|
+
</tr>
|
|
91
|
+
</thead>
|
|
92
|
+
<tbody>
|
|
93
|
+
{% for event in events %}
|
|
94
|
+
<tr>
|
|
95
|
+
<td>{{ event.id }}</td>
|
|
96
|
+
<td>{{ event.created_at|format_timestamp("Y-m-d H:i:s", "UTC") }}</td>
|
|
97
|
+
<td>{{ event.type }}</td>
|
|
98
|
+
<td>{{ event.user_id ? event.user_id : "-" }}</td>
|
|
99
|
+
<td>{{ event.metadata ? event.metadata : "-" }}</td>
|
|
100
|
+
<td>{{ event.source ? event.source : "-" }}</td>
|
|
101
|
+
<td>{{ event.ip_address ? event.ip_address : "-" }}</td>
|
|
102
|
+
</tr>
|
|
103
|
+
{% endfor %}
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
"""
|
|
107
|
+
} as $html_table
|
|
108
|
+
````
|
|
109
|
+
|
|
110
|
+
## Filters
|
|
111
|
+
|
|
112
|
+
Xanoscript filters on inputs and values are inspired by TWIG, they use `|` to separate the variable from the filter. For example on an input block:
|
|
113
|
+
|
|
114
|
+
```xs
|
|
115
|
+
input {
|
|
116
|
+
text filters=trim|lower {
|
|
117
|
+
description = "Filters to apply to the text input"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
or a variable:
|
|
123
|
+
|
|
124
|
+
```xs
|
|
125
|
+
var $formatted_date {
|
|
126
|
+
value = $event.created_at|format_timestamp("Y-m-d H:i:s", "UTC")
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
or a schema:
|
|
131
|
+
|
|
132
|
+
```xs
|
|
133
|
+
table "event" {
|
|
134
|
+
schema {
|
|
135
|
+
int id
|
|
136
|
+
timestamp created_at?=now
|
|
137
|
+
text type filters=trim|lower
|
|
138
|
+
int user_id? filters=min:0
|
|
139
|
+
text metadata? filters=trim
|
|
140
|
+
text source? filters=trim|lower
|
|
141
|
+
text ip_address? filters=trim
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "tools/**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Xanoscript AI Tool Examples
|
|
6
|
+
|
|
7
|
+
Below are some examples of AI Tool configurations that can be made using Xanoscript.
|
|
8
|
+
|
|
9
|
+
## Tool with API, Task, and Tool Calls
|
|
10
|
+
|
|
11
|
+
This example demonstrates a tool that utilizes all three tool-specific statements: `api.call`, `task.call`, and `tool.call`.
|
|
12
|
+
|
|
13
|
+
```xs
|
|
14
|
+
tool "new_statements" {
|
|
15
|
+
description = "A tool that demonstrates calling other Xano resources."
|
|
16
|
+
|
|
17
|
+
instructions = "This tool is used to test the functionality of calling APIs, tasks, and other tools from within a tool. It takes a user's name as input and returns the result of an API call."
|
|
18
|
+
|
|
19
|
+
input {
|
|
20
|
+
text name? filters=trim {
|
|
21
|
+
description = "The name of the user to be used in the various calls. Descriptions are important as they are sent to the AI."
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
stack {
|
|
26
|
+
api.call "auth/login" verb=POST {
|
|
27
|
+
api_group = "Authentication"
|
|
28
|
+
input = {email: "email@email.com", password: "password"}
|
|
29
|
+
} as $endpoint1
|
|
30
|
+
|
|
31
|
+
task.call "task_example" as $test1
|
|
32
|
+
|
|
33
|
+
tool.call "what_is_xano" {
|
|
34
|
+
input = {query: "What is Xano?"}
|
|
35
|
+
} as $tool1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
response = $endpoint1
|
|
39
|
+
|
|
40
|
+
history = "inherit"
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Simple Information Retrieval Tool
|
|
45
|
+
|
|
46
|
+
This is an example of a simple tool that retrieves information.
|
|
47
|
+
|
|
48
|
+
```xs
|
|
49
|
+
tool "what_is_xano" {
|
|
50
|
+
description = "Provides a brief description of Xano."
|
|
51
|
+
instructions = "Use this tool to get a basic explanation of what Xano is."
|
|
52
|
+
|
|
53
|
+
input {
|
|
54
|
+
text query? {
|
|
55
|
+
description = "A question about Xano. The content of the query is ignored, the tool always returns the same text."
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
stack {
|
|
60
|
+
var $xano_description {
|
|
61
|
+
value = "Xano is a no-code backend platform that allows you to build scalable, secure, and compliant backends without writing any code."
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
response = $xano_description
|
|
66
|
+
|
|
67
|
+
history = 100
|
|
68
|
+
}
|
|
69
|
+
```
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "tools/**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# How to Define AI Tools in XanoScript
|
|
6
|
+
|
|
7
|
+
AI Tools in Xano are specialized, reusable function stacks designed to be executed by [AI Agents](./agent_guideline.md) or exposed externally through an [MCP server](./mcp_server_guideline.md), also known as Model Context Protocol Server. They act as the bridge between an AI's reasoning capabilities and your application's backend, allowing agents to query databases, call APIs, and perform complex actions.
|
|
8
|
+
|
|
9
|
+
Tools are defined in `<tool_name>.xs` files within the `tools/` directory of your project, they follow the same syntax as standard Xano [function stacks](./function_guideline.md) with some additional features tailored for AI interactions.
|
|
10
|
+
|
|
11
|
+
For example, you might have a file named `tools/user_lookup.xs` that contains the definition of a tool for looking up user information:
|
|
12
|
+
|
|
13
|
+
````xs
|
|
14
|
+
tool "user_lookup" {
|
|
15
|
+
description = "Looks up user information by user ID."
|
|
16
|
+
instructions = "Use this tool to retrieve detailed information about a user given their unique user ID. Provide the user ID as input, and the tool will return the user's profile data."
|
|
17
|
+
input {
|
|
18
|
+
int user_id {
|
|
19
|
+
description = "The unique identifier of the user to look up."
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
stack {
|
|
23
|
+
db.get "user" {
|
|
24
|
+
field_name = "id"
|
|
25
|
+
field_value = $input.user_id
|
|
26
|
+
description = "Verify user exists"
|
|
27
|
+
} as $user
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
response = $user
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
## Core Tool Syntax
|
|
34
|
+
|
|
35
|
+
Every tool is defined within a `tool` block.
|
|
36
|
+
|
|
37
|
+
```xs
|
|
38
|
+
tool "unique_tool_name" {
|
|
39
|
+
description = "A brief explanation of what this tool does."
|
|
40
|
+
instructions = "Guidelines explaining how AI agents should use this tool, including use cases, input formatting, and output interpretation."
|
|
41
|
+
input {
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
stack {
|
|
45
|
+
|
|
46
|
+
}
|
|
47
|
+
response = null
|
|
48
|
+
history = false
|
|
49
|
+
}
|
|
50
|
+
````
|
|
51
|
+
|
|
52
|
+
### Key Fields
|
|
53
|
+
|
|
54
|
+
- **`tool "name"`**: The top-level declaration. The name must be a unique string identifier, as this is how the tool is referenced by Agents and MCP servers.
|
|
55
|
+
- **`description`**: An optional string for internal documentation. This is not visible to the AI.
|
|
56
|
+
- **`instructions`**: A required string that provides the AI with the context it needs to use the tool effectively. This is a crucial field for ensuring the tool is used correctly by the agent.
|
|
57
|
+
- **`input`**: An object defining the parameters the tool accepts.
|
|
58
|
+
- **`stack`**: An object containing the sequence of operations to be executed.
|
|
59
|
+
- **`response`**: An object that specifies the data returned by the tool.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Input Block
|
|
64
|
+
|
|
65
|
+
The `input` block defines the parameters that the tool can accept at runtime. The structure is identical to inputs in other Xano function stacks, but the `description` for each parameter is especially important, as it is included in the information sent to the AI.
|
|
66
|
+
|
|
67
|
+
### Input Structure
|
|
68
|
+
|
|
69
|
+
The `input` block accepts a series of field definitions, follow the [input guidleine](./input_guideline.md) for more details
|
|
70
|
+
|
|
71
|
+
## Stack Block and Tool-Specific Statements
|
|
72
|
+
|
|
73
|
+
The `stack` contains the tool's logic. In addition to all standard Xano function statements, tools have access to three unique statements for calling other Xano resources.
|
|
74
|
+
|
|
75
|
+
### 1. `api.call`
|
|
76
|
+
|
|
77
|
+
Executes an API endpoint from one of your API groups. This is the preferred way to interact with your backend from a tool, as it ensures that all business logic encapsulated in the API is respected.
|
|
78
|
+
|
|
79
|
+
```xs
|
|
80
|
+
stack {
|
|
81
|
+
api.call "auth/login" verb=POST {
|
|
82
|
+
api_group = "Authentication"
|
|
83
|
+
input = {
|
|
84
|
+
email: "user@example.com",
|
|
85
|
+
password: "password123"
|
|
86
|
+
}
|
|
87
|
+
} as $login_response
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 2. `task.call`
|
|
92
|
+
|
|
93
|
+
Executes a background task. Tasks do not accept inputs or return outputs directly.
|
|
94
|
+
|
|
95
|
+
```xs
|
|
96
|
+
stack {
|
|
97
|
+
task.call "my_background_task" as $task_call_result
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. `tool.call`
|
|
102
|
+
|
|
103
|
+
```xs
|
|
104
|
+
stack {
|
|
105
|
+
tool.call "get_user_details" {
|
|
106
|
+
input = {user_id: 123}
|
|
107
|
+
} as $user_details
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Executes another AI Tool. This allows you to create modular, reusable tools that can be composed together.
|
|
112
|
+
|
|
113
|
+
- **Syntax**: `tool.call <tool_name> { ... }`
|
|
114
|
+
|
|
115
|
+
```xs
|
|
116
|
+
stack {
|
|
117
|
+
tool.call "get_user_details" {
|
|
118
|
+
input = {user_id: 123}
|
|
119
|
+
} as $user_details
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Response Block
|
|
124
|
+
|
|
125
|
+
The `response` block specifies the data that the tool returns to the agent or client that called it. The structure is the same as in other Xano function stacks.
|
|
126
|
+
|
|
127
|
+
**Example:**
|
|
128
|
+
|
|
129
|
+
```xs
|
|
130
|
+
response = $user_details
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Best Practices
|
|
134
|
+
|
|
135
|
+
- **Write Clear Instructions**: The `instructions` field is the most important part of your tool's definition. Be explicit about what the tool does, what each input parameter is for, and what the output means.
|
|
136
|
+
- **Use Descriptive Input Fields**: Clearly describe each input parameter. This information is passed to the AI and helps it construct valid requests.
|
|
137
|
+
- **Leverage Enums**: For inputs with a fixed set of possible values, use an `enum` type. This provides the AI with the exact options it can use, reducing errors.
|
|
138
|
+
- **Keep Tools Focused**: Design tools that perform a single, well-defined task. This makes them easier for the AI to understand and combine.
|
|
139
|
+
- **Handle Errors Gracefully**: Use `filters` on input, `precondition` and `try_catch` blocks in the stack to validate inputs and handle potential errors. Return clear error messages in the response so the AI can understand what went wrong.
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "functions/**/*.xs,apis/**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Unit Testing in XanoScript
|
|
6
|
+
|
|
7
|
+
Unit tests in XanoScript are defined using the `test` block inside a `function` or `query`. Each test can use various `expect` methods to assert the correctness of your code. Below are examples for all supported `expect` methods.
|
|
8
|
+
|
|
9
|
+
## Argument-based expects
|
|
10
|
+
|
|
11
|
+
These expects require a payload block with a `value` or other parameters.
|
|
12
|
+
|
|
13
|
+
### `expect.to_equal`
|
|
14
|
+
|
|
15
|
+
```xs
|
|
16
|
+
test "should match email" {
|
|
17
|
+
expect.to_equal ($response.user.email) {
|
|
18
|
+
value = "john@example.com"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Checks that `$response.user.email` equals `"john@example.com"`.
|
|
24
|
+
|
|
25
|
+
### `expect.to_not_equal`
|
|
26
|
+
|
|
27
|
+
```xs
|
|
28
|
+
test "should not be error status" {
|
|
29
|
+
expect.to_not_equal ($response.status) {
|
|
30
|
+
value = "error"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Checks that `$response.status` is not `"error"`.
|
|
36
|
+
|
|
37
|
+
### `expect.to_start_with`
|
|
38
|
+
|
|
39
|
+
```xs
|
|
40
|
+
test "should start with John" {
|
|
41
|
+
expect.to_start_with ($response.user.name) {
|
|
42
|
+
value = "John"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Checks that `$response.user.name` starts with `"John"`.
|
|
48
|
+
|
|
49
|
+
### `expect.to_end_with`
|
|
50
|
+
|
|
51
|
+
```xs
|
|
52
|
+
test "should end with .pdf" {
|
|
53
|
+
expect.to_end_with ($response.file.name) {
|
|
54
|
+
value = ".pdf"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Checks that `$response.file.name` ends with `".pdf"`.
|
|
60
|
+
|
|
61
|
+
### `expect.to_be_greater_than`
|
|
62
|
+
|
|
63
|
+
```xs
|
|
64
|
+
test "should be greater than 100" {
|
|
65
|
+
expect.to_be_greater_than ($response.order.total) {
|
|
66
|
+
value = 100
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Checks that `$response.order.total` is greater than `100`.
|
|
72
|
+
|
|
73
|
+
### `expect.to_be_less_than`
|
|
74
|
+
|
|
75
|
+
```xs
|
|
76
|
+
test "should be less than 10" {
|
|
77
|
+
expect.to_be_less_than ($response.product.stock_quantity) {
|
|
78
|
+
value = 10
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Checks that `$response.product.stock_quantity` is less than `10`.
|
|
84
|
+
|
|
85
|
+
### `expect.to_match`
|
|
86
|
+
|
|
87
|
+
```xs
|
|
88
|
+
test "should match US phone pattern" {
|
|
89
|
+
expect.to_match ($response.user.phone) {
|
|
90
|
+
value = "^\\+1\\d{10}$"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Checks that `$response.user.phone` matches the regex pattern for a US phone number.
|
|
96
|
+
|
|
97
|
+
### `expect.to_contain`
|
|
98
|
+
|
|
99
|
+
```xs
|
|
100
|
+
test "should contain featured tag" {
|
|
101
|
+
expect.to_contain ($response.tags) {
|
|
102
|
+
value = "featured"
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Checks that `"featured"` is present in `$response.tags` array.
|
|
108
|
+
|
|
109
|
+
## Range expects
|
|
110
|
+
|
|
111
|
+
### `expect.to_be_within`
|
|
112
|
+
|
|
113
|
+
```xs
|
|
114
|
+
test "should be within range" {
|
|
115
|
+
expect.to_be_within ($response.temperature) {
|
|
116
|
+
min = 20
|
|
117
|
+
max = 30
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Checks that `$response.temperature` is between `20` and `30` (inclusive).
|
|
123
|
+
|
|
124
|
+
## Argument-less expects
|
|
125
|
+
|
|
126
|
+
These expects do not require a payload block.
|
|
127
|
+
|
|
128
|
+
### `expect.to_be_true`
|
|
129
|
+
|
|
130
|
+
```xs
|
|
131
|
+
test "should be true" {
|
|
132
|
+
expect.to_be_true ($response.is_active)
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Checks that `$response.is_active` is `true`.
|
|
137
|
+
|
|
138
|
+
### `expect.to_be_false`
|
|
139
|
+
|
|
140
|
+
```xs
|
|
141
|
+
test "should be false" {
|
|
142
|
+
expect.to_be_false ($response.is_deleted)
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Checks that `$response.is_deleted` is `false`.
|
|
147
|
+
|
|
148
|
+
### `expect.to_be_in_the_past`
|
|
149
|
+
|
|
150
|
+
```xs
|
|
151
|
+
test "should be in the past" {
|
|
152
|
+
expect.to_be_in_the_past ($response.created_at)
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Checks that `$response.created_at` is a timestamp in the past.
|
|
157
|
+
|
|
158
|
+
### `expect.to_be_in_the_future`
|
|
159
|
+
|
|
160
|
+
```xs
|
|
161
|
+
test "should be in the future" {
|
|
162
|
+
expect.to_be_in_the_future ($response.scheduled_at)
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Checks that `$response.scheduled_at` is a timestamp in the future.
|
|
167
|
+
|
|
168
|
+
### `expect.to_be_defined`
|
|
169
|
+
|
|
170
|
+
```xs
|
|
171
|
+
test "should be defined" {
|
|
172
|
+
expect.to_be_defined ($response.user_id)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Checks that `$response.user_id` is defined.
|
|
177
|
+
|
|
178
|
+
### `expect.to_not_be_defined`
|
|
179
|
+
|
|
180
|
+
```xs
|
|
181
|
+
test "should not be defined" {
|
|
182
|
+
expect.to_not_be_defined ($response.optional_field)
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Checks that `$response.optional_field` is not defined.
|
|
187
|
+
|
|
188
|
+
### `expect.to_be_null`
|
|
189
|
+
|
|
190
|
+
```xs
|
|
191
|
+
test "should be null" {
|
|
192
|
+
expect.to_be_null ($response.deleted_at)
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Checks that `$response.deleted_at` is `null`.
|
|
197
|
+
|
|
198
|
+
### `expect.to_not_be_null`
|
|
199
|
+
|
|
200
|
+
```xs
|
|
201
|
+
test "should not be null" {
|
|
202
|
+
expect.to_not_be_null ($response.updated_at)
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Checks that `$response.updated_at` is not `null`.
|
|
207
|
+
|
|
208
|
+
### `expect.to_be_empty`
|
|
209
|
+
|
|
210
|
+
```xs
|
|
211
|
+
test "should be empty" {
|
|
212
|
+
expect.to_be_empty ($response.items)
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Checks that `$response.items` is empty (array, string, etc).
|
|
217
|
+
|
|
218
|
+
## Exception expects
|
|
219
|
+
|
|
220
|
+
### `expect.to_throw`
|
|
221
|
+
|
|
222
|
+
```xs
|
|
223
|
+
test "should throw error" {
|
|
224
|
+
expect.to_throw {
|
|
225
|
+
value = "InvalidInputError"
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Checks that an error with value `"InvalidInputError"` was thrown.
|
|
231
|
+
|
|
232
|
+
```xs
|
|
233
|
+
test "should throw any error" {
|
|
234
|
+
expect.to_throw
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Checks that any error was thrown.
|
|
239
|
+
|
|
240
|
+
## Mocking
|
|
241
|
+
|
|
242
|
+
You can use the `mock` block inside your function or query to simulate external API responses or other data sources during unit testing. The mock will only be applied if its name matches the test name. This allows you to test your logic without making real network requests or depending on external systems.
|
|
243
|
+
|
|
244
|
+
Mocks can be placed on any statement with a return value, such as API requests (`...} as $foo`), variable declarations (`var $x`), or variable updates (`var.update $x`). This gives you flexibility to mock data at any point in your logic.
|
|
245
|
+
|
|
246
|
+
**Example with multiple tests and mocks:**
|
|
247
|
+
|
|
248
|
+
```xs
|
|
249
|
+
function get_weather {
|
|
250
|
+
input {
|
|
251
|
+
text city
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
stack {
|
|
255
|
+
api.request {
|
|
256
|
+
url = "https://api.weather.com/v1/current"
|
|
257
|
+
method = "GET"
|
|
258
|
+
params = {city: $input.city}
|
|
259
|
+
mock = {
|
|
260
|
+
"should return sunny weather": {response: {weather: "sunny"}}
|
|
261
|
+
"should return rainy weather": {response: {weather: "rainy"}}
|
|
262
|
+
}
|
|
263
|
+
} as $weather_response
|
|
264
|
+
|
|
265
|
+
var $weather_message {
|
|
266
|
+
value = "Today's weather is " ~ $weather_response.response.weather
|
|
267
|
+
mock = {
|
|
268
|
+
"should return sunny weather": "Today's weather is sunny"
|
|
269
|
+
"should return rainy weather": "Today's weather is rainy"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
response = $weather_message
|
|
275
|
+
|
|
276
|
+
test "should return sunny weather" {
|
|
277
|
+
input = {city: "Paris"}
|
|
278
|
+
|
|
279
|
+
expect.to_equal ($response) {
|
|
280
|
+
value = "Today's weather is sunny"
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
test "should return rainy weather" {
|
|
285
|
+
input = {city: "London"}
|
|
286
|
+
|
|
287
|
+
expect.to_equal ($response) {
|
|
288
|
+
value = "Today's weather is rainy"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
** Example without mocks **
|
|
295
|
+
|
|
296
|
+
```xs
|
|
297
|
+
query "add_number" verb=GET {
|
|
298
|
+
input {
|
|
299
|
+
int a
|
|
300
|
+
int b
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
stack {
|
|
304
|
+
var $sum {
|
|
305
|
+
value = $input.a + $input.b
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
response = $sum
|
|
310
|
+
|
|
311
|
+
test "should add two numbers" {
|
|
312
|
+
input = {a: 5, b: 10}
|
|
313
|
+
expect.to_equal ($response) {
|
|
314
|
+
value = 15
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
**How mocking works:**
|
|
320
|
+
|
|
321
|
+
- The `mock` block provides a fixed response for the statement it is attached to.
|
|
322
|
+
- The mock is only used if its name matches the test name.
|
|
323
|
+
- You can define multiple mocks for different test scenarios.
|
|
324
|
+
- Mocks can be placed on API requests, variable declarations, or updates—any statement with a return value.
|
|
325
|
+
- This ensures your tests run reliably and quickly, even when external services are unavailable.
|
|
326
|
+
|
|
327
|
+
Use mocks to isolate your code from external dependencies and focus on your business logic.
|
|
328
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# XanoScript Workspace
|
|
2
|
+
|
|
3
|
+
This document provides an overview of the XanoScript workspace structure and guidelines for organizing your XanoScript files within a VSCode environment. The workspace is designed to facilitate efficient development, version control, and collaboration.
|
|
4
|
+
|
|
5
|
+
## Environment Variables
|
|
6
|
+
|
|
7
|
+
Environment variables can be used in XanoScript by using the `$env` prefix. The user can set their own environment variables, Xano also provides some built-in environment variables. The following are some of the built-in environment variables:
|
|
8
|
+
|
|
9
|
+
- `$env.$remote_ip`:this is a special environment variable that resolves to the IP address of the individual accessing the API endpoint.
|
|
10
|
+
- `$env.$http_headers`:this is a text array of headers that are sent to the API endpoint.
|
|
11
|
+
- `$env.$request_uri`:this is a special environment variable that contains the URI that is being accessed from the API.
|
|
12
|
+
- `$env.$request_method`:this is the method (GET, POST, DELETE, etc) of the incoming API request.
|
|
13
|
+
- `$env.$request_querystring`:this is a special environment variable that contains the query string of the URI that is being accessed from the API.
|
|
14
|
+
- `$env.$datasource`:this is a special environment variable that contains which datasource is being used.
|
|
15
|
+
- `$env.$branch`:this is a special environment variable that contains which branch is being used.
|
|
16
|
+
|
|
17
|
+
Custom environment variables can be set in the Xano dashboard under Settings > Environment Variables. These variables can be accessed in XanoScript using the `$env` prefix followed by the variable name (e.g., `$env.MY_CUSTOM_VARIABLE`). When using custom environment variables ($env.SOME_SERVICE_API_KEY), You should inform the user to set them up in their Xano dashboard.
|