Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions JsonSchemaValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import org.everit.json.schema.Schema;
import org.everit.json.schema.ValidationException;
import org.everit.json.schema.loader.SchemaLoader;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

public class JsonSchemaValidator {

private static final String DEFAULT_SCHEMA_PATH = "schema.json";
private static final String SCHEMA_ENV_VAR = "JSON_SCHEMA_PATH";
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Usage: java JsonSchemaValidator <json_file>");
System.exit(1);
}

String jsonPath = args[0];
String schemaPath = System.getenv(SCHEMA_ENV_VAR);
if (schemaPath == null || schemaPath.isEmpty()) {
schemaPath = DEFAULT_SCHEMA_PATH;
System.out.println("Warning: JSON_SCHEMA_PATH not set. Using default schema path: " + DEFAULT_SCHEMA_PATH);
}

try {
String jsonContent = new String(Files.readAllBytes(Paths.get(jsonPath)));
JSONObject jsonSchema = new JSONObject(new JSONTokener(new FileInputStream(schemaPath)));
JSONObject jsonSubject = new JSONObject(jsonContent);

Schema schema = SchemaLoader.load(jsonSchema);
schema.validate(jsonSubject);

System.out.println("Validation successful! The JSON is valid against the schema.");

} catch (ValidationException ve) {
System.out.println("JSON validation failed. Errors:");
printValidationErrors(ve, jsonPath);
} catch (JSONException je) {
System.out.println("JSON parsing error:");
printJSONParsingError(je, jsonPath);
} catch (IOException e) {
System.out.println("Error reading files: " + e.getMessage());
}
}

private static void printValidationErrors(ValidationException ve, String jsonPath) {
List<String> allMessages = ve.getAllMessages();
for (int i = 0; i < allMessages.size(); i++) {
System.out.printf("%d. %s%n", i + 1, allMessages.get(i));
}
System.out.println("\nDetailed error information:");
printValidationErrorDetails(ve, jsonPath, 0);
}

private static void printValidationErrorDetails(ValidationException ve, String jsonPath, int depth) {
String indent = " ".repeat(depth);
System.out.printf("%sError: %s%n", indent, ve.getMessage());
System.out.printf("%sJSON Path: %s%n", indent, ve.getPointerToViolation());

try {
List<String> lines = Files.readAllLines(Paths.get(jsonPath));
String errorPath = ve.getPointerToViolation();
int lineNumber = findLineNumber(lines, errorPath);
if (lineNumber != -1) {
System.out.printf("%sLine number: %d%n", indent, lineNumber);
System.out.printf("%sLine content: %s%n", indent, lines.get(lineNumber - 1).trim());
}
} catch (IOException e) {
System.out.printf("%sUnable to retrieve line information: %s%n", indent, e.getMessage());
}

List<ValidationException> causingExceptions = ve.getCausingExceptions();
if (!causingExceptions.isEmpty()) {
System.out.printf("%sNested errors:%n", indent);
for (ValidationException cause : causingExceptions) {
printValidationErrorDetails(cause, jsonPath, depth + 1);
}
}
}

private static void printJSONParsingError(JSONException je, String jsonPath) {
System.out.println(je.getMessage());

try {
List<String> lines = Files.readAllLines(Paths.get(jsonPath));
if (je.getMessage().contains("at character")) {
int charPosition = Integer.parseInt(je.getMessage().replaceAll(".*at character (\\d+).*", "$1"));
int lineNumber = 1;
int currentPosition = 0;

for (String line : lines) {
if (currentPosition + line.length() + 1 > charPosition) {
System.out.printf("Line number: %d%n", lineNumber);
System.out.printf("Line content: %s%n", line.trim());
System.out.printf("Error position: %s^%n", " ".repeat(charPosition - currentPosition - 1));
break;
}
currentPosition += line.length() + 1; // +1 for newline
lineNumber++;
}
}
} catch (IOException e) {
System.out.println("Unable to retrieve line information: " + e.getMessage());
}
}

