Skip to content

Commit

Permalink
Merge pull request #31 from speekha/develop
Browse files Browse the repository at this point in the history
Publish 1.1.4
  • Loading branch information
speekha authored Jul 13, 2019
2 parents 8da4a13 + 88e9f1f commit 5a27eb7
Show file tree
Hide file tree
Showing 49 changed files with 1,255 additions and 308 deletions.
21 changes: 21 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
- restore_cache:
keys:
- source-{{ .Branch }}-{{ .Revision }}
- source-{{ .Branch }}-
- source-
- checkout
- restore_cache:
keys:
Expand Down Expand Up @@ -102,6 +104,14 @@ jobs:
steps:
- build-module:
module: "custom-adapter"
build_kotlinx:
docker:
- image: circleci/android:api-28
environment:
JVM_OPTS: -Xmx3200m
steps:
- build-module:
module: "kotlinx-adapter"
build_demo:
docker:
- image: circleci/android:api-28
Expand Down Expand Up @@ -160,6 +170,9 @@ jobs:
- restore_cache:
keys:
- build-custom-adapter-{{ .Branch }}-{{ .Revision }}
- restore_cache:
keys:
- build-kotlinx-adapter-{{ .Branch }}-{{ .Revision }}
- restore_cache:
keys:
- build-demo-{{ .Branch }}-{{ .Revision }}
Expand Down Expand Up @@ -201,6 +214,9 @@ jobs:
- restore_cache:
keys:
- build-custom-adapter-{{ .Branch }}-{{ .Revision }}
- restore_cache:
keys:
- build-kotlinx-adapter-{{ .Branch }}-{{ .Revision }}
- run:
name: Tests
command: ./gradlew tests:test --stacktrace
Expand Down Expand Up @@ -256,6 +272,9 @@ workflows:
- build_custom:
requires:
- build_core
- build_kotlinx:
requires:
- build_core
- build_demo:
requires:
- build_jackson
Expand All @@ -265,13 +284,15 @@ workflows:
- build_gson
- build_moshi
- build_custom
- build_kotlinx
- build_demo
- test:
requires:
- build_jackson
- build_gson
- build_moshi
- build_custom
- build_kotlinx
- publish_snapshot:
requires:
- test
Expand Down
24 changes: 14 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ configuration files instead. The interceptor will also allow to record scenarios
## Current Version

```gradle
httpmocker_version = '1.1.3'
httpmocker_version = '1.1.4'
```

