New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@neo4j-cypher/language-support

Package Overview
Dependencies
Maintainers
0
Versions
129
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@neo4j-cypher/language-support - npm Package Compare versions

Comparing version 2.0.0-canary-2a6f081 to 2.0.0-canary-2e72ac8

dist/types/tests/syntaxValidation/functionsValidation.test.d.ts

16

CHANGELOG.md
# @neo4j-cypher/language-support
## 2.0.0-next.8
### Patch Changes
- 05663bd: Fixes bug using non language keywords (EXPLAIN, PROFILE, etc) as symbolic names
## 2.0.0-next.7
### Patch Changes
- 3661e9d: Fixes database completions for CREATE ALIAS commands
- b76af58: Fixes bug in signature help of functions nested inside procedure calls
- 21699b7: Updates the semantic analysis module to use the antlr parser instead of the javaCC one
- 6afc0e3: Adds signature information on auto-completions
- 39b924d: Fixes bug in labels completion
## 2.0.0-next.6

@@ -4,0 +20,0 @@

13

dist/types/generated-parser/CypherCmdLexer.d.ts

@@ -311,8 +311,9 @@ import { ATN, CharStream, DFA, Lexer } from "antlr4";

static readonly YIELD = 308;
static readonly ZONED = 309;
static readonly IDENTIFIER = 310;
static readonly ARROW_LINE = 311;
static readonly ARROW_LEFT_HEAD = 312;
static readonly ARROW_RIGHT_HEAD = 313;
static readonly ErrorChar = 314;
static readonly ZONE = 309;
static readonly ZONED = 310;
static readonly IDENTIFIER = 311;
static readonly ARROW_LINE = 312;
static readonly ARROW_LEFT_HEAD = 313;
static readonly ARROW_RIGHT_HEAD = 314;
static readonly ErrorChar = 315;
static readonly EOF: number;

@@ -319,0 +320,0 @@ static readonly channelNames: string[];

@@ -7,3 +7,3 @@ export type { ParserRuleContext } from 'antlr4';

export { CypherTokenType, lexerSymbols } from './lexerSymbols';
export { parse, parserWrapper } from './parserWrapper';
export { parse, parserWrapper, parseStatementsStrs } from './parserWrapper';
export { signatureHelp, toSignatureInformation } from './signatureHelp';

@@ -10,0 +10,0 @@ export { applySyntaxColouring, mapCypherToSemanticTokenIndex, syntaxColouringLegend, } from './syntaxColouring/syntaxColouring';

@@ -9,3 +9,3 @@ import type { ParserRuleContext, Token } from 'antlr4';

ctx: StatementsOrCommandsContext;
diagnostics: SyntaxDiagnostic[];
syntaxErrors: SyntaxDiagnostic[];
stopNode: ParserRuleContext;

@@ -15,2 +15,3 @@ collectedLabelOrRelTypes: LabelOrRelType[];

collectedFunctions: ParsedFunction[];
collectedProcedures: ParsedProcedure[];
}

@@ -46,3 +47,3 @@ export interface ParsingResult {

export type ParsedFunction = {
parsedName: string;
name: string;
rawText: string;

@@ -56,3 +57,5 @@ line: number;

};
export type ParsedProcedure = ParsedFunction;
export declare function createParsingScaffolding(query: string): ParsingScaffolding;
export declare function parseStatementsStrs(query: string): string[];
export declare function parse(query: string): StatementOrCommandContext[];

@@ -59,0 +62,0 @@ export declare function createParsingResult(query: string): ParsingResult;

@@ -21,3 +21,3 @@ {

],
"version": "2.0.0-canary-2a6f081",
"version": "2.0.0-canary-2e72ac8",
"main": "./dist/cjs/index.cjs",

@@ -59,9 +59,8 @@ "module": "./dist/esm/index.mjs",

"clean": "rm -rf {dist,src/generated-parser}",
"test": "jest"
"test": "vitest run"
},
"devDependencies": {
"@types/benchmark": "^2.1.5",
"@types/jest": "^29.5.5",
"benchmark": "^2.1.4"
}
}

@@ -7,3 +7,3 @@ export type { ParserRuleContext } from 'antlr4';