private static int findLineNumber(List<String> lines, String jsonPath) {
String[] pathParts = jsonPath.split("/");
StringBuilder currentPath = new StringBuilder();
int nestingLevel = 0;

for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i).trim();
if (line.contains(":")) {
String key = line.split(":")[0].trim().replace("\"", "");
if (nestingLevel < pathParts.length && key.equals(pathParts[nestingLevel])) {
currentPath.append("/").append(key);
nestingLevel++;
if (currentPath.toString().equals(jsonPath)) {
return i + 1; // +1 because line numbers start at 1
}
}
}
if (line.contains("{")) nestingLevel++;
if (line.contains("}")) nestingLevel--;
}
return -1; // Path not found
}
}
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ public class Example {
Tax provider capabilities for new tax providers will be validated against a constantly updating JSON Schema, to validate the correctness and completeness of configurations. JSON Schema can be referenced below.
- [TaxProviderCapabilities JSONSchema](spec/capabilities/tax-provider.schema.json)

Prerequisites:
Use Java8 or higher version.

1. Clone repository in local
```shell
git clone git@github.com:chargebee/cb-provider-spi.git
```

2. Navigate to the repository
```shell
cd cb-provider-spi
```
3. Run the script to perform json schema validation:
```shell
sh json_schema_validation.sh <path to json file>
```
Example:
```shell
sh json_schema_validation.sh spec/capabilities/tax-provider.file.json
```

## Steps to follow release


Expand Down
37 changes: 34 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,45 @@ group build_group
version build_version

sourceSets {
main.resources.srcDirs = ['spec/capabilities']
main {
java {
srcDirs = ['src/main/java', '.']
}
resources {
srcDirs = ['src/main/resources', 'spec/capabilities']
}
}
}

repositories {
mavenCentral()
}

java {
sourceCompatibility = "17"
targetCompatibility = "17"
sourceCompatibility = "11"
targetCompatibility = "11"
}

// JSON Schema Validator JAR configuration
task jsonSchemaValidatorJar(type: Jar) {
archiveBaseName = 'json-schema-validator'
archiveVersion = '1.0'
manifest {
attributes 'Main-Class': 'JsonSchemaValidator'
}
from(sourceSets.main.output) {
include 'JsonSchemaValidator.class'
include 'spec/capabilities/**'
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}

duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

task buildJsonSchemaValidator {
dependsOn jsonSchemaValidatorJar
}

def loadSpecConfig() {
Expand Down Expand Up @@ -119,4 +148,6 @@ dependencies {
testImplementation 'org.mockito:mockito-core:5.6.0'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
// JSON Schema validation dependencies
implementation 'com.github.erosb:everit-json-schema:1.14.2'
}
74 changes: 74 additions & 0 deletions json_schema_validation.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/bash

# Make this script executable
chmod +x "$0"

# Check if Gradle wrapper is available
if ! command -v ./gradlew &> /dev/null
then
echo "Gradle wrapper not found. Please ensure you're in the correct directory."
exit 1
fi

# Check if a JSON file is provided
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <path_to_json_file>"
exit 1
fi

# Create a temporary directory to store the contents of the generated folder
TEMP_DIR=$(mktemp -d)

# If the generated folder exists, copy its contents to the temporary directory
if [ -d "generated" ]; then
cp -R generated/* "$TEMP_DIR" 2>/dev/null
fi

# Delete the generated folder
rm -rf generated 2>/dev/null

# Automatically detect the schema file
SCHEMA_FILE=$(find . -name "*schema.json" | head -n 1)

if [ -z "$SCHEMA_FILE" ]; then
echo "Error: Could not find a schema file in the current directory or its subdirectories."
rm -rf "$TEMP_DIR"
exit 1
fi

# Export the schema path as an environment variable
export JSON_SCHEMA_PATH="$SCHEMA_FILE"

echo "Using schema file: $JSON_SCHEMA_PATH"

# Clean and build the JSON Schema Validator
./gradlew clean buildJsonSchemaValidator

# Find the JAR file
JAR_FILE=$(find build/libs -name "json-schema-validator-1.0.jar" | head -n 1)

if [ -z "$JAR_FILE" ]; then
echo "Error: Could not find the JSON Schema Validator JAR file. Build may have failed."
rm -rf "$TEMP_DIR"
exit 1
fi

# Run the Java application using the generated JAR file
java -jar "$JAR_FILE" "$1"

# Check the exit status
VALIDATION_STATUS=$?
if [ $VALIDATION_STATUS -eq 0 ]; then
echo "Validation completed successfully."
else
echo "Validation failed. Please check the output above for details."
fi

# Recreate the generated folder and restore its contents
mkdir -p generated
cp -R "$TEMP_DIR"/* generated 2>/dev/null

# Clean up the temporary directory
rm -rf "$TEMP_DIR"

exit $VALIDATION_STATUS
Loading