Skip to content

Commit d45921d

Browse files
committed
Add command line interface
1 parent 7b5d038 commit d45921d

File tree

14 files changed

+349
-0
lines changed

14 files changed

+349
-0
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ test:
1616
build-release:
1717
./gradlew assemble
1818

19+
build-cli:
20+
./gradlew shadowJar
21+
1922
javadoc:
2023
mkdir documentation
2124
mkdir documentation/core/

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ subprojects {
6666
testCompile dependenciesList.junit
6767
testCompile dependenciesList.hamcrestJunit
6868
testCompile dependenciesList.mockito
69+
testCompile dependenciesList.googleTruth
6970
}
7071
}
7172

gradle/dependencies.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ext {
1010
okhttp3 : '3.12.7',
1111
mockito : '2.28.2',
1212
hamcrestJunit : '2.0.0.0',
13+
googleTruth : '1.0.1',
1314
errorprone : '2.3.1',
1415
]
1516

@@ -23,6 +24,8 @@ ext {
2324
bintray : '1.8.4',
2425
maven : '3.6.2',
2526
artifactory: '4.9.3',
27+
kotlin : '1.3.72',
28+
shadowJar : '4.0.4',
2629
]
2730

2831
dependenciesList = [
@@ -37,6 +40,7 @@ ext {
3740
okhttp3Logging : "com.squareup.okhttp3:logging-interceptor:${version.okhttp3}",
3841
okhttp3Mockwebserver: "com.squareup.okhttp3:mockwebserver:${version.okhttp3}",
3942
mockito : "org.mockito:mockito-core:${version.mockito}",
43+
googleTruth : "com.google.truth:truth:${version.googleTruth}",
4044
hamcrestJunit : "org.hamcrest:hamcrest-junit:${version.hamcrestJunit}",
4145
errorprone : "com.google.errorprone:error_prone_core:${version.errorprone}"
4246
]
@@ -51,5 +55,7 @@ ext {
5155
bintray : "com.jfrog.bintray.gradle:gradle-bintray-plugin:${pluginVersion.bintray}",
5256
maven : "digital.wup:android-maven-publish:${pluginVersion.maven}",
5357
artifactory: "org.jfrog.buildinfo:build-info-extractor-gradle:${pluginVersion.artifactory}",
58+
kotlin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${pluginVersion.kotlin}",
59+
shadowJar : "com.github.jengelman.gradle.plugins:shadow:${pluginVersion.shadowJar}",
5460
]
5561
}

services-cli/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Mapbox Java CLI
2+
3+
This is a wrapper on top of our mapbox-java services. This was made to be a fast
4+
debugging and testing tool, that can be used to verify new and old json contracts.
5+
6+
If you'd like to add a feature, add an issue to the mapbox-java repository
7+
and we can help. Also, pull requests are always welcome!
8+
9+
### Developing
10+
11+
From the command line
12+
1. cd mapbox-java
13+
1. Build with ./gradlew shadowJar, or make build-cli
14+
1. Run with java -jar services-cli/build/libs/services-cli-all.jar
15+
16+
From Android Studio
17+
1. Open mapbox-java with Android Studio
18+
1. Set MapboxJavaCli as the main configuration
19+
1. Open the runtime configuration and add your "Program arguments"