export { CypherTokenType, lexerSymbols } from './lexerSymbols';
export { parse, parserWrapper } from './parserWrapper';
export { parse, parserWrapper, parseStatementsStrs } from './parserWrapper';
export { signatureHelp, toSignatureInformation } from './signatureHelp';

@@ -10,0 +10,0 @@ export {

@@ -360,2 +360,3 @@ import CypherLexer from './generated-parser/CypherCmdLexer';

CypherLexer.YIELD,
CypherLexer.ZONE,
CypherLexer.ZONED,

@@ -362,0 +363,0 @@ // Preparser tokens

@@ -15,2 +15,3 @@ import type { ParserRuleContext, Token } from 'antlr4';

LabelOrRelTypeContext,
ProcedureNameContext,
StatementOrCommandContext,

@@ -24,2 +25,3 @@ StatementsOrCommandsContext,

findStopNode,
getTokens,
inNodeLabel,

@@ -43,3 +45,3 @@ inRelationshipType,

ctx: StatementsOrCommandsContext;
diagnostics: SyntaxDiagnostic[];
syntaxErrors: SyntaxDiagnostic[];
stopNode: ParserRuleContext;

@@ -49,2 +51,3 @@ collectedLabelOrRelTypes: LabelOrRelType[];

collectedFunctions: ParsedFunction[];
collectedProcedures: ParsedProcedure[];
}

@@ -103,3 +106,3 @@

export type ParsedFunction = {
parsedName: string;
name: string;
rawText: string;

@@ -113,2 +116,3 @@ line: number;

};
export type ParsedProcedure = ParsedFunction;

@@ -139,2 +143,24 @@ export function createParsingScaffolding(query: string): ParsingScaffolding {

export function parseStatementsStrs(query: string): string[] {
const statements = parse(query);
const result: string[] = [];
for (const statement of statements) {
const tokenStream = statement.parser?.getTokenStream() ?? [];
const tokens = getTokens(tokenStream as CommonTokenStream);
const statementStr = tokens
.filter((token) => token.type !== CypherLexer.EOF)
.map((token) => token.text)
.join('');
// Do not return empty statements
if (statementStr.trimLeft().length != 0) {
result.push(statementStr);
}
}
return result;
}
/* Parses a query without storing it in the cache */
export function parse(query: string): StatementOrCommandContext[] {

@@ -158,9 +184,5 @@ const statementScaffolding =

const variableFinder = new VariableCollector();
const functionFinder = new FunctionCollector(tokens);
const methodsFinder = new MethodsCollector(tokens);
const errorListener = new SyntaxErrorsListener(tokens);
parser._parseListeners = [
labelsCollector,
variableFinder,
functionFinder,
];
parser._parseListeners = [labelsCollector, variableFinder, methodsFinder];
parser.addErrorListener(errorListener);

@@ -173,7 +195,7 @@ const ctx = parser.statementsOrCommands();

) === undefined;
const diagnostics = isEmptyStatement ? [] : errorListener.errors;
const syntaxErrors = isEmptyStatement ? [] : errorListener.errors;
const collectedCommand = parseToCommand(ctx, isEmptyStatement);
if (!_internalFeatureFlags.consoleCommands) {
diagnostics.push(...errorOnNonCypherCommands(collectedCommand));
syntaxErrors.push(...errorOnNonCypherCommands(collectedCommand));
}

@@ -185,3 +207,3 @@

tokens: tokens,
diagnostics: diagnostics,
syntaxErrors: syntaxErrors,
ctx: ctx,

@@ -191,3 +213,4 @@ stopNode: findStopNode(ctx),

collectedVariables: variableFinder.variables,
collectedFunctions: functionFinder.functions,
collectedFunctions: methodsFinder.functions,
collectedProcedures: methodsFinder.procedures,
};

@@ -301,4 +324,5 @@ });

