diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..7e24eaa Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore index 3fe8131..d17babc 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ hs_err_pid* /build /.idea/ /generated/ + +*.DS_Store \ No newline at end of file diff --git a/JsonSchemaValidator.java b/JsonSchemaValidator.java new file mode 100755 index 0000000..edccedc --- /dev/null +++ b/JsonSchemaValidator.java @@ -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 "); + 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 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 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 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 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 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 + } +} diff --git a/README.md b/README.md index 58a409e..507653b 100644 --- a/README.md +++ b/README.md @@ -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 + ./json_schema_validation.sh +``` +Example: +```shell + ./json_schema_validation.sh spec/capabilities/tax-provider.file.json +``` + ## Steps to follow release @@ -128,3 +149,32 @@ Tax provider capabilities for new tax providers will be validated against a cons 6. After PR is approved and merged 7. Raise PR from release/0.0.9 to dev. Once PR is merged it will auto release the 0.0.9 version of SPI for dev code base 8. After that raise PR from release/0.0.9 to main. Once PR is merged it will auto release the 0.0.9 version of SPI for prod codebase + + +## Steps to generate a spring boot project for given spec using gradle + +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. (a) Run the script to generate spring boot project for an openapi spec file with default configurations: +```shell + ./setup_adapter_spring_boot.sh --spec +``` +3. (b) Run the script to generate spring boot project for an openapi spec file with custom configurations (params are optional, if not provided will take the default configurations) : +```shell + ./setup_adapter_spring_boot.sh --java --output --package --spec +``` +Example: +```shell + ./setup_adapter_spring_boot.sh --java 11 --output ../my-adapter --package com.adapter --spec openapi_tax.yml +``` diff --git a/build.gradle b/build.gradle index ab5f15c..6da0ec6 100644 --- a/build.gradle +++ b/build.gradle @@ -3,62 +3,134 @@ import org.openapitools.generator.gradle.plugin.tasks.ValidateTask import org.openapitools.generator.gradle.plugin.tasks.GenerateTask plugins { - id "idea" - id "java" + id 'java' + id 'org.springframework.boot' version '2.7.0' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'org.openapi.generator' version '7.0.1' id 'maven-publish' - id "org.openapi.generator" version "7.0.1" + id 'idea' } -group build_group -version build_version +group = build_group +version = build_version -sourceSets { - main.resources.srcDirs = ['spec/capabilities'] -} +sourceCompatibility = '11' +targetCompatibility = '11' repositories { mavenCentral() } -java { - sourceCompatibility = "17" - targetCompatibility = "17" +dependencies { + // Spring Boot dependencies + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-validation' + + // OpenAPI dependencies + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' + implementation 'com.google.code.gson:gson:2.10.1' + implementation 'com.google.code.findbugs:jsr305:3.0.2' + implementation 'io.gsonfire:gson-fire:1.8.5' + implementation 'javax.ws.rs:jsr311-api:1.1.1' + implementation 'javax.ws.rs:javax.ws.rs-api:2.1.1' + implementation 'org.openapitools:jackson-databind-nullable:0.2.6' + implementation 'org.apache.commons:commons-lang3:3.13.0' + implementation 'jakarta.annotation:jakarta.annotation-api:1.3.5' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' + + // JSON Schema validation dependencies + implementation 'com.github.erosb:everit-json-schema:1.14.2' + + // Test dependencies + testImplementation 'org.springframework.boot:spring-boot-starter-test' } -def loadSpecConfig() { - def configJson = "[" + file('spec.config').text + "]" +sourceSets { + main { + java { + srcDirs = ['src/main/java', '.'] + } + resources { + srcDirs = ['src/main/resources', 'spec/capabilities'] + } + } +} + +// Spring Boot configuration +bootJar { + archiveBaseName = 'spring-boot-openapi-jsonschema' + archiveVersion = '1.0' +} + +// 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) } + } - def config = new JsonSlurper().parseText(configJson) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} - project.ext.specConfig = config as ArrayList +task buildJsonSchemaValidator { + dependsOn jsonSchemaValidatorJar } -tasks.register("generateSpec") +// OpenAPI Spec generation configuration -tasks.register("validateSpec") -def openApiTask() { - loadSpecConfig() - for (int i = 0; i < specConfig.size(); i++) { - def spec = specConfig[i] +def loadSpecConfig() { + def configFile = file('spec.config') + if (!configFile.exists()) { + println "spec.config file not found" + return [] + } + def configJson = configFile.text + try { + def parsed = new groovy.json.JsonSlurper().parseText(configJson) + if (parsed instanceof List) { + return new ArrayList(parsed) + } else { + println "Warning: Parsed JSON is not a List. Wrapping in a new List." + return [parsed] + } + } catch (Exception e) { + println "Error parsing spec.config: ${e.message}" + return [] + } +} - def validateTaskName = "validateSpec_$spec.name" +tasks.register("generateSpec") +tasks.register("validateSpec") - def generateTaskName = "generateSpec_$spec.name" +def openApiTask() { + def specConfig = loadSpecConfig() + specConfig.each { spec -> + def validateTaskName = "validateSpec_${spec.name}" + def generateTaskName = "generateSpec_${spec.name}" - tasks.register(validateTaskName, ValidateTask) { - inputSpec.set("$rootDir/spec/spi/openapi_$spec.name"+".yml") + tasks.register(validateTaskName, org.openapitools.generator.gradle.plugin.tasks.ValidateTask) { + inputSpec.set("$rootDir/spec/spi/openapi_${spec.name}.yml") recommend.set(true) } - tasks.register(generateTaskName, GenerateTask) { - generatorName.set("java") - inputSpec.set("$rootDir/spec/spi/openapi_$spec.name"+".yml") - outputDir.set("$rootDir/generated/$spec.name") - apiPackage.set("org.chargebee.spi.$spec.name"+".api") - invokerPackage.set("org.chargebee.spi.$spec.name"+".client") - modelPackage.set("org.chargebee.spi.$spec.name"+".model") + tasks.register(generateTaskName, org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { + generatorName.set("spring") + inputSpec.set("$rootDir/spec/spi/openapi_${spec.name}.yml") + outputDir.set("$rootDir/generated/${spec.name}") + apiPackage.set("org.chargebee.spi.${spec.name}.api") + invokerPackage.set("org.chargebee.spi.${spec.name}.client") + modelPackage.set("org.chargebee.spi.${spec.name}.model") groupId.set(project.group) id.set(spec.projectName) version.set(project.version) @@ -68,11 +140,13 @@ def openApiTask() { validateSpec.dependsOn(validateTaskName) generateSpec.dependsOn(generateTaskName) - java.sourceSets["main"].java.srcDir("$rootDir/generated/$spec.name/src/main/java") + sourceSets.main.java.srcDir("$rootDir/generated/${spec.name}/src/main/java") } } -openApiTask(); +gradle.projectsEvaluated { + openApiTask() +} tasks.openApiGenerate.configure { actions.clear() @@ -84,39 +158,25 @@ tasks.openApiValidate.configure { dependsOn(validateSpec) } +// Publishing configuration publishing { repositories { maven { - url artifactory_publish_url + url = artifactory_publish_url credentials { - username System.env.CODEARTIFACT_USER - password System.env.CODEARTIFACT_AUTH_TOKEN + username = System.env.CODEARTIFACT_USER + password = System.env.CODEARTIFACT_AUTH_TOKEN } } } publications { mavenJava(MavenPublication) { - groupId = project.group - artifactId = rootProject.name - version = project.version from components.java } } } -//As of now we have to keep following dependencies to generate build with generated sources -dependencies { - implementation 'com.squareup.okhttp3:okhttp:4.12.0' - implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' - implementation 'com.google.code.gson:gson:2.10.1' - implementation "com.google.code.findbugs:jsr305:3.0.2" - implementation 'io.gsonfire:gson-fire:1.8.5' - implementation 'javax.ws.rs:jsr311-api:1.1.1' - implementation 'javax.ws.rs:javax.ws.rs-api:2.1.1' - implementation 'org.openapitools:jackson-databind-nullable:0.2.6' - implementation 'org.apache.commons:commons-lang3:3.13.0' - implementation 'jakarta.annotation:jakarta.annotation-api:1.3.5' - 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' +// Test configuration +test { + useJUnitPlatform() } \ No newline at end of file diff --git a/generator.config b/generator.config new file mode 100644 index 0000000..de68bd7 --- /dev/null +++ b/generator.config @@ -0,0 +1,11 @@ +outputDir: ../adapter +springBootVersion: 2.7.0 +springDependencyManagementVersion: 1.0.11.RELEASE +openApiGeneratorVersion: 7.0.1 +javaVersion: 11 +groupId: com.adapter +basePackage: com.adapter +specs: + - openapi_location_validation.yml + - openapi_tax.yml + - openapi_trn.yml \ No newline at end of file diff --git a/java_openapi_versions.txt b/java_openapi_versions.txt new file mode 100644 index 0000000..a8698f4 --- /dev/null +++ b/java_openapi_versions.txt @@ -0,0 +1,15 @@ +# OpenAPI Generator Version,Compatible Java Versions +5.3.0,8-11 +5.4.0,8-11 +6.0.0,8-14 +6.0.1,8-14 +6.1.0,8-16 +6.2.0,8-17 +6.2.1,8-17 +6.3.0,8-18 +6.4.0,8-18 +6.5.0,8-19 +6.6.0,8-20 +7.0.0,11-21 +7.0.1,11-21 +7.1.0,11-21 \ No newline at end of file diff --git a/json_schema_validation.sh b/json_schema_validation.sh new file mode 100755 index 0000000..7a9ec32 --- /dev/null +++ b/json_schema_validation.sh @@ -0,0 +1,51 @@ +#!/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 " + exit 1 +fi + +# 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." + 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." + exit 1 +fi + +# Run the Java application using the generated JAR file +java -jar "$JAR_FILE" "$1" + +# Check the exit status +if [ $? -eq 0 ]; then + echo "Validation completed successfully." +else + echo "Validation failed. Please check the output above for details." +fi \ No newline at end of file diff --git a/meta-generator.gradle b/meta-generator.gradle new file mode 100644 index 0000000..2424e41 --- /dev/null +++ b/meta-generator.gradle @@ -0,0 +1,157 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'org.yaml:snakeyaml:1.30' + } +} + +import org.yaml.snakeyaml.Yaml + +def configFile = file('generator.config') +def config = new Yaml().load(configFile.text) + +// Define command-line options +def cliOptions = [ + 'springBootVersion', + 'springDependencyManagementVersion', + 'openApiGeneratorVersion', + 'javaVersion', + 'groupId', + 'version', + 'basePackage', + 'outputDir', + 'specs', + 'singleSpec' +] + +// Override config with command-line arguments if provided +cliOptions.each { option -> + if (project.hasProperty(option)) { + config[option] = project.property(option) + } +} + +def outputDir = file(config.outputDir ?: '../adapter') + +task generateGradleProject { + doLast { + // Create the output directory if it doesn't exist + outputDir.mkdirs() + + // Create build.gradle + def buildGradleFile = new File(outputDir, 'build.gradle') + buildGradleFile.text = """ +plugins { + id 'org.springframework.boot' version '${config.springBootVersion}' + id 'io.spring.dependency-management' version '${config.springDependencyManagementVersion}' + id 'java' + id 'org.openapi.generator' version '${config.openApiGeneratorVersion}' +} + +group = '${config.groupId}' +version = '${config.version}' +sourceCompatibility = '${config.javaVersion}' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springdoc:springdoc-openapi-ui:1.6.14' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'jakarta.validation:jakarta.validation-api:3.0.0' + implementation 'org.hibernate.validator:hibernate-validator:6.2.0.Final' + implementation 'jakarta.annotation:jakarta.annotation-api:2.0.0' + implementation 'jakarta.servlet:jakarta.servlet-api:6.0.0' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.14.0' + implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.0' + implementation 'org.openapitools:jackson-databind-nullable:0.2.6' +} + +def specDir = '${projectDir}/../cb-provider-spi/spec/spi' +def specs = ${config.specs ?: []} +def singleSpec = '${config.singleSpec ?: ""}' + +if (singleSpec) { + specs = [singleSpec] +} + +specs.each { spec -> + def taskName = "generate\${spec.replace('.yml', '').capitalize()}" + tasks.register(taskName, org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { + generatorName = 'spring' + inputSpec = "\$specDir/\$spec" + outputDir = "\$projectDir/src/main/java" + apiPackage = "${config.basePackage}.api.\${spec.replace('.yml', '').replace('_', '')}" + modelPackage = "${config.basePackage}.model.\${spec.replace('.yml', '').replace('_', '')}" + configOptions = [ + dateLibrary: 'java8', + interfaceOnly: 'true', + useSpringBoot3: 'true', + groupBy: 'tags', + generateApiUtil: 'true' + ] + } +} + +tasks.named('openApiGenerate').configure { + enabled = false +} + +tasks.register('openApiGenerateFullProject') { + dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) +} + +sourceSets { + main { + java { + srcDir "src/main/java" + } + } +} + +compileJava.dependsOn tasks.openApiGenerateFullProject +""" + + // Create settings.gradle + def settingsGradleFile = new File(outputDir, 'settings.gradle') + settingsGradleFile.text = """ +rootProject.name = '${config.groupId.tokenize('.').last()}' +""" + + // Create gradle.properties + def gradlePropertiesFile = new File(outputDir, 'gradle.properties') + gradlePropertiesFile.text = """ +org.gradle.parallel=true +org.gradle.caching=true +""" + + // Create main application class + def mainClassDir = new File(outputDir, "src/main/java/${config.basePackage.replace('.', '/')}") + mainClassDir.mkdirs() + def mainClassFile = new File(mainClassDir, "Application.java") + mainClassFile.text = """ +package ${config.basePackage}; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} +""" + + // Create src directory structure + new File(outputDir, 'src/main/resources').mkdirs() + new File(outputDir, 'src/test/java').mkdirs() + new File(outputDir, 'src/test/resources').mkdirs() + + println "Generated Gradle project structure at ${outputDir.absolutePath}" + } +} \ No newline at end of file diff --git a/setup_adapter_spring_boot.sh b/setup_adapter_spring_boot.sh new file mode 100755 index 0000000..84428a5 --- /dev/null +++ b/setup_adapter_spring_boot.sh @@ -0,0 +1,165 @@ +#!/bin/sh + +# Make the script executable +if [ ! -x "$0" ]; then + chmod +x "$0" + exec "$0" "$@" +fi + +# Default values +JAVA_VERSION="11" +OUTPUT_DIR="../adapter" +SPEC_FILES="" +PACKAGE_NAME="com.adapter" +SPEC_DIR="spec/spi" + +# Function to update generator.config +update_generator_config() { + local spec_file=$1 + local config_file="generator.config" + + # Update the specs in the config file + sed -i "s/^specs:.*/specs:\n - $spec_file/" "$config_file" +} + +# Function to update spec.config +update_spec_config() { + local spec_file=$1 + local config_file="spec.config" + local spec_name=$(basename "$spec_file" .yml) + + # Create or overwrite the spec.config file with a single entry + echo "{ name: \"$spec_name\", projectName: \"$spec_name\" }" > "$config_file" +} + +# Parse command line arguments +while [ $# -gt 0 ]; do + case "$1" in + --java) + JAVA_VERSION="$2" + shift 2 + ;; + --output) + OUTPUT_DIR="$2" + shift 2 + ;; + --spec) + SPEC_FILES="$2" + shift 2 + ;; + --package) + PACKAGE_NAME="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Check if necessary directories and files exist +if [ ! -d "$SPEC_DIR" ]; then + echo "Error: Spec directory '$SPEC_DIR' not found." + exit 1 +fi + +if [ ! -f "java_openapi_versions.txt" ]; then + echo "Error: java_openapi_versions.txt not found in the current directory." + exit 1 +fi + +# Create output directory if it doesn't exist +mkdir -p "$OUTPUT_DIR" + +# Function to get all YAML files in the spec directory +get_all_spec_files() { + find "$SPEC_DIR" -name "*.yml" -o -name "*.yaml" | sed "s|$SPEC_DIR/||" | tr '\n' ',' | sed 's/,$//' +} + +# If no spec files are specified, use all available spec files +if [ -z "$SPEC_FILES" ]; then + SPEC_FILES=$(get_all_spec_files) +else + # Verify that the specified spec file exists + if [ ! -f "$SPEC_DIR/$SPEC_FILES" ]; then + echo "Error: Specified spec file '$SPEC_FILES' not found in '$SPEC_DIR'." + exit 1 + fi +fi + +if [ -z "$SPEC_FILES" ]; then + echo "Error: No spec files found in '$SPEC_DIR'." + exit 1 +fi + +# Function to get OpenAPI Generator version based on Java version +get_openapi_version() { + local java_version=$1 + local openapi_version="" + + while IFS=',' read -r openapi_ver java_range || [[ -n "$openapi_ver" ]]; do + if [[ "$openapi_ver" != \#* ]]; then # Skip comments + IFS='-' read -r min_java max_java <<< "$java_range" + if (( $java_version >= $min_java && $java_version <= $max_java )); then + openapi_version=$openapi_ver + fi + fi + done < java_openapi_versions.txt + + if [ -z "$openapi_version" ]; then + echo "Error: No compatible OpenAPI Generator version found for Java $java_version" >&2 + exit 1 + fi + echo $openapi_version +} + +# Get OpenAPI Generator version +OPENAPI_VERSION=$(get_openapi_version "$JAVA_VERSION") + +# Check if gradle is available +if ! command -v gradle &> /dev/null; then + echo "Error: gradle could not be found. Please ensure it's installed and in your PATH." + exit 1 +fi + +# Update config files if a single spec file is provided +if [ -n "$SPEC_FILES" ] && [ $(echo "$SPEC_FILES" | tr ',' '\n' | wc -l) -eq 1 ]; then + update_generator_config "$SPEC_FILES" + update_spec_config "$SPEC_FILES" + SPEC_FILES_LIST="'$SPEC_FILES'" +else + # Format SPEC_FILES for Groovy list + IFS=',' read -r -a specArray <<< "$SPEC_FILES" + SPEC_FILES_LIST=$(printf "'%s'," "${specArray[@]}") + SPEC_FILES_LIST=${SPEC_FILES_LIST%,} # Remove trailing comma +fi + +# Generate Gradle project +gradle -b meta-generator.gradle generateGradleProject \ + -PjavaVersion="$JAVA_VERSION" \ + -PopenApiGeneratorVersion="$OPENAPI_VERSION" \ + -PoutputDir="$OUTPUT_DIR" \ + -PbasePackage="$PACKAGE_NAME" \ + -Pspecs="[$SPEC_FILES_LIST]" \ + -PsingleSpec="$SPEC_FILES" + +if [ $? -ne 0 ]; then + echo "Error: Gradle project generation failed." + exit 1 +fi + +# Navigate to output directory and generate OpenAPI +cd "$OUTPUT_DIR" || exit +gradle openApiGenerateFullProject + +if [ $? -ne 0 ]; then + echo "Error: OpenAPI generation failed." + cd .. + exit 1 +fi + +cd .. + +echo "Project generation complete. Output directory: $OUTPUT_DIR" +echo "Generated spec files: $SPEC_FILES" diff --git a/spec.config b/spec.config index e1d0f3d..f35fcd3 100644 --- a/spec.config +++ b/spec.config @@ -1,12 +1,14 @@ -{ +[ + { "name": "trn", "projectName": "cb-trn-provider-spi" -}, -{ + }, + { "name": "tax", "projectName": "cb-tax-provider-spi" -}, -{ + }, + { "name": "location_validation", "projectName": "cb-tax-location-validation-spi" -} \ No newline at end of file + } +] diff --git a/spec/capabilities/tax-provider.file.json b/spec/capabilities/tax-provider.file.json new file mode 100644 index 0000000..2db0357 --- /dev/null +++ b/spec/capabilities/tax-provider.file.json @@ -0,0 +1,186 @@ +{ + "prod":{ + "api_configuration":{ + "api_base_url":"https://api.anrok.com/integrations/chargebee/api/v1", + "credential_configuration":[ + { + "id":"api_key", + "is_required":true, + "is_sensitive":true, + "name":"Anrok API Key", + "type":"text" + } + ] + }, + "capabilities":{ + "can_have_customer_identifiers":true, + "can_have_product_identifiers":false, + "can_support_currency_inclusive_of_taxes":true, + "can_sync_credit_notes":true, + "can_sync_invoices":true, + "can_validate_shipping_address":false, + "credit_note_sync_capabilities":{ + "applicable_sync_types":[ + "SYNC_ALL" + ], + "can_commit":false, + "can_delete":false, + "can_void":true, + "is_sync_supported":true, + "supported_countries":[ + "US", + "EU", + "GB", "ALL" + ] + }, + "invoice_sync_capabilities":{ + "applicable_sync_types":[ + "SYNC_ALL" + ], + "can_commit":false, + "can_delete":false, + "can_void":true, + "is_sync_supported":true, + "supported_countries":[ + "US", + "EU", + "GB", "ALL" + ] + }, + "is_consistent_pricing_supported":false, + "supported_countries":[ + "US", + "EU", + "GB", "ALL" + ], + "supported_currencies":[ + "US_DOLLARS" + ], + "tax_calculation_capabilities":{ + "accept_invalid_tax_reg_numbers":false, + "supportedNumberOfLineItems":1200 + } + }, + "customer_identifiers":[ + { + "external_id": "additionalTaxRegistrationNumber", + "display_name": "Additional Tax Registration Number", + "is_mandatory": false, + "field_type": "text" + } + ], + "identity_configuration":{ + "consent_policy_url":" https://www.anrok.com/privacy-terms", + "display_name":"Anrok", + "documentation_url":"https://www.chargebee.com/docs/2.0/anrok.html", + "id":"anrok", + "logo_url":"https://app.anrok.com/images/anrok-chargebee-app-icon.png", + "primary_description":[ + "Anrok unifies sales tax monitoring, calculation and remittance across your financial stack." + ], + "privacy_policy_url":" https://www.anrok.com/privacy-terms", + "secondary_description":[ + "Future-proof your Internet revenue." + ], + "signup_url":"https://anrok.com/contact/request-demo", + "support_email":"support@anrok.com", + "terms_of_service_url":"https://www.anrok.com/privacy-terms" + }, + "product_identifiers":[ + ], + "supported_number_of_line_items":1000, + "version":"1.0.0" + }, + "sandbox":{ + "api_configuration":{ + "api_base_url":"https://api.anrok.com/integrations/chargebee/api/v1", + "credential_configuration":[ + { + "id":"api_key", + "is_required":true, + "is_sensitive":true, + "name":"Anrok API Key", + "type":"text" + } + ] + }, + "capabilities":{ + "can_have_customer_identifiers":true, + "can_have_product_identifiers":false, + "can_support_currency_inclusive_of_taxes":true, + "can_sync_credit_notes":true, + "can_sync_invoices":true, + "can_validate_shipping_address":false, + "credit_note_sync_capabilities":{ + "applicable_sync_types":[ + "SYNC_ALL" + ], + "can_commit":false, + "can_delete":false, + "can_void":true, + "is_sync_supported":true, + "supported_countries":[ + "US", + "EU", + "GB", "ALL" + ] + }, + "invoice_sync_capabilities":{ + "applicable_sync_types":[ + "SYNC_ALL" + ], + "can_commit":false, + "can_delete":false, + "can_void":true, + "is_sync_supported":true, + "supported_countries":[ + "US", + "EU", + "GB", "ALL" + ] + }, + "is_consistent_pricing_supported":false, + "supported_countries":[ + "US", + "EU", + "GB", "ALL" + ], + "supported_currencies":[ + "US_DOLLARS" + ], + "tax_calculation_capabilities":{ + "accept_invalid_tax_reg_numbers":false, + "supportedNumberOfLineItems":1200 + } + }, + "customer_identifiers":[ + { + "external_id": "additionalTaxRegistrationNumber", + "display_name": "Additional Tax Registration Number", + "is_mandatory": false, + "field_type": "text" + } + ], + "identity_configuration":{ + "consent_policy_url":" https://www.anrok.com/privacy-terms", + "display_name":"Anrok", + "documentation_url":"https://www.chargebee.com/docs/2.0/anrok.html", + "id":"anrok", + "logo_url":"https://app.anrok.com/images/anrok-chargebee-app-icon.png", + "primary_description":[ + "Anrok unifies sales tax monitoring, calculation and remittance across your financial stack." + ], + "privacy_policy_url":" https://www.anrok.com/privacy-terms", + "secondary_description":[ + "Future-proof your Internet revenue." + ], + "signup_url":"https://anrok.com/contact/request-demo", + "support_email":"support@anrok.com", + "terms_of_service_url":"https://www.anrok.com/privacy-terms" + }, + "product_identifiers":[ + ], + "supported_number_of_line_items":1000, + "version":"1.0.0" + } +} \ No newline at end of file