@xano/xanoscript-language-server 11.7.2 → 11.8.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/lexer/tokens.js +7 -0
- package/package.json +1 -1
- package/parser/api_group_parser.js +8 -0
- package/parser/api_group_parser.spec.js +10 -0
- package/parser/clauses/nakedStackFn.js +4 -0
- package/parser/clauses/viewClause.js +4 -2
- package/parser/clauses/viewClause.spec.js +10 -1
- package/parser/functions/register.js +2 -0
- package/parser/functions/triggerCallFn.js +48 -0
- package/parser/functions/triggerCallFn.spec.js +51 -0
- package/parser/query_parser.spec.js +16 -0
- package/parser/table_parser.spec.js +20 -0
package/lexer/tokens.js
CHANGED
|
@@ -284,6 +284,12 @@ export const SensitiveToken = createTokenByName("sensitive", {
|
|
|
284
284
|
categories: [Identifier], // could also be an identifier
|
|
285
285
|
});
|
|
286
286
|
|
|
287
|
+
// define the sensitivity of a column
|
|
288
|
+
export const TriggerToken = createTokenByName("trigger", {
|
|
289
|
+
longer_alt: Identifier,
|
|
290
|
+
categories: [Identifier], // could also be an identifier
|
|
291
|
+
});
|
|
292
|
+
|
|
287
293
|
export const DescriptionToken = createTokenByName("description", {
|
|
288
294
|
longer_alt: Identifier,
|
|
289
295
|
categories: [Identifier], // could also be an identifier
|
|
@@ -350,6 +356,7 @@ export const allTokens = uniq([
|
|
|
350
356
|
AuthToken,
|
|
351
357
|
GuidToken,
|
|
352
358
|
SensitiveToken,
|
|
359
|
+
TriggerToken,
|
|
353
360
|
TagsToken,
|
|
354
361
|
DescriptionToken,
|
|
355
362
|
DocsToken,
|
package/package.json
CHANGED
|
@@ -21,6 +21,7 @@ export function apiGroupDeclaration($) {
|
|
|
21
21
|
let hasHistory = false;
|
|
22
22
|
let hasSwagger = false;
|
|
23
23
|
let hasTags = false;
|
|
24
|
+
let hasMiddleware = false;
|
|
24
25
|
|
|
25
26
|
$.sectionStack.push("apiGroupDeclaration");
|
|
26
27
|
// Allow leading comments and newlines before the api_group declaration
|
|
@@ -44,6 +45,13 @@ export function apiGroupDeclaration($) {
|
|
|
44
45
|
$.SUBRULE($.booleanValue);
|
|
45
46
|
},
|
|
46
47
|
},
|
|
48
|
+
{
|
|
49
|
+
GATE: () => !hasMiddleware,
|
|
50
|
+
ALT: () => {
|
|
51
|
+
hasMiddleware = true;
|
|
52
|
+
$.SUBRULE($.middlewareClause);
|
|
53
|
+
},
|
|
54
|
+
},
|
|
47
55
|
{
|
|
48
56
|
GATE: () => !hasCanonical,
|
|
49
57
|
ALT: () => {
|
|
@@ -39,6 +39,16 @@ describe("api_group", () => {
|
|
|
39
39
|
expect(parser.errors).to.be.empty;
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
it("should accept a middleware", () => {
|
|
43
|
+
const parser = xanoscriptParser(`api_group "some name" {
|
|
44
|
+
description = "Some description"
|
|
45
|
+
active = false
|
|
46
|
+
canonical = "HZ4jLtdc"
|
|
47
|
+
middleware = {pre: [{name: "M1"}], post: [{name: "M1"}]}
|
|
48
|
+
}`);
|
|
49
|
+
expect(parser.errors).to.be.empty;
|
|
50
|
+
});
|
|
51
|
+
|
|
42
52
|
it("should parse a disable swagger api_group", () => {
|
|
43
53
|
const parser = xanoscriptParser(`api_group Authentication {
|
|
44
54
|
active = false
|
|
@@ -55,6 +55,10 @@ export function nakedStackFn($) {
|
|
|
55
55
|
GATE: () => options.allowCallStatements,
|
|
56
56
|
ALT: () => $.SUBRULE($.middlewareCallFn),
|
|
57
57
|
},
|
|
58
|
+
{
|
|
59
|
+
GATE: () => options.allowCallStatements,
|
|
60
|
+
ALT: () => $.SUBRULE($.triggerCallFn),
|
|
61
|
+
},
|
|
58
62
|
{ ALT: () => $.SUBRULE($.workflowExpectFn) },
|
|
59
63
|
]);
|
|
60
64
|
});
|
|
@@ -36,10 +36,12 @@ export function viewClause($) {
|
|
|
36
36
|
|
|
37
37
|
// each key is the view name, ensure either search or hide is present
|
|
38
38
|
for (const viewName in captured) {
|
|
39
|
-
|
|
39
|
+
const hasSearch = !!captured[viewName].search;
|
|
40
|
+
const hasSort = !!captured[viewName].sort;
|
|
41
|
+
if (!hasSearch && !hasSort && !captured[viewName].hide) {
|
|
40
42
|
$.addMissingError(
|
|
41
43
|
parent,
|
|
42
|
-
`view ${viewName} must have either a search or hide criteria
|
|
44
|
+
`view ${viewName} must have either a search, sort or hide criteria`,
|
|
43
45
|
);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -109,13 +109,22 @@ describe("viewClause", () => {
|
|
|
109
109
|
expect(parser.errors).to.be.empty;
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
-
it("viewClause
|
|
112
|
+
it("viewClause accept either a sort criteria", () => {
|
|
113
113
|
const parser = parse(`view = {
|
|
114
114
|
"no search criteria": {
|
|
115
115
|
sort : {id: "asc"}
|
|
116
116
|
id : "9ed7daad-682f-455e-bf02-ca53444cd429"
|
|
117
117
|
}
|
|
118
118
|
}`);
|
|
119
|
+
expect(parser.errors).to.be.empty;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("viewClause requires either a search, sort or hide criteria", () => {
|
|
123
|
+
const parser = parse(`view = {
|
|
124
|
+
"no criteria": {
|
|
125
|
+
id : "9ed7daad-682f-455e-bf02-ca53444cd429"
|
|
126
|
+
}
|
|
127
|
+
}`);
|
|
119
128
|
expect(parser.errors).to.not.be.empty;
|
|
120
129
|
});
|
|
121
130
|
|
|
@@ -32,6 +32,7 @@ import { streamFn } from "./streamFn.js";
|
|
|
32
32
|
import { register as register_text } from "./text/register.js";
|
|
33
33
|
import { textFn } from "./textFn.js";
|
|
34
34
|
import { toolCallFn } from "./toolCallFn.js";
|
|
35
|
+
import { triggerCallFn } from "./triggerCallFn.js";
|
|
35
36
|
import { unitExpectFn } from "./unitExpectFn.js";
|
|
36
37
|
import { register as register_util } from "./util/register.js";
|
|
37
38
|
import { utilFn } from "./utilFn.js";
|
|
@@ -70,6 +71,7 @@ export const register = ($) => {
|
|
|
70
71
|
$.unitExpectFn = $.RULE("unitExpectFn", unitExpectFn($));
|
|
71
72
|
$.workflowExpectFn = $.RULE("workflowExpectFn", workflowExpectFn($));
|
|
72
73
|
$.toolCallFn = $.RULE("toolCallFn", toolCallFn($));
|
|
74
|
+
$.triggerCallFn = $.RULE("triggerCallFn", triggerCallFn($));
|
|
73
75
|
register_ai($);
|
|
74
76
|
register_schema($);
|
|
75
77
|
register_api($);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CallToken } from "../../lexer/function.js";
|
|
2
|
+
import { StringLiteral } from "../../lexer/literal.js";
|
|
3
|
+
import { Identifier } from "../../lexer/tokens.js";
|
|
4
|
+
import { DotToken, TriggerToken } from "../../lexer/tokens.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {import('../../base_parser.js').XanoBaseParser} $
|
|
8
|
+
*/
|
|
9
|
+
export function triggerCallFn($) {
|
|
10
|
+
return () => {
|
|
11
|
+
$.sectionStack.push("triggerCallFn");
|
|
12
|
+
|
|
13
|
+
$.CONSUME(TriggerToken); // "trigger"
|
|
14
|
+
$.CONSUME(DotToken); // "."
|
|
15
|
+
const fnToken = $.CONSUME(CallToken); // "call"
|
|
16
|
+
|
|
17
|
+
// Validate that trigger.call is only used in allowed contexts
|
|
18
|
+
const validContexts = ["workflowTestDeclaration"];
|
|
19
|
+
const isInValidContext = validContexts.some((context) =>
|
|
20
|
+
$.sectionStack.includes(context),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (!isInValidContext) {
|
|
24
|
+
$.addInvalidValueError(
|
|
25
|
+
fnToken,
|
|
26
|
+
"trigger.call can only be used within workflow_test contexts",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
$.OR([
|
|
31
|
+
{ ALT: () => $.CONSUME(StringLiteral) }, // "foo/bar"
|
|
32
|
+
{ ALT: () => $.CONSUME(Identifier) }, // foo
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
$.SUBRULE($.schemaParseAttributeFn, {
|
|
36
|
+
ARGS: [
|
|
37
|
+
fnToken,
|
|
38
|
+
{
|
|
39
|
+
"description?": "[string]",
|
|
40
|
+
"disabled?": "[boolean]",
|
|
41
|
+
"input?": { "[string]": "[expression]" },
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
});
|
|
45
|
+
$.SUBRULE($.asVariable, { ARGS: [fnToken] });
|
|
46
|
+
$.sectionStack.pop();
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { expect } from "chai";
|
|
2
|
+
import { describe, it } from "mocha";
|
|
3
|
+
import { lexDocument } from "../../lexer/lexer.js";
|
|
4
|
+
import { parser } from "../test_parser.js";
|
|
5
|
+
|
|
6
|
+
function parse(inputText, context = null) {
|
|
7
|
+
parser.reset();
|
|
8
|
+
const lexResult = lexDocument(inputText);
|
|
9
|
+
parser.input = lexResult.tokens;
|
|
10
|
+
|
|
11
|
+
if (context) {
|
|
12
|
+
parser.sectionStack.push(context);
|
|
13
|
+
parser.triggerCallFn();
|
|
14
|
+
parser.sectionStack.pop();
|
|
15
|
+
} else {
|
|
16
|
+
parser.triggerCallFn();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return parser;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("triggerCallFn", () => {
|
|
23
|
+
it("should parse trigger.call statement", () => {
|
|
24
|
+
const parser = parse(
|
|
25
|
+
`trigger.call my_agent {
|
|
26
|
+
input = {
|
|
27
|
+
"key": "value"
|
|
28
|
+
}
|
|
29
|
+
} as $result`,
|
|
30
|
+
"workflowTestDeclaration",
|
|
31
|
+
);
|
|
32
|
+
expect(parser.errors).to.be.empty;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("trigger.call can be one liner without a body", () => {
|
|
36
|
+
const parser = parse(
|
|
37
|
+
`trigger.call my_agent as $result`,
|
|
38
|
+
"workflowTestDeclaration",
|
|
39
|
+
);
|
|
40
|
+
expect(parser.errors).to.be.empty;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("should not allow trigger.call statement outside of workflowTestDeclaration", () => {
|
|
44
|
+
const parser = parse(`trigger.call my_agent {
|
|
45
|
+
input = {
|
|
46
|
+
"key": "value"
|
|
47
|
+
}
|
|
48
|
+
} as $result`);
|
|
49
|
+
expect(parser.errors).to.not.be.empty;
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -114,6 +114,22 @@ describe("query_parser", () => {
|
|
|
114
114
|
expect(parser.errors).to.be.empty;
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
it("should accept a middleware clause", () => {
|
|
118
|
+
const parser = xanoscriptParser(`query foo verb=GET {
|
|
119
|
+
input {
|
|
120
|
+
text user_id filters=trim
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
stack {
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
response = null
|
|
127
|
+
|
|
128
|
+
middleware = {pre: [{name: "M1"}], post: [{name: "M1"}]}
|
|
129
|
+
}`);
|
|
130
|
+
expect(parser.errors).to.be.empty;
|
|
131
|
+
});
|
|
132
|
+
|
|
117
133
|
it("should accept a test", () => {
|
|
118
134
|
const parser = xanoscriptParser(`query test_expect verb=GET {
|
|
119
135
|
input {
|
|
@@ -34,6 +34,26 @@ describe("table_parser", () => {
|
|
|
34
34
|
expect(parser.errors).to.be.empty;
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
it("should parse a basic table with an ordering view", () => {
|
|
38
|
+
const parser = xanoscriptParser(`table foo {
|
|
39
|
+
auth = false
|
|
40
|
+
|
|
41
|
+
schema {
|
|
42
|
+
// Primary key
|
|
43
|
+
int id
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
view = {
|
|
47
|
+
asc: {
|
|
48
|
+
alias: "testAlias"
|
|
49
|
+
sort : {Country: "asc"}
|
|
50
|
+
id : "b4530953-23df-401a-a2c9-9432b31cd808"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}`);
|
|
54
|
+
expect(parser.errors).to.be.empty;
|
|
55
|
+
});
|
|
56
|
+
|
|
37
57
|
// autocomplete = [{ name: "id" }, { name: "cd" }];
|
|
38
58
|
it("should parse a table with an autocomplete field", () => {
|
|
39
59
|
const parser = xanoscriptParser(`table foo {
|