mirror of
https://github.com/corda/corda.git
synced 2025-02-23 10:30:24 +00:00
Merge pull request #834 from corda/clint-irswebfix
IRS web demo now shows fixings + general IRS fixes
This commit is contained in:
commit
a6853be035
@ -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<LocalDate>) {
|
||||
fun toCalendar() = BusinessCalendar(holidayDates)
|
||||
}
|
||||
|
||||
object CalendarSerializer : JsonSerializer<BusinessCalendar>() {
|
||||
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<BusinessCalendar>() {
|
||||
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}")
|
||||
}
|
||||
|
@ -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<LocalDate>) {
|
||||
open class BusinessCalendar(val holidayDates: List<LocalDate>) {
|
||||
@CordaSerializable
|
||||
class UnknownCalendar(name: String) : Exception("$name not found")
|
||||
|
||||
|
@ -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<LocalDate?> {
|
||||
fun getFloatingLegFixCount(nodeApi: HttpApi) = getTrades(nodeApi)[0].calculation.floatingLegPaymentSchedule.count { it.value.rate.ratioUnit != null }
|
||||
|
||||
fun getFixingDateObservable(config: FullNodeConfiguration): Observable<LocalDate?> {
|
||||
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<InterestRateSwap.State>()
|
||||
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<Array<*>>("deals")
|
||||
private fun getTradeCount(nodeApi: HttpApi): Int {
|
||||
println("Getting trade count from ${nodeApi.root}")
|
||||
val deals = nodeApi.getJson<Array<*>>("deals")
|
||||
return deals.size
|
||||
}
|
||||
|
||||
private fun getTrades(nodeApi: HttpApi): Array<InterestRateSwap.State> {
|
||||
println("Getting trades from ${nodeApi.root}")
|
||||
val deals = nodeApi.getJson<Array<InterestRateSwap.State>>("deals")
|
||||
return deals
|
||||
}
|
||||
|
||||
fun<T> Observable<T>.firstWithTimeout(timeout: Duration, pred: (T) -> Boolean) {
|
||||
first(pred).toFuture().getOrThrow(timeout)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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(() => {
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
@ -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) => {
|
||||
|
@ -1,5 +1,7 @@
|
||||
<div class="ui container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui negative message" id="form-error" ng-show="formError">{{formError}}</div>
|
||||
<div class="ui negative message" id="http-error" ng-show="httpError">{{httpError}}</div>
|
||||
<h3 class="ui horizontal divider header">
|
||||
<i class="list icon"></i>
|
||||
New Deal
|
||||
|
@ -1,5 +1,6 @@
|
||||
<div class="ui container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui negative message" id="http-error" ng-show="httpError">{{httpError}}</div>
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide column" id="common">
|
||||
<table class="ui striped table">
|
||||
@ -199,6 +200,26 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="center aligned">
|
||||
<td colspan="2">
|
||||
<div class="ui accordion">
|
||||
<div class="title">
|
||||
<i class="dropdown icon"></i>
|
||||
Fixings
|
||||
</div>
|
||||
<div class="content">
|
||||
<table class="ui celled small table">
|
||||
<tbody>
|
||||
<tr class="center aligned" ng-repeat="fixing in deal.calculation.floatingLegPaymentSchedule">
|
||||
<td>{{fixing.fixingDate[0]}}-{{fixing.fixingDate[1]}}-{{fixing.fixingDate[2]}}</td>
|
||||
<td>{{fixing.ratePercent}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,4 +1,5 @@
|
||||
<div class="ui container">
|
||||
<div class="ui hidden divider"></div>
|
||||
<div class="ui negative message" id="http-error" ng-show="httpError">{{httpError}}</div>
|
||||
<div class="ui info message" id="info-message" ng-show="infoMsg">{{infoMsg}}</div>
|
||||
<div class="ui active dimmer" ng-show="isLoading()">
|
||||
|
@ -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 <reified T : Any> getJson(path: String, params: Map<String, String> = mapOf()) = HttpUtils.getJson<T>(URL(root, path), params)
|
||||
inline fun <reified T : Any> getJson(path: String, params: Map<String, String> = mapOf()) = HttpUtils.getJson<T>(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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ object HttpUtils {
|
||||
return makeRequest(Request.Builder().url(url).header("Content-Type", "application/json").post(body).build())
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> getJson(url: URL, params: Map<String, String> = mapOf()): T {
|
||||
inline fun <reified T : Any> getJson(url: URL, params: Map<String, String> = 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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user