## Gradle
Expand Down Expand Up @@ -46,32 +46,36 @@ repositories {
#### Adding HttpMocker

This library contains two parts: a core module handling the mock logic, and an additional adapter to parse the scenario
files for static mocks. Currently, there are four possible options that are provided for parsing, based on three of the
most commonly used libraries for JSON parsing (Jackson, Gson, Moshi) and a custom implementation (no third party dependency),
so you can choose the one matching what you already use in your application (this will help you prevent duplicate libraries in
your classpath, like Jackson and GSON). If you choose one of these options, all you need to add is the corresponding
files for static mocks. Currently, there are five possible options that are provided for parsing, based on some of the
most commonly used libraries for JSON parsing (Jackson, Gson, Moshi, Kotlinx serialization) and a custom implementation
(no third party dependency), so you can choose the one matching what you already use in your application (this will
help you prevent duplicate libraries in your classpath, like Jackson and GSON). If you choose one of these options, all
you need to add is the corresponding
`implementation` line in your gradle file:

```gradle
// Parses JSON scenarios using Jackson
implementation "fr.speekha.httpmocker:jackson-adapter:1.1.3"
implementation "fr.speekha.httpmocker:jackson-adapter:1.1.4"
// Parses JSON scenarios using Gson
implementation "fr.speekha.httpmocker:gson-adapter:1.1.3"
implementation "fr.speekha.httpmocker:gson-adapter:1.1.4"
// Parses JSON scenarios using Moshi
implementation "fr.speekha.httpmocker:moshi-adapter:1.1.3"
implementation "fr.speekha.httpmocker:moshi-adapter:1.1.4"
// Parses JSON scenarios using Kotlinx Serialization
implementation "fr.speekha.httpmocker:kotlinx-adapter:1.1.4"
// Parses JSON scenarios using a custom JSON parser
implementation "fr.speekha.httpmocker:custom-adapter:1.1.3"
implementation "fr.speekha.httpmocker:custom-adapter:1.1.4"
```

If none of those options suits your needs or if you would prefer to only use dynamic mocks, you can add
the main dependency to your project (using static mocks will require that you provide your own implementation
of the `Mapper` class):

```gradle
implementation "fr.speekha.httpmocker:mocker:1.1.3"
implementation "fr.speekha.httpmocker:mocker:1.1.4"
```

#### External dependencies
Expand Down
7 changes: 5 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ buildscript {

dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version"
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$bintray_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_version"
Expand All @@ -35,7 +36,7 @@ buildscript {
allprojects {

ext {
httpmock_version = '1.1.3'
httpmock_version = '1.1.4'
}

group 'fr.speekha.httpmocker'
Expand All @@ -44,6 +45,8 @@ allprojects {
repositories {
google()
jcenter()
maven { url "https://kotlin.bintray.com/kotlinx" }
maven { url "https://dl.bintray.com/kotlin/kotlinx/" }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2019 David Blanc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package fr.speekha.httpmocker.custom

internal abstract class BaseObjectAdapter<T : Any> : ObjectAdapter<T> {

override fun fromJson(reader: JsonStringReader): T {
reader.beginObject()
val builder = readFields(reader, createObject())
reader.endObject()
return builder
}

private fun readFields(reader: JsonStringReader, builder: T): T {
var updateObject = builder
while (reader.hasNext()) {
updateObject = updateObject(reader, updateObject)
reader.next()
}
return updateObject
}

abstract fun createObject(): T

abstract fun updateObject(reader: JsonStringReader, builder: T): T

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,31 @@ package fr.speekha.httpmocker.custom

import fr.speekha.httpmocker.Mapper
import fr.speekha.httpmocker.model.Matcher
import fr.speekha.httpmocker.readAsString
import java.io.InputStream
import java.io.OutputStream

/**
* An adapter using custom JSON parsing to serialize/deserialize scenarios.
* A mapper using custom JSON parsing to serialize/deserialize scenarios.
*/
class CustomMapper : Mapper {

override fun readMatches(stream: InputStream): List<Matcher> {
val matcherMapper = MatcherAdapter()
private val adapter = MatcherAdapter()

val list = mutableListOf<Matcher>()

val json = stream.readAsString()
val reader = JsonStringReader(json)
reader.beginList()
while (reader.hasNext()) {
list += matcherMapper.fromJson(reader)
reader.next()
}
reader.endList()
override fun deserialize(payload: String): List<Matcher> = JsonStringReader(payload).parseJson(adapter)

private fun JsonStringReader.parseJson(matcherMapper: MatcherAdapter): List<Matcher> {
beginList()
val list = populateList(matcherMapper)
endList()
return list
}

override fun writeValue(outputStream: OutputStream, matchers: List<Matcher>) = outputStream.use {
it.write(compactJson(matchers.toJson()).toByteArray(Charsets.UTF_8))
}
private fun JsonStringReader.populateList(matcherMapper: MatcherAdapter): List<Matcher> =
mutableListOf<Matcher>().also { list ->
while (hasNext()) {
list += matcherMapper.fromJson(this)
next()
}
}

override fun serialize(matchers: List<Matcher>): String = compactJson(matchers.toJson())

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,13 @@ package fr.speekha.httpmocker.custom

import fr.speekha.httpmocker.model.Header

class HeaderListAdapter : ObjectAdapter<List<Header>> {
internal class HeaderListAdapter : BaseObjectAdapter<List<Header>>() {

override fun fromJson(reader: JsonStringReader): List<Header> {
val list = mutableListOf<Header>()
reader.beginObject()
while (reader.hasNext()) {
val field = reader.readFieldName()
val value = reader.readString()
reader.next()
list += Header(field, value)
}
reader.endObject()
return list
}
override fun createObject(): List<Header> = listOf()

override fun updateObject(reader: JsonStringReader, builder: List<Header>): List<Header> =
builder + Header(
reader.readFieldName(),
reader.readString()
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,24 @@ import fr.speekha.httpmocker.model.Matcher
import fr.speekha.httpmocker.model.RequestDescriptor
import fr.speekha.httpmocker.model.ResponseDescriptor

fun compactJson(json: String): String =
/**
* Removes all unnecessary blanks from a JSON string.
* @param json the JSON stream to clean
* @return the JSON with all useless blanks removed
*/
internal fun compactJson(json: String): String =
json.split("\n").joinToString("") { it.trim() }

fun List<Matcher>.toJson() =
internal fun List<Matcher>.toJson() =
joinToString(separator = ", ", prefix = "[\n", postfix = "]") { it.toJson() }

fun Matcher.toJson(): String = """ {
internal fun Matcher.toJson(): String = """ {
"request": ${request.toJson()},
"response": ${response.toJson()}
}
"""

fun RequestDescriptor.toJson(): String = listOf(
internal fun RequestDescriptor.toJson(): String = listOf(
"method" to method.wrap(),
"host" to host.wrap(),
"port" to port.wrap(),
Expand All @@ -49,16 +54,16 @@ fun RequestDescriptor.toJson(): String = listOf(
postfix = "\n }"
) { (key, value) -> " \"$key\": $value" }

fun <K, V> Map<K, V>.toJson(): String =
internal fun <K, V> Map<K, V>.toJson(): String =
entries.joinToString(
separator = ", ",
separator = ",\n",
prefix = "{\n",
postfix = "\n }"
) { " \"${it.key}\": \"${it.value}\"" }

fun Header.toJson(): String = "\"$name\": \"$value\""
internal fun Header.toJson(): String = "\"$name\": \"$value\""

fun ResponseDescriptor.toJson(): String = listOf(
internal fun ResponseDescriptor.toJson(): String = listOf(
"delay" to delay.toString(),
"code" to code.toString(),
"media-type" to mediaType.wrap(),
Expand All @@ -77,4 +82,10 @@ private fun String?.wrap() = this?.let { "\"$it\"" }

private fun Int?.wrap() = this?.toString()

fun String.truncate(limit: Int): String = takeIf { length <= limit } ?: substring(0, limit - 3) + "..."
/**
* Truncates a string and adds ... to show that the String is incomplete
* @param limit the maximum length for the String
* @return the truncated version of the String
*/
fun String.truncate(limit: Int): String =
takeIf { length <= limit } ?: substring(0, limit - 3) + "..."
Loading

0 comments on commit 5a27eb7

Please sign in to comment.