Skip to content

Commit

Permalink
Add command line interface
Browse files Browse the repository at this point in the history
  • Loading branch information
kmadsen committed Oct 2, 2020
1 parent 7b5d038 commit 42601e3
Show file tree
Hide file tree
Showing 12 changed files with 348 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ test:
build-release:
./gradlew assemble

build-cli:
./gradlew shadowJar

javadoc:
mkdir documentation
mkdir documentation/core/
Expand Down
19 changes: 19 additions & 0 deletions services-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Mapbox Java CLI

This is a wrapper on top of our mapbox-java services. This was made to be a fast
debugging and testing tool, that can be used to verify new and old json contracts.

If you'd like to add a feature, add an issue to the mapbox-java repository
and we can help. Also, pull requests are always welcome!

### Developing

From the command line
1. cd mapbox-java
1. Build with ./gradlew shadowJar, or make build-cli
1. Run with java -jar services-cli/build/libs/services-cli-all.jar

From Android Studio
1. Open mapbox-java with Android Studio
1. Set MapboxJavaCli as the main configuration
1. Open the runtime configuration and add your "Program arguments"
66 changes: 66 additions & 0 deletions services-cli/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
apply plugin: 'de.fuerstenau.buildconfig'
apply plugin: 'application'
apply plugin: 'kotlin'
apply plugin: "com.vanniktech.android.junit.jacoco"
apply plugin: 'com.github.johnrengelman.shadow'

buildscript {
apply from: "../gradle/dependencies.gradle"

repositories {
maven { url 'https://plugins.gradle.org/m2' }
// To use snapshots
//maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' }
jcenter()
}

dependencies {
classpath pluginDependencies.buildConfig
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72'
classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.4'
}
}

sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8

dependencies {
// To use snapshots
//implementation "com.mapbox.mapboxsdk:mapbox-sdk-services:$VERSION_NAME"

// Make sure to use ./gradlew shadowJar for this to work
implementation project(":services-core")
implementation project(":services-directions-models")
implementation project(":services-directions-refresh-models")
implementation project(":services-geojson")
implementation project(":services-directions")
implementation project(":services-directions-refresh")

// The Apache Software License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0.txt
implementation("commons-cli:commons-cli:1.4")

// Kotlin
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

// Testing
testImplementation("io.mockk:mockk:1.10.0")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
testImplementation("com.google.truth:truth:1.0.1")
}

buildConfig {
packageName = 'com.mapbox.services.cli'
buildConfigField 'String', 'MAPBOX_ACCESS_TOKEN', System.getenv("MAPBOX_ACCESS_TOKEN")
}

application {
mainClassName = 'com.mapbox.services.cli.MapboxJavaCli'
}

