From a10a57a570d7e79e17e7f9b87598be8c36be6f60 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Fri, 4 Oct 2024 11:16:56 +0530 Subject: [PATCH 01/10] recentcommit --- README.md | 166 ++++++++++++++++++++++++++++++++++++++++++ generator.config | 12 +++ meta-generator.gradle | 106 +++++++++++++++++++++++++++ 3 files changed, 284 insertions(+) create mode 100644 generator.config create mode 100644 meta-generator.gradle diff --git a/README.md b/README.md index 58a409e..a503a0a 100644 --- a/README.md +++ b/README.md @@ -128,3 +128,169 @@ 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 + +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. Add meta-generator.gradle file +```groovy + +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' +] + +// 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 ?: '../generated-project') + +task generateGradleProject { + doLast { + // 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' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +def specDir = '${projectDir}/../cb-provider-spi/spec/spi' + +${config.specs.collect { spec -> + """ +openApiGenerate { + generatorName = 'spring' + inputSpec = "\${specDir}/${spec}" + outputDir = "\${projectDir}/src/main/java" + apiPackage = "${config.basePackage}.api" + modelPackage = "${config.basePackage}.model" + configOptions = [ + dateLibrary: 'java8', + interfaceOnly: 'true', + useSpringBoot3: 'true' + ] +} +""" +}.join('\n')} + +tasks.named('compileJava') { + dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) +} +""" + + // 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 src directory structure + new File(outputDir, 'src/main/java').mkdirs() + 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}" + } +} + +``` + +4. Add generator.config file + +```yaml +outputDir: ../generated-project +springBootVersion: 2.7.0 +springDependencyManagementVersion: 1.0.11.RELEASE +openApiGeneratorVersion: 7.0.1 +javaVersion: 11 +groupId: com.example +version: 0.0.1-SNAPSHOT +basePackage: com.example.generated +specs: + - openapi_location_validation.yml + - openapi_tax.yml + - openapi_trn.yml + +``` + +5. To generate the complete Gradle project structure with default values from generator.config, run: +```shell + gradle -b meta-generator.gradle generateGradleProject +``` + +6. To override versions or other properties, you can pass them as command-line arguments. For example: +```shell + gradle -b meta-generator.gradle generateGradleProject \ + -PspringBootVersion=2.7.1 \ + -PopenApiGeneratorVersion=7.0.1 \ + -PjavaVersion=11 \ + -PgroupId=com.mycompany \ + -Pversion=1.0.0-SNAPSHOT \ + -PbasePackage=com.mycompany.api \ + -PoutputDir=../my-custom-project +``` + +7. After generating the Gradle project, you can navigate to the output directory and run: +```shell + cd ../generated-project + gradle openApiGenerate +``` diff --git a/generator.config b/generator.config new file mode 100644 index 0000000..acb2dcd --- /dev/null +++ b/generator.config @@ -0,0 +1,12 @@ +outputDir: ../generated-project +springBootVersion: 2.7.0 +springDependencyManagementVersion: 1.0.11.RELEASE +openApiGeneratorVersion: 7.0.1 +javaVersion: 11 +groupId: com.example +version: 0.0.1-SNAPSHOT +basePackage: com.example.generated +specs: + - openapi_location_validation.yml + - openapi_tax.yml + - openapi_trn.yml \ No newline at end of file diff --git a/meta-generator.gradle b/meta-generator.gradle new file mode 100644 index 0000000..b5452ee --- /dev/null +++ b/meta-generator.gradle @@ -0,0 +1,106 @@ +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' +] + +// 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 ?: '../generated-project') + +task generateGradleProject { + doLast { + // 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' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +def specDir = '${projectDir}/../cb-provider-spi/spec/spi' + +${config.specs.collect { spec -> + """ +openApiGenerate { + generatorName = 'spring' + inputSpec = "\${specDir}/${spec}" + outputDir = "\${projectDir}/src/main/java" + apiPackage = "${config.basePackage}.api" + modelPackage = "${config.basePackage}.model" + configOptions = [ + dateLibrary: 'java8', + interfaceOnly: 'true', + useSpringBoot3: 'true' + ] +} +""" + }.join('\n')} + +tasks.named('compileJava') { + dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) +} +""" + + // 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 src directory structure + new File(outputDir, 'src/main/java').mkdirs() + 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 From e5631e7e48563fd9237d7fb59598d5abb97d2b03 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Fri, 4 Oct 2024 13:14:09 +0530 Subject: [PATCH 02/10] Readmecommit --- README.md | 135 ++++++++++++++++++++++++++---------------- meta-generator.gradle | 69 +++++++++++++++------ 2 files changed, 137 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index a503a0a..3af9692 100644 --- a/README.md +++ b/README.md @@ -146,12 +146,12 @@ Tax provider capabilities for new tax providers will be validated against a cons ```groovy buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.yaml:snakeyaml:1.30' - } + repositories { + mavenCentral() + } + dependencies { + classpath 'org.yaml:snakeyaml:1.30' + } } import org.yaml.snakeyaml.Yaml @@ -161,30 +161,30 @@ def config = new Yaml().load(configFile.text) // Define command-line options def cliOptions = [ - 'springBootVersion', - 'springDependencyManagementVersion', - 'openApiGeneratorVersion', - 'javaVersion', - 'groupId', - 'version', - 'basePackage', - 'outputDir' + 'springBootVersion', + 'springDependencyManagementVersion', + 'openApiGeneratorVersion', + 'javaVersion', + 'groupId', + 'version', + 'basePackage', + 'outputDir' ] // Override config with command-line arguments if provided cliOptions.each { option -> - if (project.hasProperty(option)) { - config[option] = project.property(option) - } + if (project.hasProperty(option)) { + config[option] = project.property(option) + } } def outputDir = file(config.outputDir ?: '../generated-project') task generateGradleProject { - doLast { - // Create build.gradle - def buildGradleFile = new File(outputDir, 'build.gradle') - buildGradleFile.text = """ + doLast { + // 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}' @@ -206,51 +206,86 @@ dependencies { } def specDir = '${projectDir}/../cb-provider-spi/spec/spi' +def specs = ${config.specs.inspect()} + +specs.each { spec -> + def taskName = "openApiGenerate_\${spec.replace('.yml', '')}" + tasks.register(taskName, org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { + generatorName = 'spring' + inputSpec = "\$specDir/\$spec" + outputDir = "\$projectDir/build/generated-\${spec.replace('.yml', '')}" + apiPackage = "${config.basePackage}.api.\${spec.replace('.yml', '').replace('_', '')}" + modelPackage = "${config.basePackage}.model.\${spec.replace('.yml', '').replace('_', '')}" + configOptions = [ + dateLibrary: 'java8', + interfaceOnly: 'true', + useSpringBoot3: 'true' + ] + } +} -${config.specs.collect { spec -> - """ -openApiGenerate { - generatorName = 'spring' - inputSpec = "\${specDir}/${spec}" - outputDir = "\${projectDir}/src/main/java" - apiPackage = "${config.basePackage}.api" - modelPackage = "${config.basePackage}.model" - configOptions = [ - dateLibrary: 'java8', - interfaceOnly: 'true', - useSpringBoot3: 'true' - ] +tasks.named('openApiGenerate').configure { + enabled = false } -""" -}.join('\n')} -tasks.named('compileJava') { +tasks.register('openApiGenerateAll') { dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) + .matching { it.name != 'openApiGenerate' } } + +sourceSets { + main { + java { + srcDir "src/main/java" + ${config.specs.collect { spec -> + "srcDir \"\$projectDir/build/generated-${spec.replace('.yml', '')}/src/main/java\"" + }.join('\n ')} + } + } +} + +compileJava.dependsOn tasks.openApiGenerateAll """ - // Create settings.gradle - def settingsGradleFile = new File(outputDir, 'settings.gradle') - settingsGradleFile.text = """ + // 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 = """ + // Create gradle.properties + def gradlePropertiesFile = new File(outputDir, 'gradle.properties') + gradlePropertiesFile.text = """ org.gradle.parallel=true org.gradle.caching=true """ - // Create src directory structure - new File(outputDir, 'src/main/java').mkdirs() - new File(outputDir, 'src/main/resources').mkdirs() - new File(outputDir, 'src/test/java').mkdirs() - new File(outputDir, 'src/test/resources').mkdirs() + // 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; - println "Generated Gradle project structure at ${outputDir.absolutePath}" +@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}" + } +} ``` @@ -292,5 +327,5 @@ specs: 7. After generating the Gradle project, you can navigate to the output directory and run: ```shell cd ../generated-project - gradle openApiGenerate + gradle openApiGenerateAll ``` diff --git a/meta-generator.gradle b/meta-generator.gradle index b5452ee..a33ce85 100644 --- a/meta-generator.gradle +++ b/meta-generator.gradle @@ -59,27 +59,45 @@ dependencies { } def specDir = '${projectDir}/../cb-provider-spi/spec/spi' +def specs = ${config.specs.inspect()} + +specs.each { spec -> + def taskName = "openApiGenerate_\${spec.replace('.yml', '')}" + tasks.register(taskName, org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { + generatorName = 'spring' + inputSpec = "\$specDir/\$spec" + outputDir = "\$projectDir/build/generated-\${spec.replace('.yml', '')}" + apiPackage = "${config.basePackage}.api.\${spec.replace('.yml', '').replace('_', '')}" + modelPackage = "${config.basePackage}.model.\${spec.replace('.yml', '').replace('_', '')}" + configOptions = [ + dateLibrary: 'java8', + interfaceOnly: 'true', + useSpringBoot3: 'true' + ] + } +} -${config.specs.collect { spec -> - """ -openApiGenerate { - generatorName = 'spring' - inputSpec = "\${specDir}/${spec}" - outputDir = "\${projectDir}/src/main/java" - apiPackage = "${config.basePackage}.api" - modelPackage = "${config.basePackage}.model" - configOptions = [ - dateLibrary: 'java8', - interfaceOnly: 'true', - useSpringBoot3: 'true' - ] +tasks.named('openApiGenerate').configure { + enabled = false } -""" - }.join('\n')} -tasks.named('compileJava') { +tasks.register('openApiGenerateAll') { dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) + .matching { it.name != 'openApiGenerate' } } + +sourceSets { + main { + java { + srcDir "src/main/java" + ${config.specs.collect { spec -> + "srcDir \"\$projectDir/build/generated-${spec.replace('.yml', '')}/src/main/java\"" + }.join('\n ')} + } + } +} + +compileJava.dependsOn tasks.openApiGenerateAll """ // Create settings.gradle @@ -93,10 +111,27 @@ rootProject.name = '${config.groupId.tokenize('.').last()}' 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/java').mkdirs() new File(outputDir, 'src/main/resources').mkdirs() new File(outputDir, 'src/test/java').mkdirs() new File(outputDir, 'src/test/resources').mkdirs() From ad580aecb168721c3f485ebae93b2d8df44fb668 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Mon, 7 Oct 2024 12:52:27 +0530 Subject: [PATCH 03/10] todayscommit --- README.md | 200 ++++++------------------------------------ generator.config | 7 +- meta-generator.gradle | 24 +++-- setup_spring_boot.sh | 31 +++++++ 4 files changed, 70 insertions(+), 192 deletions(-) create mode 100755 setup_spring_boot.sh diff --git a/README.md b/README.md index 3af9692..a39e1ed 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,10 @@ Tax provider capabilities for new tax providers will be validated against a cons ## Steps to generate a spring boot project for given spec using gradle +Prerequisites: +Use Java11 or higher version with gradle version greater than or equal to 7. +For gradle version less than 7 use Java8. + 1. Clone repository in local ```shell git clone git@github.com:chargebee/cb-provider-spi.git @@ -142,190 +146,38 @@ Tax provider capabilities for new tax providers will be validated against a cons cd cb-provider-spi ``` -3. Add meta-generator.gradle file -```groovy - -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' -] - -// 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 ?: '../generated-project') - -task generateGradleProject { - doLast { - // 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' - testImplementation 'org.springframework.boot:spring-boot-starter-test' -} - -def specDir = '${projectDir}/../cb-provider-spi/spec/spi' -def specs = ${config.specs.inspect()} - -specs.each { spec -> - def taskName = "openApiGenerate_\${spec.replace('.yml', '')}" - tasks.register(taskName, org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { - generatorName = 'spring' - inputSpec = "\$specDir/\$spec" - outputDir = "\$projectDir/build/generated-\${spec.replace('.yml', '')}" - apiPackage = "${config.basePackage}.api.\${spec.replace('.yml', '').replace('_', '')}" - modelPackage = "${config.basePackage}.model.\${spec.replace('.yml', '').replace('_', '')}" - configOptions = [ - dateLibrary: 'java8', - interfaceOnly: 'true', - useSpringBoot3: 'true' - ] - } -} - -tasks.named('openApiGenerate').configure { - enabled = false -} - -tasks.register('openApiGenerateAll') { - dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) - .matching { it.name != 'openApiGenerate' } -} - -sourceSets { - main { - java { - srcDir "src/main/java" - ${config.specs.collect { spec -> - "srcDir \"\$projectDir/build/generated-${spec.replace('.yml', '')}/src/main/java\"" - }.join('\n ')} - } - } -} - -compileJava.dependsOn tasks.openApiGenerateAll -""" - - // 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}" - } -} - -``` - -4. Add generator.config file - -```yaml -outputDir: ../generated-project -springBootVersion: 2.7.0 -springDependencyManagementVersion: 1.0.11.RELEASE -openApiGeneratorVersion: 7.0.1 -javaVersion: 11 -groupId: com.example -version: 0.0.1-SNAPSHOT -basePackage: com.example.generated -specs: - - openapi_location_validation.yml - - openapi_tax.yml - - openapi_trn.yml - -``` - -5. To generate the complete Gradle project structure with default values from generator.config, run: +3. (a) Generate the Gradle project structure with default configurations: ```shell gradle -b meta-generator.gradle generateGradleProject ``` -6. To override versions or other properties, you can pass them as command-line arguments. For example: +3. (b) Generate the gradle project with custom configurations use below commands. (Custom params if not provided, it will pick it from generator.config file) ```shell gradle -b meta-generator.gradle generateGradleProject \ -PspringBootVersion=2.7.1 \ -PopenApiGeneratorVersion=7.0.1 \ -PjavaVersion=11 \ - -PgroupId=com.mycompany \ - -Pversion=1.0.0-SNAPSHOT \ - -PbasePackage=com.mycompany.api \ - -PoutputDir=../my-custom-project + -PgroupId=com.adapter \ + -PbasePackage=com.adapter.api \ + -PoutputDir=../adapter ``` -7. After generating the Gradle project, you can navigate to the output directory and run: +4. After generating the Gradle project, you can navigate to the output directory and run: ```shell - cd ../generated-project - gradle openApiGenerateAll + gradle openApiGenerateFullProject ``` + +## Generating Spring boot project using shell script + +1. Perform the steps 1 and 2 as mentioned in above section. + +2. Execute the script file setup_spring_boot.sh using below command: +```shell + chmod +x setup_spring_boot.sh +``` +3. Run the script: +```shell + ./setup_spring_boot.sh +``` + + diff --git a/generator.config b/generator.config index acb2dcd..de68bd7 100644 --- a/generator.config +++ b/generator.config @@ -1,11 +1,10 @@ -outputDir: ../generated-project +outputDir: ../adapter springBootVersion: 2.7.0 springDependencyManagementVersion: 1.0.11.RELEASE openApiGeneratorVersion: 7.0.1 javaVersion: 11 -groupId: com.example -version: 0.0.1-SNAPSHOT -basePackage: com.example.generated +groupId: com.adapter +basePackage: com.adapter specs: - openapi_location_validation.yml - openapi_tax.yml diff --git a/meta-generator.gradle b/meta-generator.gradle index a33ce85..6675d7c 100644 --- a/meta-generator.gradle +++ b/meta-generator.gradle @@ -31,10 +31,13 @@ cliOptions.each { option -> } } -def outputDir = file(config.outputDir ?: '../generated-project') +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 = """ @@ -62,42 +65,35 @@ def specDir = '${projectDir}/../cb-provider-spi/spec/spi' def specs = ${config.specs.inspect()} specs.each { spec -> - def taskName = "openApiGenerate_\${spec.replace('.yml', '')}" + def taskName = "generate\${spec.replace('.yml', '').capitalize()}" tasks.register(taskName, org.openapitools.generator.gradle.plugin.tasks.GenerateTask) { generatorName = 'spring' inputSpec = "\$specDir/\$spec" - outputDir = "\$projectDir/build/generated-\${spec.replace('.yml', '')}" + 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' + useSpringBoot3: 'true', + groupBy: 'tags' ] } } -tasks.named('openApiGenerate').configure { - enabled = false -} - -tasks.register('openApiGenerateAll') { +tasks.register('openApiGenerateFullProject') { dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) - .matching { it.name != 'openApiGenerate' } } sourceSets { main { java { srcDir "src/main/java" - ${config.specs.collect { spec -> - "srcDir \"\$projectDir/build/generated-${spec.replace('.yml', '')}/src/main/java\"" - }.join('\n ')} } } } -compileJava.dependsOn tasks.openApiGenerateAll +compileJava.dependsOn tasks.openApiGenerateFullProject """ // Create settings.gradle diff --git a/setup_spring_boot.sh b/setup_spring_boot.sh new file mode 100755 index 0000000..52458ec --- /dev/null +++ b/setup_spring_boot.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Function to log messages +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" +} + +# Function to run a command and log its output +run_command() { + log "Running command: $1" + eval $1 + if [ $? -ne 0 ]; then + log "Error: Command failed" + exit 1 + fi + log "Command completed successfully" +} + +# Step 1: Generate Gradle project +log "Step 1: Generating Gradle project" +run_command "gradle -b meta-generator.gradle generateGradleProject" + +# Step 2: Generate OpenAPI code +log "Step 2: Generating OpenAPI code" +run_command "cd ../adapter && gradle openApiGenerateFullProject" + +# Step 3: Run Spring Boot application +log "Step 3: Running Spring Boot application" +run_command "cd ../adapter && gradle bootRun" + +log "Script completed successfully" \ No newline at end of file From a9d94bb67d31941bef8fb60939c5192026d53ca9 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Mon, 7 Oct 2024 14:59:02 +0530 Subject: [PATCH 04/10] newcommit --- meta-generator.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/meta-generator.gradle b/meta-generator.gradle index 6675d7c..cbb2f7c 100644 --- a/meta-generator.gradle +++ b/meta-generator.gradle @@ -81,6 +81,10 @@ specs.each { spec -> } } +tasks.named('openApiGenerate').configure { + enabled = false +} + tasks.register('openApiGenerateFullProject') { dependsOn tasks.withType(org.openapitools.generator.gradle.plugin.tasks.GenerateTask) } From e3b5eae68c03b35b644ecef18a6d8f4a46e4cb26 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Tue, 8 Oct 2024 10:22:13 +0530 Subject: [PATCH 05/10] newcommit --- README.md | 35 +++++-------------- java_openapi_versions.txt | 15 ++++++++ setup_adapter_spring_boot.sh | 68 ++++++++++++++++++++++++++++++++++++ setup_spring_boot.sh | 31 ---------------- 4 files changed, 91 insertions(+), 58 deletions(-) create mode 100644 java_openapi_versions.txt create mode 100755 setup_adapter_spring_boot.sh delete mode 100755 setup_spring_boot.sh diff --git a/README.md b/README.md index a39e1ed..12dc9c3 100644 --- a/README.md +++ b/README.md @@ -146,38 +146,19 @@ For gradle version less than 7 use Java8. cd cb-provider-spi ``` -3. (a) Generate the Gradle project structure with default configurations: +3. Execute the script file setup_spring_boot.sh using below command: ```shell - gradle -b meta-generator.gradle generateGradleProject + chmod +x setup_adapter_spring_boot.sh ``` - -3. (b) Generate the gradle project with custom configurations use below commands. (Custom params if not provided, it will pick it from generator.config file) +4. (a) Run the script for default configurations in script file : ```shell - gradle -b meta-generator.gradle generateGradleProject \ - -PspringBootVersion=2.7.1 \ - -PopenApiGeneratorVersion=7.0.1 \ - -PjavaVersion=11 \ - -PgroupId=com.adapter \ - -PbasePackage=com.adapter.api \ - -PoutputDir=../adapter + ./setup_adapter_spring_boot.sh ``` - -4. After generating the Gradle project, you can navigate to the output directory and run: +4. (a) Run the script for custom configurations (all params are optional if not provided will take the default configurations) : ```shell - gradle openApiGenerateFullProject + ./setup_adapter_spring_boot.sh --java-version --output-dir <"output_directory"> --base-package <"package_name"> ``` - -## Generating Spring boot project using shell script - -1. Perform the steps 1 and 2 as mentioned in above section. - -2. Execute the script file setup_spring_boot.sh using below command: +Example: ```shell - chmod +x setup_spring_boot.sh + ./generate_project.sh --java-version 17 --output-dir "../my-adapter" --base-package "com.adapter" ``` -3. Run the script: -```shell - ./setup_spring_boot.sh -``` - - 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/setup_adapter_spring_boot.sh b/setup_adapter_spring_boot.sh new file mode 100755 index 0000000..87ac95e --- /dev/null +++ b/setup_adapter_spring_boot.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# Default values +JAVA_VERSION="11" +OUTPUT_DIR="../adapter" +BASE_PACKAGE="com.adapter" + +# 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 +} + +# Parse command-line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --java-version) + JAVA_VERSION="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --base-package) + BASE_PACKAGE="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + exit 1 + ;; + esac +done + +# Get OpenAPI Generator version based on Java version +OPENAPI_VERSION=$(get_openapi_version $JAVA_VERSION) + +echo "Using Java version: $JAVA_VERSION" +echo "Using OpenAPI Generator version: $OPENAPI_VERSION" + +# Run the Gradle command to generate the project +gradle -b meta-generator.gradle generateGradleProject \ + -PjavaVersion="$JAVA_VERSION" \ + -PopenApiGeneratorVersion="$OPENAPI_VERSION" \ + -PoutputDir="$OUTPUT_DIR" \ + -PbasePackage="$BASE_PACKAGE" + +# Navigate to the output directory and run openApiGenerate +cd "$OUTPUT_DIR" +gradle openApiGenerateFullProject + +echo "Project generation and OpenAPI code generation completed successfully!" \ No newline at end of file diff --git a/setup_spring_boot.sh b/setup_spring_boot.sh deleted file mode 100755 index 52458ec..0000000 --- a/setup_spring_boot.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -# Function to log messages -log() { - echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" -} - -# Function to run a command and log its output -run_command() { - log "Running command: $1" - eval $1 - if [ $? -ne 0 ]; then - log "Error: Command failed" - exit 1 - fi - log "Command completed successfully" -} - -# Step 1: Generate Gradle project -log "Step 1: Generating Gradle project" -run_command "gradle -b meta-generator.gradle generateGradleProject" - -# Step 2: Generate OpenAPI code -log "Step 2: Generating OpenAPI code" -run_command "cd ../adapter && gradle openApiGenerateFullProject" - -# Step 3: Run Spring Boot application -log "Step 3: Running Spring Boot application" -run_command "cd ../adapter && gradle bootRun" - -log "Script completed successfully" \ No newline at end of file From 02f83890c5d4f83d8e16d859d744d4842f99bec9 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Wed, 9 Oct 2024 11:42:38 +0530 Subject: [PATCH 06/10] todaycommit --- .DS_Store | Bin 0 -> 8196 bytes JsonSchemaValidator.java | 350 +++++++++++++++++++++++ meta-generator.gradle | 11 +- setup_adapter_spring_boot.sh | 130 ++++++--- spec/capabilities/tax-provider.file.json | 186 ++++++++++++ 5 files changed, 644 insertions(+), 33 deletions(-) create mode 100644 .DS_Store create mode 100644 JsonSchemaValidator.java create mode 100644 spec/capabilities/tax-provider.file.json diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..403f9e2f6bf0fb4394aee40c52e51ec1764790a2 GIT binary patch literal 8196 zcmeHM(P|Sx6g^XeRw7y-3MnY;gO9r4A8bqdpdg3^Ut>2}x0N*;Ork#SyZ_)X_u*-U2(z#Xk;Pk|ADT^7N;A*(A)#-;RZC-m$Q9pix+ zQk3|BQ?|@Z0aL&fFa=BjQ{Z1zfM>SY?v``kk6LXCm;(Q$0`mKiun1-W8;|o+dcTdE(PbCU|vr|0bTJ`tg<$I{!-qL;iwWaZGyrNcz7~=&oCGV`C8|{dDs_#m5 zoin?v=GJ_=m)p?~LiCy|GvWMY6=jWNe@r+gd;6TLRq|8LwXZprvi~femF~Va??7V< zOk+QN654*lOF!KYTb@=YPsGotjGyG_Z9M8J%*xJx>S1OITt@|N_(_lC{(sEl+};CR z$5pgBn*x8PfE$cY#z*APQ~e4qIpq_UH!Nb}*Lc(rR?!~<{Ji;xA=?REB_<2lcw`UF OUIeHNR+$2SRe>KdBpsgs literal 0 HcmV?d00001 diff --git a/JsonSchemaValidator.java b/JsonSchemaValidator.java new file mode 100644 index 0000000..f5ac485 --- /dev/null +++ b/JsonSchemaValidator.java @@ -0,0 +1,350 @@ +import java.io.FileReader; +import java.io.IOException; +import java.util.*; +import java.util.regex.Pattern; + +public class JsonSchemaValidator { + private static int index = 0; + private static String json; + + public static void main(String[] args) { + if (args.length != 2) { + System.out.println("Usage: java JsonSchemaValidator "); + System.exit(1); + } + + String schemaFile = args[0]; + String jsonFile = args[1]; + + try { + String schemaContent = readFile(schemaFile); + String jsonContent = readFile(jsonFile); + + Map schema = parseJson(schemaContent); + Map json = parseJson(jsonContent); + + List errors = validateJson(schema, json, "$"); + if (errors.isEmpty()) { + System.out.println("Success: The JSON content is valid against the schema."); + } else { + System.out.println("Validation Error(s):"); + for (String error : errors) { + System.out.println("- " + error); + } + } + } catch (IOException e) { + System.out.println("Error reading file: " + e.getMessage()); + } catch (JsonParseException e) { + System.out.println("Error parsing JSON: " + e.getMessage()); + } + } + + private static String readFile(String filePath) throws IOException { + StringBuilder content = new StringBuilder(); + try (FileReader reader = new FileReader(filePath)) { + int character; + while ((character = reader.read()) != -1) { + content.append((char) character); + } + } + return content.toString(); + } + + private static Map parseJson(String jsonStr) throws JsonParseException { + json = jsonStr.trim(); + index = 0; + if (!json.startsWith("{") || !json.endsWith("}")) { + throw new JsonParseException("Invalid JSON object"); + } + return parseObject(); + } + + private static Map parseObject() throws JsonParseException { + Map result = new HashMap<>(); + index++; // Skip opening '{' + skipWhitespace(); + + while (index < json.length() && json.charAt(index) != '}') { + String key = parseString(); + skipWhitespace(); + + if (index >= json.length() || json.charAt(index) != ':') { + throw new JsonParseException("Expected ':' after key in object"); + } + index++; // Skip ':' + skipWhitespace(); + + Object value = parseValue(); + result.put(key, value); + + skipWhitespace(); + if (index < json.length() && json.charAt(index) == ',') { + index++; + skipWhitespace(); + } + } + + if (index >= json.length() || json.charAt(index) != '}') { + throw new JsonParseException("Unterminated object"); + } + index++; // Skip closing '}' + return result; + } + + private static Object parseValue() throws JsonParseException { + skipWhitespace(); + char c = json.charAt(index); + if (c == '"') { + return parseString(); + } else if (c == '{') { + return parseObject(); + } else if (c == '[') { + return parseArray(); + } else if (c == 't' && json.startsWith("true", index)) { + index += 4; + return true; + } else if (c == 'f' && json.startsWith("false", index)) { + index += 5; + return false; + } else if (c == 'n' && json.startsWith("null", index)) { + index += 4; + return null; + } else if (c == '-' || (c >= '0' && c <= '9')) { + return parseNumber(); + } + throw new JsonParseException("Unexpected character in JSON: " + c); + } + + private static String parseString() throws JsonParseException { + StringBuilder sb = new StringBuilder(); + index++; // Skip opening quote + while (index < json.length()) { + char c = json.charAt(index++); + if (c == '"') { + return sb.toString(); + } else if (c == '\\') { + if (index >= json.length()) { + throw new JsonParseException("Unterminated string"); + } + c = json.charAt(index++); + switch (c) { + case '"': + case '\\': + case '/': + sb.append(c); + break; + case 'b': + sb.append('\b'); + break; + case 'f': + sb.append('\f'); + break; + case 'n': + sb.append('\n'); + break; + case 'r': + sb.append('\r'); + break; + case 't': + sb.append('\t'); + break; + default: + throw new JsonParseException("Invalid escape sequence: \\" + c); + } + } else { + sb.append(c); + } + } + throw new JsonParseException("Unterminated string"); + } + + private static List parseArray() throws JsonParseException { + List result = new ArrayList<>(); + index++; // Skip opening '[' + skipWhitespace(); + + while (index < json.length() && json.charAt(index) != ']') { + result.add(parseValue()); + skipWhitespace(); + if (index < json.length() && json.charAt(index) == ',') { + index++; + skipWhitespace(); + } + } + + if (index >= json.length() || json.charAt(index) != ']') { + throw new JsonParseException("Unterminated array"); + } + index++; // Skip closing ']' + return result; + } + + private static Double parseNumber() throws JsonParseException { + int start = index; + while (index < json.length()) { + char c = json.charAt(index); + if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E') { + index++; + } else { + break; + } + } + try { + return Double.parseDouble(json.substring(start, index)); + } catch (NumberFormatException e) { + throw new JsonParseException("Invalid number format"); + } + } + + private static void skipWhitespace() { + while (index < json.length() && Character.isWhitespace(json.charAt(index))) { + index++; + } + } + + private static List validateJson(Map schema, Map json, String path) { + List errors = new ArrayList<>(); + + if (schema.containsKey("additionalProperties") && !(boolean)schema.get("additionalProperties")) { + Set allowedProperties = ((Map) schema.get("properties")).keySet(); + for (String key : json.keySet()) { + if (!allowedProperties.contains(key)) { + errors.add(path + ": Additional property '" + key + "' is not allowed"); + } + } + } + + for (Map.Entry entry : schema.entrySet()) { + String key = entry.getKey(); + Object schemaValue = entry.getValue(); + + if (schemaValue instanceof Map) { + Map schemaObject = (Map) schemaValue; + String type = (String) schemaObject.get("type"); + Object jsonValue = json.get(key); + + String currentPath = path + "." + key; + + if (jsonValue == null) { + if (schemaObject.containsKey("required") && (boolean) schemaObject.get("required")) { + errors.add(currentPath + ": Required field is missing"); + } + continue; + } + + switch (type) { + case "string": + if (!(jsonValue instanceof String)) { + errors.add(currentPath + ": Expected string, got " + jsonValue.getClass().getSimpleName()); + } else { + String stringValue = (String) jsonValue; + if (schemaObject.containsKey("pattern")) { + String pattern = (String) schemaObject.get("pattern"); + if (!Pattern.matches(pattern, stringValue)) { + errors.add(currentPath + ": Does not match pattern " + pattern); + } + } + if (schemaObject.containsKey("minLength")) { + int minLength = (int) schemaObject.get("minLength"); + if (stringValue.length() < minLength) { + errors.add(currentPath + ": String length is less than minLength " + minLength); + } + } + if (schemaObject.containsKey("maxLength")) { + int maxLength = (int) schemaObject.get("maxLength"); + if (stringValue.length() > maxLength) { + errors.add(currentPath + ": String length is greater than maxLength " + maxLength); + } + } + if (schemaObject.containsKey("enum")) { + List enumValues = (List) schemaObject.get("enum"); + if (!enumValues.contains(stringValue)) { + errors.add(currentPath + ": Value is not in enum " + enumValues); + } + } + } + break; + case "number": + if (!(jsonValue instanceof Number)) { + errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); + } + break; + + case "integer": + if (!(jsonValue instanceof Number)) { + errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); + } else { + double numberValue = ((Number) jsonValue).doubleValue(); + if (schemaObject.containsKey("minimum")) { + double minimum = ((Number) schemaObject.get("minimum")).doubleValue(); + if (numberValue < minimum) { + errors.add(currentPath + ": Value is less than minimum " + minimum); + } + } + if (schemaObject.containsKey("maximum")) { + double maximum = ((Number) schemaObject.get("maximum")).doubleValue(); + if (numberValue > maximum) { + errors.add(currentPath + ": Value is greater than maximum " + maximum); + } + } + } + break; + case "boolean": + if (!(jsonValue instanceof Boolean)) { + errors.add(currentPath + ": Expected boolean, got " + jsonValue.getClass().getSimpleName()); + } + break; + case "object": + if (!(jsonValue instanceof Map)) { + errors.add(currentPath + ": Expected object, got " + jsonValue.getClass().getSimpleName()); + } else if (schemaObject.containsKey("properties")) { + Map properties = (Map) schemaObject.get("properties"); + errors.addAll(validateJson(properties, (Map) jsonValue, currentPath)); + } + break; + case "array": + if (!(jsonValue instanceof List)) { + errors.add(currentPath + ": Expected array, got " + jsonValue.getClass().getSimpleName()); + } else { + List jsonArray = (List) jsonValue; + if (schemaObject.containsKey("items")) { + Map itemsSchema = (Map) schemaObject.get("items"); + for (int i = 0; i < jsonArray.size(); i++) { + if (itemsSchema.containsKey("type") && itemsSchema.get("type").equals("object")) { + errors.addAll(validateJson(itemsSchema, (Map) jsonArray.get(i), currentPath + "[" + i + "]")); + } + } + } + if (schemaObject.containsKey("minItems")) { + int minItems = (int) schemaObject.get("minItems"); + if (jsonArray.size() < minItems) { + errors.add(currentPath + ": Array has fewer than minItems " + minItems); + } + } + if (schemaObject.containsKey("maxItems")) { + int maxItems = (int) schemaObject.get("maxItems"); + if (jsonArray.size() > maxItems) { + errors.add(currentPath + ": Array has more than maxItems " + maxItems); + } + } + if (schemaObject.containsKey("uniqueItems") && (boolean) schemaObject.get("uniqueItems")) { + Set uniqueItems = new HashSet<>(jsonArray); + if (uniqueItems.size() != jsonArray.size()) { + errors.add(currentPath + ": Array contains non-unique items"); + } + } + } + break; + } + } + } + + return errors; + } + + private static class JsonParseException extends Exception { + public JsonParseException(String message) { + super(message); + } + } +} diff --git a/meta-generator.gradle b/meta-generator.gradle index cbb2f7c..02b2e96 100644 --- a/meta-generator.gradle +++ b/meta-generator.gradle @@ -58,7 +58,15 @@ repositories { 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' @@ -76,7 +84,8 @@ specs.each { spec -> dateLibrary: 'java8', interfaceOnly: 'true', useSpringBoot3: 'true', - groupBy: 'tags' + groupBy: 'tags', + generateApiUtil: 'true' ] } } diff --git a/setup_adapter_spring_boot.sh b/setup_adapter_spring_boot.sh index 87ac95e..cab6447 100755 --- a/setup_adapter_spring_boot.sh +++ b/setup_adapter_spring_boot.sh @@ -1,9 +1,72 @@ -#!/bin/bash +#!/bin/sh + +# Make the script executable +if [ ! -x "$0" ]; then + chmod +x "$0" + exec "$0" "$@" +fi # Default values JAVA_VERSION="11" OUTPUT_DIR="../adapter" -BASE_PACKAGE="com.adapter" +SPEC_FILES="" +PACKAGE_NAME="com.adapter" +SPEC_DIR="spec/spi" + +# 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" # Set SPEC_FILES to the provided argument + 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) +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() { @@ -26,43 +89,46 @@ get_openapi_version() { echo $openapi_version } -# Parse command-line arguments -while [[ $# -gt 0 ]]; do - case $1 in - --java-version) - JAVA_VERSION="$2" - shift 2 - ;; - --output-dir) - OUTPUT_DIR="$2" - shift 2 - ;; - --base-package) - BASE_PACKAGE="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" - exit 1 - ;; - esac -done +# Get OpenAPI Generator version +OPENAPI_VERSION=$(get_openapi_version "$JAVA_VERSION") -# Get OpenAPI Generator version based on Java 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 -echo "Using Java version: $JAVA_VERSION" -echo "Using OpenAPI Generator version: $OPENAPI_VERSION" +# 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 -# Run the Gradle command to generate the project +# Generate Gradle project gradle -b meta-generator.gradle generateGradleProject \ -PjavaVersion="$JAVA_VERSION" \ -PopenApiGeneratorVersion="$OPENAPI_VERSION" \ -PoutputDir="$OUTPUT_DIR" \ - -PbasePackage="$BASE_PACKAGE" + -PbasePackage="$PACKAGE_NAME" \ + -Pspecs="[$SPEC_FILES_LIST]" -# Navigate to the output directory and run openApiGenerate -cd "$OUTPUT_DIR" +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 -echo "Project generation and OpenAPI code generation completed successfully!" \ No newline at end of file +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/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 From d340f06017962e29fa7b39a2ce74c648111a6edd Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Thu, 10 Oct 2024 16:44:24 +0530 Subject: [PATCH 07/10] todaycommit --- JsonSchemaValidator.java | 0 build.gradle | 7 +- json_validation.sh | 61 ++++++ spec/JsonSchemaValidator.java | 350 ++++++++++++++++++++++++++++++++++ 4 files changed, 415 insertions(+), 3 deletions(-) mode change 100644 => 100755 JsonSchemaValidator.java create mode 100755 json_validation.sh create mode 100755 spec/JsonSchemaValidator.java diff --git a/JsonSchemaValidator.java b/JsonSchemaValidator.java old mode 100644 new mode 100755 diff --git a/build.gradle b/build.gradle index ab5f15c..0738fb4 100644 --- a/build.gradle +++ b/build.gradle @@ -116,7 +116,8 @@ dependencies { 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' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' + implementation 'com.github.erosb:everit-json-schema:1.14.1' + implementation files('libs/everit-json-schema-1.14.1.jar') + } \ No newline at end of file diff --git a/json_validation.sh b/json_validation.sh new file mode 100755 index 0000000..c7be1be --- /dev/null +++ b/json_validation.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Make the script executable if it's not already +if [ ! -x "$0" ]; then + chmod +x "$0" + exec "$0" "$@" +fi + +# Check if the correct number of arguments is provided +if [ "$#" -ne 3 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Assign arguments to variables +JAVA_FILE="$1" +SCHEMA_PATH="$2" +JSON_PATH="$3" + +# Check if Java is installed +if ! command -v java &> /dev/null; then + echo "Error: Java is not installed or not in the system PATH." + exit 1 +fi + +# Check if the Java file exists +if [ ! -f "$JAVA_FILE" ]; then + echo "Error: Java file '$JAVA_FILE' does not exist." + exit 1 +fi + +# Check if the schema file exists +if [ ! -f "$SCHEMA_PATH" ]; then + echo "Error: Schema file '$SCHEMA_PATH' does not exist." + exit 1 +fi + +# Check if the JSON file exists +if [ ! -f "$JSON_PATH" ]; then + echo "Error: JSON file '$JSON_PATH' does not exist." + exit 1 +fi + +# Compile the Java file +echo "Compiling Java file..." +javac "$JAVA_FILE" + +if [ $? -ne 0 ]; then + echo "Error: Compilation failed." + exit 1 +fi + +# Extract the class name from the Java file name +CLASS_NAME=$(basename "$JAVA_FILE" .java) + +# Run the Java program +echo "Running JSON validation..." +java -cp .:$(dirname "$JAVA_FILE") "$CLASS_NAME" "$SCHEMA_PATH" "$JSON_PATH" + +# Clean up the compiled class file +rm -f "$(dirname "$JAVA_FILE")/$CLASS_NAME.class" \ No newline at end of file diff --git a/spec/JsonSchemaValidator.java b/spec/JsonSchemaValidator.java new file mode 100755 index 0000000..f5ac485 --- /dev/null +++ b/spec/JsonSchemaValidator.java @@ -0,0 +1,350 @@ +import java.io.FileReader; +import java.io.IOException; +import java.util.*; +import java.util.regex.Pattern; + +public class JsonSchemaValidator { + private static int index = 0; + private static String json; + + public static void main(String[] args) { + if (args.length != 2) { + System.out.println("Usage: java JsonSchemaValidator "); + System.exit(1); + } + + String schemaFile = args[0]; + String jsonFile = args[1]; + + try { + String schemaContent = readFile(schemaFile); + String jsonContent = readFile(jsonFile); + + Map schema = parseJson(schemaContent); + Map json = parseJson(jsonContent); + + List errors = validateJson(schema, json, "$"); + if (errors.isEmpty()) { + System.out.println("Success: The JSON content is valid against the schema."); + } else { + System.out.println("Validation Error(s):"); + for (String error : errors) { + System.out.println("- " + error); + } + } + } catch (IOException e) { + System.out.println("Error reading file: " + e.getMessage()); + } catch (JsonParseException e) { + System.out.println("Error parsing JSON: " + e.getMessage()); + } + } + + private static String readFile(String filePath) throws IOException { + StringBuilder content = new StringBuilder(); + try (FileReader reader = new FileReader(filePath)) { + int character; + while ((character = reader.read()) != -1) { + content.append((char) character); + } + } + return content.toString(); + } + + private static Map parseJson(String jsonStr) throws JsonParseException { + json = jsonStr.trim(); + index = 0; + if (!json.startsWith("{") || !json.endsWith("}")) { + throw new JsonParseException("Invalid JSON object"); + } + return parseObject(); + } + + private static Map parseObject() throws JsonParseException { + Map result = new HashMap<>(); + index++; // Skip opening '{' + skipWhitespace(); + + while (index < json.length() && json.charAt(index) != '}') { + String key = parseString(); + skipWhitespace(); + + if (index >= json.length() || json.charAt(index) != ':') { + throw new JsonParseException("Expected ':' after key in object"); + } + index++; // Skip ':' + skipWhitespace(); + + Object value = parseValue(); + result.put(key, value); + + skipWhitespace(); + if (index < json.length() && json.charAt(index) == ',') { + index++; + skipWhitespace(); + } + } + + if (index >= json.length() || json.charAt(index) != '}') { + throw new JsonParseException("Unterminated object"); + } + index++; // Skip closing '}' + return result; + } + + private static Object parseValue() throws JsonParseException { + skipWhitespace(); + char c = json.charAt(index); + if (c == '"') { + return parseString(); + } else if (c == '{') { + return parseObject(); + } else if (c == '[') { + return parseArray(); + } else if (c == 't' && json.startsWith("true", index)) { + index += 4; + return true; + } else if (c == 'f' && json.startsWith("false", index)) { + index += 5; + return false; + } else if (c == 'n' && json.startsWith("null", index)) { + index += 4; + return null; + } else if (c == '-' || (c >= '0' && c <= '9')) { + return parseNumber(); + } + throw new JsonParseException("Unexpected character in JSON: " + c); + } + + private static String parseString() throws JsonParseException { + StringBuilder sb = new StringBuilder(); + index++; // Skip opening quote + while (index < json.length()) { + char c = json.charAt(index++); + if (c == '"') { + return sb.toString(); + } else if (c == '\\') { + if (index >= json.length()) { + throw new JsonParseException("Unterminated string"); + } + c = json.charAt(index++); + switch (c) { + case '"': + case '\\': + case '/': + sb.append(c); + break; + case 'b': + sb.append('\b'); + break; + case 'f': + sb.append('\f'); + break; + case 'n': + sb.append('\n'); + break; + case 'r': + sb.append('\r'); + break; + case 't': + sb.append('\t'); + break; + default: + throw new JsonParseException("Invalid escape sequence: \\" + c); + } + } else { + sb.append(c); + } + } + throw new JsonParseException("Unterminated string"); + } + + private static List parseArray() throws JsonParseException { + List result = new ArrayList<>(); + index++; // Skip opening '[' + skipWhitespace(); + + while (index < json.length() && json.charAt(index) != ']') { + result.add(parseValue()); + skipWhitespace(); + if (index < json.length() && json.charAt(index) == ',') { + index++; + skipWhitespace(); + } + } + + if (index >= json.length() || json.charAt(index) != ']') { + throw new JsonParseException("Unterminated array"); + } + index++; // Skip closing ']' + return result; + } + + private static Double parseNumber() throws JsonParseException { + int start = index; + while (index < json.length()) { + char c = json.charAt(index); + if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E') { + index++; + } else { + break; + } + } + try { + return Double.parseDouble(json.substring(start, index)); + } catch (NumberFormatException e) { + throw new JsonParseException("Invalid number format"); + } + } + + private static void skipWhitespace() { + while (index < json.length() && Character.isWhitespace(json.charAt(index))) { + index++; + } + } + + private static List validateJson(Map schema, Map json, String path) { + List errors = new ArrayList<>(); + + if (schema.containsKey("additionalProperties") && !(boolean)schema.get("additionalProperties")) { + Set allowedProperties = ((Map) schema.get("properties")).keySet(); + for (String key : json.keySet()) { + if (!allowedProperties.contains(key)) { + errors.add(path + ": Additional property '" + key + "' is not allowed"); + } + } + } + + for (Map.Entry entry : schema.entrySet()) { + String key = entry.getKey(); + Object schemaValue = entry.getValue(); + + if (schemaValue instanceof Map) { + Map schemaObject = (Map) schemaValue; + String type = (String) schemaObject.get("type"); + Object jsonValue = json.get(key); + + String currentPath = path + "." + key; + + if (jsonValue == null) { + if (schemaObject.containsKey("required") && (boolean) schemaObject.get("required")) { + errors.add(currentPath + ": Required field is missing"); + } + continue; + } + + switch (type) { + case "string": + if (!(jsonValue instanceof String)) { + errors.add(currentPath + ": Expected string, got " + jsonValue.getClass().getSimpleName()); + } else { + String stringValue = (String) jsonValue; + if (schemaObject.containsKey("pattern")) { + String pattern = (String) schemaObject.get("pattern"); + if (!Pattern.matches(pattern, stringValue)) { + errors.add(currentPath + ": Does not match pattern " + pattern); + } + } + if (schemaObject.containsKey("minLength")) { + int minLength = (int) schemaObject.get("minLength"); + if (stringValue.length() < minLength) { + errors.add(currentPath + ": String length is less than minLength " + minLength); + } + } + if (schemaObject.containsKey("maxLength")) { + int maxLength = (int) schemaObject.get("maxLength"); + if (stringValue.length() > maxLength) { + errors.add(currentPath + ": String length is greater than maxLength " + maxLength); + } + } + if (schemaObject.containsKey("enum")) { + List enumValues = (List) schemaObject.get("enum"); + if (!enumValues.contains(stringValue)) { + errors.add(currentPath + ": Value is not in enum " + enumValues); + } + } + } + break; + case "number": + if (!(jsonValue instanceof Number)) { + errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); + } + break; + + case "integer": + if (!(jsonValue instanceof Number)) { + errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); + } else { + double numberValue = ((Number) jsonValue).doubleValue(); + if (schemaObject.containsKey("minimum")) { + double minimum = ((Number) schemaObject.get("minimum")).doubleValue(); + if (numberValue < minimum) { + errors.add(currentPath + ": Value is less than minimum " + minimum); + } + } + if (schemaObject.containsKey("maximum")) { + double maximum = ((Number) schemaObject.get("maximum")).doubleValue(); + if (numberValue > maximum) { + errors.add(currentPath + ": Value is greater than maximum " + maximum); + } + } + } + break; + case "boolean": + if (!(jsonValue instanceof Boolean)) { + errors.add(currentPath + ": Expected boolean, got " + jsonValue.getClass().getSimpleName()); + } + break; + case "object": + if (!(jsonValue instanceof Map)) { + errors.add(currentPath + ": Expected object, got " + jsonValue.getClass().getSimpleName()); + } else if (schemaObject.containsKey("properties")) { + Map properties = (Map) schemaObject.get("properties"); + errors.addAll(validateJson(properties, (Map) jsonValue, currentPath)); + } + break; + case "array": + if (!(jsonValue instanceof List)) { + errors.add(currentPath + ": Expected array, got " + jsonValue.getClass().getSimpleName()); + } else { + List jsonArray = (List) jsonValue; + if (schemaObject.containsKey("items")) { + Map itemsSchema = (Map) schemaObject.get("items"); + for (int i = 0; i < jsonArray.size(); i++) { + if (itemsSchema.containsKey("type") && itemsSchema.get("type").equals("object")) { + errors.addAll(validateJson(itemsSchema, (Map) jsonArray.get(i), currentPath + "[" + i + "]")); + } + } + } + if (schemaObject.containsKey("minItems")) { + int minItems = (int) schemaObject.get("minItems"); + if (jsonArray.size() < minItems) { + errors.add(currentPath + ": Array has fewer than minItems " + minItems); + } + } + if (schemaObject.containsKey("maxItems")) { + int maxItems = (int) schemaObject.get("maxItems"); + if (jsonArray.size() > maxItems) { + errors.add(currentPath + ": Array has more than maxItems " + maxItems); + } + } + if (schemaObject.containsKey("uniqueItems") && (boolean) schemaObject.get("uniqueItems")) { + Set uniqueItems = new HashSet<>(jsonArray); + if (uniqueItems.size() != jsonArray.size()) { + errors.add(currentPath + ": Array contains non-unique items"); + } + } + } + break; + } + } + } + + return errors; + } + + private static class JsonParseException extends Exception { + public JsonParseException(String message) { + super(message); + } + } +} From 48e7c754ddabfe1a16175ca8381e51e3c0fa1762 Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Fri, 11 Oct 2024 10:52:49 +0530 Subject: [PATCH 08/10] initialcommit --- JsonSchemaValidator.java | 399 ++++++++-------------------------- build.gradle | 35 ++- json_validation.sh | 69 ++---- spec/JsonSchemaValidator.java | 350 ----------------------------- 4 files changed, 141 insertions(+), 712 deletions(-) delete mode 100755 spec/JsonSchemaValidator.java diff --git a/JsonSchemaValidator.java b/JsonSchemaValidator.java index f5ac485..836fa73 100755 --- a/JsonSchemaValidator.java +++ b/JsonSchemaValidator.java @@ -1,350 +1,129 @@ -import java.io.FileReader; +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.util.*; -import java.util.regex.Pattern; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; public class JsonSchemaValidator { - private static int index = 0; - private static String json; - public static void main(String[] args) { if (args.length != 2) { System.out.println("Usage: java JsonSchemaValidator "); System.exit(1); } - String schemaFile = args[0]; - String jsonFile = args[1]; + String schemaPath = args[0]; + String jsonPath = args[1]; try { - String schemaContent = readFile(schemaFile); - String jsonContent = readFile(jsonFile); - - Map schema = parseJson(schemaContent); - Map json = parseJson(jsonContent); - - List errors = validateJson(schema, json, "$"); - if (errors.isEmpty()) { - System.out.println("Success: The JSON content is valid against the schema."); - } else { - System.out.println("Validation Error(s):"); - for (String error : errors) { - System.out.println("- " + error); - } - } - } catch (IOException e) { - System.out.println("Error reading file: " + e.getMessage()); - } catch (JsonParseException e) { - System.out.println("Error parsing JSON: " + e.getMessage()); - } - } - - private static String readFile(String filePath) throws IOException { - StringBuilder content = new StringBuilder(); - try (FileReader reader = new FileReader(filePath)) { - int character; - while ((character = reader.read()) != -1) { - content.append((char) character); - } - } - return content.toString(); - } - - private static Map parseJson(String jsonStr) throws JsonParseException { - json = jsonStr.trim(); - index = 0; - if (!json.startsWith("{") || !json.endsWith("}")) { - throw new JsonParseException("Invalid JSON object"); - } - return parseObject(); - } - - private static Map parseObject() throws JsonParseException { - Map result = new HashMap<>(); - index++; // Skip opening '{' - skipWhitespace(); + String jsonContent = new String(Files.readAllBytes(Paths.get(jsonPath))); - while (index < json.length() && json.charAt(index) != '}') { - String key = parseString(); - skipWhitespace(); + JSONObject jsonSchema = new JSONObject(new JSONTokener(new FileInputStream(schemaPath))); + JSONObject jsonSubject = new JSONObject(jsonContent); - if (index >= json.length() || json.charAt(index) != ':') { - throw new JsonParseException("Expected ':' after key in object"); - } - index++; // Skip ':' - skipWhitespace(); - - Object value = parseValue(); - result.put(key, value); - - skipWhitespace(); - if (index < json.length() && json.charAt(index) == ',') { - index++; - skipWhitespace(); - } - } + Schema schema = SchemaLoader.load(jsonSchema); + schema.validate(jsonSubject); - if (index >= json.length() || json.charAt(index) != '}') { - throw new JsonParseException("Unterminated object"); - } - index++; // Skip closing '}' - return result; - } + System.out.println("Validation successful! The JSON is valid against the schema."); - private static Object parseValue() throws JsonParseException { - skipWhitespace(); - char c = json.charAt(index); - if (c == '"') { - return parseString(); - } else if (c == '{') { - return parseObject(); - } else if (c == '[') { - return parseArray(); - } else if (c == 't' && json.startsWith("true", index)) { - index += 4; - return true; - } else if (c == 'f' && json.startsWith("false", index)) { - index += 5; - return false; - } else if (c == 'n' && json.startsWith("null", index)) { - index += 4; - return null; - } else if (c == '-' || (c >= '0' && c <= '9')) { - return parseNumber(); + } 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()); } - throw new JsonParseException("Unexpected character in JSON: " + c); } - private static String parseString() throws JsonParseException { - StringBuilder sb = new StringBuilder(); - index++; // Skip opening quote - while (index < json.length()) { - char c = json.charAt(index++); - if (c == '"') { - return sb.toString(); - } else if (c == '\\') { - if (index >= json.length()) { - throw new JsonParseException("Unterminated string"); - } - c = json.charAt(index++); - switch (c) { - case '"': - case '\\': - case '/': - sb.append(c); - break; - case 'b': - sb.append('\b'); - break; - case 'f': - sb.append('\f'); - break; - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 't': - sb.append('\t'); - break; - default: - throw new JsonParseException("Invalid escape sequence: \\" + c); - } - } else { - sb.append(c); - } + 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)); } - throw new JsonParseException("Unterminated string"); + System.out.println("\nDetailed error information:"); + printValidationErrorDetails(ve, jsonPath, 0); } - private static List parseArray() throws JsonParseException { - List result = new ArrayList<>(); - index++; // Skip opening '[' - skipWhitespace(); + 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()); - while (index < json.length() && json.charAt(index) != ']') { - result.add(parseValue()); - skipWhitespace(); - if (index < json.length() && json.charAt(index) == ',') { - index++; - skipWhitespace(); + 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()); } - if (index >= json.length() || json.charAt(index) != ']') { - throw new JsonParseException("Unterminated array"); - } - index++; // Skip closing ']' - return result; - } - - private static Double parseNumber() throws JsonParseException { - int start = index; - while (index < json.length()) { - char c = json.charAt(index); - if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E') { - index++; - } else { - break; + List causingExceptions = ve.getCausingExceptions(); + if (!causingExceptions.isEmpty()) { + System.out.printf("%sNested errors:%n", indent); + for (ValidationException cause : causingExceptions) { + printValidationErrorDetails(cause, jsonPath, depth + 1); } } - try { - return Double.parseDouble(json.substring(start, index)); - } catch (NumberFormatException e) { - throw new JsonParseException("Invalid number format"); - } } - private static void skipWhitespace() { - while (index < json.length() && Character.isWhitespace(json.charAt(index))) { - index++; - } - } + private static void printJSONParsingError(JSONException je, String jsonPath) { + System.out.println(je.getMessage()); - private static List validateJson(Map schema, Map json, String path) { - List errors = new ArrayList<>(); - - if (schema.containsKey("additionalProperties") && !(boolean)schema.get("additionalProperties")) { - Set allowedProperties = ((Map) schema.get("properties")).keySet(); - for (String key : json.keySet()) { - if (!allowedProperties.contains(key)) { - errors.add(path + ": Additional property '" + key + "' is not allowed"); + 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()); } + } - for (Map.Entry entry : schema.entrySet()) { - String key = entry.getKey(); - Object schemaValue = entry.getValue(); - - if (schemaValue instanceof Map) { - Map schemaObject = (Map) schemaValue; - String type = (String) schemaObject.get("type"); - Object jsonValue = json.get(key); - - String currentPath = path + "." + key; - - if (jsonValue == null) { - if (schemaObject.containsKey("required") && (boolean) schemaObject.get("required")) { - errors.add(currentPath + ": Required field is missing"); + 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 } - continue; - } - - switch (type) { - case "string": - if (!(jsonValue instanceof String)) { - errors.add(currentPath + ": Expected string, got " + jsonValue.getClass().getSimpleName()); - } else { - String stringValue = (String) jsonValue; - if (schemaObject.containsKey("pattern")) { - String pattern = (String) schemaObject.get("pattern"); - if (!Pattern.matches(pattern, stringValue)) { - errors.add(currentPath + ": Does not match pattern " + pattern); - } - } - if (schemaObject.containsKey("minLength")) { - int minLength = (int) schemaObject.get("minLength"); - if (stringValue.length() < minLength) { - errors.add(currentPath + ": String length is less than minLength " + minLength); - } - } - if (schemaObject.containsKey("maxLength")) { - int maxLength = (int) schemaObject.get("maxLength"); - if (stringValue.length() > maxLength) { - errors.add(currentPath + ": String length is greater than maxLength " + maxLength); - } - } - if (schemaObject.containsKey("enum")) { - List enumValues = (List) schemaObject.get("enum"); - if (!enumValues.contains(stringValue)) { - errors.add(currentPath + ": Value is not in enum " + enumValues); - } - } - } - break; - case "number": - if (!(jsonValue instanceof Number)) { - errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); - } - break; - - case "integer": - if (!(jsonValue instanceof Number)) { - errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); - } else { - double numberValue = ((Number) jsonValue).doubleValue(); - if (schemaObject.containsKey("minimum")) { - double minimum = ((Number) schemaObject.get("minimum")).doubleValue(); - if (numberValue < minimum) { - errors.add(currentPath + ": Value is less than minimum " + minimum); - } - } - if (schemaObject.containsKey("maximum")) { - double maximum = ((Number) schemaObject.get("maximum")).doubleValue(); - if (numberValue > maximum) { - errors.add(currentPath + ": Value is greater than maximum " + maximum); - } - } - } - break; - case "boolean": - if (!(jsonValue instanceof Boolean)) { - errors.add(currentPath + ": Expected boolean, got " + jsonValue.getClass().getSimpleName()); - } - break; - case "object": - if (!(jsonValue instanceof Map)) { - errors.add(currentPath + ": Expected object, got " + jsonValue.getClass().getSimpleName()); - } else if (schemaObject.containsKey("properties")) { - Map properties = (Map) schemaObject.get("properties"); - errors.addAll(validateJson(properties, (Map) jsonValue, currentPath)); - } - break; - case "array": - if (!(jsonValue instanceof List)) { - errors.add(currentPath + ": Expected array, got " + jsonValue.getClass().getSimpleName()); - } else { - List jsonArray = (List) jsonValue; - if (schemaObject.containsKey("items")) { - Map itemsSchema = (Map) schemaObject.get("items"); - for (int i = 0; i < jsonArray.size(); i++) { - if (itemsSchema.containsKey("type") && itemsSchema.get("type").equals("object")) { - errors.addAll(validateJson(itemsSchema, (Map) jsonArray.get(i), currentPath + "[" + i + "]")); - } - } - } - if (schemaObject.containsKey("minItems")) { - int minItems = (int) schemaObject.get("minItems"); - if (jsonArray.size() < minItems) { - errors.add(currentPath + ": Array has fewer than minItems " + minItems); - } - } - if (schemaObject.containsKey("maxItems")) { - int maxItems = (int) schemaObject.get("maxItems"); - if (jsonArray.size() > maxItems) { - errors.add(currentPath + ": Array has more than maxItems " + maxItems); - } - } - if (schemaObject.containsKey("uniqueItems") && (boolean) schemaObject.get("uniqueItems")) { - Set uniqueItems = new HashSet<>(jsonArray); - if (uniqueItems.size() != jsonArray.size()) { - errors.add(currentPath + ": Array contains non-unique items"); - } - } - } - break; } } + if (line.contains("{")) nestingLevel++; + if (line.contains("}")) nestingLevel--; } - - return errors; - } - - private static class JsonParseException extends Exception { - public JsonParseException(String message) { - super(message); - } + return -1; // Path not found } } diff --git a/build.gradle b/build.gradle index 0738fb4..cb31c00 100644 --- a/build.gradle +++ b/build.gradle @@ -5,6 +5,7 @@ import org.openapitools.generator.gradle.plugin.tasks.GenerateTask plugins { id "idea" id "java" + id "application" id 'maven-publish' id "org.openapi.generator" version "7.0.1" } @@ -12,17 +13,41 @@ plugins { group build_group version build_version +sourceSets { + main { + java { + srcDirs = ['.'] + } + } +} + sourceSets { main.resources.srcDirs = ['spec/capabilities'] } +jar { + archiveBaseName = 'json-schema-validator' + archiveVersion = '1.0' + manifest { + attributes 'Main-Class': 'JsonSchemaValidator' + } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from { + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } + } +} + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + repositories { mavenCentral() } java { - sourceCompatibility = "17" - targetCompatibility = "17" + sourceCompatibility = "11" + targetCompatibility = "11" } def loadSpecConfig() { @@ -118,6 +143,10 @@ dependencies { implementation 'jakarta.annotation:jakarta.annotation-api:1.3.5' implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' implementation 'com.github.erosb:everit-json-schema:1.14.1' - implementation files('libs/everit-json-schema-1.14.1.jar') + implementation 'com.github.erosb:everit-json-schema:1.14.2' + +} +application { + mainClass = 'JsonSchemaValidator' } \ No newline at end of file diff --git a/json_validation.sh b/json_validation.sh index c7be1be..2b095c2 100755 --- a/json_validation.sh +++ b/json_validation.sh @@ -1,61 +1,32 @@ #!/bin/bash -# Make the script executable if it's not already -if [ ! -x "$0" ]; then - chmod +x "$0" - exec "$0" "$@" -fi - -# Check if the correct number of arguments is provided -if [ "$#" -ne 3 ]; then - echo "Usage: $0 " - exit 1 -fi - -# Assign arguments to variables -JAVA_FILE="$1" -SCHEMA_PATH="$2" -JSON_PATH="$3" - -# Check if Java is installed -if ! command -v java &> /dev/null; then - echo "Error: Java is not installed or not in the system PATH." - exit 1 -fi +# Make this script executable +chmod +x "$0" -# Check if the Java file exists -if [ ! -f "$JAVA_FILE" ]; then - echo "Error: Java file '$JAVA_FILE' does not exist." +# Check if Gradle is installed +if ! command -v gradle &> /dev/null +then + echo "Gradle is not installed. Please install Gradle and try again." exit 1 fi -# Check if the schema file exists -if [ ! -f "$SCHEMA_PATH" ]; then - echo "Error: Schema file '$SCHEMA_PATH' does not exist." - exit 1 -fi +# Clean and build the project +gradle clean build -# Check if the JSON file exists -if [ ! -f "$JSON_PATH" ]; then - echo "Error: JSON file '$JSON_PATH' does not exist." - exit 1 -fi - -# Compile the Java file -echo "Compiling Java file..." -javac "$JAVA_FILE" +# Find the JAR file +JAR_FILE=$(find build/libs -name "json-schema-validator*.jar" | head -n 1) -if [ $? -ne 0 ]; then - echo "Error: Compilation failed." +if [ -z "$JAR_FILE" ]; then + echo "Error: Could not find the JAR file. Build may have failed." exit 1 fi -# Extract the class name from the Java file name -CLASS_NAME=$(basename "$JAVA_FILE" .java) - -# Run the Java program -echo "Running JSON validation..." -java -cp .:$(dirname "$JAVA_FILE") "$CLASS_NAME" "$SCHEMA_PATH" "$JSON_PATH" +# Run the Java application using the generated JAR file +java -jar "$JAR_FILE" "$1" "$2" -# Clean up the compiled class file -rm -f "$(dirname "$JAVA_FILE")/$CLASS_NAME.class" \ No newline at end of file +# 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/spec/JsonSchemaValidator.java b/spec/JsonSchemaValidator.java deleted file mode 100755 index f5ac485..0000000 --- a/spec/JsonSchemaValidator.java +++ /dev/null @@ -1,350 +0,0 @@ -import java.io.FileReader; -import java.io.IOException; -import java.util.*; -import java.util.regex.Pattern; - -public class JsonSchemaValidator { - private static int index = 0; - private static String json; - - public static void main(String[] args) { - if (args.length != 2) { - System.out.println("Usage: java JsonSchemaValidator "); - System.exit(1); - } - - String schemaFile = args[0]; - String jsonFile = args[1]; - - try { - String schemaContent = readFile(schemaFile); - String jsonContent = readFile(jsonFile); - - Map schema = parseJson(schemaContent); - Map json = parseJson(jsonContent); - - List errors = validateJson(schema, json, "$"); - if (errors.isEmpty()) { - System.out.println("Success: The JSON content is valid against the schema."); - } else { - System.out.println("Validation Error(s):"); - for (String error : errors) { - System.out.println("- " + error); - } - } - } catch (IOException e) { - System.out.println("Error reading file: " + e.getMessage()); - } catch (JsonParseException e) { - System.out.println("Error parsing JSON: " + e.getMessage()); - } - } - - private static String readFile(String filePath) throws IOException { - StringBuilder content = new StringBuilder(); - try (FileReader reader = new FileReader(filePath)) { - int character; - while ((character = reader.read()) != -1) { - content.append((char) character); - } - } - return content.toString(); - } - - private static Map parseJson(String jsonStr) throws JsonParseException { - json = jsonStr.trim(); - index = 0; - if (!json.startsWith("{") || !json.endsWith("}")) { - throw new JsonParseException("Invalid JSON object"); - } - return parseObject(); - } - - private static Map parseObject() throws JsonParseException { - Map result = new HashMap<>(); - index++; // Skip opening '{' - skipWhitespace(); - - while (index < json.length() && json.charAt(index) != '}') { - String key = parseString(); - skipWhitespace(); - - if (index >= json.length() || json.charAt(index) != ':') { - throw new JsonParseException("Expected ':' after key in object"); - } - index++; // Skip ':' - skipWhitespace(); - - Object value = parseValue(); - result.put(key, value); - - skipWhitespace(); - if (index < json.length() && json.charAt(index) == ',') { - index++; - skipWhitespace(); - } - } - - if (index >= json.length() || json.charAt(index) != '}') { - throw new JsonParseException("Unterminated object"); - } - index++; // Skip closing '}' - return result; - } - - private static Object parseValue() throws JsonParseException { - skipWhitespace(); - char c = json.charAt(index); - if (c == '"') { - return parseString(); - } else if (c == '{') { - return parseObject(); - } else if (c == '[') { - return parseArray(); - } else if (c == 't' && json.startsWith("true", index)) { - index += 4; - return true; - } else if (c == 'f' && json.startsWith("false", index)) { - index += 5; - return false; - } else if (c == 'n' && json.startsWith("null", index)) { - index += 4; - return null; - } else if (c == '-' || (c >= '0' && c <= '9')) { - return parseNumber(); - } - throw new JsonParseException("Unexpected character in JSON: " + c); - } - - private static String parseString() throws JsonParseException { - StringBuilder sb = new StringBuilder(); - index++; // Skip opening quote - while (index < json.length()) { - char c = json.charAt(index++); - if (c == '"') { - return sb.toString(); - } else if (c == '\\') { - if (index >= json.length()) { - throw new JsonParseException("Unterminated string"); - } - c = json.charAt(index++); - switch (c) { - case '"': - case '\\': - case '/': - sb.append(c); - break; - case 'b': - sb.append('\b'); - break; - case 'f': - sb.append('\f'); - break; - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 't': - sb.append('\t'); - break; - default: - throw new JsonParseException("Invalid escape sequence: \\" + c); - } - } else { - sb.append(c); - } - } - throw new JsonParseException("Unterminated string"); - } - - private static List parseArray() throws JsonParseException { - List result = new ArrayList<>(); - index++; // Skip opening '[' - skipWhitespace(); - - while (index < json.length() && json.charAt(index) != ']') { - result.add(parseValue()); - skipWhitespace(); - if (index < json.length() && json.charAt(index) == ',') { - index++; - skipWhitespace(); - } - } - - if (index >= json.length() || json.charAt(index) != ']') { - throw new JsonParseException("Unterminated array"); - } - index++; // Skip closing ']' - return result; - } - - private static Double parseNumber() throws JsonParseException { - int start = index; - while (index < json.length()) { - char c = json.charAt(index); - if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || c == 'e' || c == 'E') { - index++; - } else { - break; - } - } - try { - return Double.parseDouble(json.substring(start, index)); - } catch (NumberFormatException e) { - throw new JsonParseException("Invalid number format"); - } - } - - private static void skipWhitespace() { - while (index < json.length() && Character.isWhitespace(json.charAt(index))) { - index++; - } - } - - private static List validateJson(Map schema, Map json, String path) { - List errors = new ArrayList<>(); - - if (schema.containsKey("additionalProperties") && !(boolean)schema.get("additionalProperties")) { - Set allowedProperties = ((Map) schema.get("properties")).keySet(); - for (String key : json.keySet()) { - if (!allowedProperties.contains(key)) { - errors.add(path + ": Additional property '" + key + "' is not allowed"); - } - } - } - - for (Map.Entry entry : schema.entrySet()) { - String key = entry.getKey(); - Object schemaValue = entry.getValue(); - - if (schemaValue instanceof Map) { - Map schemaObject = (Map) schemaValue; - String type = (String) schemaObject.get("type"); - Object jsonValue = json.get(key); - - String currentPath = path + "." + key; - - if (jsonValue == null) { - if (schemaObject.containsKey("required") && (boolean) schemaObject.get("required")) { - errors.add(currentPath + ": Required field is missing"); - } - continue; - } - - switch (type) { - case "string": - if (!(jsonValue instanceof String)) { - errors.add(currentPath + ": Expected string, got " + jsonValue.getClass().getSimpleName()); - } else { - String stringValue = (String) jsonValue; - if (schemaObject.containsKey("pattern")) { - String pattern = (String) schemaObject.get("pattern"); - if (!Pattern.matches(pattern, stringValue)) { - errors.add(currentPath + ": Does not match pattern " + pattern); - } - } - if (schemaObject.containsKey("minLength")) { - int minLength = (int) schemaObject.get("minLength"); - if (stringValue.length() < minLength) { - errors.add(currentPath + ": String length is less than minLength " + minLength); - } - } - if (schemaObject.containsKey("maxLength")) { - int maxLength = (int) schemaObject.get("maxLength"); - if (stringValue.length() > maxLength) { - errors.add(currentPath + ": String length is greater than maxLength " + maxLength); - } - } - if (schemaObject.containsKey("enum")) { - List enumValues = (List) schemaObject.get("enum"); - if (!enumValues.contains(stringValue)) { - errors.add(currentPath + ": Value is not in enum " + enumValues); - } - } - } - break; - case "number": - if (!(jsonValue instanceof Number)) { - errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); - } - break; - - case "integer": - if (!(jsonValue instanceof Number)) { - errors.add(currentPath + ": Expected number, got " + jsonValue.getClass().getSimpleName()); - } else { - double numberValue = ((Number) jsonValue).doubleValue(); - if (schemaObject.containsKey("minimum")) { - double minimum = ((Number) schemaObject.get("minimum")).doubleValue(); - if (numberValue < minimum) { - errors.add(currentPath + ": Value is less than minimum " + minimum); - } - } - if (schemaObject.containsKey("maximum")) { - double maximum = ((Number) schemaObject.get("maximum")).doubleValue(); - if (numberValue > maximum) { - errors.add(currentPath + ": Value is greater than maximum " + maximum); - } - } - } - break; - case "boolean": - if (!(jsonValue instanceof Boolean)) { - errors.add(currentPath + ": Expected boolean, got " + jsonValue.getClass().getSimpleName()); - } - break; - case "object": - if (!(jsonValue instanceof Map)) { - errors.add(currentPath + ": Expected object, got " + jsonValue.getClass().getSimpleName()); - } else if (schemaObject.containsKey("properties")) { - Map properties = (Map) schemaObject.get("properties"); - errors.addAll(validateJson(properties, (Map) jsonValue, currentPath)); - } - break; - case "array": - if (!(jsonValue instanceof List)) { - errors.add(currentPath + ": Expected array, got " + jsonValue.getClass().getSimpleName()); - } else { - List jsonArray = (List) jsonValue; - if (schemaObject.containsKey("items")) { - Map itemsSchema = (Map) schemaObject.get("items"); - for (int i = 0; i < jsonArray.size(); i++) { - if (itemsSchema.containsKey("type") && itemsSchema.get("type").equals("object")) { - errors.addAll(validateJson(itemsSchema, (Map) jsonArray.get(i), currentPath + "[" + i + "]")); - } - } - } - if (schemaObject.containsKey("minItems")) { - int minItems = (int) schemaObject.get("minItems"); - if (jsonArray.size() < minItems) { - errors.add(currentPath + ": Array has fewer than minItems " + minItems); - } - } - if (schemaObject.containsKey("maxItems")) { - int maxItems = (int) schemaObject.get("maxItems"); - if (jsonArray.size() > maxItems) { - errors.add(currentPath + ": Array has more than maxItems " + maxItems); - } - } - if (schemaObject.containsKey("uniqueItems") && (boolean) schemaObject.get("uniqueItems")) { - Set uniqueItems = new HashSet<>(jsonArray); - if (uniqueItems.size() != jsonArray.size()) { - errors.add(currentPath + ": Array contains non-unique items"); - } - } - } - break; - } - } - } - - return errors; - } - - private static class JsonParseException extends Exception { - public JsonParseException(String message) { - super(message); - } - } -} From e6f1c26e81f3b0bc90907d27023ae4a22104ae6b Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Mon, 14 Oct 2024 10:22:19 +0530 Subject: [PATCH 09/10] finalcommit --- .DS_Store | Bin 8196 -> 8196 bytes JsonSchemaValidator.java | 16 ++-- README.md | 38 +++++--- build.gradle | 170 ++++++++++++++++++++--------------- json_schema_validation.sh | 51 +++++++++++ json_validation.sh | 32 ------- meta-generator.gradle | 11 ++- setup_adapter_spring_boot.sh | 47 ++++++++-- spec.config | 14 +-- 9 files changed, 245 insertions(+), 134 deletions(-) create mode 100755 json_schema_validation.sh delete mode 100755 json_validation.sh diff --git a/.DS_Store b/.DS_Store index 403f9e2f6bf0fb4394aee40c52e51ec1764790a2..7e24eaac6ab23bb594e9d2932b980c6f37a853b5 100644 GIT binary patch delta 204 zcmZp1XmOa}FD%T!z`)4BAi%(o;+d15oRpKFw6Sm+`@{zR&Fma39NHjxR)!>oQie>1 z9E2L6C{PRIPax*|4+abjlX(QH8O0`F5E7jnE5NP5pvRC5)RM}O$dCs!8d=-s8UcMq zV@ZZAhGK?%hCGINAf3#R0o0HS)Dq871|)NUwx$5-5+DsyqX$%zF*!^~e)DR<4yKJI MJj|QfCH}Gl0B<}py#N3J delta 66 zcmZp1XmOa}FDk&mz`)4B0HjhF^cd0^iWm|ZQZ^P&W1rZ-yP2JXg@aLGvY9~j=Dz|e VjGK#vI+!*a^IT!x%r5bl9RT#85hMTr diff --git a/JsonSchemaValidator.java b/JsonSchemaValidator.java index 836fa73..edccedc 100755 --- a/JsonSchemaValidator.java +++ b/JsonSchemaValidator.java @@ -12,18 +12,24 @@ 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 != 2) { - System.out.println("Usage: java JsonSchemaValidator "); + if (args.length != 1) { + System.out.println("Usage: java JsonSchemaValidator "); System.exit(1); } - String schemaPath = args[0]; - String jsonPath = args[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); diff --git a/README.md b/README.md index 12dc9c3..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 @@ -133,8 +154,7 @@ Tax provider capabilities for new tax providers will be validated against a cons ## Steps to generate a spring boot project for given spec using gradle Prerequisites: -Use Java11 or higher version with gradle version greater than or equal to 7. -For gradle version less than 7 use Java8. +Use Java8 or higher version. 1. Clone repository in local ```shell @@ -146,19 +166,15 @@ For gradle version less than 7 use Java8. cd cb-provider-spi ``` -3. Execute the script file setup_spring_boot.sh using below command: -```shell - chmod +x setup_adapter_spring_boot.sh -``` -4. (a) Run the script for default configurations in script file : +3. (a) Run the script to generate spring boot project for an openapi spec file with default configurations: ```shell - ./setup_adapter_spring_boot.sh + ./setup_adapter_spring_boot.sh --spec ``` -4. (a) Run the script for custom configurations (all params are optional if not provided will take the default configurations) : +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-version --output-dir <"output_directory"> --base-package <"package_name"> + ./setup_adapter_spring_boot.sh --java --output --package --spec ``` Example: ```shell - ./generate_project.sh --java-version 17 --output-dir "../my-adapter" --base-package "com.adapter" + ./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 cb31c00..6da0ec6 100644 --- a/build.gradle +++ b/build.gradle @@ -3,87 +3,134 @@ import org.openapitools.generator.gradle.plugin.tasks.ValidateTask import org.openapitools.generator.gradle.plugin.tasks.GenerateTask plugins { - id "idea" - id "java" - id "application" + 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 + +sourceCompatibility = '11' +targetCompatibility = '11' + +repositories { + mavenCentral() +} + +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' +} sourceSets { main { java { - srcDirs = ['.'] + srcDirs = ['src/main/java', '.'] + } + resources { + srcDirs = ['src/main/resources', 'spec/capabilities'] } } } -sourceSets { - main.resources.srcDirs = ['spec/capabilities'] +// Spring Boot configuration +bootJar { + archiveBaseName = 'spring-boot-openapi-jsonschema' + archiveVersion = '1.0' } -jar { +// JSON Schema Validator JAR configuration +task jsonSchemaValidatorJar(type: Jar) { archiveBaseName = 'json-schema-validator' archiveVersion = '1.0' manifest { attributes 'Main-Class': 'JsonSchemaValidator' } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from(sourceSets.main.output) { + include 'JsonSchemaValidator.class' + include 'spec/capabilities/**' + } from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } } -} -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } -repositories { - mavenCentral() +task buildJsonSchemaValidator { + dependsOn jsonSchemaValidatorJar } -java { - sourceCompatibility = "11" - targetCompatibility = "11" -} +// OpenAPI Spec generation configuration -def loadSpecConfig() { - def configJson = "[" + file('spec.config').text + "]" - def config = new JsonSlurper().parseText(configJson) - project.ext.specConfig = config as ArrayList +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 [] + } } tasks.register("generateSpec") - tasks.register("validateSpec") def openApiTask() { - loadSpecConfig() - - for (int i = 0; i < specConfig.size(); i++) { - def spec = specConfig[i] - - def validateTaskName = "validateSpec_$spec.name" + def specConfig = loadSpecConfig() + specConfig.each { spec -> + def validateTaskName = "validateSpec_${spec.name}" + def generateTaskName = "generateSpec_${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) @@ -93,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() @@ -109,44 +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' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0' - implementation 'com.github.erosb:everit-json-schema:1.14.1' - implementation 'com.github.erosb:everit-json-schema:1.14.2' - -} - -application { - mainClass = 'JsonSchemaValidator' +// Test configuration +test { + useJUnitPlatform() } \ 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/json_validation.sh b/json_validation.sh deleted file mode 100755 index 2b095c2..0000000 --- a/json_validation.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Make this script executable -chmod +x "$0" - -# Check if Gradle is installed -if ! command -v gradle &> /dev/null -then - echo "Gradle is not installed. Please install Gradle and try again." - exit 1 -fi - -# Clean and build the project -gradle clean build - -# Find the JAR file -JAR_FILE=$(find build/libs -name "json-schema-validator*.jar" | head -n 1) - -if [ -z "$JAR_FILE" ]; then - echo "Error: Could not find the JAR file. Build may have failed." - exit 1 -fi - -# Run the Java application using the generated JAR file -java -jar "$JAR_FILE" "$1" "$2" - -# 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 index 02b2e96..2424e41 100644 --- a/meta-generator.gradle +++ b/meta-generator.gradle @@ -21,7 +21,9 @@ def cliOptions = [ 'groupId', 'version', 'basePackage', - 'outputDir' + 'outputDir', + 'specs', + 'singleSpec' ] // Override config with command-line arguments if provided @@ -70,7 +72,12 @@ dependencies { } def specDir = '${projectDir}/../cb-provider-spi/spec/spi' -def specs = ${config.specs.inspect()} +def specs = ${config.specs ?: []} +def singleSpec = '${config.singleSpec ?: ""}' + +if (singleSpec) { + specs = [singleSpec] +} specs.each { spec -> def taskName = "generate\${spec.replace('.yml', '').capitalize()}" diff --git a/setup_adapter_spring_boot.sh b/setup_adapter_spring_boot.sh index cab6447..84428a5 100755 --- a/setup_adapter_spring_boot.sh +++ b/setup_adapter_spring_boot.sh @@ -13,6 +13,25 @@ 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 @@ -25,7 +44,7 @@ while [ $# -gt 0 ]; do shift 2 ;; --spec) - SPEC_FILES="$2" # Set SPEC_FILES to the provided argument + SPEC_FILES="$2" shift 2 ;; --package) @@ -61,6 +80,12 @@ get_all_spec_files() { # 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 @@ -98,10 +123,17 @@ if ! command -v gradle &> /dev/null; then exit 1 fi -# 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 +# 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 \ @@ -109,7 +141,8 @@ gradle -b meta-generator.gradle generateGradleProject \ -PopenApiGeneratorVersion="$OPENAPI_VERSION" \ -PoutputDir="$OUTPUT_DIR" \ -PbasePackage="$PACKAGE_NAME" \ - -Pspecs="[$SPEC_FILES_LIST]" + -Pspecs="[$SPEC_FILES_LIST]" \ + -PsingleSpec="$SPEC_FILES" if [ $? -ne 0 ]; then echo "Error: Gradle project generation failed." @@ -130,5 +163,3 @@ 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 + } +] From b1a1cff9622f07472468a723e6ad4d748401f31f Mon Sep 17 00:00:00 2001 From: Shishir Yadav Date: Mon, 14 Oct 2024 10:29:04 +0530 Subject: [PATCH 10/10] finalcommit --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) 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