services-cli/build.gradle

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
apply plugin: 'de.fuerstenau.buildconfig'
2+
apply plugin: 'application'
3+
apply plugin: 'kotlin'
4+
apply plugin: "com.vanniktech.android.junit.jacoco"
5+
apply plugin: 'com.github.johnrengelman.shadow'
6+
7+
buildscript {
8+
apply from: "../gradle/dependencies.gradle"
9+
10+
repositories {
11+
maven { url 'https://plugins.gradle.org/m2' }
12+
// To use snapshots
13+
//maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' }
14+
jcenter()
15+
}
16+
17+
dependencies {
18+
classpath pluginDependencies.buildConfig
19+
classpath pluginDependencies.kotlin
20+
classpath pluginDependencies.shadowJar
21+
}
22+
}
23+
24+
sourceCompatibility = JavaVersion.VERSION_1_8
25+
targetCompatibility = JavaVersion.VERSION_1_8
26+
27+
dependencies {
28+
// To use snapshots
29+
//implementation "com.mapbox.mapboxsdk:mapbox-sdk-services:$VERSION_NAME"
30+
31+
// Make sure to use ./gradlew shadowJar for this to work
32+
implementation project(":services-core")
33+
implementation project(":services-directions-models")
34+
implementation project(":services-directions-refresh-models")
35+
implementation project(":services-geojson")
36+
implementation project(":services-directions")
37+
implementation project(":services-directions-refresh")
38+
39+
// The Apache Software License, Version 2.0
40+
// http://www.apache.org/licenses/LICENSE-2.0.txt
41+
implementation("commons-cli:commons-cli:1.4")
42+
43+
// Kotlin
44+
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
45+
46+
// Testing
47+
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
48+
}
49+
50+
buildConfig {
51+
packageName = 'com.mapbox.services.cli'
52+
buildConfigField 'String', 'MAPBOX_ACCESS_TOKEN', System.getenv("MAPBOX_ACCESS_TOKEN")
53+
}
54+
55+
application {
56+
mainClassName = 'com.mapbox.services.cli.MapboxJavaCli'
57+
}
58+
59+
jar {
60+
manifest {
61+
attributes('Main-Class': application.mainClassName)
62+
}
63+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.mapbox.services.cli
2+
3+
import com.google.gson.Gson
4+
import com.mapbox.services.cli.validator.DirectionsResponseValidator
5+
import org.apache.commons.cli.CommandLine
6+
import org.apache.commons.cli.DefaultParser
7+
import org.apache.commons.cli.HelpFormatter
8+
import org.apache.commons.cli.Option
9+
import org.apache.commons.cli.Options
10+
import org.apache.commons.cli.ParseException
11+
12+
/**
13+
* Entry point for the command line interface.
14+
*/
15+
object MapboxJavaCli {
16+
17+
private const val COMMAND_FILE_INPUT = "f"
18+
private const val COMMAND_HELP = "h"
19+
20+
@JvmStatic
21+
fun main(args: Array<String>) {
22+
val options = Options()
23+
.addOption(Option.builder(COMMAND_HELP)
24+
.longOpt("help")
25+
.desc("Shows this help message")
26+
.build())
27+
.addOption(Option.builder(COMMAND_FILE_INPUT)
28+
.longOpt("file")
29+
.hasArg(true)
30+
.desc("Path to a json file or directory")
31+
.required()
32+
.build())
33+
34+
try {
35+
val commandLine = DefaultParser().parse(options, args)
36+
parseCommands(commandLine, options)
37+
} catch (pe: ParseException) {
38+
println(pe.message)
39+
printHelp(options)
40+
}
41+
}
42+
43+
private fun parseCommands(commandLine: CommandLine, options: Options) {
44+
if (commandLine.hasOption(COMMAND_HELP)) {
45+
printHelp(options)
46+
}
47+
48+
val fileInput = commandLine.getOptionValue(COMMAND_FILE_INPUT)
49+
val directionsResponseValidator = DirectionsResponseValidator()
50+
val results = directionsResponseValidator.parse(fileInput)
51+
print(Gson().toJson(results))
52+
}
53+
54+
private fun printHelp(options: Options) {
55+
val syntax = "java -jar services-cli/build/libs/services-cli-all.jar"
56+
HelpFormatter().printHelp(syntax, options)
57+
}
58+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.mapbox.services.cli.validator
2+
3+
import com.mapbox.api.directions.v5.models.DirectionsResponse
4+
import java.io.File
5+
import kotlin.text.Charsets.UTF_8
6+
7+
/**
8+
* Validate DirectionsResponse json strings.
9+
*/
10+
class DirectionsResponseValidator {
11+
12+
/**
13+
* @param filePath path to the json file or directory
14+
* @return results for all the files
15+
*/
16+
fun parse(filePath: String): List<ValidatorResult> {
17+
val inputFile = File(filePath)
18+
19+
val results = mutableListOf<ValidatorResult>()
20+
inputFile.forEachFile { file ->
21+
val result = validateJson(file)
22+
results.add(result)
23+
}
24+
return results
25+
}
26+
27+
private fun File.forEachFile(function: (File) -> Unit) = walk()
28+
.filter { !it.isDirectory }
29+
.forEach(function)
30+
31+
private fun validateJson(file: File): ValidatorResult {
32+
val json = file.readText(UTF_8)
33+
return try {
34+
val directionsResponse = DirectionsResponse.fromJson(json)
35+
val toJson = directionsResponse.toJson()
36+
val convertsBack = json == toJson
37+
ValidatorResult(
38+
filename = file.name,
39+
success = true,
40+
convertsBack = convertsBack
41+
)
42+
} catch (throwable: Throwable) {
43+
ValidatorResult(
44+
filename = file.name,
45+
success = false,
46+
convertsBack = false,
47+
throwable = throwable
48+
)
49+
}
50+
}
51+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.mapbox.services.cli.validator
2+
3+
import com.google.gson.annotations.SerializedName
4+
5+
data class ValidatorResult(
6+
val filename: String,
7+
val success: Boolean,
8+
@SerializedName("converts_back")
9+
val convertsBack: Boolean,
10+
val throwable: Throwable? = null
11+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.mapbox.services.cli.validator
2+
3+
import com.google.common.truth.Truth.assertThat
4+
import org.junit.Test
5+
import kotlin.test.assertTrue
6+
7+
class DirectionsResponseValidatorTest {
8+
9+
private val directionsResponseValidator = DirectionsResponseValidator()
10+
11+
@Test
12+
fun `should successfully read file with DirectionsResponse json`() {
13+
val testFile = "./src/test/resources/directions_v5.json"
14+
15+
val results = directionsResponseValidator.parse(testFile)
16+
17+
assertTrue(results[0].success)
18+
assertThat(results[0].filename).isEqualTo("directions_v5.json")
19+
assertThat(results[0].throwable).isNull()
20+
}
21+
22+
@Test
23+
fun `should detect if file is not DirectionsResponse json`() {
24+
val testFile = "./src/test/resources/geojson_feature.json"
25+
26+
val results = directionsResponseValidator.parse(testFile)
27+
28+
assertThat(results[0].success).isFalse()
29+
assertThat(results[0].filename).isEqualTo("geojson_feature.json")
30+
assertThat(results[0].throwable).isNotNull()
31+
assertThat(results[0].convertsBack).isFalse()
32+
}
33+
34+
@Test(expected = Exception::class)
35+
fun `should crash when json does not exist`() {
36+
val testFile = "not a real file path"
37+
38+
val results = directionsResponseValidator.parse(testFile)
39+
40+
assertTrue(results[0].success)
41+
}
42+
43+
@Test
44+
fun `should parse every file in the directory`() {
45+
val testFile = "./src/test/resources"
46+
47+
val results = directionsResponseValidator.parse(testFile)
48+
49+
assertThat(results.size).isGreaterThan(1)
50+
}
51+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.mapbox.services.cli.validator
2+
3+
import com.google.common.truth.Truth.assertThat
4+
import com.mapbox.services.cli.MapboxJavaCli
5+
import org.junit.Rule
6+
import org.junit.Test
7+
8+
9+
class MapboxJavaCliTest {
10+
11+
@Rule
12+
@JvmField
13+
val systemOutRule = SystemOutRule()
14+
15+
@Test
16+
fun `should display successful results as json`() {
17+
val testFile = "./src/test/resources/directions_v5.json"
18+
19+
MapboxJavaCli.main(arrayOf("-f", testFile))
20+
21+
val expected = """[{"filename":"directions_v5.json","success":true,"converts_back":false}]"""
22+
assertThat(systemOutRule.results()).isEqualTo(expected)
23+
}
24+
25+
@Test
26+
fun `should not crash with missing arguments`() {
27+
MapboxJavaCli.main(arrayOf())
28+
29+
val consoleOutput = systemOutRule.results()
30+
assertThat(consoleOutput).contains("Missing required option")
31+
}
32+
33+
@Test
34+
fun `should display help with other arguments`() {
35+
val testFile = "./src/test/resources/directions_v5.json"
36+
MapboxJavaCli.main(arrayOf("-h", "-f", testFile))
37+
38+
val consoleOutput = systemOutRule.results()
39+
40+
assertThat(consoleOutput).contains("Shows this help message")
41+
}
42+
}

0 commit comments

Comments
 (0)