diff --git a/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt b/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt index 63ab295e36..308c4b26fc 100644 --- a/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt +++ b/client/jackson/src/main/kotlin/net/corda/jackson/JacksonSupport.kt @@ -23,6 +23,7 @@ import net.i2p.crypto.eddsa.EdDSAPublicKey import org.bouncycastle.asn1.x500.X500Name import java.math.BigDecimal import java.security.PublicKey +import java.time.LocalDate import java.util.* /** @@ -80,6 +81,7 @@ object JacksonSupport { addSerializer(SecureHash.SHA256::class.java, SecureHashSerializer) addDeserializer(SecureHash::class.java, SecureHashDeserializer()) addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer()) + addSerializer(BusinessCalendar::class.java, CalendarSerializer) addDeserializer(BusinessCalendar::class.java, CalendarDeserializer) // For ed25519 pubkeys @@ -268,11 +270,30 @@ object JacksonSupport { } } + data class BusinessCalendarWrapper(val holidayDates: List) { + fun toCalendar() = BusinessCalendar(holidayDates) + } + + object CalendarSerializer : JsonSerializer() { + override fun serialize(obj: BusinessCalendar, generator: JsonGenerator, context: SerializerProvider) { + val calendarName = BusinessCalendar.calendars.find { BusinessCalendar.getInstance(it) == obj } + if(calendarName != null) { + generator.writeString(calendarName) + } else { + generator.writeObject(BusinessCalendarWrapper(obj.holidayDates)) + } + } + } + object CalendarDeserializer : JsonDeserializer() { override fun deserialize(parser: JsonParser, context: DeserializationContext): BusinessCalendar { return try { - val array = StringArrayDeserializer.instance.deserialize(parser, context) - BusinessCalendar.getInstance(*array) + try { + val array = StringArrayDeserializer.instance.deserialize(parser, context) + BusinessCalendar.getInstance(*array) + } catch (e: Exception) { + parser.readValueAs(BusinessCalendarWrapper::class.java).toCalendar() + } } catch (e: Exception) { throw JsonParseException(parser, "Invalid calendar(s) ${parser.text}: ${e.message}") } diff --git a/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt b/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt index 9ccff91150..0967b0a644 100644 --- a/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt +++ b/core/src/main/kotlin/net/corda/core/contracts/FinanceTypes.kt @@ -633,7 +633,7 @@ fun LocalDate.isWorkingDay(accordingToCalendar: BusinessCalendar): Boolean = acc * no staff are around to handle problems. */ @CordaSerializable -open class BusinessCalendar private constructor(val holidayDates: List) { +open class BusinessCalendar(val holidayDates: List) { @CordaSerializable class UnknownCalendar(name: String) : Exception("$name not found") diff --git a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt index cfe87d5761..e4175eaf1a 100644 --- a/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt +++ b/samples/irs-demo/src/integration-test/kotlin/net/corda/irs/IRSDemoTest.kt @@ -4,7 +4,9 @@ import com.google.common.net.HostAndPort import com.google.common.util.concurrent.Futures import net.corda.client.rpc.CordaRPCClient import net.corda.core.getOrThrow +import net.corda.core.messaging.CordaRPCOps import net.corda.core.node.services.ServiceInfo +import net.corda.core.toFuture import net.corda.core.utilities.DUMMY_BANK_A import net.corda.core.utilities.DUMMY_BANK_B import net.corda.core.utilities.DUMMY_NOTARY @@ -22,14 +24,18 @@ import net.corda.testing.http.HttpApi import org.apache.commons.io.IOUtils import org.assertj.core.api.Assertions.assertThat import org.junit.Test +import rx.Observable import rx.observables.BlockingObservable import java.net.URL +import java.time.Duration import java.time.LocalDate +import java.time.temporal.ChronoUnit class IRSDemoTest : IntegrationTestCategory { val rpcUser = User("user", "password", emptySet()) val currentDate: LocalDate = LocalDate.now() val futureDate: LocalDate = currentDate.plusMonths(6) + val maxWaitTime: Duration = Duration.of(60, ChronoUnit.SECONDS) @Test fun `runs IRS demo`() { @@ -40,56 +46,67 @@ class IRSDemoTest : IntegrationTestCategory { startNode(DUMMY_BANK_B.name) ).getOrThrow() + println("All nodes started") + val (controllerAddr, nodeAAddr, nodeBAddr) = Futures.allAsList( startWebserver(controller), startWebserver(nodeA), startWebserver(nodeB) ).getOrThrow().map { it.listenAddress } + println("All webservers started") + + val (controllerApi, nodeAApi, nodeBApi) = listOf(controller, nodeA, nodeB).zip(listOf(controllerAddr, nodeAAddr, nodeBAddr)).map { + val mapper = net.corda.jackson.JacksonSupport.createDefaultMapper(it.first.rpc) + HttpApi.fromHostAndPort(it.second, "api/irs", mapper = mapper) + } val nextFixingDates = getFixingDateObservable(nodeA.configuration) - val numADeals = getTradeCount(nodeAAddr) - val numBDeals = getTradeCount(nodeBAddr) + val numADeals = getTradeCount(nodeAApi) + val numBDeals = getTradeCount(nodeBApi) runUploadRates(controllerAddr) - runTrade(nodeAAddr) + runTrade(nodeAApi) - assertThat(getTradeCount(nodeAAddr)).isEqualTo(numADeals + 1) - assertThat(getTradeCount(nodeBAddr)).isEqualTo(numBDeals + 1) + assertThat(getTradeCount(nodeAApi)).isEqualTo(numADeals + 1) + assertThat(getTradeCount(nodeBApi)).isEqualTo(numBDeals + 1) + assertThat(getFloatingLegFixCount(nodeAApi) == 0) // Wait until the initial trade and all scheduled fixings up to the current date have finished - nextFixingDates.first { it == null || it > currentDate } + nextFixingDates.firstWithTimeout(maxWaitTime){ it == null || it > currentDate } + runDateChange(nodeBApi) + nextFixingDates.firstWithTimeout(maxWaitTime) { it == null || it > futureDate } - runDateChange(nodeBAddr) - nextFixingDates.first { it == null || it > futureDate } + assertThat(getFloatingLegFixCount(nodeAApi) > 0) } } - fun getFixingDateObservable(config: FullNodeConfiguration): BlockingObservable { + fun getFloatingLegFixCount(nodeApi: HttpApi) = getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null } + + fun getFixingDateObservable(config: FullNodeConfiguration): Observable { val client = CordaRPCClient(config.rpcAddress!!) val proxy = client.start("user", "password").proxy val vaultUpdates = proxy.vaultAndUpdates().second - val fixingDates = vaultUpdates.map { update -> + return vaultUpdates.map { update -> val irsStates = update.produced.map { it.state.data }.filterIsInstance() irsStates.mapNotNull { it.calculation.nextFixingDate() }.max() - }.cache().toBlocking() - - return fixingDates + }.cache() } - private fun runDateChange(nodeAddr: HostAndPort) { - val url = URL("http://$nodeAddr/api/irs/demodate") - assertThat(putJson(url, "\"$futureDate\"")).isTrue() + private fun runDateChange(nodeApi: HttpApi) { + println("Running date change against ${nodeApi.root}") + assertThat(nodeApi.putJson("demodate", "\"$futureDate\"")).isTrue() } - private fun runTrade(nodeAddr: HostAndPort) { + private fun runTrade(nodeApi: HttpApi) { + println("Running trade against ${nodeApi.root}") val fileContents = loadResourceFile("net/corda/irs/simulation/example-irs-trade.json") val tradeFile = fileContents.replace("tradeXXX", "trade1") - val url = URL("http://$nodeAddr/api/irs/deals") - assertThat(postJson(url, tradeFile)).isTrue() + assertThat(nodeApi.postJson("deals", tradeFile)).isTrue() } private fun runUploadRates(host: HostAndPort) { + println("Running upload rates against $host") val fileContents = loadResourceFile("net/corda/irs/simulation/example.rates.txt") val url = URL("http://$host/upload/interest-rates") assertThat(uploadFile(url, fileContents)).isTrue() @@ -99,9 +116,19 @@ class IRSDemoTest : IntegrationTestCategory { return IOUtils.toString(Thread.currentThread().contextClassLoader.getResourceAsStream(filename), Charsets.UTF_8.name()) } - private fun getTradeCount(nodeAddr: HostAndPort): Int { - val api = HttpApi.fromHostAndPort(nodeAddr, "api/irs") - val deals = api.getJson>("deals") + private fun getTradeCount(nodeApi: HttpApi): Int { + println("Getting trade count from ${nodeApi.root}") + val deals = nodeApi.getJson>("deals") return deals.size } + + private fun getTrades(nodeApi: HttpApi): Array { + println("Getting trades from ${nodeApi.root}") + val deals = nodeApi.getJson>("deals") + return deals + } + + fun Observable.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) { + first(pred).toFuture().getOrThrow(timeout) + } } diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt index 485a1c428a..552bf33174 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRS.kt @@ -1,5 +1,8 @@ package net.corda.irs.contract +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty import net.corda.core.contracts.* import net.corda.core.contracts.clauses.* import net.corda.core.crypto.SecureHash @@ -106,6 +109,7 @@ abstract class RatePaymentEvent(date: LocalDate, * Assumes that the rate is valid. */ @CordaSerializable +@JsonIgnoreProperties(ignoreUnknown = true) class FixedRatePaymentEvent(date: LocalDate, accrualStartDate: LocalDate, accrualEndDate: LocalDate, @@ -129,6 +133,7 @@ class FixedRatePaymentEvent(date: LocalDate, * If the rate is null returns a zero payment. // TODO: Is this the desired behaviour? */ @CordaSerializable +@JsonIgnoreProperties(ignoreUnknown = true) class FloatingRatePaymentEvent(date: LocalDate, accrualStartDate: LocalDate, accrualEndDate: LocalDate, @@ -651,6 +656,7 @@ class InterestRateSwap : Contract { /** * The state class contains the 4 major data classes. */ + @JsonIgnoreProperties("parties", "participants", ignoreUnknown = true) data class State( val fixedLeg: FixedLeg, val floatingLeg: FloatingLeg, diff --git a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRSUtils.kt b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRSUtils.kt index 809cb7c05f..f836c89ca1 100644 --- a/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRSUtils.kt +++ b/samples/irs-demo/src/main/kotlin/net/corda/irs/contract/IRSUtils.kt @@ -1,5 +1,7 @@ package net.corda.irs.contract +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonIgnoreProperties import net.corda.core.contracts.Amount import net.corda.core.contracts.Tenor import net.corda.core.serialization.CordaSerializable @@ -36,6 +38,7 @@ val String.percent: PercentageRatioUnit get() = PercentageRatioUnit(this) /** * Parent of the Rate family. Used to denote fixed rates, floating rates, reference rates etc. */ +@JsonIgnoreProperties(ignoreUnknown = true) open class Rate(val ratioUnit: RatioUnit? = null) { override fun equals(other: Any?): Boolean { if (this === other) return true @@ -63,6 +66,7 @@ open class Rate(val ratioUnit: RatioUnit? = null) { */ @CordaSerializable class FixedRate(ratioUnit: RatioUnit) : Rate(ratioUnit) { + @JsonIgnore fun isPositive(): Boolean = ratioUnit!!.value > BigDecimal("0.0") override fun equals(other: Any?) = other?.javaClass == javaClass && super.equals(other) diff --git a/samples/irs-demo/src/main/resources/irsweb/js/controllers/CreateDeal.js b/samples/irs-demo/src/main/resources/irsweb/js/controllers/CreateDeal.js index c6d4a7f81e..41b30a6172 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/controllers/CreateDeal.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/controllers/CreateDeal.js @@ -6,10 +6,12 @@ define([ 'utils/semantic', 'utils/dayCountBasisLookup', 'services/NodeApi', - 'Deal' + 'Deal', + 'services/HttpErrorHandler' ], (angular, maskedInput, semantic, dayCountBasisLookup, nodeApi, Deal) => { - angular.module('irsViewer').controller('CreateDealController', function CreateDealController($http, $scope, $location, nodeService) { + angular.module('irsViewer').controller('CreateDealController', function CreateDealController($http, $scope, $location, nodeService, httpErrorHandler) { semantic.init($scope, nodeService.isLoading); + let handleHttpFail = httpErrorHandler.createErrorHandler($scope); $scope.dayCountBasisLookup = dayCountBasisLookup; $scope.deal = nodeService.newDeal(); @@ -17,7 +19,7 @@ define([ nodeService.createDeal(new Deal($scope.deal)) .then((tradeId) => $location.path('#/deal/' + tradeId), (resp) => { $scope.formError = resp.data; - }); + }, handleHttpFail); }; $('input.percent').mask("9.999999%", {placeholder: "", autoclear: false}); $('#swapirscolumns').click(() => { diff --git a/samples/irs-demo/src/main/resources/irsweb/js/controllers/Deal.js b/samples/irs-demo/src/main/resources/irsweb/js/controllers/Deal.js index 6bb8d5cdad..2ccaca2e9b 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/controllers/Deal.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/controllers/Deal.js @@ -1,9 +1,19 @@ 'use strict'; -define(['angular', 'utils/semantic', 'services/NodeApi'], (angular, semantic, nodeApi) => { - angular.module('irsViewer').controller('DealController', function DealController($http, $scope, $routeParams, nodeService) { +define(['angular', 'utils/semantic', 'services/NodeApi', 'services/HttpErrorHandler'], (angular, semantic) => { + angular.module('irsViewer').controller('DealController', function DealController($http, $scope, $routeParams, nodeService, httpErrorHandler) { semantic.init($scope, nodeService.isLoading); + let handleHttpFail = httpErrorHandler.createErrorHandler($scope); + let decorateDeal = (deal) => { + let paymentSchedule = deal.calculation.floatingLegPaymentSchedule; + Object.keys(paymentSchedule).map((key, index) => { + const sign = paymentSchedule[key].rate.positive ? 1 : -1; + paymentSchedule[key].ratePercent = paymentSchedule[key].rate.ratioUnit ? (paymentSchedule[key].rate.ratioUnit.value * 100 * sign).toFixed(5) + "%": ""; + }); - nodeService.getDeal($routeParams.dealId).then((deal) => $scope.deal = deal); + return deal; + }; + + nodeService.getDeal($routeParams.dealId).then((deal) => $scope.deal = decorateDeal(deal), handleHttpFail); }); }); \ No newline at end of file diff --git a/samples/irs-demo/src/main/resources/irsweb/js/controllers/Home.js b/samples/irs-demo/src/main/resources/irsweb/js/controllers/Home.js index d4c26de109..b92d31f1bc 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/controllers/Home.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/controllers/Home.js @@ -1,12 +1,10 @@ 'use strict'; -define(['angular', 'utils/semantic', 'services/NodeApi'], (angular, semantic, nodeApi) => { - angular.module('irsViewer').controller('HomeController', function HomeController($http, $scope, nodeService) { +define(['angular', 'utils/semantic', 'services/NodeApi', 'services/HttpErrorHandler'], (angular, semantic) => { + angular.module('irsViewer').controller('HomeController', function HomeController($http, $scope, nodeService, httpErrorHandler) { semantic.addLoadingModal($scope, nodeService.isLoading); - let handleHttpFail = (resp) => { - $scope.httpError = resp.data - }; + let handleHttpFail = httpErrorHandler.createErrorHandler($scope); $scope.infoMsg = ""; $scope.errorText = ""; @@ -28,7 +26,7 @@ define(['angular', 'utils/semantic', 'services/NodeApi'], (angular, semantic, no return name; }; - nodeService.getDate().then((date) => $scope.date = date); - nodeService.getDeals().then((deals) => $scope.deals = deals); + nodeService.getDate().then((date) => $scope.date = date, handleHttpFail); + nodeService.getDeals().then((deals) => $scope.deals = deals, handleHttpFail); }); }); diff --git a/samples/irs-demo/src/main/resources/irsweb/js/services/HttpErrorHandler.js b/samples/irs-demo/src/main/resources/irsweb/js/services/HttpErrorHandler.js new file mode 100644 index 0000000000..0ac757b5f6 --- /dev/null +++ b/samples/irs-demo/src/main/resources/irsweb/js/services/HttpErrorHandler.js @@ -0,0 +1,17 @@ +'use strict'; + +define(['angular', 'lodash', 'viewmodel/Deal'], (angular, _) => { + angular.module('irsViewer').factory('httpErrorHandler', () => { + return { + createErrorHandler: (scope) => { + return (resp) => { + if(resp.status == -1) { + scope.httpError = "Could not connect to node web server"; + } else { + scope.httpError = resp.data; + } + }; + } + }; + }); +}); \ No newline at end of file diff --git a/samples/irs-demo/src/main/resources/irsweb/js/services/NodeApi.js b/samples/irs-demo/src/main/resources/irsweb/js/services/NodeApi.js index 2d84ae295b..1cfdf401c5 100644 --- a/samples/irs-demo/src/main/resources/irsweb/js/services/NodeApi.js +++ b/samples/irs-demo/src/main/resources/irsweb/js/services/NodeApi.js @@ -5,6 +5,7 @@ define(['angular', 'lodash', 'viewmodel/Deal'], (angular, _, dealViewModel) => { return new (function() { let date = new Date(2016, 0, 1, 0, 0, 0); let curLoading = {}; + let serverAddr = ''; // Leave empty to target the same host this page is served from let load = (type, promise) => { curLoading[type] = true; @@ -17,19 +18,20 @@ define(['angular', 'lodash', 'viewmodel/Deal'], (angular, _, dealViewModel) => { }); }; + let endpoint = (target) => serverAddr + target; + let changeDateOnNode = (newDate) => { const dateStr = formatDateForNode(newDate); - let endpoint = '/api/irs/demodate'; - return load('date', $http.put(endpoint, "\"" + dateStr + "\"")).then((resp) => { + return load('date', $http.put(endpoint('/api/irs/demodate'), "\"" + dateStr + "\"")).then((resp) => { date = newDate; return this.getDateModel(date); }); }; this.getDate = () => { - return load('date', $http.get('/api/irs/demodate')).then((resp) => { - const parts = resp.data.split("-"); - date = new Date(parts[0], parts[1] - 1, parts[2]); // JS uses 0 based months + return load('date', $http.get(endpoint('/api/irs/demodate'))).then((resp) => { + const dateParts = resp.data; + date = new Date(dateParts[0], dateParts[1] - 1, dateParts[2]); // JS uses 0 based months return this.getDateModel(date); }); }; @@ -54,13 +56,13 @@ define(['angular', 'lodash', 'viewmodel/Deal'], (angular, _, dealViewModel) => { }; this.getDeals = () => { - return load('deals', $http.get('/api/irs/deals')).then((resp) => { + return load('deals', $http.get(endpoint('/api/irs/deals'))).then((resp) => { return resp.data.reverse(); }); }; this.getDeal = (dealId) => { - return load('deal' + dealId, $http.get('/api/irs/deals/' + dealId)).then((resp) => { + return load('deal' + dealId, $http.get(endpoint('/api/irs/deals/' + dealId))).then((resp) => { // Do some data modification to simplify the model let deal = resp.data; deal.fixedLeg.fixedRate.value = (deal.fixedLeg.fixedRate.ratioUnit.value * 100).toString().slice(0, 6); @@ -87,7 +89,7 @@ define(['angular', 'lodash', 'viewmodel/Deal'], (angular, _, dealViewModel) => { }; this.createDeal = (deal) => { - return load('create-deal', $http.post('/api/irs/deals', deal.toJson())) + return load('create-deal', $http.post(endpoint('/api/irs/deals'), deal.toJson())) .then((resp) => { return deal.tradeId; }, (resp) => { diff --git a/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html b/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html index 42b1de25a6..4b3decbf3a 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/create-deal.html @@ -1,5 +1,7 @@
+
{{formError}}
+
{{httpError}}

New Deal diff --git a/samples/irs-demo/src/main/resources/irsweb/view/deal.html b/samples/irs-demo/src/main/resources/irsweb/view/deal.html index fae30c99b7..9e2afe97d5 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/deal.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/deal.html @@ -1,5 +1,6 @@
+
{{httpError}}
@@ -199,6 +200,26 @@ + + +
+
+
+ + Fixings +
+
+ + + + + + + +
{{fixing.fixingDate[0]}}-{{fixing.fixingDate[1]}}-{{fixing.fixingDate[2]}}{{fixing.ratePercent}}
+
+
+
diff --git a/samples/irs-demo/src/main/resources/irsweb/view/home.html b/samples/irs-demo/src/main/resources/irsweb/view/home.html index 7ab4d05107..2cf47d95c8 100644 --- a/samples/irs-demo/src/main/resources/irsweb/view/home.html +++ b/samples/irs-demo/src/main/resources/irsweb/view/home.html @@ -1,4 +1,5 @@
+
{{httpError}}
{{infoMsg}}
diff --git a/test-utils/src/main/kotlin/net/corda/testing/http/HttpApi.kt b/test-utils/src/main/kotlin/net/corda/testing/http/HttpApi.kt index c6f7da5705..42b3221264 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/http/HttpApi.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/http/HttpApi.kt @@ -1,9 +1,10 @@ package net.corda.testing.http +import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.net.HostAndPort import java.net.URL -class HttpApi(val root: URL) { +class HttpApi(val root: URL, val mapper: ObjectMapper = defaultMapper) { /** * Send a PUT with a payload to the path on the API specified. * @@ -21,12 +22,15 @@ class HttpApi(val root: URL) { /** * Send a GET request to the path on the API specified. */ - inline fun getJson(path: String, params: Map = mapOf()) = HttpUtils.getJson(URL(root, path), params) + inline fun getJson(path: String, params: Map = mapOf()) = HttpUtils.getJson(URL(root, path), params, mapper) private fun toJson(any: Any) = any as? String ?: HttpUtils.defaultMapper.writeValueAsString(any) companion object { - fun fromHostAndPort(hostAndPort: HostAndPort, base: String, protocol: String = "http"): HttpApi - = HttpApi(URL("$protocol://$hostAndPort/$base/")) + fun fromHostAndPort(hostAndPort: HostAndPort, base: String, protocol: String = "http", mapper: ObjectMapper = defaultMapper): HttpApi + = HttpApi(URL("$protocol://$hostAndPort/$base/"), mapper) + private val defaultMapper: ObjectMapper by lazy { + net.corda.jackson.JacksonSupport.createNonRpcMapper() + } } } diff --git a/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt b/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt index 9b2e454f13..94eef77537 100644 --- a/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt +++ b/test-utils/src/main/kotlin/net/corda/testing/http/HttpUtils.kt @@ -35,10 +35,10 @@ object HttpUtils { return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build()) } - inline fun getJson(url: URL, params: Map = mapOf()): T { + inline fun getJson(url: URL, params: Map = mapOf(), mapper: ObjectMapper = defaultMapper): T { val paramString = if (params.isEmpty()) "" else "?" + params.map { "${it.key}=${it.value}" }.joinToString("&") val parameterisedUrl = URL(url.toExternalForm() + paramString) - return defaultMapper.readValue(parameterisedUrl, T::class.java) + return mapper.readValue(parameterisedUrl, T::class.java) } private fun makeRequest(request: Request): Boolean {