jar {
manifest {
attributes('Main-Class': application.mainClassName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.mapbox.services.cli

import com.google.gson.Gson
import com.mapbox.services.cli.validator.DirectionsResponseValidator
import org.apache.commons.cli.CommandLine
import org.apache.commons.cli.DefaultParser
import org.apache.commons.cli.HelpFormatter
import org.apache.commons.cli.Option
import org.apache.commons.cli.Options
import org.apache.commons.cli.ParseException

/**
* Entry point for the command line interface.
*/
object MapboxJavaCli {

private const val COMMAND_FILE_INPUT = "f"
private const val COMMAND_HELP = "h"

@JvmStatic
fun main(args: Array<String>) {
val options = Options()
.addOption(Option.builder(COMMAND_HELP)
.longOpt("help")
.desc("Shows this help message")
.build())
.addOption(Option.builder(COMMAND_FILE_INPUT)
.longOpt("file")
.hasArg(true)
.desc("Path to a json file or directory")
.required()
.build())

try {
val commandLine = DefaultParser().parse(options, args)
parseCommands(commandLine, options)
} catch (pe: ParseException) {
println(pe.message)
printHelp(options)
}
}

private fun parseCommands(commandLine: CommandLine, options: Options) {
if (commandLine.hasOption(COMMAND_HELP)) {
printHelp(options)
}

val fileInput = commandLine.getOptionValue(COMMAND_FILE_INPUT)
val directionsResponseValidator = DirectionsResponseValidator()
val results = directionsResponseValidator.parse(fileInput)
print(Gson().toJson(results))
}

private fun printHelp(options: Options) {
val syntax = "java -jar services-cli/build/libs/services-cli-all.jar"
HelpFormatter().printHelp(syntax, options)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.mapbox.services.cli.validator

import com.mapbox.api.directions.v5.models.DirectionsResponse
import java.io.File
import kotlin.text.Charsets.UTF_8

/**
* Validate DirectionsResponse json strings.
*/
class DirectionsResponseValidator {

/**
* @param filePath path to the json file or directory
* @return results for all the files
*/
fun parse(filePath: String): List<ValidatorResult> {
val inputFile = File(filePath)

val results = mutableListOf<ValidatorResult>()
inputFile.forEachFile { file ->
val result = validateJson(file)
results.add(result)
}
return results
}

private fun File.forEachFile(function: (File) -> Unit) = walk()
.filter { !it.isDirectory }
.forEach(function)

private fun validateJson(file: File): ValidatorResult {
val json = file.readText(UTF_8)
return try {
val directionsResponse = DirectionsResponse.fromJson(json)
val toJson = directionsResponse.toJson()
val convertsBack = json == toJson
ValidatorResult(
filename = file.name,
success = true,
convertsBack = convertsBack
)
} catch (throwable: Throwable) {
ValidatorResult(
filename = file.name,
success = false,
convertsBack = false,
throwable = throwable
)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mapbox.services.cli.validator

import com.google.gson.annotations.SerializedName

data class ValidatorResult(
val filename: String,
val success: Boolean,
@SerializedName("converts_back")
val convertsBack: Boolean,
val throwable: Throwable? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.mapbox.services.cli.validator

import com.google.common.truth.Truth.assertThat
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class DirectionsResponseValidatorTest {

private val directionsResponseValidator = DirectionsResponseValidator()

@Test
fun `should successfully read file with DirectionsResponse json`() {
val testFile = "./src/test/resources/directions_v5.json"

val results = directionsResponseValidator.parse(testFile)

assertTrue(results[0].success)
assertThat(results[0].filename).isEqualTo("directions_v5.json")
assertThat(results[0].throwable).isNull()
}

@Test
fun `should detect if file is not DirectionsResponse json`() {
val testFile = "./src/test/resources/geojson_feature.json"

val results = directionsResponseValidator.parse(testFile)

assertThat(results[0].success).isFalse()
assertThat(results[0].filename).isEqualTo("geojson_feature.json")
assertThat(results[0].throwable).isNotNull()
assertThat(results[0].convertsBack).isFalse()
}

@Test(expected = Exception::class)
fun `should crash when json does not exist`() {
val testFile = "not a real file path"

val results = directionsResponseValidator.parse(testFile)

assertTrue(results[0].success)
}

@Test
fun `should parse every file in the directory`() {
val testFile = "./src/test/resources"

val results = directionsResponseValidator.parse(testFile)

assertThat(results.size).isGreaterThan(1)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.mapbox.services.cli.validator

import com.google.common.truth.Truth.assertThat
import com.mapbox.services.cli.MapboxJavaCli
import org.junit.Rule
import org.junit.Test


class MapboxJavaCliTest {

@Rule
@JvmField
val systemOutRule = SystemOutRule()

@Test
fun `should display successful results as json`() {
val testFile = "./src/test/resources/directions_v5.json"

MapboxJavaCli.main(arrayOf("-f", testFile))

val expected = """[{"filename":"directions_v5.json","success":true,"converts_back":false}]"""
assertThat(systemOutRule.results()).isEqualTo(expected)
}

@Test
fun `should not crash with missing arguments`() {
MapboxJavaCli.main(arrayOf())

val consoleOutput = systemOutRule.results()
assertThat(consoleOutput).contains("Missing required option")
}

@Test
fun `should display help with other arguments`() {
val testFile = "./src/test/resources/directions_v5.json"
MapboxJavaCli.main(arrayOf("-h", "-f", testFile))

val consoleOutput = systemOutRule.results()

assertThat(consoleOutput).contains("Shows this help message")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.mapbox.services.cli.validator

import org.junit.rules.TestWatcher
import org.junit.runner.Description
import java.io.ByteArrayOutputStream
import java.io.PrintStream

/**
* Use this rule to test what has been printed to the console.
*/
class SystemOutRule : TestWatcher() {

private val systemOutStream = ByteArrayOutputStream()
private lateinit var stdout: PrintStream

/**
* In the test, assert that these results are expected.
*/
fun results(): String = systemOutStream.toString()

override fun starting(description: Description) {
stdout = System.out
System.setOut(PrintStream(systemOutStream))
}

override fun finished(description: Description) {
System.setOut(stdout)
}
}
1 change: 1 addition & 0 deletions services-cli/src/test/resources/directions_v5.json

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions services-cli/src/test/resources/geojson_feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
125.6,
10.1
]
},
"properties": {
"name": "Dinagat Islands"
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ include ':services-directions-refresh'
include ':services-directions-refresh-models'
include ':services-isochrone'
include 'samples'
include 'services-cli'

0 comments on commit 42601e3

Please sign in to comment.