mirror of
https://github.com/corda/corda.git
synced 2025-03-23 04:25:19 +00:00
Merge pull request #72 from corda/clint-simmdemointegrationtest
Add SIMM valuation demo integration test
This commit is contained in:
commit
d9663f1698
@ -25,49 +25,53 @@ import java.time.LocalDateTime
|
||||
* the java.time API, some core types, and Kotlin data classes.
|
||||
*/
|
||||
object JsonSupport {
|
||||
|
||||
fun createDefaultMapper(identities: IdentityService): ObjectMapper {
|
||||
val mapper = ServiceHubObjectMapper(identities)
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT)
|
||||
mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
|
||||
|
||||
val timeModule = SimpleModule("java.time")
|
||||
timeModule.addSerializer(LocalDate::class.java, ToStringSerializer)
|
||||
timeModule.addDeserializer(LocalDate::class.java, LocalDateDeserializer)
|
||||
timeModule.addKeyDeserializer(LocalDate::class.java, LocalDateKeyDeserializer)
|
||||
timeModule.addSerializer(LocalDateTime::class.java, ToStringSerializer)
|
||||
|
||||
val cordaModule = SimpleModule("core")
|
||||
cordaModule.addSerializer(Party::class.java, PartySerializer)
|
||||
cordaModule.addDeserializer(Party::class.java, PartyDeserializer)
|
||||
cordaModule.addSerializer(BigDecimal::class.java, ToStringSerializer)
|
||||
cordaModule.addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
|
||||
cordaModule.addSerializer(SecureHash::class.java, SecureHashSerializer)
|
||||
// It's slightly remarkable, but apparently Jackson works out that this is the only possibility
|
||||
// for a SecureHash at the moment and tries to use SHA256 directly even though we only give it SecureHash
|
||||
cordaModule.addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer())
|
||||
cordaModule.addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
|
||||
|
||||
// For ed25519 pubkeys
|
||||
cordaModule.addSerializer(EdDSAPublicKey::class.java, PublicKeySerializer)
|
||||
cordaModule.addDeserializer(EdDSAPublicKey::class.java, PublicKeyDeserializer)
|
||||
|
||||
// For composite keys
|
||||
cordaModule.addSerializer(CompositeKey::class.java, CompositeKeySerializer)
|
||||
cordaModule.addDeserializer(CompositeKey::class.java, CompositeKeyDeserializer)
|
||||
|
||||
// For NodeInfo
|
||||
// TODO this tunnels the Kryo representation as a Base58 encoded string. Replace when RPC supports this.
|
||||
cordaModule.addSerializer(NodeInfo::class.java, NodeInfoSerializer)
|
||||
cordaModule.addDeserializer(NodeInfo::class.java, NodeInfoDeserializer)
|
||||
|
||||
mapper.registerModule(timeModule)
|
||||
mapper.registerModule(cordaModule)
|
||||
mapper.registerModule(KotlinModule())
|
||||
return mapper
|
||||
val javaTimeModule : Module by lazy {
|
||||
SimpleModule("java.time").apply {
|
||||
addSerializer(LocalDate::class.java, ToStringSerializer)
|
||||
addDeserializer(LocalDate::class.java, LocalDateDeserializer)
|
||||
addKeyDeserializer(LocalDate::class.java, LocalDateKeyDeserializer)
|
||||
addSerializer(LocalDateTime::class.java, ToStringSerializer)
|
||||
}
|
||||
}
|
||||
|
||||
val cordaModule : Module by lazy {
|
||||
SimpleModule("core").apply {
|
||||
addSerializer(Party::class.java, PartySerializer)
|
||||
addDeserializer(Party::class.java, PartyDeserializer)
|
||||
addSerializer(BigDecimal::class.java, ToStringSerializer)
|
||||
addDeserializer(BigDecimal::class.java, NumberDeserializers.BigDecimalDeserializer())
|
||||
addSerializer(SecureHash::class.java, SecureHashSerializer)
|
||||
// It's slightly remarkable, but apparently Jackson works out that this is the only possibility
|
||||
// for a SecureHash at the moment and tries to use SHA256 directly even though we only give it SecureHash
|
||||
addDeserializer(SecureHash.SHA256::class.java, SecureHashDeserializer())
|
||||
addDeserializer(BusinessCalendar::class.java, CalendarDeserializer)
|
||||
|
||||
// For ed25519 pubkeys
|
||||
addSerializer(EdDSAPublicKey::class.java, PublicKeySerializer)
|
||||
addDeserializer(EdDSAPublicKey::class.java, PublicKeyDeserializer)
|
||||
|
||||
// For composite keys
|
||||
addSerializer(CompositeKey::class.java, CompositeKeySerializer)
|
||||
addDeserializer(CompositeKey::class.java, CompositeKeyDeserializer)
|
||||
|
||||
// For NodeInfo
|
||||
// TODO this tunnels the Kryo representation as a Base58 encoded string. Replace when RPC supports this.
|
||||
addSerializer(NodeInfo::class.java, NodeInfoSerializer)
|
||||
addDeserializer(NodeInfo::class.java, NodeInfoDeserializer)
|
||||
}
|
||||
}
|
||||
|
||||
fun createDefaultMapper(identities: IdentityService): ObjectMapper =
|
||||
ServiceHubObjectMapper(identities).apply {
|
||||
enable(SerializationFeature.INDENT_OUTPUT)
|
||||
enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
|
||||
enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
|
||||
|
||||
registerModule(javaTimeModule)
|
||||
registerModule(cordaModule)
|
||||
registerModule(KotlinModule())
|
||||
}
|
||||
|
||||
class ServiceHubObjectMapper(val identities: IdentityService) : ObjectMapper()
|
||||
|
||||
object ToStringSerializer : JsonSerializer<Any>() {
|
||||
|
@ -34,6 +34,18 @@ sourceSets {
|
||||
srcDir "../../config/test"
|
||||
}
|
||||
}
|
||||
integrationTest {
|
||||
kotlin {
|
||||
compileClasspath += main.output + test.output
|
||||
runtimeClasspath += main.output + test.output
|
||||
srcDir file('src/integration-test/kotlin')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestRuntime.extendsFrom testRuntime
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -105,6 +117,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['build']) {
|
||||
}
|
||||
}
|
||||
|
||||
task integrationTest(type: Test, dependsOn: []) {
|
||||
testClassesDir = sourceSets.integrationTest.output.classesDir
|
||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||
}
|
||||
|
||||
task npmInstall(type: Exec) {
|
||||
workingDir 'src/main/web'
|
||||
|
@ -0,0 +1,78 @@
|
||||
package net.corda.vega
|
||||
|
||||
import com.opengamma.strata.product.common.BuySell
|
||||
import net.corda.core.getOrThrow
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
import net.corda.node.driver.NodeHandle
|
||||
import net.corda.node.driver.driver
|
||||
import net.corda.node.services.transactions.SimpleNotaryService
|
||||
import net.corda.testing.IntegrationTestCategory
|
||||
import net.corda.testing.getHostAndPort
|
||||
import net.corda.testing.http.HttpApi
|
||||
import net.corda.vega.api.PortfolioApi
|
||||
import net.corda.vega.api.PortfolioApiUtils
|
||||
import net.corda.vega.api.SwapDataModel
|
||||
import net.corda.vega.api.SwapDataView
|
||||
import net.corda.vega.portfolio.Portfolio
|
||||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
||||
import java.time.LocalDate
|
||||
import java.util.*
|
||||
import java.util.concurrent.Future
|
||||
|
||||
class SimmValuationTest: IntegrationTestCategory {
|
||||
private companion object {
|
||||
// SIMM demo can only currently handle one valuation date due to a lack of market data or a market data source.
|
||||
val valuationDate = LocalDate.parse("2016-06-06")
|
||||
val nodeALegalName = "Bank A"
|
||||
val nodeBLegalName = "Bank B"
|
||||
val testTradeId = "trade1"
|
||||
}
|
||||
|
||||
@Test fun `runs SIMM valuation demo`() {
|
||||
driver(isDebug = true) {
|
||||
startNode("Controller", setOf(ServiceInfo(SimpleNotaryService.type))).getOrThrow()
|
||||
val nodeA = getSimmNodeApi(startNode(nodeALegalName))
|
||||
val nodeB = getSimmNodeApi(startNode(nodeBLegalName))
|
||||
val nodeBParty = getPartyWithName(nodeA, nodeBLegalName)
|
||||
val nodeAParty = getPartyWithName(nodeB, nodeALegalName)
|
||||
|
||||
assert(createTradeBetween(nodeA, nodeBParty, testTradeId))
|
||||
assert(tradeExists(nodeB, nodeAParty, testTradeId))
|
||||
assert(runValuationsBetween(nodeA, nodeBParty))
|
||||
assert(valuationExists(nodeB, nodeAParty))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSimmNodeApi(futureNode: Future<NodeHandle>): HttpApi {
|
||||
val nodeAddr = futureNode.getOrThrow().config.getHostAndPort("webAddress")
|
||||
return HttpApi.fromHostAndPort(nodeAddr, "api/simmvaluationdemo")
|
||||
}
|
||||
|
||||
private fun getPartyWithName(partyApi: HttpApi, countryparty: String): PortfolioApi.ApiParty =
|
||||
getAvailablePartiesFor(partyApi).counterparties.single { it.text == countryparty }
|
||||
|
||||
private fun getAvailablePartiesFor(partyApi: HttpApi): PortfolioApi.AvailableParties {
|
||||
return partyApi.getJson<PortfolioApi.AvailableParties>("whoami")
|
||||
}
|
||||
|
||||
private fun createTradeBetween(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty, tradeId: String): Boolean {
|
||||
val trade = SwapDataModel(tradeId, "desc", valuationDate, "EUR_FIXED_1Y_EURIBOR_3M",
|
||||
valuationDate, LocalDate.parse("2020-01-02"), BuySell.BUY, BigDecimal.valueOf(1000), BigDecimal.valueOf(0.1))
|
||||
return partyApi.putJson("${counterparty.id}/trades", trade)
|
||||
}
|
||||
|
||||
private fun tradeExists(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty, tradeId: String): Boolean {
|
||||
val trades = partyApi.getJson<Array<SwapDataView>>("${counterparty.id}/trades")
|
||||
return (trades.find { it.id == tradeId } != null)
|
||||
}
|
||||
|
||||
private fun runValuationsBetween(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty): Boolean {
|
||||
return partyApi.postJson("${counterparty.id}/portfolio/valuations/calculate", PortfolioApi.ValuationCreationParams(valuationDate))
|
||||
}
|
||||
|
||||
private fun valuationExists(partyApi: HttpApi, counterparty: PortfolioApi.ApiParty): Boolean {
|
||||
val valuations = partyApi.getJson<PortfolioApiUtils.ValuationsView>("${counterparty.id}/portfolio/valuations")
|
||||
return (valuations.initialMargin.call["total"] != 0.0)
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package net.corda.vega.api
|
||||
|
||||
/**
|
||||
* A small JSON DSL to create structures for APIs on the fly that mimic JSON in structure.
|
||||
* Use: json { obj("a" to 100, "b" to "hello", "c" to arr(1, 2, "c")) }
|
||||
*/
|
||||
class JsonBuilder {
|
||||
fun obj(vararg objs: Pair<String, Any>): Map<String, Any> {
|
||||
return objs.toMap()
|
||||
}
|
||||
|
||||
fun arr(vararg objs: Any): List<Any> {
|
||||
return objs.toList()
|
||||
}
|
||||
}
|
||||
|
||||
fun json(body: JsonBuilder.() -> Map<String, Any>): Map<String, Any> {
|
||||
val jsonWrapper = JsonBuilder()
|
||||
return jsonWrapper.body()
|
||||
}
|
@ -98,11 +98,9 @@ class PortfolioApi(val rpc: CordaRPCOps) {
|
||||
@Path("business-date")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
fun getBusinessDate(): Any {
|
||||
return json {
|
||||
obj(
|
||||
"business-date" to LocalDateTime.ofInstant(rpc.currentNodeTime(), ZoneId.systemDefault()).toLocalDate()
|
||||
)
|
||||
}
|
||||
return mapOf(
|
||||
"business-date" to LocalDateTime.ofInstant(rpc.currentNodeTime(), ZoneId.systemDefault()).toLocalDate()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,12 +193,10 @@ class PortfolioApi(val rpc: CordaRPCOps) {
|
||||
return withParty(partyName) { party ->
|
||||
val trades = getTradesWith(party)
|
||||
val portfolio = Portfolio(trades)
|
||||
val summary = json {
|
||||
obj(
|
||||
"trades" to portfolio.trades.size,
|
||||
"notional" to portfolio.getNotionalForParty(ownParty).toDouble()
|
||||
)
|
||||
}
|
||||
val summary = mapOf(
|
||||
"trades" to portfolio.trades.size,
|
||||
"notional" to portfolio.getNotionalForParty(ownParty).toDouble()
|
||||
)
|
||||
Response.ok().entity(summary).build()
|
||||
}
|
||||
}
|
||||
@ -239,28 +235,23 @@ class PortfolioApi(val rpc: CordaRPCOps) {
|
||||
}
|
||||
}
|
||||
|
||||
data class ApiParty(val id: String, val text: String)
|
||||
data class AvailableParties(val self: ApiParty, val counterparties: List<ApiParty>)
|
||||
|
||||
/**
|
||||
* Returns the identity of the current node as well as a list of other counterparties that it is aware of.
|
||||
*/
|
||||
@GET
|
||||
@Path("whoami")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
fun getWhoAmI(): Any {
|
||||
val counterParties = rpc.networkMapUpdates().first.filter { it.legalIdentity.name != "NetworkMapService" && it.legalIdentity.name != "Notary" && it.legalIdentity.name != ownParty.name }
|
||||
return json {
|
||||
obj(
|
||||
"self" to obj(
|
||||
"id" to ownParty.owningKey.toBase58String(),
|
||||
"text" to ownParty.name
|
||||
),
|
||||
"counterparties" to counterParties.map {
|
||||
obj(
|
||||
"id" to it.legalIdentity.owningKey.toBase58String(),
|
||||
"text" to it.legalIdentity.name
|
||||
)
|
||||
}
|
||||
)
|
||||
fun getWhoAmI(): AvailableParties {
|
||||
val counterParties = rpc.networkMapUpdates().first.filter {
|
||||
it.legalIdentity.name != "NetworkMapService" && it.legalIdentity.name != "Notary" && it.legalIdentity.name != ownParty.name
|
||||
}
|
||||
|
||||
return AvailableParties(
|
||||
self = ApiParty(ownParty.owningKey.toBase58String(), ownParty.name),
|
||||
counterparties = counterParties.map { ApiParty(it.legalIdentity.owningKey.toBase58String(), it.legalIdentity.name) })
|
||||
}
|
||||
|
||||
data class ValuationCreationParams(val valuationDate: LocalDate)
|
||||
|
@ -15,7 +15,16 @@ import java.time.LocalDate
|
||||
* API JSON generation functions for larger JSON outputs.
|
||||
*/
|
||||
class PortfolioApiUtils(private val ownParty: Party) {
|
||||
fun createValuations(state: PortfolioState, portfolio: Portfolio): Any {
|
||||
data class InitialMarginView(val baseCurrency: String, val post: Map<String, Double>, val call: Map<String, Double>, val agreed: Boolean)
|
||||
data class ValuationsView(
|
||||
val businessDate: LocalDate,
|
||||
val portfolio: Map<String, Any>,
|
||||
val marketData: Map<String, Any>,
|
||||
val sensitivities: Map<String, Any>,
|
||||
val initialMargin: InitialMarginView,
|
||||
val confirmation: Map<String, Any>)
|
||||
|
||||
fun createValuations(state: PortfolioState, portfolio: Portfolio): ValuationsView {
|
||||
val valuation = state.valuation!!
|
||||
|
||||
val currency = if (portfolio.trades.isNotEmpty()) {
|
||||
@ -32,141 +41,137 @@ class PortfolioApiUtils(private val ownParty: Party) {
|
||||
|
||||
val completeSubgroups = subgroups.mapValues { it.value.mapValues { it.value[0].third.toDouble() }.toSortedMap() }
|
||||
|
||||
val yieldCurves = json {
|
||||
obj(
|
||||
"name" to "EUR",
|
||||
"values" to completeSubgroups.get("EUR")!!.filter { !it.key.contains("Fixing") }.map {
|
||||
json {
|
||||
obj(
|
||||
"tenor" to it.key,
|
||||
"rate" to it.value
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val fixings = json {
|
||||
obj(
|
||||
"name" to "EUR",
|
||||
"values" to completeSubgroups.get("EUR")!!.filter { it.key.contains("Fixing") }.map {
|
||||
json {
|
||||
obj(
|
||||
"tenor" to it.key,
|
||||
"rate" to it.value
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val yieldCurves = mapOf(
|
||||
"name" to "EUR",
|
||||
"values" to completeSubgroups.get("EUR")!!.filter { !it.key.contains("Fixing") }.map {
|
||||
mapOf(
|
||||
"tenor" to it.key,
|
||||
"rate" to it.value
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val fixings = mapOf(
|
||||
"name" to "EUR",
|
||||
"values" to completeSubgroups.get("EUR")!!.filter { it.key.contains("Fixing") }.map {
|
||||
mapOf(
|
||||
"tenor" to it.key,
|
||||
"rate" to it.value
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val processedSensitivities = valuation.totalSensivities.sensitivities.map { it.marketDataName to it.parameterMetadata.map { it.label }.zip(it.sensitivity.toList()).toMap() }.toMap()
|
||||
|
||||
return json {
|
||||
obj(
|
||||
"businessDate" to LocalDate.now(),
|
||||
"portfolio" to obj(
|
||||
"trades" to tradeCount,
|
||||
"baseCurrency" to currency,
|
||||
"IRFX" to tradeCount,
|
||||
"commodity" to 0,
|
||||
"equity" to 0,
|
||||
"credit" to 0,
|
||||
"total" to tradeCount,
|
||||
"agreed" to true
|
||||
),
|
||||
"marketData" to obj(
|
||||
"yieldCurves" to yieldCurves,
|
||||
"fixings" to fixings,
|
||||
"agreed" to true
|
||||
),
|
||||
"sensitivities" to obj("curves" to processedSensitivities,
|
||||
"currency" to valuation.currencySensitivies.amounts.toList().map {
|
||||
obj(
|
||||
"currency" to it.currency.code,
|
||||
"amount" to it.amount
|
||||
)
|
||||
},
|
||||
"agreed" to true
|
||||
),
|
||||
"initialMargin" to obj(
|
||||
"baseCurrency" to currency,
|
||||
"post" to obj(
|
||||
"IRFX" to valuation.margin.first,
|
||||
"commodity" to 0,
|
||||
"equity" to 0,
|
||||
"credit" to 0,
|
||||
"total" to valuation.margin.first
|
||||
),
|
||||
"call" to obj(
|
||||
"IRFX" to valuation.margin.first,
|
||||
"commodity" to 0,
|
||||
"equity" to 0,
|
||||
"credit" to 0,
|
||||
"total" to valuation.margin.first
|
||||
),
|
||||
"agreed" to true
|
||||
),
|
||||
"confirmation" to obj(
|
||||
"hash" to state.hash().toString(),
|
||||
"agreed" to true
|
||||
)
|
||||
)
|
||||
}
|
||||
val initialMarginView = InitialMarginView(
|
||||
baseCurrency = currency,
|
||||
post = mapOf(
|
||||
"IRFX" to valuation.margin.first,
|
||||
"commodity" to 0.0,
|
||||
"equity" to 0.0,
|
||||
"credit" to 0.0,
|
||||
"total" to valuation.margin.first
|
||||
),
|
||||
call = mapOf(
|
||||
"IRFX" to valuation.margin.first,
|
||||
"commodity" to 0.0,
|
||||
"equity" to 0.0,
|
||||
"credit" to 0.0,
|
||||
"total" to valuation.margin.first
|
||||
),
|
||||
agreed = true)
|
||||
|
||||
return ValuationsView(
|
||||
businessDate = LocalDate.now(),
|
||||
portfolio = mapOf(
|
||||
"trades" to tradeCount,
|
||||
"baseCurrency" to currency,
|
||||
"IRFX" to tradeCount,
|
||||
"commodity" to 0,
|
||||
"equity" to 0,
|
||||
"credit" to 0,
|
||||
"total" to tradeCount,
|
||||
"agreed" to true
|
||||
),
|
||||
marketData = mapOf(
|
||||
"yieldCurves" to yieldCurves,
|
||||
"fixings" to fixings,
|
||||
"agreed" to true
|
||||
),
|
||||
sensitivities = mapOf("curves" to processedSensitivities,
|
||||
"currency" to valuation.currencySensitivies.amounts.toList().map {
|
||||
mapOf(
|
||||
"currency" to it.currency.code,
|
||||
"amount" to it.amount
|
||||
)
|
||||
},
|
||||
"agreed" to true
|
||||
),
|
||||
initialMargin = initialMarginView,
|
||||
confirmation = mapOf(
|
||||
"hash" to state.hash().toString(),
|
||||
"agreed" to true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun createTradeView(state: IRSState): Any {
|
||||
data class TradeView(
|
||||
val fixedLeg: Map<String, Any>,
|
||||
val floatingLeg: Map<String, Any>,
|
||||
val common: Map<String, Any>,
|
||||
val ref: String)
|
||||
|
||||
fun createTradeView(state: IRSState): TradeView {
|
||||
val trade = if (state.buyer.name == ownParty.name) state.swap.toFloatingLeg() else state.swap.toFloatingLeg()
|
||||
val fixedLeg = trade.product.legs.first { it.type == SwapLegType.FIXED } as RateCalculationSwapLeg
|
||||
val floatingLeg = trade.product.legs.first { it.type != SwapLegType.FIXED } as RateCalculationSwapLeg
|
||||
val fixedRate = fixedLeg.calculation as FixedRateCalculation
|
||||
val floatingRate = floatingLeg.calculation as IborRateCalculation
|
||||
|
||||
return json {
|
||||
obj(
|
||||
"fixedLeg" to obj(
|
||||
"fixedRatePayer" to state.buyer.name,
|
||||
"notional" to obj(
|
||||
"token" to fixedLeg.currency.code,
|
||||
"quantity" to fixedLeg.notionalSchedule.amount.initialValue
|
||||
),
|
||||
"paymentFrequency" to fixedLeg.paymentSchedule.paymentFrequency.toString(),
|
||||
"effectiveDate" to fixedLeg.startDate.unadjusted,
|
||||
"terminationDate" to fixedLeg.endDate.unadjusted,
|
||||
"fixedRate" to obj(
|
||||
"value" to fixedRate.rate.initialValue
|
||||
),
|
||||
"paymentRule" to fixedLeg.paymentSchedule.paymentRelativeTo.name,
|
||||
"calendar" to arr("TODO"),
|
||||
"paymentCalendar" to obj() // TODO
|
||||
),
|
||||
"floatingLeg" to obj(
|
||||
"floatingRatePayer" to state.seller.name,
|
||||
"notional" to obj(
|
||||
"token" to floatingLeg.currency.code,
|
||||
"quantity" to floatingLeg.notionalSchedule.amount.initialValue
|
||||
),
|
||||
"paymentFrequency" to floatingLeg.paymentSchedule.paymentFrequency.toString(),
|
||||
"effectiveDate" to floatingLeg.startDate.unadjusted,
|
||||
"terminationDate" to floatingLeg.endDate.unadjusted,
|
||||
"index" to floatingRate.index.name,
|
||||
"paymentRule" to floatingLeg.paymentSchedule.paymentRelativeTo,
|
||||
"calendar" to arr("TODO"),
|
||||
"paymentCalendar" to arr("TODO"),
|
||||
"fixingCalendar" to obj() // TODO
|
||||
),
|
||||
"common" to obj(
|
||||
"valuationDate" to trade.product.startDate.unadjusted,
|
||||
"hashLegalDocs" to state.contract.legalContractReference.toString(),
|
||||
"interestRate" to obj(
|
||||
"name" to "TODO",
|
||||
"oracle" to "TODO",
|
||||
"tenor" to obj(
|
||||
"name" to "TODO"
|
||||
)
|
||||
)
|
||||
),
|
||||
"ref" to trade.info.id.get().value
|
||||
)
|
||||
}
|
||||
return TradeView(
|
||||
fixedLeg = mapOf(
|
||||
"fixedRatePayer" to state.buyer.name,
|
||||
"notional" to mapOf(
|
||||
"token" to fixedLeg.currency.code,
|
||||
"quantity" to fixedLeg.notionalSchedule.amount.initialValue
|
||||
),
|
||||
"paymentFrequency" to fixedLeg.paymentSchedule.paymentFrequency.toString(),
|
||||
"effectiveDate" to fixedLeg.startDate.unadjusted,
|
||||
"terminationDate" to fixedLeg.endDate.unadjusted,
|
||||
"fixedRate" to mapOf(
|
||||
"value" to fixedRate.rate.initialValue
|
||||
),
|
||||
"paymentRule" to fixedLeg.paymentSchedule.paymentRelativeTo.name,
|
||||
"calendar" to listOf("TODO"),
|
||||
"paymentCalendar" to mapOf<String, Any>() // TODO
|
||||
),
|
||||
floatingLeg = mapOf(
|
||||
"floatingRatePayer" to state.seller.name,
|
||||
"notional" to mapOf(
|
||||
"token" to floatingLeg.currency.code,
|
||||
"quantity" to floatingLeg.notionalSchedule.amount.initialValue
|
||||
),
|
||||
"paymentFrequency" to floatingLeg.paymentSchedule.paymentFrequency.toString(),
|
||||
"effectiveDate" to floatingLeg.startDate.unadjusted,
|
||||
"terminationDate" to floatingLeg.endDate.unadjusted,
|
||||
"index" to floatingRate.index.name,
|
||||
"paymentRule" to floatingLeg.paymentSchedule.paymentRelativeTo,
|
||||
"calendar" to listOf("TODO"),
|
||||
"paymentCalendar" to listOf("TODO"),
|
||||
"fixingCalendar" to mapOf<String, Any>() // TODO
|
||||
),
|
||||
common = mapOf(
|
||||
"valuationDate" to trade.product.startDate.unadjusted,
|
||||
"hashLegalDocs" to state.contract.legalContractReference.toString(),
|
||||
"interestRate" to mapOf(
|
||||
"name" to "TODO",
|
||||
"oracle" to "TODO",
|
||||
"tenor" to mapOf(
|
||||
"name" to "TODO"
|
||||
)
|
||||
)
|
||||
),
|
||||
ref = trade.info.id.get().value
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,12 @@ class HttpApi(val root: URL) {
|
||||
*/
|
||||
fun postJson(path: String, data: Any = Unit) = HttpUtils.postJson(URL(root, path), toJson(data))
|
||||
|
||||
private fun toJson(any: Any) = if (any is String) any else ObjectMapper().writeValueAsString(any)
|
||||
/**
|
||||
* 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)
|
||||
|
||||
private fun toJson(any: Any) = any as? String ?: HttpUtils.defaultMapper.writeValueAsString(any)
|
||||
|
||||
companion object {
|
||||
fun fromHostAndPort(hostAndPort: HostAndPort, base: String, protocol: String = "http"): HttpApi
|
||||
|
@ -1,6 +1,9 @@
|
||||
package net.corda.testing.http
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.KotlinModule
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.node.utilities.JsonSupport
|
||||
import okhttp3.*
|
||||
import java.net.URL
|
||||
import java.util.concurrent.TimeUnit
|
||||
@ -15,6 +18,9 @@ object HttpUtils {
|
||||
.connectTimeout(5, TimeUnit.SECONDS)
|
||||
.readTimeout(60, TimeUnit.SECONDS).build()
|
||||
}
|
||||
val defaultMapper: ObjectMapper by lazy {
|
||||
ObjectMapper().registerModule(JsonSupport.javaTimeModule).registerModule(KotlinModule())
|
||||
}
|
||||
|
||||
fun putJson(url: URL, data: String) : Boolean {
|
||||
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), data)
|
||||
@ -26,12 +32,19 @@ 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
private fun makeRequest(request: Request): Boolean {
|
||||
val response = client.newCall(request).execute()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
logger.error("Could not fulfill HTTP request of type ${request.method()} to ${request.url()}. Status Code: ${response.code()}. Message: ${response.body().string()}")
|
||||
}
|
||||
|
||||
return response.isSuccessful
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user