// This listener collects all functions
class FunctionCollector extends ParseTreeListener {
// This listener collects all functions and procedures
class MethodsCollector extends ParseTreeListener {
public procedures: ParsedProcedure[] = [];
public functions: ParsedFunction[] = [];

@@ -323,4 +347,7 @@ private tokens: Token[];

exitEveryRule(ctx: unknown) {
if (ctx instanceof FunctionNameContext) {
const functionName = this.getNormalizedFunctionName(ctx);
if (
ctx instanceof FunctionNameContext ||
ctx instanceof ProcedureNameContext
) {
const methodName = this.getMethodName(ctx);

@@ -337,4 +364,4 @@ const startTokenIndex = ctx.start.tokenIndex;

this.functions.push({
parsedName: functionName,
const result = {
name: methodName,
rawText: rawText,

@@ -347,13 +374,21 @@ line: ctx.start.line,

},
});
};
if (ctx instanceof FunctionNameContext) {
this.functions.push(result);
} else {
this.procedures.push(result);
}
}
}
private getNormalizedFunctionName(ctx: FunctionNameContext): string {
private getMethodName(
ctx: ProcedureNameContext | FunctionNameContext,
): string {
const namespaces = ctx.namespace().symbolicNameString_list();
const functionName = ctx.symbolicNameString();
const methodName = ctx.symbolicNameString();
const normalizedName = [...namespaces, functionName]
const normalizedName = [...namespaces, methodName]
.map((symbolicName) => {
return this.getFunctionNamespaceString(symbolicName);
return this.getNamespaceString(symbolicName);
})

@@ -365,3 +400,3 @@ .join('.');

private getFunctionNamespaceString(ctx: SymbolicNameStringContext): string {
private getNamespaceString(ctx: SymbolicNameStringContext): string {
const text = ctx.getText();

@@ -368,0 +403,0 @@ const isEscaped = Boolean(ctx.escapedSymbolicNameString());

@@ -37,8 +37,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */

if (dbSchema.functions && dbSchema.procedures) {
updateSignatureResolver({
procedures: Object.values(dbSchema.procedures),
functions: Object.values(dbSchema.functions),
});
}
updateSignatureResolver({
procedures: Object.values(dbSchema.procedures ?? {}),
functions: Object.values(dbSchema.functions ?? {}),
});
semanticAnalysis([query], (a) => {

@@ -45,0 +43,0 @@ semanticErrorsResult = a;

@@ -9,2 +9,3 @@ import { DiagnosticSeverity, Position } from 'vscode-languageserver-types';

ParsedFunction,
ParsedProcedure,
ParsedStatement,

@@ -67,3 +68,3 @@ parserWrapper,

): SyntaxDiagnostic | undefined {
const lowercaseFunctionName = parsedFunction.parsedName.toLowerCase();
const lowercaseFunctionName = parsedFunction.name.toLowerCase();
const caseInsensitiveFunctionInDatabase =

@@ -81,10 +82,10 @@ functionsSchema[lowercaseFunctionName];

const functionExistsWithExactName = Boolean(
functionsSchema[parsedFunction.parsedName],
functionsSchema[parsedFunction.name],
);
if (!functionExistsWithExactName) {
return generateFunctionNotFoundWarning(parsedFunction);
return generateFunctionNotFoundError(parsedFunction);
}
}
function generateFunctionNotFoundWarning(
function generateFunctionNotFoundError(
parsedFunction: ParsedFunction,

@@ -102,4 +103,4 @@ ): SyntaxDiagnostic {

const warning: SyntaxDiagnostic = {
severity: DiagnosticSeverity.Warning,
const error: SyntaxDiagnostic = {
severity: DiagnosticSeverity.Error,
range: {

@@ -110,8 +111,34 @@ start: Position.create(lineIndex, startColumn),

offsets: parsedFunction.offsets,
message: `Function ${parsedFunction.parsedName} is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application`,
message: `Function ${parsedFunction.name} is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application`,
};
return warning;
return error;
}
function generateProcedureNotFoundError(
parsedProcedure: ParsedProcedure,
): SyntaxDiagnostic {
const rawText = parsedProcedure.rawText;
const nameChunks = rawText.split('\n');
const linesOffset = nameChunks.length - 1;
const lineIndex = parsedProcedure.line - 1;
const startColumn = parsedProcedure.column;
const endColumn =
linesOffset == 0
? startColumn + rawText.length
: nameChunks.at(-1)?.length ?? 0;
const error: SyntaxDiagnostic = {
severity: DiagnosticSeverity.Error,
range: {
start: Position.create(lineIndex, startColumn),
end: Position.create(lineIndex + linesOffset, endColumn),
},
offsets: parsedProcedure.offsets,
message: `Procedure ${parsedProcedure.name} is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application`,
};
return error;
}
function warnOnUndeclaredLabels(

@@ -234,3 +261,4 @@ parsingResult: ParsedStatement,

const syntaxErrors = validateSyntax(query, dbSchema);
if (syntaxErrors.length > 0) {
// If there are any syntactic errors in the query, do not run the semantic validation
if (syntaxErrors.find((d) => d.severity === DiagnosticSeverity.Error)) {
return syntaxErrors;

@@ -240,3 +268,3 @@ }

const semanticErrors = validateSemantics(query, dbSchema);
return semanticErrors;
return syntaxErrors.concat(semanticErrors);
}

@@ -253,9 +281,5 @@

const result = statements.statementsParsing.flatMap((statement) => {
const diagnostics = statement.diagnostics;
const syntaxErrors = statement.syntaxErrors;
const labelWarnings = warnOnUndeclaredLabels(statement, dbSchema);
const functionWarnings = warnOnUndeclaredFunctions(statement, dbSchema);
return diagnostics
.concat(labelWarnings, functionWarnings)
.sort(sortByPositionAndMessage);
return syntaxErrors.concat(labelWarnings).sort(sortByPositionAndMessage);
});

@@ -277,5 +301,11 @@

const semanticErrors = statements.flatMap((current) => {
if (current.diagnostics.length === 0) {
if (current.syntaxErrors.length === 0) {
const cmd = current.command;
if (cmd.type === 'cypher' && cmd.statement.length > 0) {
const functionErrors = errorOnUndeclaredFunctions(current, dbSchema);
const procedureErrors = errorOnUndeclaredProcedures(
current,
dbSchema,
);
const { notifications, errors } = wrappedSemanticAnalysis(

@@ -287,7 +317,9 @@ cmd.statement,

const elements = notifications.concat(errors);
const result = fixSemanticAnalysisPositions({
const semanticDiagnostics = fixSemanticAnalysisPositions({
semanticElements: elements,
parseResult: current,
}).sort(sortByPositionAndMessage);
return result;
});
return semanticDiagnostics
.concat(functionErrors, procedureErrors)
.sort(sortByPositionAndMessage);
}

@@ -304,3 +336,3 @@ }

function warnOnUndeclaredFunctions(
function errorOnUndeclaredFunctions(
parsingResult: ParsedStatement,

@@ -326,1 +358,23 @@ dbSchema: DbSchema,

}
function errorOnUndeclaredProcedures(
parsingResult: ParsedStatement,
dbSchema: DbSchema,
): SyntaxDiagnostic[] {
const errors: SyntaxDiagnostic[] = [];
if (dbSchema.procedures) {
const proceduresInQuery = parsingResult.collectedProcedures;
proceduresInQuery.forEach((parsedProcedure) => {
const procedureExists = Boolean(
dbSchema.procedures[parsedProcedure.name],
);
if (!procedureExists) {
errors.push(generateProcedureNotFoundError(parsedProcedure));
}
});
}
return errors;
}

@@ -13,3 +13,3 @@ import {

} from 'vscode-languageserver-types';
import { CypherLexer } from '..';
import CypherLexer from '../generated-parser/CypherCmdLexer';
import CypherParser from '../generated-parser/CypherCmdParser';

@@ -16,0 +16,0 @@ import { isCommentOpener } from '../helpers';

@@ -62,3 +62,3 @@ import { autocomplete } from '../../autocompletion/autocompletion';

const unexpectedCompletions = excluded.map((notExpectedItem) =>
const unexpectedCompletions = excluded.filter((notExpectedItem) =>
actualCompletionList.find((value) => {

@@ -65,0 +65,0 @@ // if label is left out -> only check kind and vice versa

@@ -77,3 +77,3 @@ import { CompletionItemKind } from 'vscode-languageserver-types';

test.only('Correctly completes unstarted label when caret is passed and there is a space', () => {
test('Correctly completes unstarted label when caret is passed and there is a space', () => {
const query = 'MATCH (n : ';

@@ -80,0 +80,0 @@

@@ -13,3 +13,3 @@ import { autocomplete } from '../autocompletion/autocompletion';

expect(
result.statementsParsing.flatMap((statement) => statement.diagnostics),
result.statementsParsing.flatMap((statement) => statement.syntaxErrors),
).toEqual([]);

@@ -34,3 +34,3 @@ expect(

result.statementsParsing
.flatMap((statement) => statement.diagnostics)
.flatMap((statement) => statement.syntaxErrors)
.map((e) => e.message),

@@ -37,0 +37,0 @@ ).toContain(msg);

@@ -748,14 +748,15 @@ import { _internalFeatureFlags } from '../../featureFlags';

{
message: 'Path selectors such as `ANY 2 PATHS` are not supported yet',
message:
'Multiple path patterns cannot be used in the same clause in combination with a selective path selector.',
offsets: {
end: 32,
start: 21,
end: 82,
start: 16,
},
range: {
end: {
character: 26,
line: 1,
character: 31,
line: 2,
},
start: {
character: 15,
character: 10,
line: 1,

@@ -893,4 +894,3 @@ },

{
message:
'Type mismatch: p defined with conflicting type List<T> (expected Path)',
message: 'Variable `p` already declared',
offsets: {

@@ -1179,3 +1179,3 @@ end: 35,

message:
'A pattern expression should only be used in order to test the existence of a pattern. It should therefore only be used in contexts that evaluate to a boolean, e.g. inside the function exists() or in a WHERE-clause. No other uses are allowed, instead they should be replaced by a pattern comprehension.',
'A pattern expression should only be used in order to test the existence of a pattern. It can no longer be used inside the function size(), an alternative is to replace size() with COUNT {}.',
offsets: {

@@ -1428,262 +1428,12 @@ end: 29,

test('Does not provide semantic validation for pluggeable functions when schema is not available', () => {
test('Does not error on SHORTEST k', () => {
expect(
getDiagnosticsForQuery({
query: `RETURN apoc.coll.sum(['a', 'b'])`,
}),
).toEqual([]);
});
test('Provides semantic validation for built-in functions', () => {
expect(
getDiagnosticsForQuery({
query: `WITH character_length() AS a
WITH character_length(1) AS b, a
RETURN a,b`,
}),
).toEqual([
{
message: "Insufficient parameters for function 'character_length'",
offsets: {
end: 28,
start: 5,
},
range: {
end: {
character: 28,
line: 0,
},
start: {
character: 5,
line: 0,
},
},
severity: 1,
},
{
message: 'Type mismatch: expected String but was Integer',
offsets: {
end: 60,
start: 59,
},
range: {
end: {
character: 31,
line: 1,
},
start: {
character: 30,
line: 1,
},
},
severity: 1,
},
]);
});
test('Provides semantic validation for procedures when a schema is available', () => {
expect(
getDiagnosticsForQuery({
query: `
CALL db.awaitIndex('index', 'time')
CALL db.awaitIndex()
`,
query: `MATCH p = SHORTEST 2 (a)-[]-+(b)
WHERE a.name = "foo" AND b.name = "bar"
RETURN [n in nodes(p) | n.name] AS stops;`,
dbSchema: testData.mockSchema,
}),
).toEqual([
{
message: 'Type mismatch: expected Integer but was String',
offsets: {
end: 43,
start: 37,
},
range: {
end: {
character: 42,
line: 1,
},
start: {
character: 36,
line: 1,
},
},
severity: 1,
},
{
message: `Procedure call does not provide the required number of arguments: got 0 expected at least 1 (total: 2, 1 of which have default values).
Procedure db.awaitIndex has signature: db.awaitIndex(indexName :: STRING, timeOutSeconds = 300 :: INTEGER) ::
meaning that it expects at least 1 argument of type STRING
`,
offsets: {
end: 73,
start: 53,
},
range: {
end: {
character: 28,
line: 2,
},
start: {
character: 8,
line: 2,
},
},
severity: 1,
},
]);
);
});
test('Shows default values correctly for external procedures', () => {
expect(
getDiagnosticsForQuery({
query: 'CALL apoc.load.xml()',
dbSchema: testData.mockSchema,
}),
).toEqual([
{
message: `Procedure call does not provide the required number of arguments: got 0 expected at least 1 (total: 4, 3 of which have default values).
Procedure apoc.load.xml has signature: apoc.load.xml(urlOrBinary :: ANY, path = / :: STRING, config = {} :: MAP, simple = false :: BOOLEAN) :: value :: MAP
meaning that it expects at least 1 argument of type ANY
`,
offsets: {
end: 20,
start: 0,
},
range: {
end: {
character: 20,
line: 0,
},
start: {
character: 0,
line: 0,
},
},
severity: 1,
},
]);
});
test('Does not fail if default arguments for procedure not provided', () => {
expect(
getDiagnosticsForQuery({
query: `CALL apoc.load.xml('url', '/path')`,
dbSchema: testData.mockSchema,
}),
).toEqual([]);
});
test('Does not fail semantic validation for functions that expect LIST<ANY>', () => {
expect(
getDiagnosticsForQuery({
query: `RETURN apoc.coll.max(['a'])`,
dbSchema: testData.mockSchema,
}),
).toEqual([]);
});
test('Provides semantic validation for functions that expect LIST<NUMBER>', () => {
expect(
getDiagnosticsForQuery({
query: `RETURN apoc.coll.sum(['a', 'b'])`,
dbSchema: testData.mockSchema,
}),
).toEqual([
{
message:
'Type mismatch: expected List<Float>, List<Integer> or List<Number> but was List<String>',
offsets: {
end: 31,
start: 21,
},
range: {
end: {
character: 31,
line: 0,
},
start: {
character: 21,
line: 0,
},
},
severity: 1,
},
]);
});
// TODO This doesn't seem to warn on deprecated
// arguments for either functions or procedures,
// needs to be solved in the database first
test('Notifies of deprecated returns in procedures', () => {
expect(
getDiagnosticsForQuery({
query: `CALL apoc.meta.graphSample({})`,
dbSchema: {
functions: {},
procedures: {
'apoc.meta.graphSample': {
name: 'apoc.meta.graphSample',
description:
'Examines the full graph and returns a meta-graph.\nUnlike `apoc.meta.graph`, this procedure does not filter away non-existing paths.',
mode: 'DEFAULT',
worksOnSystem: false,
argumentDescription: [
{
isDeprecated: false,
default: 'DefaultParameterValue{value={}, type=MAP}',
description: 'config = {} :: MAP',
name: 'config',
type: 'MAP',
},
],
signature:
'apoc.meta.graphSample(config = {} :: MAP) :: (nodes :: LIST<NODE>, relationships :: LIST<RELATIONSHIP>)',
returnDescription: [
{
isDeprecated: true,
description: 'nodes :: LIST<NODE>',
name: 'nodes',
type: 'LIST<NODE>',
},
{
isDeprecated: false,
description: 'relationships :: LIST<RELATIONSHIP>',
name: 'relationships',
type: 'LIST<RELATIONSHIP>',
},
],
admin: false,
option: {
deprecated: false,
},
},
},
},
}),
).toEqual([
{
message:
"The query used a deprecated field from a procedure. ('nodes' returned by 'apoc.meta.graphSample' is deprecated.)",
offsets: {
end: 30,
start: 0,
},
range: {
end: {
character: 30,
line: 0,
},
start: {
character: 0,
line: 0,
},
},
severity: 2,
},
]);
});
});

@@ -1,2 +0,1 @@

import { testData } from '../testData';
import { getDiagnosticsForQuery } from './helpers';

@@ -699,3 +698,3 @@

{
message: "Expected '{' or a procedure name",
message: "Expected any of '{', '(' or a procedure name",
offsets: {

@@ -982,456 +981,16 @@ end: 6,

test('Syntax validation warns on missing function when database can be contacted', () => {
const query = `RETURN dontpanic("marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
offsets: {
end: 16,
start: 7,
},
message:
"Function dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
range: {
end: {
character: 16,
line: 0,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test('Syntax validation does not warn on existing function when database can be contacted', () => {
const query = `RETURN abs(4)`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation warns on missing function with namespace when database can be contacted', () => {
const query = `RETURN test.dontpanic("marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
offsets: {
end: 21,
start: 7,
},
message:
"Function test.dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
range: {
end: {
character: 21,
line: 0,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test('Syntax validation does not warn on existing function with namespace when database can be contacted', () => {
const query = `RETURN apoc.text.decapitalize("Marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation warns on missing function with namespace split in multiple lines when database can be contacted', () => {
const query = `RETURN test.
dontpanic
("marvin")
`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
offsets: {
end: 26,
start: 7,
},
message:
"Function test.dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
range: {
end: {
character: 13,
line: 1,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test('Syntax validation does not warn on existing function with namespace split in multiple lines when database can be contacted', () => {
const query = `
RETURN apoc.text.
capitalize
("marvin")
`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation is case insensitive on built-in functions', () => {
const query = `
RETURN aBS(123)
`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation is not case insensitive on user defined functions', () => {
const query = `
RETURN apoc.text.capiTALize("marvin")
`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
message:
"Function apoc.text.capiTALize is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 32,
start: 12,
},
range: {
end: {
character: 31,
line: 1,
},
start: {
character: 11,
line: 1,
},
},
severity: 2,
},
]);
});
test('Syntax validation does not warn on existing function with spaces when database can be contacted', () => {
const query = `RETURN apoc. text. decapitalize ("Marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation warns with correct positions with spaces when database can be contacted', () => {
const query = `RETURN apoc. text. dontpanic ("Marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
message:
"Function apoc.text.dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 31,
start: 7,
},
range: {
end: {
character: 31,
line: 0,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test('Syntax validation does not warn on existing function with new lines when database can be contacted', () => {
const query = `RETURN apoc.
text.
decapitalize
("Marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation warns with correct positions with spaces when database can be contacted', () => {
const query = `RETURN apoc. text.
dontpanic
("Marvin")`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
message:
"Function apoc.text.dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 36,
start: 7,
},
range: {
end: {
character: 14,
line: 1,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test('Syntax validation should recognize escaped function names', () => {
const query = `
RETURN \`abs\`(123)
`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation should fail on escaped function names that do not exist', () => {
const query = `
RETURN \`dontpanic\`(123)
`;
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
message:
"Function dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 23,
start: 12,
},
range: {
end: {
character: 22,
line: 1,
},
start: {
character: 11,
line: 1,
},
},
severity: 2,
},
]);
});
test('Syntax validation should pass on escaped function names with namespaces', () => {
const query = "RETURN `apoc`.text.`capitalize`('Marvin');";
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([]);
});
test('Syntax validation should fail on escaped function names with namespaces that do not exist', () => {
const query = "RETURN `apoc`.text.`dontpanic`('Marvin');";
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
message:
"Function apoc.text.dontpanic is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 30,
start: 7,
},
range: {
end: {
character: 30,
line: 0,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test('Syntax validation should fail if whole name and namespaces are escaped', () => {
const query = "RETURN `apoc.text.capitalize`('Marvin');";
expect(
getDiagnosticsForQuery({
query,
dbSchema: {
labels: ['Dog', 'Cat'],
relationshipTypes: ['Person'],
functions: testData.mockSchema.functions,
},
}),
).toEqual([
{
message:
"Function `apoc.text.capitalize` is not present in the database. Make sure you didn't misspell it or that it is available when you run this statement in your application",
offsets: {
end: 29,
start: 7,
},
range: {
end: {
character: 29,
line: 0,
},
start: {
character: 7,
line: 0,
},
},
severity: 2,
},
]);
});
test.each([
`MATCH (n:Test1) RETURN n.profile`,
`CREATE (n:Test1 {explain: 'Explain'});`,
`RETURN { clear: 'Clear', params: 'params', history: 'history'}`,
])(
'Syntax validation should not fail if cmd keywords are used in map properties %s',
(query) => {
expect(
getDiagnosticsForQuery({
query,
}),
).toEqual([]);
},
);
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc