Skip to content

Commit 53ea84f

Browse files
committed
replace the script with a new one that generates the index file from a template and populates the codes, update the verification, add bash to automate the workflow, and add a readme
1 parent f7f50bf commit 53ea84f

File tree

7 files changed

+625
-360
lines changed

7 files changed

+625
-360
lines changed

modules/ROOT/pages/errors/gql-errors/index.adoc

Lines changed: 12 additions & 55 deletions
Large diffs are not rendered by default.
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// This is an automatically generated file. Do not edit it directly.
2+
// It is generated from the templates/gql-index-template.adoc file and should be edited there. //// //// //// //// //// ////
3+
4+
:description: This section describes the GQLSTATUS errors that Neo4j can return, grouped by category, and an example of when they can occur.
5+
6+
[[neo4j-gqlstatus-errors]]
7+
= List of GQLSTATUS error codes
8+
9+
The following page provides an overview of all GQLSTATUS server error codes in Neo4j.
10+
All errors in Neo4j have severity level `ERROR`.
11+
12+
[WARNING]
13+
====
14+
Please note that while GQLSTATUS codes remain stable (any changes to them will be breaking), changes to status descriptions associated with these codes are not breaking and may happen at any time.
15+
For this reason, parsing the status descriptions or incorporating them into scripts is not recommended.
16+
====
17+
18+
[[connection-exceptions]]
19+
== Connection exceptions
20+
21+
Connection exceptions occur when the client (e.g. Browser/Bloom/Cypher Shell) is unable to connect to the server for various reasons such as network issues, server-side routing being disabled, or the database being unavailable, etc.
22+
23+
{codes_starting_with:'08'}
24+
25+
[[data-exceptions]]
26+
== Data exceptions
27+
28+
Database exceptions occur when a client request contains the wrong format, types, or other unsupported input.
29+
Some examples are data and constraint creation, which conflicts with existing constraints, properties of non-storable type, and spatial and temporal values with invalid components.
30+
31+
{codes_starting_with:'22'}
32+
33+
[[invalid-transaction-state]]
34+
== Invalid transaction state
35+
36+
Invalid transaction state errors occur when the transaction is in an invalid state, such as when the transaction is terminated or closed, or when there is a conflict between the transaction state and applied updates.
37+
38+
{codes-starting with: '25'}
39+
40+
[[invalid-transaction-termination]]
41+
== Invalid transaction termination
42+
43+
Invalid transaction termination errors occur when the transaction termination fails, such as when the transaction or constituent transaction fails to commit, or when the transaction termination fails to apply or append the transaction.
44+
45+
{codes-starting with: '2D'}
46+
47+
[[transaction-rollback]]
48+
== Transaction rollback
49+
50+
Transaction rollback errors occur when there is a failure in a transaction or a constituent transaction rollback.
51+
52+
{codes-starting with: '40'}
53+
54+
[[syntax-error-or-access-rule-violation]]
55+
== Syntax error or access rule violation
56+
57+
Syntax error or access rule violation errors occur when a Cypher query contains invalid syntax or when a client request violates the access rules, such as when a query tries to access a database without enough privileges, etc.
58+
59+
{codes-starting with: '42'}
60+
61+
[[general-processing-exceptions]]
62+
== General processing exceptions
63+
64+
General processing exceptions occur when there is a general processing error, such as an internal error, deadlock, execution failure, invalid server state transition, constraint creation or drop failure, etc.
65+
66+
{codes-starting with: '50'}
67+
68+
[[system-configuration-or-operation-exceptions]]
69+
== System configuration or operation exceptions
70+
71+
System configuration or operation exception errors occur when there is an error in the system configuration or operation, such as procedure registration failure, a missing class field annotation, an unsupported injectable component type, duplicate field names, invalid map key type, etc.
72+
73+
{codes-starting with: '51'}
74+
75+
[[procedure-exceptions]]
76+
== Procedure exceptions
77+
78+
Procedure exceptions occur when there is an error in executing a procedure, such as when the procedure execution fails due to a client error, when the procedure cannot be invoked on a primary, when the number of arguments to checkConnectivity is invalid, etc.
79+
80+
{codes-starting with: '52'}
81+
82+
[[function-exceptions]]
83+
== Function exceptions
84+
85+
{codes-starting with: '53'}
86+
87+
[[dependent-object-errors]]
88+
== Dependent object errors
89+
90+
{codes-starting with: 'G1'}
91+
92+
ifndef::backend-pdf[]
93+
[discrete.glossary]
94+
== Glossary
95+
96+
include::partial$glossary.adoc[]
97+
endif::[]

scripts/README.adoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
= Scripts for generating and validating gql error index.adoc file
2+
3+
This folder contains utility scripts for generating and validating gql error index.adoc file.
4+
5+
== Scripts overview
6+
7+
The folder contains the following scripts:
8+
9+
10+
=== `generate-gql-error-index-from-template.py`
11+
12+
This script generates the _auto-index.adoc_ file by using the template in _templates/gql-index-template.adoc_ and populating it with error codes from the individual files located in the _gql-errors_ directory.
13+
14+
What it does::
15+
* Extracts error codes from filenames (e.g., 42007.adoc → 42007).
16+
Extracts the status descriptions from individual error files.
17+
* Extracts page roles from files (`:page-role: changed-2025.04`).
18+
* Creates a new _auto-index.adoc_ file based on the template and populates it with the extracted data.
19+
20+
=== `validate_error_index.py`
21+
22+
This index validates the consistency between _index.adoc_ and individual error files, identifying discrepancies in Error codes, status descriptions, and page roles.
23+
24+
What it does::
25+
* Checks for error codes mentioned in the index.adoc file that don't have corresponding files.
26+
* Finds error files without index entries.
27+
* Detects status description mismatches between the index and individual files.
28+
* Verifies page role consistency.
29+
30+
=== `update-gql-error-index.sh`
31+
32+
This script runs the previous two scripts (the generation and validation scripts), and if validation passes, it replaces _index.adoc_ with _auto-index.adoc_.
33+
34+
What it does::
35+
* Runs the `generate-gql-error-index-from-template.py` script to generate the _auto-index.adoc_ file.
36+
* Runs the `validate_error_index.py` script to validate the generated file against the individual error files.
37+
* If validation passes, it replaces the existing _index.adoc_ file with _auto-index.adoc_.
38+
* If validation fails, it prints an error message and does not replace the _index.adoc_ file, while also keeping the _auto-index.adoc_ file for review.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import re
5+
from pathlib import Path
6+
import argparse
7+
8+
# For debugging - examine a single file to understand its structure
9+
def examine_error_file(file_path):
10+
"""Print the contents of an error file for debugging."""
11+
try:
12+
with open(file_path, 'r') as f:
13+
content = f.read()
14+
print(f"=== File Content for {file_path.name} ===")
15+
print(content)
16+
print("===================================")
17+
18+
# Try to extract the description with different patterns
19+
patterns = [
20+
r'Status description::\s*(.*?)(?=\n\n|\n==|\Z)',
21+
r'== Status description\s*\n(.*?)(?=\n\n|\n==|\Z)',
22+
r'== Status description\s*\n\s*Status description:: (.*?)(?=\n\n|\n==|\Z)'
23+
]
24+
25+
for i, pattern in enumerate(patterns):
26+
match = re.search(pattern, content, re.DOTALL)
27+
if match:
28+
print(f"Pattern {i+1} matched! Description: {match.group(1)[:50]}...")
29+
else:
30+
print(f"Pattern {i+1} did not match")
31+
except Exception as e:
32+
print(f"Error reading file: {e}")
33+
34+
def get_error_codes_from_files(errors_dir, verbose=False):
35+
"""Get all error codes and their details from individual .adoc files."""
36+
error_codes = {}
37+
sample_file = None
38+
39+
for file in os.listdir(errors_dir):
40+
if file.endswith('.adoc') and file != 'index.adoc' and file != 'auto-index.adoc':
41+
error_code = file[:-5] # Remove .adoc extension
42+
file_path = os.path.join(errors_dir, file)
43+
44+
if sample_file is None:
45+
sample_file = file_path # Save first file for debugging
46+
47+
# Extract details from file
48+
with open(file_path, 'r') as f:
49+
content = f.read()
50+
51+
# Extract page role
52+
page_role = None
53+
role_match = re.search(r':page-role:\s*(.*?)(?=\n|\Z)', content)
54+
if role_match:
55+
page_role = role_match.group(1).strip()
56+
57+
# Extract description - try several patterns
58+
description = None
59+
desc_patterns = [
60+
r'Status description::\s*(.*?)(?=\n\n|\n==|\Z)', # Simple format
61+
r'== Status description\s*\n\s*Status description:: (.*?)(?=\n\n|\n==|\Z)', # Section + desc
62+
r'== Status description\s*\n(.*?)(?=\n\n|\n==|\Z)' # Section only
63+
]
64+
65+
for pattern in desc_patterns:
66+
match = re.search(pattern, content, re.DOTALL)
67+
if match:
68+
description = match.group(1).strip()
69+
break
70+
71+
error_codes[error_code] = {
72+
'description': description,
73+
'page_role': page_role
74+
}
75+
76+
if verbose and description:
77+
print(f"Found description for {error_code}: {description[:50]}...")
78+
elif verbose:
79+
print(f"No description found for {error_code}")
80+
81+
# If we didn't find any descriptions, examine a sample file
82+
if verbose and sample_file and not any(code['description'] for code in error_codes.values()):
83+
print("\nNo descriptions were found in any files. Examining a sample file:")
84+
examine_error_file(sample_file)
85+
86+
return error_codes
87+
88+
def generate_from_template(template_file, errors_dir, output_file, include_descriptions=True, verbose=False):
89+
"""Generate the index file from template and individual error files."""
90+
if verbose:
91+
print("Extracting error codes and descriptions...")
92+
93+
# Get error codes and their info with improved extraction
94+
error_codes = get_error_codes_from_files(errors_dir, verbose)
95+
96+
if verbose:
97+
desc_count = sum(1 for code in error_codes.values() if code['description'])
98+
print(f"Found {len(error_codes)} error code files, {desc_count} with descriptions")
99+
100+
# Read the template
101+
with open(template_file, 'r') as f:
102+
template_content = f.read()
103+
104+
# Define patterns to find placeholders
105+
patterns = [
106+
r'\{codes_starting_with:\'([^\']+)\'\}',
107+
r'\{codes-starting with: \'([^\']+)\'\}'
108+
]
109+
110+
# Replace placeholders with generated content
111+
for pattern in patterns:
112+
for match in re.finditer(pattern, template_content):
113+
prefix = match.group(1)
114+
placeholder = match.group(0)
115+
116+
if verbose:
117+
print(f"Processing prefix: {prefix}")
118+
119+
# Generate content for this prefix
120+
content = []
121+
matching_codes = [code for code in sorted(error_codes.keys()) if code.startswith(prefix)]
122+
123+
for error_code in matching_codes:
124+
# Add page role if exists
125+
if error_codes[error_code]['page_role']:
126+
content.append(f'[role=label--{error_codes[error_code]["page_role"]}]')
127+
128+
content.append(f'=== xref:errors/gql-errors/{error_code}.adoc[{error_code}]')
129+
content.append('')
130+
131+
# Add description if available and requested
132+
if include_descriptions and error_codes[error_code]['description']:
133+
content.append(f'Status description:: {error_codes[error_code]["description"]}')
134+
content.append('')
135+
136+
section_content = '\n'.join(content)
137+
138+
# Replace placeholder with generated content
139+
template_content = template_content.replace(placeholder, section_content)
140+
141+
# Write the result to the output file
142+
with open(output_file, 'w') as f:
143+
f.write(template_content)
144+
145+
print(f'Generated index file at: {output_file}')
146+
print(f'Used template: {template_file}')
147+
print(f'Total error codes processed: {len(error_codes)}')
148+
149+
def main():
150+
# Set up argument parser
151+
parser = argparse.ArgumentParser(description='Generate GraphQL error code index from template')
152+
parser.add_argument('--no-descriptions', action='store_true',
153+
help='If set, only error codes will be listed without their descriptions')
154+
parser.add_argument('--verbose', action='store_true',
155+
help='Show detailed information during processing')
156+
parser.add_argument('--debug-file', help='Debug a specific error file')
157+
args = parser.parse_args()
158+
159+
# Get the script's directory
160+
script_dir = Path(__file__).parent.absolute()
161+
162+
# Navigate to the required directories
163+
errors_dir = script_dir.parent / 'modules' / 'ROOT' / 'pages' / 'errors' / 'gql-errors'
164+
template_file = script_dir.parent / 'modules' / 'ROOT' / 'templates' / 'gql-index-template.adoc'
165+
output_file = errors_dir / 'auto-index.adoc'
166+
167+
# Debug specific file if requested
168+
if args.debug_file:
169+
debug_path = errors_dir / f"{args.debug_file}.adoc"
170+
if debug_path.exists():
171+
examine_error_file(debug_path)
172+
else:
173+
print(f"Error: Debug file not found at {debug_path}")
174+
return
175+
176+
if not template_file.exists():
177+
print(f"Error: Template file not found at {template_file}")
178+
return
179+
180+
generate_from_template(template_file, errors_dir, output_file,
181+
not args.no_descriptions, args.verbose)
182+
183+
if __name__ == '__main__':
184+
main()

0 commit comments

Comments
 (0)