From 2d1336953a1c83d3c3293db6b47014b483a3484d Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Tue, 19 May 2020 20:51:55 +0200 Subject: [PATCH 01/26] integrate permissions contract and setup integration test Signed-off-by: Adam Staveley --- build.gradle.kts | 1 - .../contracts/Permissions.java | 331 ++++++++++++++++++ .../contracts/Registry.java | 5 +- .../node/config/NodeConfig.kt | 13 +- .../node/config/NodeInfoLogger.kt | 20 +- .../node/config/NodeProperties.kt | 1 + .../node/listeners/HubClientInfoListener.kt | 1 + .../openchargingnetwork/node/models/Ocn.kt | 20 ++ .../node/services/RequestHandlerBuilder.kt | 1 + .../node/services/RoutingService.kt | 30 ++ src/main/resources/application.dev.properties | 1 + .../resources/application.docker.properties | 1 + .../node/integration/AppInterfaceTest.kt | 38 ++ .../HubClientInfoIntegrationTest.kt | 27 +- .../integration/PlannedPartySearchTest.kt | 25 +- .../node/integration/parties/CpoServer.kt | 22 +- .../node/integration/parties/MspServer.kt | 30 +- .../node/integration/parties/PartyServer.kt | 54 ++- .../node/integration/utils/Network.kt | 59 ++-- .../node/integration/utils/Types.kt | 10 +- .../node/services/RoutingServiceTest.kt | 3 + 21 files changed, 580 insertions(+), 113 deletions(-) create mode 100644 src/main/java/snc/openchargingnetwork/contracts/Permissions.java create mode 100644 src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 8df90ec..deeb3d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -96,7 +96,6 @@ tasks.register("integrationTest") { tasks.register("ganache") { group = "help" description = "Runs a ganache-cli instance for integration testing." - commandLine(listOf("/usr/bin/env", "npm", "install", "-g", "ganache-cli")) commandLine(listOf( "/usr/bin/env", "ganache-cli", diff --git a/src/main/java/snc/openchargingnetwork/contracts/Permissions.java b/src/main/java/snc/openchargingnetwork/contracts/Permissions.java new file mode 100644 index 0000000..3512d43 --- /dev/null +++ b/src/main/java/snc/openchargingnetwork/contracts/Permissions.java @@ -0,0 +1,331 @@ +package snc.openchargingnetwork.contracts; + +import io.reactivex.Flowable; +import io.reactivex.functions.Function; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.Callable; +import org.web3j.abi.EventEncoder; +import org.web3j.abi.FunctionEncoder; +import org.web3j.abi.TypeReference; +import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.DynamicArray; +import org.web3j.abi.datatypes.Event; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.Utf8String; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.crypto.Credentials; +import org.web3j.protocol.Web3j; +import org.web3j.protocol.core.DefaultBlockParameter; +import org.web3j.protocol.core.RemoteCall; +import org.web3j.protocol.core.RemoteFunctionCall; +import org.web3j.protocol.core.methods.request.EthFilter; +import org.web3j.protocol.core.methods.response.BaseEventResponse; +import org.web3j.protocol.core.methods.response.Log; +import org.web3j.protocol.core.methods.response.TransactionReceipt; +import org.web3j.tuples.generated.Tuple3; +import org.web3j.tx.Contract; +import org.web3j.tx.TransactionManager; +import org.web3j.tx.gas.ContractGasProvider; + +/** + *

Auto generated code. + *

Do not modify! + *

Please use the web3j command line tools, + * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the + * codegen module to update. + * + *

Generated with web3j version 4.5.16. + */ +@SuppressWarnings("rawtypes") +public class Permissions extends Contract { + public static final String BINARY = "0x608060405234801561001057600080fd5b50604051611db4380380611db48339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050611d20806100946000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063adb9cae61161005b578063adb9cae6146103ab578063c13489ca14610444578063dc3acebe1461062a578063edc922a91461079b57610088565b8063099e97621461008d578063340f1e7c1461029457806350f3fc81146102f9578063884eb94914610367575b600080fd5b610292600480360360c08110156100a357600080fd5b81019080803590602001906401000000008111156100c057600080fd5b8201836020820111156100d257600080fd5b803590602001918460018302840111640100000000831117156100f457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561015757600080fd5b82018360208201111561016957600080fd5b8035906020019184600183028401116401000000008311171561018b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156101ee57600080fd5b82018360208201111561020057600080fd5b8035906020019184602083028401116401000000008311171561022257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff16906020019092919080359060200190929190803590602001909291905050506107fa565b005b6102f7600480360360808110156102aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050610a27565b005b6103256004803603602081101561030f57600080fd5b8101908080359060200190929190505050610bac565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103a96004803603602081101561037d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610be8565b005b6103ed600480360360208110156103c157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bf5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610430578082015181840152602081019050610415565b505050509050019250505060405180910390f35b6106286004803603606081101561045a57600080fd5b810190808035906020019064010000000081111561047757600080fd5b82018360208201111561048957600080fd5b803590602001918460018302840111640100000000831117156104ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561050e57600080fd5b82018360208201111561052057600080fd5b8035906020019184600183028401116401000000008311171561054257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156105a557600080fd5b8201836020820111156105b757600080fd5b803590602001918460208302840111640100000000831117156105d957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050610cc2565b005b61066c6004803603602081101561064057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cd3565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156106b4578082015181840152602081019050610699565b50505050905090810190601f1680156106e15780820380516001836020036101000a031916815260200191505b50848103835286818151815260200191508051906020019080838360005b8381101561071a5780820151818401526020810190506106ff565b50505050905090810190601f1680156107475780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019060200280838360005b83811015610783578082015181840152602081019050610768565b50505050905001965050505050505060405180910390f35b6107a3610f2c565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156107e65780820151818401526020810190506107cb565b505050509050019250505060405180910390f35b60008686866040516020018084805190602001908083835b602083106108355780518252602082019150602081019050602083039250610812565b6001836020036101000a03801982511681845116808217855250505050505090500183805190602001908083835b602083106108865780518252602082019150602081019050602083039250610863565b6001836020036101000a038019825116818451168082178552505050505050905001828051906020019060200280838360005b838110156108d45780820151818401526020810190506108b9565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106109695780518252602082019150602081019050602083039250610946565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610a05573d6000803e3d6000fd5b505050602060405103519050610a1d81898989610fba565b5050505050505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310610af25780518252602082019150602081019050602083039250610acf565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610b8e573d6000803e3d6000fd5b505050602060405103519050610ba481876115b6565b505050505050565b60018181548110610bb957fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610bf233826115b6565b50565b6060600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610cb657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610c6c575b50505050509050919050565b610cce33848484610fba565b505050565b6060806060600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610dae5780601f10610d8357610100808354040283529160200191610dae565b820191906000526020600020905b815481529060010190602001808311610d9157829003601f168201915b50505050509250600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e8b5780601f10610e6057610100808354040283529160200191610e8b565b820191906000526020600020905b815481529060010190602001808311610e6e57829003601f168201915b50505050509150600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600201805480602002602001604051908101604052809291908181526020018280548015610f1e57602002820191906000526020600020905b815481526020019060010190808311610f0a575b505050505090509193909250565b60606001805480602002602001604051908101604052809291908181526020018280548015610fb057602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610f66575b5050505050905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b15801561105a57600080fd5b505afa15801561106e573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561109857600080fd5b8101908080519060200190929190805160405193929190846401000000008211156110c257600080fd5b838201915060208201858111156110d857600080fd5b82518660018202830111640100000000821117156110f557600080fd5b8083526020830192505050908051906020019080838360005b8381101561112957808201518184015260208101905061110e565b50505050905090810190601f1680156111565780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156111e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611cb0603c913960400191505060405180910390fd5b600082511161125d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f207065726d697373696f6e7320676976656e2e000000000000000000000081525060200191505060405180910390fd5b604051806060016040528085815260200184815260200183815250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000190805190602001906112d3929190611b62565b5060208201518160010190805190602001906112f0929190611b62565b50604082015181600201908051906020019061130d929190611be2565b5090505060001515600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514156114295760018590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b7f5217645fd1a5d80a7a561ee8df6541e94bff9b1151d8c1281e17c1a304d9d6fa84848488604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845288818151815260200191508051906020019080838360005b838110156114c85780820151818401526020810190506114ad565b50505050905090810190601f1680156114f55780820380516001836020036101000a031916815260200191505b50848103835287818151815260200191508051906020019080838360005b8381101561152e578082015181840152602081019050611513565b50505050905090810190601f16801561155b5780820380516001836020036101000a031916815260200191505b50848103825286818151815260200191508051906020019060200280838360005b8381101561159757808201518184015260208101905061157c565b5050505090500197505050505050505060405180910390a15050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b15801561165657600080fd5b505afa15801561166a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561169457600080fd5b8101908080519060200190929190805160405193929190846401000000008211156116be57600080fd5b838201915060208201858111156116d457600080fd5b82518660018202830111640100000000821117156116f157600080fd5b8083526020830192505050908051906020019080838360005b8381101561172557808201518184015260208101905061170a565b50505050905090810190601f1680156117525780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156117e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180611c55602a913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146118a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f50726f766964657220686173206e6f2072656769737465726564204170702e0081525060200191505060405180910390fd5b60001515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151461198e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180611c7f6031913960400191505060405180910390fd5b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f59c05fe4657968f976c095206514e77e8ed37c2fe56e7614cd8f245dfbb008298383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611ba357805160ff1916838001178555611bd1565b82800160010185558215611bd1579182015b82811115611bd0578251825591602001919060010190611bb5565b5b509050611bde9190611c2f565b5090565b828054828255906000526020600020908101928215611c1e579160200282015b82811115611c1d578251825591602001919060010190611c02565b5b509050611c2b9190611c2f565b5090565b611c5191905b80821115611c4d576000816000905550600101611c35565b5090565b9056fe417070207573657220686173206e6f207061727479206c697374696e6720696e2052656769737472792e41677265656d656e7420616c7265616479206d616465206265747765656e207573657220616e642070726f76696465722e547279696e6720746f20726567697374657220616e2061707020776974686f7574207061727479206c697374696e6720696e2052656769737472792ea265627a7a72315820d4d1ad154860d44803cd80584f7935eb25c503b78eaaa5930742156da612405d64736f6c634300050f0032"; + + public static final String FUNC_PROVIDERS = "providers"; + + public static final String FUNC_SETAPP = "setApp"; + + public static final String FUNC_SETAPPRAW = "setAppRaw"; + + public static final String FUNC_GETAPP = "getApp"; + + public static final String FUNC_GETPROVIDERS = "getProviders"; + + public static final String FUNC_CREATEAGREEMENT = "createAgreement"; + + public static final String FUNC_CREATEAGREEMENTRAW = "createAgreementRaw"; + + public static final String FUNC_GETUSERAGREEMENTS = "getUserAgreements"; + + public static final Event APPAGREEMENT_EVENT = new Event("AppAgreement", + Arrays.>asList(new TypeReference

() {}, new TypeReference
() {})); + ; + + public static final Event APPUPDATE_EVENT = new Event("AppUpdate", + Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference>() {}, new TypeReference
() {})); + ; + + protected static final HashMap _addresses; + + static { + _addresses = new HashMap(); + _addresses.put("73799", "0x520896B666fCcDb6458D4eC5C1FdD0D6d9EB97A3"); + _addresses.put("9", "0xf25186B5081Ff5cE73482AD761DB0eB0d25abfBF"); + } + + @Deprecated + protected Permissions(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + protected Permissions(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, credentials, contractGasProvider); + } + + @Deprecated + protected Permissions(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + protected Permissions(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); + } + + public List getAppAgreementEvents(TransactionReceipt transactionReceipt) { + List valueList = extractEventParametersWithLog(APPAGREEMENT_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + AppAgreementEventResponse typedResponse = new AppAgreementEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.user = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.provider = (String) eventValues.getNonIndexedValues().get(1).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public Flowable appAgreementEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(new Function() { + @Override + public AppAgreementEventResponse apply(Log log) { + Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(APPAGREEMENT_EVENT, log); + AppAgreementEventResponse typedResponse = new AppAgreementEventResponse(); + typedResponse.log = log; + typedResponse.user = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.provider = (String) eventValues.getNonIndexedValues().get(1).getValue(); + return typedResponse; + } + }); + } + + public Flowable appAgreementEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(APPAGREEMENT_EVENT)); + return appAgreementEventFlowable(filter); + } + + public List getAppUpdateEvents(TransactionReceipt transactionReceipt) { + List valueList = extractEventParametersWithLog(APPUPDATE_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); + for (Contract.EventValuesWithLog eventValues : valueList) { + AppUpdateEventResponse typedResponse = new AppUpdateEventResponse(); + typedResponse.log = eventValues.getLog(); + typedResponse.name = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.url = (String) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.permissions = (List) eventValues.getNonIndexedValues().get(2).getValue(); + typedResponse.provider = (String) eventValues.getNonIndexedValues().get(3).getValue(); + responses.add(typedResponse); + } + return responses; + } + + public Flowable appUpdateEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(new Function() { + @Override + public AppUpdateEventResponse apply(Log log) { + Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(APPUPDATE_EVENT, log); + AppUpdateEventResponse typedResponse = new AppUpdateEventResponse(); + typedResponse.log = log; + typedResponse.name = (String) eventValues.getNonIndexedValues().get(0).getValue(); + typedResponse.url = (String) eventValues.getNonIndexedValues().get(1).getValue(); + typedResponse.permissions = (List) eventValues.getNonIndexedValues().get(2).getValue(); + typedResponse.provider = (String) eventValues.getNonIndexedValues().get(3).getValue(); + return typedResponse; + } + }); + } + + public Flowable appUpdateEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); + filter.addSingleTopic(EventEncoder.encode(APPUPDATE_EVENT)); + return appUpdateEventFlowable(filter); + } + + public RemoteFunctionCall providers(BigInteger param0) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_PROVIDERS, + Arrays.asList(new org.web3j.abi.datatypes.generated.Uint256(param0)), + Arrays.>asList(new TypeReference
() {})); + return executeRemoteCallSingleValueReturn(function, String.class); + } + + public RemoteFunctionCall setApp(String name, String url, List permissions) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_SETAPP, + Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name), + new org.web3j.abi.datatypes.Utf8String(url), + new org.web3j.abi.datatypes.DynamicArray( + org.web3j.abi.datatypes.generated.Uint256.class, + org.web3j.abi.Utils.typeMap(permissions, org.web3j.abi.datatypes.generated.Uint256.class))), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall setAppRaw(String name, String url, List permissions, BigInteger v, byte[] r, byte[] s) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_SETAPPRAW, + Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name), + new org.web3j.abi.datatypes.Utf8String(url), + new org.web3j.abi.datatypes.DynamicArray( + org.web3j.abi.datatypes.generated.Uint256.class, + org.web3j.abi.Utils.typeMap(permissions, org.web3j.abi.datatypes.generated.Uint256.class)), + new org.web3j.abi.datatypes.generated.Uint8(v), + new org.web3j.abi.datatypes.generated.Bytes32(r), + new org.web3j.abi.datatypes.generated.Bytes32(s)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall>> getApp(String provider) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETAPP, + Arrays.asList(new org.web3j.abi.datatypes.Address(provider)), + Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference>() {})); + return new RemoteFunctionCall>>(function, + new Callable>>() { + @Override + public Tuple3> call() throws Exception { + List results = executeCallMultipleValueReturn(function); + return new Tuple3>( + (String) results.get(0).getValue(), + (String) results.get(1).getValue(), + convertToNative((List) results.get(2).getValue())); + } + }); + } + + public RemoteFunctionCall getProviders() { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETPROVIDERS, + Arrays.asList(), + Arrays.>asList(new TypeReference>() {})); + return new RemoteFunctionCall(function, + new Callable() { + @Override + @SuppressWarnings("unchecked") + public List call() throws Exception { + List result = (List) executeCallSingleValueReturn(function, List.class); + return convertToNative(result); + } + }); + } + + public RemoteFunctionCall createAgreement(String provider) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_CREATEAGREEMENT, + Arrays.asList(new org.web3j.abi.datatypes.Address(provider)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall createAgreementRaw(String provider, BigInteger v, byte[] r, byte[] s) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_CREATEAGREEMENTRAW, + Arrays.asList(new org.web3j.abi.datatypes.Address(provider), + new org.web3j.abi.datatypes.generated.Uint8(v), + new org.web3j.abi.datatypes.generated.Bytes32(r), + new org.web3j.abi.datatypes.generated.Bytes32(s)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall getUserAgreements(String user) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETUSERAGREEMENTS, + Arrays.asList(new org.web3j.abi.datatypes.Address(user)), + Arrays.>asList(new TypeReference>() {})); + return new RemoteFunctionCall(function, + new Callable() { + @Override + @SuppressWarnings("unchecked") + public List call() throws Exception { + List result = (List) executeCallSingleValueReturn(function, List.class); + return convertToNative(result); + } + }); + } + + @Deprecated + public static Permissions load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { + return new Permissions(contractAddress, web3j, credentials, gasPrice, gasLimit); + } + + @Deprecated + public static Permissions load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) { + return new Permissions(contractAddress, web3j, transactionManager, gasPrice, gasLimit); + } + + public static Permissions load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) { + return new Permissions(contractAddress, web3j, credentials, contractGasProvider); + } + + public static Permissions load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) { + return new Permissions(contractAddress, web3j, transactionManager, contractGasProvider); + } + + public static RemoteCall deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, String registryAddress) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(registryAddress))); + return deployRemoteCall(Permissions.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor); + } + + public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, String registryAddress) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(registryAddress))); + return deployRemoteCall(Permissions.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor); + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, String registryAddress) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(registryAddress))); + return deployRemoteCall(Permissions.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor); + } + + @Deprecated + public static RemoteCall deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, String registryAddress) { + String encodedConstructor = FunctionEncoder.encodeConstructor(Arrays.asList(new org.web3j.abi.datatypes.Address(registryAddress))); + return deployRemoteCall(Permissions.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor); + } + + protected String getStaticDeployedAddress(String networkId) { + return _addresses.get(networkId); + } + + public static String getPreviouslyDeployedAddress(String networkId) { + return _addresses.get(networkId); + } + + public static class AppAgreementEventResponse extends BaseEventResponse { + public String user; + + public String provider; + } + + public static class AppUpdateEventResponse extends BaseEventResponse { + public String name; + + public String url; + + public List permissions; + + public String provider; + } +} diff --git a/src/main/java/snc/openchargingnetwork/contracts/Registry.java b/src/main/java/snc/openchargingnetwork/contracts/Registry.java index b0a868f..f18595b 100644 --- a/src/main/java/snc/openchargingnetwork/contracts/Registry.java +++ b/src/main/java/snc/openchargingnetwork/contracts/Registry.java @@ -45,11 +45,11 @@ * or the org.web3j.codegen.SolidityFunctionWrapperGenerator in the * codegen module to update. * - *

Generated with web3j version 4.5.5. + *

Generated with web3j version 4.5.16. */ @SuppressWarnings("rawtypes") public class Registry extends Contract { - private static final String BINARY = "0x6080604052600062000016620000ba60201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350620000c2565b600033905090565b61568280620000d26000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c806399bd6901116100c3578063b29d65151161007c578063b29d651514610cf3578063b2dbadcb14610e80578063bdb8107814610fcc578063d63a84d414610fd6578063df5161281461104f578063f2fde38b146112a75761014d565b806399bd6901146107745780639d20904814610870578063a5b5ffbb1461092d578063a7bcc11d14610b5d578063a8311aa814610c8a578063acaef0e014610ce95761014d565b8063715018a611610115578063715018a61461042e5780637a091aa51461043857806385cb86021461055d57806388cf72a0146106185780638da5cb5b146107085780638f32d59b146107525761014d565b80632d8fe45214610152578063492dbcc51461019657806366acaa331461030557806366e706fb146103645780636c265bbc146103c9575b600080fd5b6101946004803603602081101561016857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506112eb565b005b61030360048036036101008110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291908035906020019064010000000081111561023f57600080fd5b82018360208201111561025157600080fd5b8035906020019184602083028401116401000000008311171561027357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611371565b005b61030d61167f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610350578082015181840152602081019050610335565b505050509050019250505060405180910390f35b6103c76004803603608081101561037a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff169060200190929190803590602001909291908035906020019092919050505061170d565b005b61042c600480360360808110156103df57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611915565b005b610436611a99565b005b6104af6004803603604081101561044e57600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050611bd2565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610521578082015181840152602081019050610506565b50505050905090810190601f16801561054e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b6106166004803603602081101561057357600080fd5b810190808035906020019064010000000081111561059057600080fd5b8201836020820111156105a257600080fd5b803590602001918460018302840111640100000000831117156105c457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611de5565b005b61065a6004803603602081101561062e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611df2565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156106cc5780820151818401526020810190506106b1565b50505050905090810190601f1680156106f95780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b610710611f37565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61075a611f60565b604051808215151515815260200191505060405180910390f35b61086e600480360360a081101561078a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156107c757600080fd5b8201836020820111156107d957600080fd5b803590602001918460018302840111640100000000831117156107fb57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050611fbe565b005b6108b26004803603602081101561088657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612197565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156108f25780820151818401526020810190506108d7565b50505050905090810190601f16801561091f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6109a46004803603604081101561094357600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050612278565b604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200180602001806020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200185810385528a818151815260200191508051906020019060200280838360005b83811015610a57578082015181840152602081019050610a3c565b50505050905001858103845289818151815260200191508051906020019060200280838360005b83811015610a99578082015181840152602081019050610a7e565b50505050905001858103835288818151815260200191508051906020019060200280838360005b83811015610adb578082015181840152602081019050610ac0565b50505050905001858103825286818151815260200191508051906020019080838360005b83811015610b1a578082015181840152602081019050610aff565b50505050905090810190601f168015610b475780820380516001836020036101000a031916815260200191505b509a505050505050505050505060405180910390f35b610c8860048036036080811015610b7357600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080359060200190640100000000811115610be557600080fd5b820183602082011115610bf757600080fd5b80359060200191846020830284011164010000000083111715610c1957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061275a565b005b610c9261276d565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610cd5578082015181840152602081019050610cba565b505050509050019250505060405180910390f35b610cf16127fb565b005b610e7e600480360360c0811015610d0957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610d4657600080fd5b820183602082011115610d5857600080fd5b80359060200191846020830284011164010000000083111715610d7a57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610dda57600080fd5b820183602082011115610dec57600080fd5b80359060200191846020830284011164010000000083111715610e0e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050612806565b005b610fca60048036036040811015610e9657600080fd5b8101908080359060200190640100000000811115610eb357600080fd5b820183602082011115610ec557600080fd5b80359060200191846020830284011164010000000083111715610ee757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610f4757600080fd5b820183602082011115610f5957600080fd5b80359060200191846020830284011164010000000083111715610f7b57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050612a7c565b005b610fd4612a8b565b005b61104d60048036036040811015610fec57600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050612a96565b005b6110916004803603602081101561106557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612be9565b60405180887dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001877cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018060200180602001806020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200185810385528a818151815260200191508051906020019060200280838360005b838110156111a0578082015181840152602081019050611185565b50505050905001858103845289818151815260200191508051906020019060200280838360005b838110156111e25780820151818401526020810190506111c7565b50505050905001858103835288818151815260200191508051906020019060200280838360005b83811015611224578082015181840152602081019050611209565b50505050905001858103825286818151815260200191508051906020019080838360005b83811015611263578082015181840152602081019050611248565b50505050905090810190601f1680156112905780820380516001836020036101000a031916815260200191505b509b50505050505050505050505060405180910390f35b6112e9600480360360208110156112bd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613012565b005b6112f3611f60565b611365576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61136e81613098565b50565b60008888888888604051602001808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401857dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600201847cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600301838051906020019060200280838360005b8381101561146e578082015181840152602081019050611453565b505050509050018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140195505050505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b6020831061153a5780518252602082019150602081019050602083039250611517565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156115d6573d6000803e3d6000fd5b5050506020604051035190508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806154c2602c913960400191505060405180910390fd5b611673818a8a8a8a6132f9565b50505050505050505050565b6060600480548060200260200160405190810160405280929190818152602001828054801561170357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116116b9575b5050505050905090565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106117d857805182526020820191506020810190506020830392506117b5565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611874573d6000803e3d6000fd5b5050506020604051035190508573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611904576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806154c2602c913960400191505060405180910390fd5b61190d81613d0e565b505050505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106119e057805182526020820191506020810190506020830392506119bd565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611a7c573d6000803e3d6000fd5b505050602060405103519050611a9181613098565b505050505050565b611aa1611f60565b611b13576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60006060600060056000867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600860008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd65780601f10611dab57610100808354040283529160200191611dd6565b820191906000526020600020905b815481529060010190602001808311611db957829003601f168201915b50505050509150509250929050565b611def3382614450565b50565b60006060600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169150600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611f2b5780601f10611f0057610100808354040283529160200191611f2b565b820191906000526020600020905b815481529060010190602001808311611f0e57829003601f168201915b50505050509050915091565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16611fa2614840565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60008585604051602001808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140182805190602001908083835b6020831061202d578051825260208201915060208101905060208303925061200a565b6001836020036101000a03801982511681845116808217855250505050505090500192505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106120dc57805182526020820191506020810190506020830392506120b9565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015612178573d6000803e3d6000fd5b50505060206040510351905061218e8187614450565b50505050505050565b6060600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561226c5780601f106122415761010080835404028352916020019161226c565b820191906000526020600020905b81548152906001019060200180831161224f57829003601f168201915b50505050509050919050565b600060608060606000606060056000897dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000887cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169550612354614fb1565b600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600182018054806020026020016040519081016040528092919081815260200182805480156124cb57602002820191906000526020600020906000905b82829054906101000a900460ff1660068111156124a557fe5b8152602001906001019060208260000104928301926001038202915080841161248c5790505b50505050508152602001600282016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801561256157602002820191906000526020600020906000905b82829054906101000a900460ff16600681111561253b57fe5b815260200190600101906020826000010492830192600103820291508084116125225790505b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156125e757602002820191906000526020600020906000905b82829054906101000a900460ff1660068111156125c157fe5b815260200190600101906020826000010492830192600103820291508084116125a85790505b5050505050815250508152505090508060400151955080606001516000015194508060600151602001519350600860008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156127485780601f1061271d57610100808354040283529160200191612748565b820191906000526020600020905b81548152906001019060200180831161272b57829003601f168201915b50505050509150509295509295509295565b61276733858585856132f9565b50505050565b606060098054806020026020016040519081016040528092919081815260200182805480156127f157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116127a7575b5050505050905090565b61280433613098565b565b6000868686604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401838051906020019060200280838360005b83811015612873578082015181840152602081019050612858565b50505050905001828051906020019060200280838360005b838110156128a657808201518184015260208101905061288b565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b6020831061293b5780518252602082019150602081019050602083039250612918565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156129d7573d6000803e3d6000fd5b5050506020604051035190508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614612a67576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806154c2602c913960400191505060405180910390fd5b612a72818888614848565b5050505050505050565b612a87338383614848565b5050565b612a9433613d0e565b565b612a9e611f60565b612b10576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600060056000847dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000837cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050612be481613d0e565b505050565b600080606080606060006060612bfd614fb1565b600760008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160018201805480602002602001604051908101604052809291908181526020018280548015612d7457602002820191906000526020600020906000905b82829054906101000a900460ff166006811115612d4e57fe5b81526020019060010190602082600001049283019260010382029150808411612d355790505b505050505081526020016002820160405180604001604052908160008201805480602002602001604051908101604052809291908181526020018280548015612e0a57602002820191906000526020600020906000905b82829054906101000a900460ff166006811115612de457fe5b81526020019060010190602082600001049283019260010382029150808411612dcb5790505b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015612e9057602002820191906000526020600020906000905b82829054906101000a900460ff166006811115612e6a57fe5b81526020019060010190602082600001049283019260010382029150808411612e515790505b50505050508152505081525050905080600001519750806020015196508060400151935080606001516000015195508060600151602001519450600860008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612fff5780601f10612fd457610100808354040283529160200191612fff565b820191906000526020600020905b815481529060010190602001808311612fe257829003601f168201915b5050505050915050919395979092949650565b61301a611f60565b61308c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61309581614e6d565b50565b6060600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561316d5780601f106131425761010080835404028352916020019161316d565b820191906000526020600020905b81548152906001019060200180831161315057829003601f168201915b5050505050905060008151116131ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061549b6027913960400191505060405180910390fd5b60006002826040518082805190602001908083835b6020831061320657805182526020820191506020810190506020830392506131e3565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902060006101000a81548160ff021916908315150217905550600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061329b9190615020565b8173ffffffffffffffffffffffffffffffffffffffff167fb1f254c0ae74a753185ba13a1b17818bc803e32d05ef56851955c0ebf230f0d6604051808060200182810382526000815260200160200191505060405180910390a25050565b600060f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916847dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415613398576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603e81526020018061551e603e913960400191505060405180910390fd5b600060e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916837cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415613435576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806155c8603a913960400191505060405180910390fd5b60008251116134ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4e6f20726f6c65732070726f76696465642e000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613532576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180615461603a913960400191505060405180910390fd5b600060056000867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061366357508573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6136b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604c815260200180615602604c913960600191505060405180910390fd5b8560056000877dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000867cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141561384a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061555c6021913960400191505060405180910390fd5b60001515600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515141561390a5760098690806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b6001600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555061396a615068565b6040518060800160405280877dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001867cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200185815260200182815250600760008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548161ffff021916908360f01c021790555060208201518160000160026101000a81548162ffffff021916908360e81c02179055506040820151816001019080519060200190613a6c929190615082565b506060820151816002016000820151816000019080519060200190613a92929190615131565b506020820151816001019080519060200190613aaf929190615131565b50505090505082600860008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167ff692507bb861072d3245d22242eb55cb7fdbe2e0d92290177ed7c490eb076cd78888888660000151876020015160405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015613c68578082015181840152602081019050613c4d565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015613caa578082015181840152602081019050613c8f565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015613cec578082015181840152602081019050613cd1565b505050509050019850505050505050505060405180910390a350505050505050565b600073ffffffffffffffffffffffffffffffffffffffff16600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613df3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604b81526020018061557d604b913960600191505060405180910390fd5b600860008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055613e5d614fb1565b600760008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160018201805480602002602001604051908101604052809291908181526020018280548015613fd457602002820191906000526020600020906000905b82829054906101000a900460ff166006811115613fae57fe5b81526020019060010190602082600001049283019260010382029150808411613f955790505b50505050508152602001600282016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801561406a57602002820191906000526020600020906000905b82829054906101000a900460ff16600681111561404457fe5b8152602001906001019060208260000104928301926001038202915080841161402b5790505b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156140f057602002820191906000526020600020906000905b82829054906101000a900460ff1660068111156140ca57fe5b815260200190600101906020826000010492830192600103820291508084116140b15790505b5050505050815250508152505090506005600082600001517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020600082602001517cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600760008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549061ffff02191690556000820160026101000a81549062ffffff021916905560018201600061424b91906151e0565b60028201600080820160006142609190615208565b6001820160006142709190615208565b50505050606080600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167ff692507bb861072d3245d22242eb55cb7fdbe2e0d92290177ed7c490eb076cd78560000151866020015186868760405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156143ad578082015181840152602081019050614392565b50505050905001848103835286818151815260200191508051906020019060200280838360005b838110156143ef5780820151818401526020810190506143d4565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015614431578082015181840152602081019050614416565b505050509050019850505050505050505060405180910390a350505050565b6000815114156144ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c8152602001806153ff603c913960400191505060405180910390fd5b600015156002826040518082805190602001908083835b602083106144e557805182526020820191506020810190506020830392506144c2565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902060009054906101000a900460ff1615151461459b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f446f6d61696e206e616d6520616c726561647920726567697374657265642e0081525060200191505060405180910390fd5b60016002826040518082805190602001908083835b602083106145d357805182526020820191506020810190506020830392506145b0565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902060006101000a81548160ff02191690831515021790555060001515600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514156146dd5760048290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190614788929190615230565b508173ffffffffffffffffffffffffffffffffffffffff167fb1f254c0ae74a753185ba13a1b17818bc803e32d05ef56851955c0ebf230f0d6826040518080602001828103825283818151815260200191508051906020019080838360005b838110156148025780820151818401526020810190506147e7565b50505050905090810190601f16801561482f5780820380516001836020036101000a031916815260200191505b509250505060405180910390a25050565b600033905090565b6000600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614932576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806154ee6030913960400191505060405180910390fd5b82600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600201600001908051906020019061498b9291906152b0565b5081600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160010190805190602001906149e59291906152b0565b506149ee614fb1565b600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160018201805480602002602001604051908101604052809291908181526020018280548015614b6557602002820191906000526020600020906000905b82829054906101000a900460ff166006811115614b3f57fe5b81526020019060010190602082600001049283019260010382029150808411614b265790505b505050505081526020016002820160405180604001604052908160008201805480602002602001604051908101604052809291908181526020018280548015614bfb57602002820191906000526020600020906000905b82829054906101000a900460ff166006811115614bd557fe5b81526020019060010190602082600001049283019260010382029150808411614bbc5790505b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015614c8157602002820191906000526020600020906000905b82829054906101000a900460ff166006811115614c5b57fe5b81526020019060010190602082600001049283019260010382029150808411614c425790505b5050505050815250508152505090508173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167ff692507bb861072d3245d22242eb55cb7fdbe2e0d92290177ed7c490eb076cd7836000015184602001518560400151898960405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015614dc9578082015181840152602081019050614dae565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015614e0b578082015181840152602081019050614df0565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015614e4d578082015181840152602081019050614e32565b505050509050019850505050505050505060405180910390a35050505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614ef3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061543b6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b604051806080016040528060007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016060815260200161501a61535f565b81525090565b50805460018160011615610100020316600290046000825580601f106150465750615065565b601f0160209004906000526020600020908101906150649190615379565b5b50565b604051806040016040528060608152602001606081525090565b82805482825590600052602060002090601f016020900481019282156151205791602002820160005b838211156150f157835183826101000a81548160ff021916908360068111156150d057fe5b021790555092602001926001016020816000010492830192600103026150ab565b801561511e5782816101000a81549060ff02191690556001016020816000010492830192600103026150f1565b505b50905061512d919061539e565b5090565b82805482825590600052602060002090601f016020900481019282156151cf5791602002820160005b838211156151a057835183826101000a81548160ff0219169083600681111561517f57fe5b0217905550926020019260010160208160000104928301926001030261515a565b80156151cd5782816101000a81549060ff02191690556001016020816000010492830192600103026151a0565b505b5090506151dc91906153ce565b5090565b50805460008255601f0160209004906000526020600020908101906152059190615379565b50565b50805460008255601f01602090049060005260206000209081019061522d9190615379565b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061527157805160ff191683800117855561529f565b8280016001018555821561529f579182015b8281111561529e578251825591602001919060010190615283565b5b5090506152ac9190615379565b5090565b82805482825590600052602060002090601f0160209004810192821561534e5791602002820160005b8382111561531f57835183826101000a81548160ff021916908360068111156152fe57fe5b021790555092602001926001016020816000010492830192600103026152d9565b801561534c5782816101000a81549060ff021916905560010160208160000104928301926001030261531f565b505b50905061535b91906153ce565b5090565b604051806040016040528060608152602001606081525090565b61539b91905b8082111561539757600081600090555060010161537f565b5090565b90565b6153cb91905b808211156153c757600081816101000a81549060ff0219169055506001016153a4565b5090565b90565b6153fb91905b808211156153f757600081816101000a81549060ff0219169055506001016153d4565b5090565b9056fe43616e6e6f742073657420656d70747920646f6d61696e206e616d652e205573652064656c6574654e6f6465206d6574686f6420696e73746561642e4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737343616e6e6f742073657420656d707479206f70657261746f722e205573652064656c6574655061727479206d6574686f6420696e73746561642e43616e6e6f742064656c657465206e6f6465207468617420646f6573206e6f742065786973742e5369676e657220616e642070726f7669646564207061727479206164647265737320646966666572656e742e5061727479206e6f7420726567697374657265642e20557365207365745061727479206d6574686f642066697273742e43616e6e6f742073657420656d70747920636f756e7472795f636f64652e205573652064656c6574655061727479206d6574686f6420696e73746561642e50726f7669646564206f70657261746f72206e6f7420726567697374657265642e43616e6e6f742064656c657465207061727479207468617420646f6573206e6f742065786973742e204e6f206f70657261746f7220666f756e6420666f7220676976656e2070617274792e43616e6e6f742073657420656d7074792070617274795f69642e205573652064656c6574655061727479206d6574686f6420696e73746561642e5061727479207769746820636f756e7472795f636f64652f70617274795f696420616c7265616479207265676973746572656420756e64657220646966666572656e7420616464726573732ea265627a7a72315820aee2be1c34f7df01952d3edf3db809c98654d43cc86493ccbc4e11414edba55d64736f6c634300050f0032"; + public static final String BINARY = "0x6080604052600062000016620000ba60201b60201c565b9050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a350620000c2565b600033905090565b61568280620000d26000396000f3fe608060405234801561001057600080fd5b506004361061014d5760003560e01c806399bd6901116100c3578063b29d65151161007c578063b29d651514610cf3578063b2dbadcb14610e80578063bdb8107814610fcc578063d63a84d414610fd6578063df5161281461104f578063f2fde38b146112a75761014d565b806399bd6901146107745780639d20904814610870578063a5b5ffbb1461092d578063a7bcc11d14610b5d578063a8311aa814610c8a578063acaef0e014610ce95761014d565b8063715018a611610115578063715018a61461042e5780637a091aa51461043857806385cb86021461055d57806388cf72a0146106185780638da5cb5b146107085780638f32d59b146107525761014d565b80632d8fe45214610152578063492dbcc51461019657806366acaa331461030557806366e706fb146103645780636c265bbc146103c9575b600080fd5b6101946004803603602081101561016857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506112eb565b005b61030360048036036101008110156101ad57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690602001909291908035906020019064010000000081111561023f57600080fd5b82018360208201111561025157600080fd5b8035906020019184602083028401116401000000008311171561027357600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611371565b005b61030d61167f565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610350578082015181840152602081019050610335565b505050509050019250505060405180910390f35b6103c76004803603608081101561037a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff169060200190929190803590602001909291908035906020019092919050505061170d565b005b61042c600480360360808110156103df57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611915565b005b610436611a99565b005b6104af6004803603604081101561044e57600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050611bd2565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610521578082015181840152602081019050610506565b50505050905090810190601f16801561054e5780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b6106166004803603602081101561057357600080fd5b810190808035906020019064010000000081111561059057600080fd5b8201836020820111156105a257600080fd5b803590602001918460018302840111640100000000831117156105c457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050611de5565b005b61065a6004803603602081101561062e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611df2565b604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200180602001828103825283818151815260200191508051906020019080838360005b838110156106cc5780820151818401526020810190506106b1565b50505050905090810190601f1680156106f95780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b610710611f37565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61075a611f60565b604051808215151515815260200191505060405180910390f35b61086e600480360360a081101561078a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001906401000000008111156107c757600080fd5b8201836020820111156107d957600080fd5b803590602001918460018302840111640100000000831117156107fb57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050611fbe565b005b6108b26004803603602081101561088657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612197565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156108f25780820151818401526020810190506108d7565b50505050905090810190601f16801561091f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6109a46004803603604081101561094357600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050612278565b604051808773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200180602001806020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200185810385528a818151815260200191508051906020019060200280838360005b83811015610a57578082015181840152602081019050610a3c565b50505050905001858103845289818151815260200191508051906020019060200280838360005b83811015610a99578082015181840152602081019050610a7e565b50505050905001858103835288818151815260200191508051906020019060200280838360005b83811015610adb578082015181840152602081019050610ac0565b50505050905001858103825286818151815260200191508051906020019080838360005b83811015610b1a578082015181840152602081019050610aff565b50505050905090810190601f168015610b475780820380516001836020036101000a031916815260200191505b509a505050505050505050505060405180910390f35b610c8860048036036080811015610b7357600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080359060200190640100000000811115610be557600080fd5b820183602082011115610bf757600080fd5b80359060200191846020830284011164010000000083111715610c1957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061275a565b005b610c9261276d565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610cd5578082015181840152602081019050610cba565b505050509050019250505060405180910390f35b610cf16127fb565b005b610e7e600480360360c0811015610d0957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190640100000000811115610d4657600080fd5b820183602082011115610d5857600080fd5b80359060200191846020830284011164010000000083111715610d7a57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610dda57600080fd5b820183602082011115610dec57600080fd5b80359060200191846020830284011164010000000083111715610e0e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050612806565b005b610fca60048036036040811015610e9657600080fd5b8101908080359060200190640100000000811115610eb357600080fd5b820183602082011115610ec557600080fd5b80359060200191846020830284011164010000000083111715610ee757600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610f4757600080fd5b820183602082011115610f5957600080fd5b80359060200191846020830284011164010000000083111715610f7b57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050612a7c565b005b610fd4612a8b565b005b61104d60048036036040811015610fec57600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050612a96565b005b6110916004803603602081101561106557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612be9565b60405180887dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001877cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020018060200180602001806020018673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018060200185810385528a818151815260200191508051906020019060200280838360005b838110156111a0578082015181840152602081019050611185565b50505050905001858103845289818151815260200191508051906020019060200280838360005b838110156111e25780820151818401526020810190506111c7565b50505050905001858103835288818151815260200191508051906020019060200280838360005b83811015611224578082015181840152602081019050611209565b50505050905001858103825286818151815260200191508051906020019080838360005b83811015611263578082015181840152602081019050611248565b50505050905090810190601f1680156112905780820380516001836020036101000a031916815260200191505b509b50505050505050505050505060405180910390f35b6112e9600480360360208110156112bd57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050613012565b005b6112f3611f60565b611365576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61136e81613098565b50565b60008888888888604051602001808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401857dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600201847cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152600301838051906020019060200280838360005b8381101561146e578082015181840152602081019050611453565b505050509050018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140195505050505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b6020831061153a5780518252602082019150602081019050602083039250611517565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156115d6573d6000803e3d6000fd5b5050506020604051035190508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611666576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806154c2602c913960400191505060405180910390fd5b611673818a8a8a8a6132f9565b50505050505050505050565b6060600480548060200260200160405190810160405280929190818152602001828054801561170357602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116116b9575b5050505050905090565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106117d857805182526020820191506020810190506020830392506117b5565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611874573d6000803e3d6000fd5b5050506020604051035190508573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614611904576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806154c2602c913960400191505060405180910390fd5b61190d81613d0e565b505050505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106119e057805182526020820191506020810190506020830392506119bd565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611a7c573d6000803e3d6000fd5b505050602060405103519050611a9181613098565b505050505050565b611aa1611f60565b611b13576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a360008060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550565b60006060600060056000867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600860008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611dd65780601f10611dab57610100808354040283529160200191611dd6565b820191906000526020600020905b815481529060010190602001808311611db957829003601f168201915b50505050509150509250929050565b611def3382614450565b50565b60006060600860008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169150600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611f2b5780601f10611f0057610100808354040283529160200191611f2b565b820191906000526020600020905b815481529060010190602001808311611f0e57829003601f168201915b50505050509050915091565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16611fa2614840565b73ffffffffffffffffffffffffffffffffffffffff1614905090565b60008585604051602001808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b815260140182805190602001908083835b6020831061202d578051825260208201915060208101905060208303925061200a565b6001836020036101000a03801982511681845116808217855250505050505090500192505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106120dc57805182526020820191506020810190506020830392506120b9565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015612178573d6000803e3d6000fd5b50505060206040510351905061218e8187614450565b50505050505050565b6060600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561226c5780601f106122415761010080835404028352916020019161226c565b820191906000526020600020905b81548152906001019060200180831161224f57829003601f168201915b50505050509050919050565b600060608060606000606060056000897dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000887cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169550612354614fb1565b600760008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001600182018054806020026020016040519081016040528092919081815260200182805480156124cb57602002820191906000526020600020906000905b82829054906101000a900460ff1660068111156124a557fe5b8152602001906001019060208260000104928301926001038202915080841161248c5790505b50505050508152602001600282016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801561256157602002820191906000526020600020906000905b82829054906101000a900460ff16600681111561253b57fe5b815260200190600101906020826000010492830192600103820291508084116125225790505b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156125e757602002820191906000526020600020906000905b82829054906101000a900460ff1660068111156125c157fe5b815260200190600101906020826000010492830192600103820291508084116125a85790505b5050505050815250508152505090508060400151955080606001516000015194508060600151602001519350600860008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156127485780601f1061271d57610100808354040283529160200191612748565b820191906000526020600020905b81548152906001019060200180831161272b57829003601f168201915b50505050509150509295509295509295565b61276733858585856132f9565b50505050565b606060098054806020026020016040519081016040528092919081815260200182805480156127f157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116127a7575b5050505050905090565b61280433613098565b565b6000868686604051602001808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401838051906020019060200280838360005b83811015612873578082015181840152602081019050612858565b50505050905001828051906020019060200280838360005b838110156128a657808201518184015260208101905061288b565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b6020831061293b5780518252602082019150602081019050602083039250612918565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156129d7573d6000803e3d6000fd5b5050506020604051035190508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614612a67576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c8152602001806154c2602c913960400191505060405180910390fd5b612a72818888614848565b5050505050505050565b612a87338383614848565b5050565b612a9433613d0e565b565b612a9e611f60565b612b10576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b600060056000847dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000837cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050612be481613d0e565b505050565b600080606080606060006060612bfd614fb1565b600760008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160018201805480602002602001604051908101604052809291908181526020018280548015612d7457602002820191906000526020600020906000905b82829054906101000a900460ff166006811115612d4e57fe5b81526020019060010190602082600001049283019260010382029150808411612d355790505b505050505081526020016002820160405180604001604052908160008201805480602002602001604051908101604052809291908181526020018280548015612e0a57602002820191906000526020600020906000905b82829054906101000a900460ff166006811115612de457fe5b81526020019060010190602082600001049283019260010382029150808411612dcb5790505b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015612e9057602002820191906000526020600020906000905b82829054906101000a900460ff166006811115612e6a57fe5b81526020019060010190602082600001049283019260010382029150808411612e515790505b50505050508152505081525050905080600001519750806020015196508060400151935080606001516000015195508060600151602001519450600860008a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169250600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612fff5780601f10612fd457610100808354040283529160200191612fff565b820191906000526020600020905b815481529060010190602001808311612fe257829003601f168201915b5050505050915050919395979092949650565b61301a611f60565b61308c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260208152602001807f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657281525060200191505060405180910390fd5b61309581614e6d565b50565b6060600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561316d5780601f106131425761010080835404028352916020019161316d565b820191906000526020600020905b81548152906001019060200180831161315057829003601f168201915b5050505050905060008151116131ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602781526020018061549b6027913960400191505060405180910390fd5b60006002826040518082805190602001908083835b6020831061320657805182526020820191506020810190506020830392506131e3565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902060006101000a81548160ff021916908315150217905550600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600061329b9190615020565b8173ffffffffffffffffffffffffffffffffffffffff167fb1f254c0ae74a753185ba13a1b17818bc803e32d05ef56851955c0ebf230f0d6604051808060200182810382526000815260200160200191505060405180910390a25050565b600060f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916847dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415613398576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603e81526020018061551e603e913960400191505060405180910390fd5b600060e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916837cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415613435576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a8152602001806155c8603a913960400191505060405180910390fd5b60008251116134ac576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260128152602001807f4e6f20726f6c65732070726f76696465642e000000000000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613532576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603a815260200180615461603a913960400191505060405180910390fd5b600060056000867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16148061366357508573ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6136b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604c815260200180615602604c913960600191505060405180910390fd5b8560056000877dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019081526020016000206000867cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141561384a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061555c6021913960400191505060405180910390fd5b60001515600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515141561390a5760098690806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b6001600660008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555061396a615068565b6040518060800160405280877dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001867cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200185815260200182815250600760008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548161ffff021916908360f01c021790555060208201518160000160026101000a81548162ffffff021916908360e81c02179055506040820151816001019080519060200190613a6c929190615082565b506060820151816002016000820151816000019080519060200190613a92929190615131565b506020820151816001019080519060200190613aaf929190615131565b50505090505082600860008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167ff692507bb861072d3245d22242eb55cb7fdbe2e0d92290177ed7c490eb076cd78888888660000151876020015160405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015613c68578082015181840152602081019050613c4d565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015613caa578082015181840152602081019050613c8f565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015613cec578082015181840152602081019050613cd1565b505050509050019850505050505050505060405180910390a350505050505050565b600073ffffffffffffffffffffffffffffffffffffffff16600860008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415613df3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252604b81526020018061557d604b913960600191505060405180910390fd5b600860008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055613e5d614fb1565b600760008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160018201805480602002602001604051908101604052809291908181526020018280548015613fd457602002820191906000526020600020906000905b82829054906101000a900460ff166006811115613fae57fe5b81526020019060010190602082600001049283019260010382029150808411613f955790505b50505050508152602001600282016040518060400160405290816000820180548060200260200160405190810160405280929190818152602001828054801561406a57602002820191906000526020600020906000905b82829054906101000a900460ff16600681111561404457fe5b8152602001906001019060208260000104928301926001038202915080841161402b5790505b50505050508152602001600182018054806020026020016040519081016040528092919081815260200182805480156140f057602002820191906000526020600020906000905b82829054906101000a900460ff1660068111156140ca57fe5b815260200190600101906020826000010492830192600103820291508084116140b15790505b5050505050815250508152505090506005600082600001517dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001908152602001600020600082602001517cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600760008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600080820160006101000a81549061ffff02191690556000820160026101000a81549062ffffff021916905560018201600061424b91906151e0565b60028201600080820160006142609190615208565b6001820160006142709190615208565b50505050606080600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167ff692507bb861072d3245d22242eb55cb7fdbe2e0d92290177ed7c490eb076cd78560000151866020015186868760405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b838110156143ad578082015181840152602081019050614392565b50505050905001848103835286818151815260200191508051906020019060200280838360005b838110156143ef5780820151818401526020810190506143d4565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015614431578082015181840152602081019050614416565b505050509050019850505050505050505060405180910390a350505050565b6000815114156144ab576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c8152602001806153ff603c913960400191505060405180910390fd5b600015156002826040518082805190602001908083835b602083106144e557805182526020820191506020810190506020830392506144c2565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902060009054906101000a900460ff1615151461459b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f446f6d61696e206e616d6520616c726561647920726567697374657265642e0081525060200191505060405180910390fd5b60016002826040518082805190602001908083835b602083106145d357805182526020820191506020810190506020830392506145b0565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902060006101000a81548160ff02191690831515021790555060001515600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514156146dd5760048290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505b6001600360008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555080600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190614788929190615230565b508173ffffffffffffffffffffffffffffffffffffffff167fb1f254c0ae74a753185ba13a1b17818bc803e32d05ef56851955c0ebf230f0d6826040518080602001828103825283818151815260200191508051906020019080838360005b838110156148025780820151818401526020810190506147e7565b50505050905090810190601f16801561482f5780820380516001836020036101000a031916815260200191505b509250505060405180910390a25050565b600033905090565b6000600860008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614932576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260308152602001806154ee6030913960400191505060405180910390fd5b82600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600201600001908051906020019061498b9291906152b0565b5081600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020160010190805190602001906149e59291906152b0565b506149ee614fb1565b600760008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518060800160405290816000820160009054906101000a900460f01b7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016000820160029054906101000a900460e81b7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160018201805480602002602001604051908101604052809291908181526020018280548015614b6557602002820191906000526020600020906000905b82829054906101000a900460ff166006811115614b3f57fe5b81526020019060010190602082600001049283019260010382029150808411614b265790505b505050505081526020016002820160405180604001604052908160008201805480602002602001604051908101604052809291908181526020018280548015614bfb57602002820191906000526020600020906000905b82829054906101000a900460ff166006811115614bd557fe5b81526020019060010190602082600001049283019260010382029150808411614bbc5790505b5050505050815260200160018201805480602002602001604051908101604052809291908181526020018280548015614c8157602002820191906000526020600020906000905b82829054906101000a900460ff166006811115614c5b57fe5b81526020019060010190602082600001049283019260010382029150808411614c425790505b5050505050815250508152505090508173ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167ff692507bb861072d3245d22242eb55cb7fdbe2e0d92290177ed7c490eb076cd7836000015184602001518560400151898960405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015614dc9578082015181840152602081019050614dae565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015614e0b578082015181840152602081019050614df0565b50505050905001848103825285818151815260200191508051906020019060200280838360005b83811015614e4d578082015181840152602081019050614e32565b505050509050019850505050505050505060405180910390a35050505050565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415614ef3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602681526020018061543b6026913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff166000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b604051806080016040528060007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200160007cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020016060815260200161501a61535f565b81525090565b50805460018160011615610100020316600290046000825580601f106150465750615065565b601f0160209004906000526020600020908101906150649190615379565b5b50565b604051806040016040528060608152602001606081525090565b82805482825590600052602060002090601f016020900481019282156151205791602002820160005b838211156150f157835183826101000a81548160ff021916908360068111156150d057fe5b021790555092602001926001016020816000010492830192600103026150ab565b801561511e5782816101000a81549060ff02191690556001016020816000010492830192600103026150f1565b505b50905061512d919061539e565b5090565b82805482825590600052602060002090601f016020900481019282156151cf5791602002820160005b838211156151a057835183826101000a81548160ff0219169083600681111561517f57fe5b0217905550926020019260010160208160000104928301926001030261515a565b80156151cd5782816101000a81549060ff02191690556001016020816000010492830192600103026151a0565b505b5090506151dc91906153ce565b5090565b50805460008255601f0160209004906000526020600020908101906152059190615379565b50565b50805460008255601f01602090049060005260206000209081019061522d9190615379565b50565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061527157805160ff191683800117855561529f565b8280016001018555821561529f579182015b8281111561529e578251825591602001919060010190615283565b5b5090506152ac9190615379565b5090565b82805482825590600052602060002090601f0160209004810192821561534e5791602002820160005b8382111561531f57835183826101000a81548160ff021916908360068111156152fe57fe5b021790555092602001926001016020816000010492830192600103026152d9565b801561534c5782816101000a81549060ff021916905560010160208160000104928301926001030261531f565b505b50905061535b91906153ce565b5090565b604051806040016040528060608152602001606081525090565b61539b91905b8082111561539757600081600090555060010161537f565b5090565b90565b6153cb91905b808211156153c757600081816101000a81549060ff0219169055506001016153a4565b5090565b90565b6153fb91905b808211156153f757600081816101000a81549060ff0219169055506001016153d4565b5090565b9056fe43616e6e6f742073657420656d70747920646f6d61696e206e616d652e205573652064656c6574654e6f6465206d6574686f6420696e73746561642e4f776e61626c653a206e6577206f776e657220697320746865207a65726f206164647265737343616e6e6f742073657420656d707479206f70657261746f722e205573652064656c6574655061727479206d6574686f6420696e73746561642e43616e6e6f742064656c657465206e6f6465207468617420646f6573206e6f742065786973742e5369676e657220616e642070726f7669646564207061727479206164647265737320646966666572656e742e5061727479206e6f7420726567697374657265642e20557365207365745061727479206d6574686f642066697273742e43616e6e6f742073657420656d70747920636f756e7472795f636f64652e205573652064656c6574655061727479206d6574686f6420696e73746561642e50726f7669646564206f70657261746f72206e6f7420726567697374657265642e43616e6e6f742064656c657465207061727479207468617420646f6573206e6f742065786973742e204e6f206f70657261746f7220666f756e6420666f7220676976656e2070617274792e43616e6e6f742073657420656d7074792070617274795f69642e205573652064656c6574655061727479206d6574686f6420696e73746561642e5061727479207769746820636f756e7472795f636f64652f70617274795f696420616c7265616479207265676973746572656420756e64657220646966666572656e7420616464726573732ea265627a7a723158206ef945aec39df98fe727e32d16dabc8a988a92fa4be61a8b61152f53576a2df564736f6c634300050f0032"; public static final String FUNC_ISOWNER = "isOwner"; @@ -121,7 +121,6 @@ public class Registry extends Contract { static { _addresses = new HashMap(); - _addresses.put("73799", "0xd57595D5FA1F94725C426739C449b15D92758D55"); _addresses.put("9", "0x345cA3e014Aaf5dcA488057592ee47305D9B3e10"); } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt index 32eed68..39dc51b 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt @@ -25,6 +25,7 @@ import org.web3j.protocol.http.HttpService as Web3jHttpService import org.web3j.tx.ClientTransactionManager import org.web3j.tx.TransactionManager import org.web3j.tx.gas.StaticGasProvider +import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.scheduledTasks.HubClientInfoStillAliveCheck @@ -51,8 +52,16 @@ open class NodeConfig(private val properties: NodeProperties) { properties.web3.contracts.registry, web3, txManager, - gasProvider - ) + gasProvider) + } + + @Bean + fun permissions(): Permissions { + return Permissions.load( + properties.web3.contracts.permissions, + web3, + txManager, + gasProvider) } @Bean diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeInfoLogger.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeInfoLogger.kt index c87a911..788a692 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeInfoLogger.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeInfoLogger.kt @@ -36,13 +36,17 @@ class NodeInfoLogger(private val properties: NodeProperties) { val stillAliveText = getStillAliveText() val plannedPartyText = getPlannedPartyText() - println("\n${border.substring(0, 3)} NODE INFO ${border.substring(14)}\n" + - " URL | ${properties.url}\n" + - " ADDRESS | $addressText\n" + - " API KEY | ${properties.apikey}\n" + - " ETH RPC | ${properties.web3.provider}\n" + - " REGISTRY | ${properties.web3.contracts.registry} [$registryStage]\n" + - "${border.substring(0, 3)} FEATURES ${border.substring(13)}\n" + + println("\n${border.substring(0, 3)} NODE INFO ${border.substring(17)}\n" + + " URL | ${properties.url}\n" + + " ADDRESS | $addressText\n" + + " API KEY | ${properties.apikey}") + + println("${border.substring(0, 3)} NETWORK ${border.substring(15)}\n" + + " ETHEREUM RPC | ${properties.web3.provider}\n" + + " REGISTRY | ${properties.web3.contracts.registry} [$registryStage]\n" + + " PERMISSIONS | ${properties.web3.contracts.permissions} [$registryStage]") + + println("${border.substring(0, 3)} FEATURES ${border.substring(16)}\n" + " DEV MODE | ${properties.dev}\n" + " SIGNATURES | ${properties.signatures}\n" + " STILL ALIVE CHECK | $stillAliveText\n" + @@ -50,7 +54,7 @@ class NodeInfoLogger(private val properties: NodeProperties) { } private fun calculateBorderLength(url: Int, apikey: Int): Int { - val baseLength = 22 + val baseLength = 27 val address = 42 return baseLength + when { url >= apikey && url >= address -> url diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt index b69532d..acf45e0 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt @@ -42,6 +42,7 @@ class NodeProperties { class Contracts { lateinit var registry: String + lateinit var permissions: String } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt b/src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt index 06fc9d5..06b5c7f 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt @@ -78,6 +78,7 @@ class HubClientInfoListener(private val hubClientInfoService: HubClientInfoServi val parties = hubClientInfoService.getPartiesToNotifyOfClientInfoChange(changedPlatform, updatedClientInfo) hubClientInfoService.notifyPartiesOfClientInfoChange(parties, updatedClientInfo) + // TODO: handle connection issues hubClientInfoService.notifyNodesOfClientInfoChange(updatedClientInfo) } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index b74476e..fc0e099 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -21,7 +21,10 @@ import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.annotation.JsonProperty import shareandcharge.openchargingnetwork.notary.SignableHeaders import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID import snc.openchargingnetwork.node.models.ocpi.Role +import java.math.BigInteger data class OcnMessageHeaders(val requestID: String, val signature: String) { @@ -92,3 +95,20 @@ data class RegistryPartyDetailsBasic(val address: String, val operator: String) data class RegistryPartyDetails(val party: BasicRole, val roles: List, val nodeOperator: String) data class RegistryNode(val operator: String, val url: String) + +enum class OcnAppPermission() { + FORWARD_ALL, + FORWARD_ALL_SENDER, + FORWARD_ALL_RECEIVER; + + companion object { + fun getByIndex(index: BigInteger): OcnAppPermission { + return values()[index.intValueExact()] + } + } +} + +fun OcnAppPermission.matches(moduleID: ModuleID, interfaceRole: InterfaceRole): Boolean { + // TODO: match based on properties + return true +} diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt index f507b9e..5fe2d88 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt @@ -155,6 +155,7 @@ class RequestHandler(private val request: OcpiRequestVariables, * saved by the OCN Node. */ fun forwardRequest(proxied: Boolean = false): RequestHandler { + routingService.getAdditionalRecipients(request.headers.sender, request.module, request.interfaceRole) response = when (routingService.validateReceiver(request.headers.receiver)) { Receiver.LOCAL -> { knownReceiver = true diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 5d76590..429dc3c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -17,9 +17,11 @@ package snc.openchargingnetwork.node.services import org.springframework.data.repository.findByIdOrNull +import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import org.web3j.crypto.Credentials import org.web3j.crypto.Keys +import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.* @@ -35,6 +37,7 @@ import snc.openchargingnetwork.node.tools.checksum import snc.openchargingnetwork.node.tools.extractToken import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.urlJoin +import java.util.concurrent.CompletableFuture @Service class RoutingService(private val platformRepo: PlatformRepository, @@ -42,6 +45,7 @@ class RoutingService(private val platformRepo: PlatformRepository, private val endpointRepo: EndpointRepository, private val proxyResourceRepo: ProxyResourceRepository, private val registry: Registry, + private val permissions: Permissions, private val httpService: HttpService, private val walletService: WalletService, private val ocnRulesService: OcnRulesService, @@ -281,6 +285,32 @@ class RoutingService(private val platformRepo: PlatformRepository, } + /** + * Searches the Permissions contract for additional recipients a request/response could be forwarded to + */ + @Async + fun getAdditionalRecipients(party: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): CompletableFuture> { + val recipients: MutableList = mutableListOf() + val (senderAddress, _) = getPartyDetails(party) + val agreements = permissions.getUserAgreements(senderAddress).sendAsync().get() + + for (agreement in agreements) { + val (_, _, needs) = permissions.getApp(agreement as String).sendAsync().get() + + for (need in needs) { + if (OcnAppPermission.getByIndex(need).matches(module, interfaceRole)) { + val (country, id) = registry.getPartyDetailsByAddress(agreement).sendAsync().get() + recipients.add(BasicRole(id.toString(), country.toString())) + break + } + } + } + // - iterate over agreements: match permissions to and + // - if match, add to additional recipient list + return CompletableFuture.completedFuture(recipients) + } + + /** * Get a generic proxy resource by its ID */ diff --git a/src/main/resources/application.dev.properties b/src/main/resources/application.dev.properties index 2c48db5..348646b 100644 --- a/src/main/resources/application.dev.properties +++ b/src/main/resources/application.dev.properties @@ -14,3 +14,4 @@ ocn.node.url = http://localhost:8080 ocn.node.apikey = randomkey ocn.node.web3.provider = https://volta-rpc.energyweb.org/ ocn.node.web3.contracts.registry = 0xd57595D5FA1F94725C426739C449b15D92758D55 +ocn.node.web3.contracts.permissions = 0x520896B666fCcDb6458D4eC5C1FdD0D6d9EB97A3 diff --git a/src/main/resources/application.docker.properties b/src/main/resources/application.docker.properties index b677c0a..528188a 100644 --- a/src/main/resources/application.docker.properties +++ b/src/main/resources/application.docker.properties @@ -14,3 +14,4 @@ ocn.node.url = http://localhost:8080 ocn.node.apikey = randomkey ocn.node.web3.provider = http://localhost:8544 ocn.node.web3.contracts.registry = 0x345cA3e014Aaf5dcA488057592ee47305D9B3e10 +ocn.node.web3.contracts.permissions = 0xf25186B5081Ff5cE73482AD761DB0eB0d25abfBF diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt new file mode 100644 index 0000000..e7d7237 --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -0,0 +1,38 @@ +package snc.openchargingnetwork.node.integration + +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.models.OcnAppPermission + +class AppInterfaceTest { + + // TODO: integration test setup could be in an inheritable class + private lateinit var networkComponents: NetworkComponents + private lateinit var cpo1: TestCpo + private lateinit var cpo2: TestCpo + private lateinit var msp: TestMsp + + + @BeforeEach + fun bootStrap() { + networkComponents = setupNetwork(HubClientInfoParams()) + cpo1 = networkComponents.cpos[0] + cpo2 = networkComponents.cpos[1] + msp = networkComponents.msps.first() + } + + @AfterEach + fun stopTestParties() { + stopPartyServers(networkComponents) + } + + @Test + fun fowardsRequestToApp() { + cpo2.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) + msp.server.agreeToAppPermissions(cpo2.address) + msp.server.getLocation(cpo1.party) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt index f574081..a188b39 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt @@ -3,10 +3,9 @@ package snc.openchargingnetwork.node.integration import com.fasterxml.jackson.module.kotlin.readValue import org.assertj.core.api.Assertions.assertThat import org.awaitility.Awaitility.await -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test +import org.junit.jupiter.api.* import org.web3j.crypto.Credentials +import snc.openchargingnetwork.node.integration.parties.PartyServer import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.OcnRulesListType import snc.openchargingnetwork.node.models.ocpi.* @@ -23,7 +22,7 @@ class HubClientInfoIntegrationTest { private val hubClientInfoParams = HubClientInfoParams(plannedPartySearch = Scheduler(true)) - @BeforeAll + @BeforeEach fun bootStrap() { networkComponents = setupNetwork(hubClientInfoParams) val cpos = networkComponents.cpos @@ -32,7 +31,7 @@ class HubClientInfoIntegrationTest { msp = networkComponents.msps.first() } - @AfterAll + @AfterEach fun stopTestParties() { stopPartyServers(networkComponents) } @@ -44,13 +43,13 @@ class HubClientInfoIntegrationTest { fun hubClientInfo_partyRegisteredNotification() { val newCpoDefinition = PartyDefinition( nodeNumber = 1, - port = 8102, + port = 8301, party = BasicRole("CPC", "CH"), credentials = Credentials.create("0x82d052c865f5763aad42add438569276c00d3d88a2d062d36b2bae914d58b8c8")) val newCpo = setUpCpo( newCpoDefinition, networkComponents.nodes.first { d -> d.definition.nodeNumber == newCpoDefinition.nodeNumber }.definition, - networkComponents.registry) + networkComponents.contracts) // The assumption is that cpo1 is also connected to node1 await().atMost(2, TimeUnit.SECONDS).until{cpo1.server.hubClientInfoStatuses.containsKey(newCpoDefinition.party)} @@ -106,11 +105,15 @@ class HubClientInfoIntegrationTest { @Test fun hubClientInfo_returnsPlannedParties() { - val newParty = Credentials.create("0x49b2e2b48cfc25fda1d1cbdb2197b83902142c6da502dcf1871c628ea524f11b") - val registry = getRegistryInstance(newParty, networkComponents.registry.contractAddress) - - val operator = networkComponents.nodes[0].definition.credentials.address - registry.setParty("GB".toByteArray(), "TON".toByteArray(), listOf(1.toBigInteger()), operator).sendAsync().get() + val newParty = PartyServer( + config = PartyDefinition( + nodeNumber = 0, + port = 8302, + party = BasicRole("TON", "GB"), + credentials = Credentials.create("0x49b2e2b48cfc25fda1d1cbdb2197b83902142c6da502dcf1871c628ea524f11b")), + deployedContracts = networkComponents.contracts) + val nodeAddress = networkComponents.nodes[newParty.config.nodeNumber].definition.credentials.address + newParty.setPartyInRegistry(nodeAddress, Role.EMSP) sleep(hubClientInfoParams.plannedPartySearch.rate * 2) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/PlannedPartySearchTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/PlannedPartySearchTest.kt index 1aa6d37..863dd98 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/PlannedPartySearchTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/PlannedPartySearchTest.kt @@ -1,13 +1,13 @@ package snc.openchargingnetwork.node.integration import org.awaitility.Awaitility.await -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test +import org.junit.jupiter.api.* import org.web3j.crypto.Credentials +import snc.openchargingnetwork.node.integration.parties.PartyServer import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.ocpi.BasicRole import snc.openchargingnetwork.node.models.ocpi.ConnectionStatus +import snc.openchargingnetwork.node.models.ocpi.Role import java.util.concurrent.TimeUnit @@ -21,7 +21,7 @@ class PlannedPartySearchTest { private val hubClientInfoParams = HubClientInfoParams(plannedPartySearch = Scheduler(true)) - @BeforeAll + @BeforeEach fun bootStrap() { networkComponents = setupNetwork(hubClientInfoParams) cpo1 = networkComponents.cpos[0] @@ -29,7 +29,7 @@ class PlannedPartySearchTest { emsp = networkComponents.msps[0] } - @AfterAll + @AfterEach fun stopTestParties() { stopPartyServers(networkComponents) } @@ -39,11 +39,16 @@ class PlannedPartySearchTest { */ @Test fun hubClientInfo_getsPlannedParties() { - val newParty = Credentials.create("0x49b2e2b48cfc25fda1d1cbdb2197b83902142c6da502dcf1871c628ea524f11b") - val registry = getRegistryInstance(newParty, networkComponents.registry.contractAddress) - - val operator = networkComponents.nodes[0].definition.credentials.address - registry.setParty("GB".toByteArray(), "SEV".toByteArray(), listOf(1.toBigInteger()), operator).sendAsync().get() + val newParty = PartyServer( + config = PartyDefinition( + nodeNumber = 0, + port = 8300, + party = BasicRole("SEV", "GB"), + credentials = Credentials.create("0x49b2e2b48cfc25fda1d1cbdb2197b83902142c6da502dcf1871c628ea524f11b")), + deployedContracts = networkComponents.contracts) + + val operator = networkComponents.nodes[newParty.config.nodeNumber].definition.credentials.address + newParty.setPartyInRegistry(operator, role = Role.EMSP) await().atMost(hubClientInfoParams.plannedPartySearch.rate * 2, TimeUnit.MILLISECONDS).until { val expectedRole = BasicRole("SEV", "GB") diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt index e51e439..d600984 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt @@ -8,12 +8,10 @@ import shareandcharge.openchargingnetwork.notary.ValuesToSign import snc.openchargingnetwork.node.data.exampleCDR import org.web3j.crypto.Credentials as KeyPair import snc.openchargingnetwork.node.data.exampleLocation1 -import snc.openchargingnetwork.node.integration.utils.objectMapper -import snc.openchargingnetwork.node.integration.utils.privateKey -import snc.openchargingnetwork.node.integration.utils.toMap +import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.ocpi.* -class CpoServer(private val credentials: KeyPair, party: BasicRole, val port: Int): PartyServer(credentials, party, port) { +class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(config, contracts) { init { app.get("/ocpi/versions/2.2") { @@ -40,7 +38,7 @@ class CpoServer(private val credentials: KeyPair, party: BasicRole, val port: In app.get("/ocpi/cpo/2.2/locations/1") { val body = OcpiResponse(statusCode = 1000, data = exampleLocation1) - body.signature = Notary().sign(ValuesToSign(body = body), credentials.privateKey()).serialize() + body.signature = sign(body = body) it.json(body) } @@ -55,7 +53,7 @@ class CpoServer(private val credentials: KeyPair, party: BasicRole, val port: In } val body = OcpiResponse(statusCode = 1000, data = data) - body.signature = Notary().sign(ValuesToSign(headers, body = body), credentials.privateKey()).serialize() + body.signature = sign(headers = headers, body = body) it.header("x-limit", "5") .header("x-total-count", "50") .header("link", "<$next>; rel=\"next\"") @@ -65,16 +63,14 @@ class CpoServer(private val credentials: KeyPair, party: BasicRole, val port: In app.post("/ocpi/cpo/2.2/commands/START_SESSION") { val body = OcpiResponse(statusCode = 1000, data = CommandResponse(result = CommandResponseType.ACCEPTED, timeout = 10)) - val valuesToSign = ValuesToSign(body = body) - body.signature = Notary().sign(valuesToSign, credentials.privateKey()).serialize() + body.signature = sign(body = body) val url = it.body().responseURL // send async POST /START_SESSION val asyncHeaders = getSignableHeaders(BasicRole("MSP", "DE")) val asyncBody = CommandResult(result = CommandResultType.ACCEPTED) - val asyncValues = ValuesToSign(headers = asyncHeaders, body = asyncBody) - val asyncSignature = Notary().sign(asyncValues, credentials.privateKey()).serialize() + val asyncSignature = sign(headers = asyncHeaders, body = asyncBody) val asyncJson: Map = objectMapper.readValue(objectMapper.writeValueAsString(asyncBody)) khttp.post(url, headers = asyncHeaders.toMap(tokenC, asyncSignature), json = asyncJson) @@ -85,16 +81,14 @@ class CpoServer(private val credentials: KeyPair, party: BasicRole, val port: In fun sendCdr(to: BasicRole): Response { val headers = getSignableHeaders(to) - val valuesToSign = ValuesToSign(headers = headers, body = exampleCDR) - val signature = Notary().sign(valuesToSign, credentials.privateKey()).serialize() + val signature = sign(headers = headers, body = exampleCDR) val json: Map = objectMapper.readValue(objectMapper.writeValueAsString(exampleCDR)) return khttp.post("$node/ocpi/receiver/2.2/cdrs", headers = headers.toMap(tokenC, signature), json = json) } fun getCdr(to: BasicRole, location: String): Response { val headers = getSignableHeaders(to) - val valuesToSign = ValuesToSign(headers = headers, body = null) - val signature = Notary().sign(valuesToSign, credentials.privateKey()).serialize() + val signature = sign(headers = headers) return khttp.get(location, headers = headers.toMap(tokenC, signature)) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt index 94a9ca2..98bde8e 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt @@ -9,12 +9,10 @@ import shareandcharge.openchargingnetwork.notary.ValuesToSign import snc.openchargingnetwork.node.data.exampleCDR import snc.openchargingnetwork.node.data.exampleLocation1 import snc.openchargingnetwork.node.data.exampleToken -import snc.openchargingnetwork.node.integration.utils.objectMapper -import snc.openchargingnetwork.node.integration.utils.privateKey -import snc.openchargingnetwork.node.integration.utils.toMap +import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.ocpi.* -class MspServer(private val credentials: KeyPair, val party: BasicRole, port: Int): PartyServer(credentials, party, port) { +class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(config, contracts) { // could be nicer to use e.g. RxJava instead var asyncCommandsResponse: CommandResult? = null @@ -44,55 +42,48 @@ class MspServer(private val credentials: KeyPair, val party: BasicRole, port: In app.get("/ocpi/msp/2.2/cdrs/1") { val body = OcpiResponse(statusCode = 1000, data = exampleCDR) - val valuesToSign = ValuesToSign(body = body) - body.signature = Notary().sign(valuesToSign, credentials.privateKey()).serialize() + body.signature = sign(body = body) it.json(body) } app.post("/ocpi/msp/2.2/cdrs") { val headers = SignableHeaders(location = urlBuilder("/ocpi/msp/2.2/cdrs/1")) val body = OcpiResponse(statusCode = 1000, data = null) - val valuesToSign = ValuesToSign(headers = headers, body = body) - body.signature = Notary().sign(valuesToSign, credentials.privateKey()).serialize() + body.signature = sign(headers = headers, body = body) it.header("location", headers.location!!).json(body) } app.post("/ocpi/msp/2.2/commands/START_SESSION/1") { asyncCommandsResponse = it.body() val body = OcpiResponse(statusCode = 1000, data = null) - val valuesToSign = ValuesToSign(body = body) - body.signature = Notary().sign(valuesToSign, credentials.privateKey()).serialize() + body.signature = sign(body = body) it.json(body) } } fun getLocation(to: BasicRole): Response { val headers = getSignableHeaders(to) - val request = ValuesToSign(headers = headers, body = null) - val signature = Notary().sign(request, credentials.privateKey()).serialize() + val signature = sign(headers = headers) return khttp.get("$node/ocpi/sender/2.2/locations/1", headers = headers.toMap(tokenC, signature)) } fun getLocationList(to: BasicRole): Response { val headers = getSignableHeaders(to) val params = mapOf("limit" to "4") - val request = ValuesToSign(headers = headers, params = params, body = null) - val signature = Notary().sign(request, credentials.privateKey()).serialize() + val signature = sign(headers = headers, params = params) return khttp.get("$node/ocpi/sender/2.2/locations", params=params, headers = headers.toMap(tokenC, signature)) } fun getHubClientInfoList(to: BasicRole): Response { val headers = getSignableHeaders(to) val params = mapOf("limit" to "4") - val request = ValuesToSign(headers = headers, params = params, body = null) - val signature = Notary().sign(request, credentials.privateKey()).serialize() + val signature = sign(headers = headers, params = params) return khttp.get("$node/ocpi/2.2/hubclientinfo", params=params, headers = headers.toMap(tokenC, signature)) } fun getNextLink(to: BasicRole, next: String): Response { val headers = getSignableHeaders(to) - val request = ValuesToSign(headers = headers, body = null) - val signature = Notary().sign(request, credentials.privateKey()).serialize() + val signature = sign(headers = headers) return khttp.get(next, headers = headers.toMap(tokenC, signature)) } @@ -103,8 +94,7 @@ class MspServer(private val credentials: KeyPair, val party: BasicRole, port: In locationID = exampleLocation1.id, evseUID = exampleLocation1.evses!![0].uid) val headers = getSignableHeaders(to) - val request = ValuesToSign(headers = headers, body = body) - val signature = Notary().sign(request, credentials.privateKey()).serialize() + val signature = sign(headers = headers, body = body) val json: Map = objectMapper.readValue(objectMapper.writeValueAsString(body)) return khttp.post("$node/ocpi/receiver/2.2/commands/START_SESSION", headers = headers.toMap(tokenC, signature), json = json) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt index 2893897..6c76d64 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt @@ -1,24 +1,33 @@ package snc.openchargingnetwork.node.integration.parties import io.javalin.Javalin +import org.web3j.tx.ClientTransactionManager import shareandcharge.openchargingnetwork.notary.Notary -import org.web3j.crypto.Credentials as KeyPair import shareandcharge.openchargingnetwork.notary.SignableHeaders import shareandcharge.openchargingnetwork.notary.ValuesToSign +import snc.openchargingnetwork.contracts.Permissions +import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.models.OcnAppPermission import snc.openchargingnetwork.node.models.OcnRulesListType import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.tools.generateUUIDv4Token -open class PartyServer(private val credentials: KeyPair, private val party: BasicRole, private val port: Int) { +open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContracts) { - val app: Javalin = Javalin.create().start(port) + val app: Javalin = Javalin.create().start(config.port) private val tokenB: String = generateUUIDv4Token() lateinit var tokenC: String lateinit var node: String val hubClientInfoStatuses = mutableMapOf() + // replace deployed contract instances with own party's transaction manager + private val txManager = ClientTransactionManager(web3, config.credentials.address) + val contracts = deployedContracts.copy( + registry = Registry.load(deployedContracts.registry.contractAddress, web3, txManager, gasProvider), + permissions = Permissions.load(deployedContracts.permissions.contractAddress, web3, txManager, gasProvider)) + init { app.exception(JavalinException::class.java) { e, ctx -> ctx.status(e.httpCode).json(OcpiResponse(statusCode = e.ocpiCode, statusMessage = e.message)) @@ -40,19 +49,36 @@ open class PartyServer(private val credentials: KeyPair, private val party: Basi app.put("ocpi/cpo/2.2/clientinfo/:countryCode/:partyID") { this.hubClientInfoStatuses[BasicRole(id = it.pathParam("partyID"), country = it.pathParam("countryCode"))] = it.body().status val body = OcpiResponse(statusCode = 1000, data = "") - body.signature = Notary().sign(ValuesToSign(body = body), credentials.privateKey()).serialize() + body.signature = sign(body = body) it.json(body) } } - fun setPartyInRegistry(registryAddress: String, operator: String) { - val registry = getRegistryInstance(credentials, registryAddress) - registry.setParty(party.country.toByteArray(), party.id.toByteArray(), listOf(0.toBigInteger()), operator).sendAsync().get() - node = registry.getNode(operator).sendAsync().get() + fun sign(headers: SignableHeaders? = null, params: Map? = null, body: Any? = null): String { + val valuesToSign = ValuesToSign(headers, params, body) + return Notary().sign(valuesToSign, config.credentials.privateKey()).serialize() + } + + fun setPartyInRegistry(operator: String, role: Role) { + val (id, country) = config.party + val rolesList = listOf(role.ordinal.toBigInteger()) + contracts.registry.setParty(country.toByteArray(), id.toByteArray(), rolesList, operator).sendAsync().get() + node = contracts.registry.getNode(operator).sendAsync().get() + } + + fun setAppPermissions(permissions: List) { + val name = "Test App" // optional name + val url = "https://test.app" // optional public url + val permissionsIntList = permissions.map { it.ordinal.toBigInteger() } + contracts.permissions.setApp(name, url, permissionsIntList).sendAsync().get() + } + + fun agreeToAppPermissions(provider: String) { + contracts.permissions.createAgreement(provider).sendAsync().get() } fun registerCredentials() { - val tokenA = getTokenA(node, listOf(party)) + val tokenA = getTokenA(node, listOf(config.party)) val response = khttp.post("$node/ocpi/2.2/credentials", headers = mapOf("Authorization" to "Token $tokenA"), json = coerceToJson(Credentials( @@ -61,8 +87,8 @@ open class PartyServer(private val credentials: KeyPair, private val party: Basi roles = listOf(CredentialsRole( role = Role.CPO, businessDetails = BusinessDetails(name = "Some CPO"), - countryCode = party.country, - partyID = party.id))))) + countryCode = config.party.country, + partyID = config.party.id))))) tokenC = response.jsonObject.getJSONObject("data").getString("token") } @@ -72,14 +98,14 @@ open class PartyServer(private val credentials: KeyPair, private val party: Basi } fun urlBuilder(path: String): String { - return "http://localhost:$port$path" + return "http://localhost:${config.port}$path" } fun getSignableHeaders(to: BasicRole): SignableHeaders { return SignableHeaders( correlationId = generateUUIDv4Token(), - fromCountryCode = party.country, - fromPartyId = party.id, + fromCountryCode = config.party.country, + fromPartyId = config.party.id, toCountryCode = to.country, toPartyId = to.id) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Network.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Network.kt index cb34b0a..685216b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Network.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Network.kt @@ -6,13 +6,18 @@ import org.web3j.protocol.Web3j import org.web3j.protocol.http.HttpService import org.web3j.tx.ClientTransactionManager import org.web3j.tx.gas.StaticGasProvider +import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.Application import snc.openchargingnetwork.node.integration.parties.CpoServer import snc.openchargingnetwork.node.integration.parties.MspServer import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.Role const val provider = "http://localhost:8544" +val web3: Web3j = Web3j.build(HttpService(provider)) +val gasProvider = StaticGasProvider(0.toBigInteger(), 10000000.toBigInteger()) + val cpoDefinitions: List = listOf( PartyDefinition( @@ -53,57 +58,54 @@ fun getNodeDefinitions(hubClientInfoParams: HubClientInfoParams): List setUpNode(nodeDefinition, registry) + nodeDefinition -> setUpNode(nodeDefinition, contracts) } val cpos = cpoDefinitions.map { - cpoDefinition -> setUpCpo(cpoDefinition, nodeDefinitions.first { d -> d.nodeNumber == cpoDefinition.nodeNumber }, registry) + cpoDefinition -> setUpCpo(cpoDefinition, nodeDefinitions.first { d -> d.nodeNumber == cpoDefinition.nodeNumber }, contracts) } val msps = mspDefinitions.map { - mspDefinition -> setUpMsp(mspDefinition, nodeDefinitions.first { d -> d.nodeNumber == mspDefinition.nodeNumber }, registry) + mspDefinition -> setUpMsp(mspDefinition, nodeDefinitions.first { d -> d.nodeNumber == mspDefinition.nodeNumber }, contracts) } - return NetworkComponents(cpos, msps, nodes, registry) + return NetworkComponents(cpos, msps, nodes, contracts) } /** * Adds a CPO to the test OCN Network */ -fun setUpCpo(definition: PartyDefinition, nodeDefinition: NodeDefinition, registry: Registry): TestCpo { - val cpoServer = CpoServer(definition.credentials, definition.party, definition.port) - cpoServer.setPartyInRegistry(registry.contractAddress, nodeDefinition.credentials.address) +fun setUpCpo(definition: PartyDefinition, nodeDefinition: NodeDefinition, contracts: OcnContracts): TestCpo { + val cpoServer = CpoServer(definition, contracts) + cpoServer.setPartyInRegistry(nodeDefinition.credentials.address, Role.CPO) cpoServer.registerCredentials() return TestCpo( definition = definition, operator = nodeDefinition.credentials.address, - server = cpoServer - ) + server = cpoServer) } /** * Adds an MSP to the test OCN Network */ -fun setUpMsp(definition: PartyDefinition, nodeDefinition: NodeDefinition, registry: Registry): TestMsp { - val mspServer = MspServer(definition.credentials, definition.party, definition.port) - mspServer.setPartyInRegistry(registry.contractAddress, nodeDefinition.credentials.address) +fun setUpMsp(definition: PartyDefinition, nodeDefinition: NodeDefinition, contracts: OcnContracts): TestMsp { + val mspServer = MspServer(definition, contracts) + mspServer.setPartyInRegistry(nodeDefinition.credentials.address, Role.EMSP) mspServer.registerCredentials() return TestMsp( definition = definition, - server = mspServer - ) + server = mspServer) } /** * Adds a node to the test OCN Network */ -fun setUpNode(definition: NodeDefinition, registry: Registry): OcnNode { +fun setUpNode(definition: NodeDefinition, contracts: OcnContracts): OcnNode { val domain = "http://localhost:${definition.port}" - val registryAddress = registry.contractAddress val appContext = SpringApplicationBuilder(Application::class.java) .addCommandLineProperties(true) .run("--server.port=${definition.port}", @@ -111,13 +113,14 @@ fun setUpNode(definition: NodeDefinition, registry: Registry): OcnNode { "--ocn.node.url=$domain", "--ocn.node.privatekey=${definition.credentials.ecKeyPair.privateKey.toString(16)}", "--ocn.node.web3.provider=$provider", - "--ocn.node.web3.contracts.registry=$registryAddress", + "--ocn.node.web3.contracts.registry=${contracts.registry.contractAddress}", + "--ocn.node.web3.contracts.permissions=${contracts.permissions.contractAddress}", "--ocn.node.signatures=${definition.signatures}", "--ocn.node.stillAliveEnabled=${definition.hubClientInfoParams.stillAlive.enabled}", "--ocn.node.stillAliveRate=${definition.hubClientInfoParams.stillAlive.rate}", "--ocn.node.plannedPartySearchEnabled=${definition.hubClientInfoParams.plannedPartySearch.enabled}", "--ocn.node.plannedPartySearchRate=${definition.hubClientInfoParams.plannedPartySearch.rate}") - getRegistryInstance(definition.credentials, registryAddress).setNode(domain).sendAsync().get() + listNodeInRegistry(contracts.registry.contractAddress, definition.credentials, domain) return OcnNode(definition, appContext) } @@ -139,19 +142,19 @@ fun stopPartyServers(components: NetworkComponents) { /** * Deploys and gets instance */ -fun deployRegistry(): Registry { - val web3 = Web3j.build(HttpService(provider)) +fun deployContracts(): OcnContracts { val txManager = ClientTransactionManager(web3, "0x627306090abaB3A6e1400e9345bC60c78a8BEf57") - val gasProvider = StaticGasProvider(0.toBigInteger(), 10000000.toBigInteger()) - return Registry.deploy(web3, txManager, gasProvider).sendAsync().get() + val registry = Registry.deploy(web3, txManager, gasProvider).sendAsync().get() + val permissions = Permissions.deploy(web3, txManager, gasProvider, registry.contractAddress).sendAsync().get() + return OcnContracts(registry, permissions) } /** - * Gets deployed instance + * Registers node operator in Registry smart contract */ -fun getRegistryInstance(credentials: Credentials, contractAddress: String): Registry { - val web3 = Web3j.build(HttpService(provider)) +fun listNodeInRegistry(registryAddress: String, credentials: Credentials, domain: String) { val txManager = ClientTransactionManager(web3, credentials.address) - val gasProvider = StaticGasProvider(0.toBigInteger(), 10000000.toBigInteger()) - return Registry.load(contractAddress, web3, txManager, gasProvider) + val registry = Registry.load(registryAddress, web3, txManager, gasProvider) + registry.setNode(domain).sendAsync().get() } + diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt index 4d3efdb..23522ac 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt @@ -2,6 +2,7 @@ package snc.openchargingnetwork.node.integration.utils import org.springframework.context.ConfigurableApplicationContext import org.web3j.crypto.Credentials +import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.integration.parties.CpoServer import snc.openchargingnetwork.node.integration.parties.MspServer @@ -10,17 +11,24 @@ import snc.openchargingnetwork.node.models.ocpi.BasicRole class JavalinException(val httpCode: Int = 200, val ocpiCode: Int = 2001, message: String): Exception(message) data class PartyDefinition(val nodeNumber: Int, val port: Int, val party: BasicRole, val credentials: Credentials) + data class NodeDefinition(val nodeNumber: Int, val port: Int, val signatures: Boolean, val hubClientInfoParams: HubClientInfoParams, val credentials: Credentials) + data class OcnNode(val definition: NodeDefinition, val appContext: ConfigurableApplicationContext) + data class TestCpo(private val definition: PartyDefinition, val server: CpoServer, val operator: String) { val party = this.definition.party val address: String = this.definition.credentials.address } + data class TestMsp(private val definition: PartyDefinition, val server: MspServer) { val party = this.definition.party val address: String = this.definition.credentials.address } -data class NetworkComponents(val cpos: List, val msps: List, val nodes: List, val registry: Registry) + +data class OcnContracts(var registry: Registry, var permissions: Permissions) + +data class NetworkComponents(val cpos: List, val msps: List, val nodes: List, val contracts: OcnContracts) data class Scheduler(val enabled: Boolean, val rate: Long = 2000) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt index 81ae81c..4eca296 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt @@ -7,6 +7,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.http.HttpMethod import org.web3j.tuples.generated.Tuple2 +import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.entities.* @@ -25,6 +26,7 @@ class RoutingServiceTest { private val proxyResourceRepo: ProxyResourceRepository = mockk() private val httpService: HttpService = mockk() private val registry: Registry = mockk() + private val permissions: Permissions = mockk() private val walletService: WalletService = mockk() private val ocnRulesService: OcnRulesService = mockk() private val properties: NodeProperties = mockk() @@ -38,6 +40,7 @@ class RoutingServiceTest { endpointRepo, proxyResourceRepo, registry, + permissions, httpService, walletService, ocnRulesService, From efe6faa3ef0fa946ca37e730f05b4201b6b67d91 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Wed, 20 May 2020 18:50:44 +0200 Subject: [PATCH 02/26] integrate new permissions contract with updated getters Signed-off-by: Adam Staveley --- .../contracts/Permissions.java | 122 +++++++++++++++--- .../contracts/Registry.java | 2 +- .../openchargingnetwork/node/models/Ocn.kt | 14 +- .../node/services/RequestHandlerBuilder.kt | 2 + .../node/services/RoutingService.kt | 20 ++- 5 files changed, 132 insertions(+), 28 deletions(-) diff --git a/src/main/java/snc/openchargingnetwork/contracts/Permissions.java b/src/main/java/snc/openchargingnetwork/contracts/Permissions.java index 3512d43..273e9f1 100644 --- a/src/main/java/snc/openchargingnetwork/contracts/Permissions.java +++ b/src/main/java/snc/openchargingnetwork/contracts/Permissions.java @@ -13,10 +13,13 @@ import org.web3j.abi.FunctionEncoder; import org.web3j.abi.TypeReference; import org.web3j.abi.datatypes.Address; +import org.web3j.abi.datatypes.Bool; import org.web3j.abi.datatypes.DynamicArray; import org.web3j.abi.datatypes.Event; import org.web3j.abi.datatypes.Type; import org.web3j.abi.datatypes.Utf8String; +import org.web3j.abi.datatypes.generated.Bytes2; +import org.web3j.abi.datatypes.generated.Bytes3; import org.web3j.abi.datatypes.generated.Uint256; import org.web3j.crypto.Credentials; import org.web3j.protocol.Web3j; @@ -27,7 +30,7 @@ import org.web3j.protocol.core.methods.response.BaseEventResponse; import org.web3j.protocol.core.methods.response.Log; import org.web3j.protocol.core.methods.response.TransactionReceipt; -import org.web3j.tuples.generated.Tuple3; +import org.web3j.tuples.generated.Tuple5; import org.web3j.tx.Contract; import org.web3j.tx.TransactionManager; import org.web3j.tx.gas.ContractGasProvider; @@ -43,7 +46,7 @@ */ @SuppressWarnings("rawtypes") public class Permissions extends Contract { - public static final String BINARY = "0x608060405234801561001057600080fd5b50604051611db4380380611db48339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050611d20806100946000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063adb9cae61161005b578063adb9cae6146103ab578063c13489ca14610444578063dc3acebe1461062a578063edc922a91461079b57610088565b8063099e97621461008d578063340f1e7c1461029457806350f3fc81146102f9578063884eb94914610367575b600080fd5b610292600480360360c08110156100a357600080fd5b81019080803590602001906401000000008111156100c057600080fd5b8201836020820111156100d257600080fd5b803590602001918460018302840111640100000000831117156100f457600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561015757600080fd5b82018360208201111561016957600080fd5b8035906020019184600183028401116401000000008311171561018b57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156101ee57600080fd5b82018360208201111561020057600080fd5b8035906020019184602083028401116401000000008311171561022257600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff16906020019092919080359060200190929190803590602001909291905050506107fa565b005b6102f7600480360360808110156102aa57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050610a27565b005b6103256004803603602081101561030f57600080fd5b8101908080359060200190929190505050610bac565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6103a96004803603602081101561037d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610be8565b005b6103ed600480360360208110156103c157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bf5565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610430578082015181840152602081019050610415565b505050509050019250505060405180910390f35b6106286004803603606081101561045a57600080fd5b810190808035906020019064010000000081111561047757600080fd5b82018360208201111561048957600080fd5b803590602001918460018302840111640100000000831117156104ab57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561050e57600080fd5b82018360208201111561052057600080fd5b8035906020019184600183028401116401000000008311171561054257600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156105a557600080fd5b8201836020820111156105b757600080fd5b803590602001918460208302840111640100000000831117156105d957600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050610cc2565b005b61066c6004803603602081101561064057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610cd3565b60405180806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156106b4578082015181840152602081019050610699565b50505050905090810190601f1680156106e15780820380516001836020036101000a031916815260200191505b50848103835286818151815260200191508051906020019080838360005b8381101561071a5780820151818401526020810190506106ff565b50505050905090810190601f1680156107475780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019060200280838360005b83811015610783578082015181840152602081019050610768565b50505050905001965050505050505060405180910390f35b6107a3610f2c565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156107e65780820151818401526020810190506107cb565b505050509050019250505060405180910390f35b60008686866040516020018084805190602001908083835b602083106108355780518252602082019150602081019050602083039250610812565b6001836020036101000a03801982511681845116808217855250505050505090500183805190602001908083835b602083106108865780518252602082019150602081019050602083039250610863565b6001836020036101000a038019825116818451168082178552505050505050905001828051906020019060200280838360005b838110156108d45780820151818401526020810190506108b9565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106109695780518252602082019150602081019050602083039250610946565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610a05573d6000803e3d6000fd5b505050602060405103519050610a1d81898989610fba565b5050505050505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310610af25780518252602082019150602081019050602083039250610acf565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610b8e573d6000803e3d6000fd5b505050602060405103519050610ba481876115b6565b505050505050565b60018181548110610bb957fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b610bf233826115b6565b50565b6060600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610cb657602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610c6c575b50505050509050919050565b610cce33848484610fba565b505050565b6060806060600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610dae5780601f10610d8357610100808354040283529160200191610dae565b820191906000526020600020905b815481529060010190602001808311610d9157829003601f168201915b50505050509250600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e8b5780601f10610e6057610100808354040283529160200191610e8b565b820191906000526020600020905b815481529060010190602001808311610e6e57829003601f168201915b50505050509150600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600201805480602002602001604051908101604052809291908181526020018280548015610f1e57602002820191906000526020600020905b815481526020019060010190808311610f0a575b505050505090509193909250565b60606001805480602002602001604051908101604052809291908181526020018280548015610fb057602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311610f66575b5050505050905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b15801561105a57600080fd5b505afa15801561106e573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561109857600080fd5b8101908080519060200190929190805160405193929190846401000000008211156110c257600080fd5b838201915060208201858111156110d857600080fd5b82518660018202830111640100000000821117156110f557600080fd5b8083526020830192505050908051906020019080838360005b8381101561112957808201518184015260208101905061110e565b50505050905090810190601f1680156111565780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156111e6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180611cb0603c913960400191505060405180910390fd5b600082511161125d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f207065726d697373696f6e7320676976656e2e000000000000000000000081525060200191505060405180910390fd5b604051806060016040528085815260200184815260200183815250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000190805190602001906112d3929190611b62565b5060208201518160010190805190602001906112f0929190611b62565b50604082015181600201908051906020019061130d929190611be2565b5090505060001515600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514156114295760018590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b7f5217645fd1a5d80a7a561ee8df6541e94bff9b1151d8c1281e17c1a304d9d6fa84848488604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845288818151815260200191508051906020019080838360005b838110156114c85780820151818401526020810190506114ad565b50505050905090810190601f1680156114f55780820380516001836020036101000a031916815260200191505b50848103835287818151815260200191508051906020019080838360005b8381101561152e578082015181840152602081019050611513565b50505050905090810190601f16801561155b5780820380516001836020036101000a031916815260200191505b50848103825286818151815260200191508051906020019060200280838360005b8381101561159757808201518184015260208101905061157c565b5050505090500197505050505050505060405180910390a15050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b15801561165657600080fd5b505afa15801561166a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f82011682018060405250604081101561169457600080fd5b8101908080519060200190929190805160405193929190846401000000008211156116be57600080fd5b838201915060208201858111156116d457600080fd5b82518660018202830111640100000000821117156116f157600080fd5b8083526020830192505050908051906020019080838360005b8381101561172557808201518184015260208101905061170a565b50505050905090810190601f1680156117525780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156117e2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180611c55602a913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146118a8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f50726f766964657220686173206e6f2072656769737465726564204170702e0081525060200191505060405180910390fd5b60001515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151461198e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180611c7f6031913960400191505060405180910390fd5b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f59c05fe4657968f976c095206514e77e8ed37c2fe56e7614cd8f245dfbb008298383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10611ba357805160ff1916838001178555611bd1565b82800160010185558215611bd1579182015b82811115611bd0578251825591602001919060010190611bb5565b5b509050611bde9190611c2f565b5090565b828054828255906000526020600020908101928215611c1e579160200282015b82811115611c1d578251825591602001919060010190611c02565b5b509050611c2b9190611c2f565b5090565b611c5191905b80821115611c4d576000816000905550600101611c35565b5090565b9056fe417070207573657220686173206e6f207061727479206c697374696e6720696e2052656769737472792e41677265656d656e7420616c7265616479206d616465206265747765656e207573657220616e642070726f76696465722e547279696e6720746f20726567697374657220616e2061707020776974686f7574207061727479206c697374696e6720696e2052656769737472792ea265627a7a72315820d4d1ad154860d44803cd80584f7935eb25c503b78eaaa5930742156da612405d64736f6c634300050f0032"; + public static final String BINARY = "0x608060405234801561001057600080fd5b50604051613c39380380613c398339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050613ba5806100946000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80635c0f93bb11610097578063c13489ca11610066578063c13489ca14610780578063cdebf96e14610966578063dc3acebe146109aa578063edc922a914610bab576100f5565b80635c0f93bb1461058d578063674a730d146105f2578063884eb949146106c0578063b5d9f61f14610704576100f5565b8063340f1e7c116100d3578063340f1e7c146103bc57806338f7e0e1146104215780633fd62464146104ba57806350f3fc811461051f576100f5565b8063099e9762146100fa5780631cf939261461030157806323db5e8c1461030b575b600080fd5b6102ff600480360360c081101561011057600080fd5b810190808035906020019064010000000081111561012d57600080fd5b82018360208201111561013f57600080fd5b8035906020019184600183028401116401000000008311171561016157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156101c457600080fd5b8201836020820111156101d657600080fd5b803590602001918460018302840111640100000000831117156101f857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561025b57600080fd5b82018360208201111561026d57600080fd5b8035906020019184602083028401116401000000008311171561028f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050610c0a565b005b610309610e37565b005b6103a26004803603606081101561032157600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e42565b604051808215151515815260200191505060405180910390f35b61041f600480360360808110156103d257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506112ae565b005b6104636004803603602081101561043757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611433565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156104a657808201518184015260208101905061048b565b505050509050019250505060405180910390f35b61051d600480360360808110156104d057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611500565b005b61054b6004803603602081101561053557600080fd5b8101908080359060200190929190505050611684565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105f0600480360360808110156105a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506116c0565b005b6106696004803603604081101561060857600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050611845565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106ac578082015181840152602081019050610691565b505050509050019250505060405180910390f35b610702600480360360208110156106d657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ceb565b005b6107666004803603604081101561071a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611cf8565b604051808215151515815260200191505060405180910390f35b6109646004803603606081101561079657600080fd5b81019080803590602001906401000000008111156107b357600080fd5b8201836020820111156107c557600080fd5b803590602001918460018302840111640100000000831117156107e757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561084a57600080fd5b82018360208201111561085c57600080fd5b8035906020019184600183028401116401000000008311171561087e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156108e157600080fd5b8201836020820111156108f357600080fd5b8035906020019184602083028401116401000000008311171561091557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050611d8c565b005b6109a86004803603602081101561097c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d9d565b005b6109ec600480360360208110156109c057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611daa565b60405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019080838360005b83811015610ac2578082015181840152602081019050610aa7565b50505050905090810190601f168015610aef5780820380516001836020036101000a031916815260200191505b50848103835286818151815260200191508051906020019080838360005b83811015610b28578082015181840152602081019050610b0d565b50505050905090810190601f168015610b555780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019060200280838360005b83811015610b91578082015181840152602081019050610b76565b505050509050019850505050505050505060405180910390f35b610bb361238d565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610bf6578082015181840152602081019050610bdb565b505050509050019250505060405180910390f35b60008686866040516020018084805190602001908083835b60208310610c455780518252602082019150602081019050602083039250610c22565b6001836020036101000a03801982511681845116808217855250505050505090500183805190602001908083835b60208310610c965780518252602082019150602081019050602083039250610c73565b6001836020036101000a038019825116818451168082178552505050505050905001828051906020019060200280838360005b83811015610ce4578082015181840152602081019050610cc9565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310610d795780518252602082019150602081019050602083039250610d56565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610e15573d6000803e3d6000fd5b505050602060405103519050610e2d8189898961241b565b5050505050505050565b610e4033612a17565b565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a5b5ffbb86866040518363ffffffff1660e01b815260040180837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001827cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019250505060006040518083038186803b158015610f4157600080fd5b505afa158015610f55573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060c0811015610f7f57600080fd5b810190808051906020019092919080516040519392919084640100000000821115610fa957600080fd5b83820191506020820185811115610fbf57600080fd5b8251866020820283011164010000000082111715610fdc57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611013578082015181840152602081019050610ff8565b505050509050016040526020018051604051939291908464010000000082111561103c57600080fd5b8382019150602082018581111561105257600080fd5b825186602082028301116401000000008211171561106f57600080fd5b8083526020830192505050908051906020019060200280838360005b838110156110a657808201518184015260208101905061108b565b50505050905001604052602001805160405193929190846401000000008211156110cf57600080fd5b838201915060208201858111156110e557600080fd5b825186602082028301116401000000008211171561110257600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561113957808201518184015260208101905061111e565b50505050905001604052602001805190602001909291908051604051939291908464010000000082111561116c57600080fd5b8382019150602082018581111561118257600080fd5b825186600182028301116401000000008211171561119f57600080fd5b8083526020830192505050908051906020019080838360005b838110156111d35780820151818401526020810190506111b8565b50505050905090810190601f1680156112005780820380516001836020036101000a031916815260200191505b5060405250505090919293509091925090915090505080915050600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169150509392505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106113795780518252602082019150602081019050602083039250611356565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611415573d6000803e3d6000fd5b50505060206040510351905061142b8187612e56565b505050505050565b6060600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156114f457602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116114aa575b50505050509050919050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106115cb57805182526020820191506020810190506020830392506115a8565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611667573d6000803e3d6000fd5b50505060206040510351905061167c81612a17565b505050505050565b6001818154811061169157fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b6020831061178b5780518252602082019150602081019050602083039250611768565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611827573d6000803e3d6000fd5b50505060206040510351905061183d8187613402565b505050505050565b606060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a5b5ffbb85856040518363ffffffff1660e01b815260040180837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001827cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019250505060006040518083038186803b15801561194557600080fd5b505afa158015611959573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060c081101561198357600080fd5b8101908080519060200190929190805160405193929190846401000000008211156119ad57600080fd5b838201915060208201858111156119c357600080fd5b82518660208202830111640100000000821117156119e057600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611a175780820151818401526020810190506119fc565b5050505090500160405260200180516040519392919084640100000000821115611a4057600080fd5b83820191506020820185811115611a5657600080fd5b8251866020820283011164010000000082111715611a7357600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611aaa578082015181840152602081019050611a8f565b5050505090500160405260200180516040519392919084640100000000821115611ad357600080fd5b83820191506020820185811115611ae957600080fd5b8251866020820283011164010000000082111715611b0657600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611b3d578082015181840152602081019050611b22565b505050509050016040526020018051906020019092919080516040519392919084640100000000821115611b7057600080fd5b83820191506020820185811115611b8657600080fd5b8251866001820283011164010000000082111715611ba357600080fd5b8083526020830192505050908051906020019080838360005b83811015611bd7578082015181840152602081019050611bbc565b50505050905090810190601f168015611c045780820380516001836020036101000a031916815260200191505b5060405250505090919293509091925090915090505080915050600560008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015611cdd57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611c93575b505050505091505092915050565b611cf53382612e56565b50565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b611d983384848461241b565b505050565b611da73382613402565b50565b60008060608060606000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663df516128876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b158015611e5057600080fd5b505afa158015611e64573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060e0811015611e8e57600080fd5b81019080805190602001909291908051906020019092919080516040519392919084640100000000821115611ec257600080fd5b83820191506020820185811115611ed857600080fd5b8251866020820283011164010000000082111715611ef557600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611f2c578082015181840152602081019050611f11565b5050505090500160405260200180516040519392919084640100000000821115611f5557600080fd5b83820191506020820185811115611f6b57600080fd5b8251866020820283011164010000000082111715611f8857600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611fbf578082015181840152602081019050611fa4565b5050505090500160405260200180516040519392919084640100000000821115611fe857600080fd5b83820191506020820185811115611ffe57600080fd5b825186602082028301116401000000008211171561201b57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015612052578082015181840152602081019050612037565b50505050905001604052602001805190602001909291908051604051939291908464010000000082111561208557600080fd5b8382019150602082018581111561209b57600080fd5b82518660018202830111640100000000821117156120b857600080fd5b8083526020830192505050908051906020019080838360005b838110156120ec5780820151818401526020810190506120d1565b50505050905090810190601f1680156121195780820380516001836020036101000a031916815260200191505b506040525050509091929350909192509091509050508095508196505050600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561220d5780601f106121e25761010080835404028352916020019161220d565b820191906000526020600020905b8154815290600101906020018083116121f057829003601f168201915b50505050509250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156122ea5780601f106122bf576101008083540402835291602001916122ea565b820191906000526020600020905b8154815290600101906020018083116122cd57829003601f168201915b50505050509150600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020180548060200260200160405190810160405280929190818152602001828054801561237d57602002820191906000526020600020905b815481526020019060010190808311612369575b5050505050905091939590929450565b6060600180548060200260200160405190810160405280929190818152602001828054801561241157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116123c7575b5050505050905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b1580156124bb57600080fd5b505afa1580156124cf573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060408110156124f957600080fd5b81019080805190602001909291908051604051939291908464010000000082111561252357600080fd5b8382019150602082018581111561253957600080fd5b825186600182028301116401000000008211171561255657600080fd5b8083526020830192505050908051906020019080838360005b8381101561258a57808201518184015260208101905061256f565b50505050905090810190601f1680156125b75780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612647576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180613b35603c913960400191505060405180910390fd5b60008251116126be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f207065726d697373696f6e7320676976656e2e000000000000000000000081525060200191505060405180910390fd5b604051806060016040528085815260200184815260200183815250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082015181600001908051906020019061273492919061390b565b50602082015181600101908051906020019061275192919061390b565b50604082015181600201908051906020019061276e92919061398b565b5090505060001515600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515141561288a5760018590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b7f5217645fd1a5d80a7a561ee8df6541e94bff9b1151d8c1281e17c1a304d9d6fa84848488604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845288818151815260200191508051906020019080838360005b8381101561292957808201518184015260208101905061290e565b50505050905090810190601f1680156129565780820380516001836020036101000a031916815260200191505b50848103835287818151815260200191508051906020019080838360005b8381101561298f578082015181840152602081019050612974565b50505050905090810190601f1680156129bc5780820380516001836020036101000a031916815260200191505b50848103825286818151815260200191508051906020019060200280838360005b838110156129f85780820151818401526020810190506129dd565b5050505090500197505050505050505060405180910390a15050505050565b60011515600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514612ac0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180613ab26026913960400191505060405180910390fd5b612ac86139d8565b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051806060016040529081600082018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612bab5780601f10612b8057610100808354040283529160200191612bab565b820191906000526020600020905b815481529060010190602001808311612b8e57829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612c4d5780601f10612c2257610100808354040283529160200191612c4d565b820191906000526020600020905b815481529060010190602001808311612c3057829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015612ca557602002820191906000526020600020905b815481526020019060010190808311612c91575b5050505050815250509050600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008082016000612d0091906139f9565b600182016000612d1091906139f9565b600282016000612d209190613a41565b50506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f5217645fd1a5d80a7a561ee8df6541e94bff9b1151d8c1281e17c1a304d9d6fa816040015183604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845260008152602001602001848103835260008152602001602001848103825286818151815260200191508051906020019060200280838360005b83811015612e3c578082015181840152602081019050612e21565b505050509050019550505050505060405180910390a15050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b158015612ef657600080fd5b505afa158015612f0a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506040811015612f3457600080fd5b810190808051906020019092919080516040519392919084640100000000821115612f5e57600080fd5b83820191506020820185811115612f7457600080fd5b8251866001820283011164010000000082111715612f9157600080fd5b8083526020830192505050908051906020019080838360005b83811015612fc5578082015181840152602081019050612faa565b50505050905090810190601f168015612ff25780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613082576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613a88602a913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514613148576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f50726f766964657220686173206e6f2072656769737465726564204170702e0081525060200191505060405180910390fd5b60001515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151461322e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180613ad86031913960400191505060405180910390fd5b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f59c05fe4657968f976c095206514e77e8ed37c2fe56e7614cd8f245dfbb008298383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b1580156134a257600080fd5b505afa1580156134b6573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060408110156134e057600080fd5b81019080805190602001909291908051604051939291908464010000000082111561350a57600080fd5b8382019150602082018581111561352057600080fd5b825186600182028301116401000000008211171561353d57600080fd5b8083526020830192505050908051906020019080838360005b83811015613571578082015181840152602081019050613556565b50505050905090810190601f16801561359e5780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561362e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613a88602a913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146136f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f50726f766964657220686173206e6f2072656769737465726564204170702e0081525060200191505060405180910390fd5b60011515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146137da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613b09602c913960400191505060405180910390fd5b6000600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f59c05fe4657968f976c095206514e77e8ed37c2fe56e7614cd8f245dfbb008298383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061394c57805160ff191683800117855561397a565b8280016001018555821561397a579182015b8281111561397957825182559160200191906001019061395e565b5b5090506139879190613a62565b5090565b8280548282559060005260206000209081019282156139c7579160200282015b828111156139c65782518255916020019190600101906139ab565b5b5090506139d49190613a62565b5090565b60405180606001604052806060815260200160608152602001606081525090565b50805460018160011615610100020316600290046000825580601f10613a1f5750613a3e565b601f016020900490600052602060002090810190613a3d9190613a62565b5b50565b5080546000825590600052602060002090810190613a5f9190613a62565b50565b613a8491905b80821115613a80576000816000905550600101613a68565b5090565b9056fe417070207573657220686173206e6f207061727479206c697374696e6720696e2052656769737472792e43616e6e6f742064656c65746520617070207468617420646f6573206e6f742065786973742e41677265656d656e7420616c7265616479206d616465206265747765656e207573657220616e642070726f76696465722e4e6f2041677265656d656e74206d616465206265747765656e207573657220616e642070726f76696465722e547279696e6720746f20726567697374657220616e2061707020776974686f7574207061727479206c697374696e6720696e2052656769737472792ea265627a7a7231582022ad71273afc23e7112660d5d26832ec62d291dd2e9f14b7f6a1701451f9940964736f6c634300050f0032"; public static final String FUNC_PROVIDERS = "providers"; @@ -53,13 +56,27 @@ public class Permissions extends Contract { public static final String FUNC_GETAPP = "getApp"; + public static final String FUNC_DELETEAPP = "deleteApp"; + + public static final String FUNC_DELETEAPPRAW = "deleteAppRaw"; + public static final String FUNC_GETPROVIDERS = "getProviders"; public static final String FUNC_CREATEAGREEMENT = "createAgreement"; public static final String FUNC_CREATEAGREEMENTRAW = "createAgreementRaw"; - public static final String FUNC_GETUSERAGREEMENTS = "getUserAgreements"; + public static final String FUNC_REVOKEAGREEMENT = "revokeAgreement"; + + public static final String FUNC_REVOKEAGREEMENTRAW = "revokeAgreementRaw"; + + public static final String FUNC_GETUSERAGREEMENTSBYADDRESS = "getUserAgreementsByAddress"; + + public static final String FUNC_GETUSERAGREEMENTSBYOCPI = "getUserAgreementsByOcpi"; + + public static final String FUNC_HASUSERAGREEMENTBYADDRESS = "hasUserAgreementByAddress"; + + public static final String FUNC_HASUSERAGREEMENTBYOCPI = "hasUserAgreementByOcpi"; public static final Event APPAGREEMENT_EVENT = new Event("AppAgreement", Arrays.>asList(new TypeReference

() {}, new TypeReference
() {})); @@ -73,8 +90,8 @@ public class Permissions extends Contract { static { _addresses = new HashMap(); - _addresses.put("73799", "0x520896B666fCcDb6458D4eC5C1FdD0D6d9EB97A3"); - _addresses.put("9", "0xf25186B5081Ff5cE73482AD761DB0eB0d25abfBF"); + _addresses.put("73799", "0xB02f333b9E32EFCdE0394373C39f5d59fBDB2A0F"); + _addresses.put("9", "0x75c35C980C0d37ef46DF04d31A140b65503c0eEd"); } @Deprecated @@ -199,23 +216,44 @@ public RemoteFunctionCall setAppRaw(String name, String url, return executeRemoteCallTransaction(function); } - public RemoteFunctionCall>> getApp(String provider) { + public RemoteFunctionCall>> getApp(String provider) { final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETAPP, Arrays.asList(new org.web3j.abi.datatypes.Address(provider)), - Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference>() {})); - return new RemoteFunctionCall>>(function, - new Callable>>() { + Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference>() {})); + return new RemoteFunctionCall>>(function, + new Callable>>() { @Override - public Tuple3> call() throws Exception { + public Tuple5> call() throws Exception { List results = executeCallMultipleValueReturn(function); - return new Tuple3>( - (String) results.get(0).getValue(), - (String) results.get(1).getValue(), - convertToNative((List) results.get(2).getValue())); + return new Tuple5>( + (byte[]) results.get(0).getValue(), + (byte[]) results.get(1).getValue(), + (String) results.get(2).getValue(), + (String) results.get(3).getValue(), + convertToNative((List) results.get(4).getValue())); } }); } + public RemoteFunctionCall deleteApp() { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_DELETEAPP, + Arrays.asList(), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall deleteAppRaw(String provider, BigInteger v, byte[] r, byte[] s) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_DELETEAPPRAW, + Arrays.asList(new org.web3j.abi.datatypes.Address(provider), + new org.web3j.abi.datatypes.generated.Uint8(v), + new org.web3j.abi.datatypes.generated.Bytes32(r), + new org.web3j.abi.datatypes.generated.Bytes32(s)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + public RemoteFunctionCall getProviders() { final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETPROVIDERS, Arrays.asList(), @@ -250,8 +288,27 @@ public RemoteFunctionCall createAgreementRaw(String provider return executeRemoteCallTransaction(function); } - public RemoteFunctionCall getUserAgreements(String user) { - final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETUSERAGREEMENTS, + public RemoteFunctionCall revokeAgreement(String provider) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_REVOKEAGREEMENT, + Arrays.asList(new org.web3j.abi.datatypes.Address(provider)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall revokeAgreementRaw(String provider, BigInteger v, byte[] r, byte[] s) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( + FUNC_REVOKEAGREEMENTRAW, + Arrays.asList(new org.web3j.abi.datatypes.Address(provider), + new org.web3j.abi.datatypes.generated.Uint8(v), + new org.web3j.abi.datatypes.generated.Bytes32(r), + new org.web3j.abi.datatypes.generated.Bytes32(s)), + Collections.>emptyList()); + return executeRemoteCallTransaction(function); + } + + public RemoteFunctionCall getUserAgreementsByAddress(String user) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETUSERAGREEMENTSBYADDRESS, Arrays.asList(new org.web3j.abi.datatypes.Address(user)), Arrays.>asList(new TypeReference>() {})); return new RemoteFunctionCall(function, @@ -265,6 +322,39 @@ public List call() throws Exception { }); } + public RemoteFunctionCall getUserAgreementsByOcpi(byte[] countryCode, byte[] partyId) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETUSERAGREEMENTSBYOCPI, + Arrays.asList(new org.web3j.abi.datatypes.generated.Bytes2(countryCode), + new org.web3j.abi.datatypes.generated.Bytes3(partyId)), + Arrays.>asList(new TypeReference>() {})); + return new RemoteFunctionCall(function, + new Callable() { + @Override + @SuppressWarnings("unchecked") + public List call() throws Exception { + List result = (List) executeCallSingleValueReturn(function, List.class); + return convertToNative(result); + } + }); + } + + public RemoteFunctionCall hasUserAgreementByAddress(String user, String provider) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_HASUSERAGREEMENTBYADDRESS, + Arrays.asList(new org.web3j.abi.datatypes.Address(user), + new org.web3j.abi.datatypes.Address(provider)), + Arrays.>asList(new TypeReference() {})); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + + public RemoteFunctionCall hasUserAgreementByOcpi(byte[] countryCode, byte[] partyId, String provider) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_HASUSERAGREEMENTBYOCPI, + Arrays.asList(new org.web3j.abi.datatypes.generated.Bytes2(countryCode), + new org.web3j.abi.datatypes.generated.Bytes3(partyId), + new org.web3j.abi.datatypes.Address(provider)), + Arrays.>asList(new TypeReference() {})); + return executeRemoteCallSingleValueReturn(function, Boolean.class); + } + @Deprecated public static Permissions load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) { return new Permissions(contractAddress, web3j, credentials, gasPrice, gasLimit); diff --git a/src/main/java/snc/openchargingnetwork/contracts/Registry.java b/src/main/java/snc/openchargingnetwork/contracts/Registry.java index f18595b..512aa9e 100644 --- a/src/main/java/snc/openchargingnetwork/contracts/Registry.java +++ b/src/main/java/snc/openchargingnetwork/contracts/Registry.java @@ -121,7 +121,7 @@ public class Registry extends Contract { static { _addresses = new HashMap(); - _addresses.put("9", "0x345cA3e014Aaf5dcA488057592ee47305D9B3e10"); + _addresses.put("9", "0xf204a4Ef082f5c04bB89F7D5E6568B796096735a"); } @Deprecated diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index fc0e099..9e80625 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -96,10 +96,13 @@ data class RegistryPartyDetails(val party: BasicRole, val roles: List, val data class RegistryNode(val operator: String, val url: String) -enum class OcnAppPermission() { - FORWARD_ALL, - FORWARD_ALL_SENDER, - FORWARD_ALL_RECEIVER; +data class BasicRequestType(val moduleID: ModuleID, val interfaceRole: InterfaceRole) + +// each enum value takes a "matcher" which tests a given module/interface +enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) { + FORWARD_ALL({true}), + FORWARD_ALL_SENDER({it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_ALL_RECEIVER({it.interfaceRole == InterfaceRole.RECEIVER}); companion object { fun getByIndex(index: BigInteger): OcnAppPermission { @@ -109,6 +112,5 @@ enum class OcnAppPermission() { } fun OcnAppPermission.matches(moduleID: ModuleID, interfaceRole: InterfaceRole): Boolean { - // TODO: match based on properties - return true + return matches(BasicRequestType(moduleID, interfaceRole)) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt index 5fe2d88..c7746e6 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt @@ -155,7 +155,9 @@ class RequestHandler(private val request: OcpiRequestVariables, * saved by the OCN Node. */ fun forwardRequest(proxied: Boolean = false): RequestHandler { + // TODO: move routingService.getAdditionalRecipients(request.headers.sender, request.module, request.interfaceRole) + response = when (routingService.validateReceiver(request.headers.receiver)) { Receiver.LOCAL -> { knownReceiver = true diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 429dc3c..86c4d0a 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -291,20 +291,30 @@ class RoutingService(private val platformRepo: PlatformRepository, @Async fun getAdditionalRecipients(party: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): CompletableFuture> { val recipients: MutableList = mutableListOf() - val (senderAddress, _) = getPartyDetails(party) - val agreements = permissions.getUserAgreements(senderAddress).sendAsync().get() + + val agreements = permissions + .getUserAgreementsByOcpi(party.country.toByteArray(), party.id.toByteArray()) + .sendAsync() + .get() + + println("agreements=$agreements") for (agreement in agreements) { - val (_, _, needs) = permissions.getApp(agreement as String).sendAsync().get() + val (countryCode, partyId, _, _, needs) = permissions.getApp(agreement as String).sendAsync().get() + + println("countryCode=$countryCode") + println("partyId=$partyId") + println("needs=$needs") for (need in needs) { if (OcnAppPermission.getByIndex(need).matches(module, interfaceRole)) { - val (country, id) = registry.getPartyDetailsByAddress(agreement).sendAsync().get() - recipients.add(BasicRole(id.toString(), country.toString())) + recipients.add(BasicRole(partyId.toString(), countryCode.toString())) break } } } + + println("recipients=$recipients") // - iterate over agreements: match permissions to and // - if match, add to additional recipient list return CompletableFuture.completedFuture(recipients) From 81e37a187fa81ca5ebf6a9c24d466403d9b65ba5 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Tue, 26 May 2020 15:07:39 +0200 Subject: [PATCH 03/26] create async task service for request forwarding Signed-off-by: Adam Staveley --- .../openchargingnetwork/node/Application.kt | 2 + .../node/config/NodeConfig.kt | 6 +- .../openchargingnetwork/node/models/Ocn.kt | 7 +++ .../node/services/AsyncTaskService.kt | 61 +++++++++++++++++++ .../node/services/RequestHandlerBuilder.kt | 17 +++++- .../node/services/RoutingService.kt | 46 +++----------- .../node/integration/AppInterfaceTest.kt | 5 ++ 7 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/Application.kt b/src/main/kotlin/snc/openchargingnetwork/node/Application.kt index 4a7a94e..57078dc 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/Application.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/Application.kt @@ -19,12 +19,14 @@ package snc.openchargingnetwork.node import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.runApplication +import org.springframework.scheduling.annotation.EnableAsync import org.springframework.scheduling.annotation.EnableScheduling import snc.openchargingnetwork.node.config.NodeProperties @SpringBootApplication @EnableConfigurationProperties(NodeProperties::class) @EnableScheduling +@EnableAsync class Application fun main(args: Array) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt index 39dc51b..808159f 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt @@ -19,17 +19,19 @@ package snc.openchargingnetwork.node.config import org.springframework.boot.ApplicationRunner import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor import org.springframework.scheduling.config.IntervalTask import org.web3j.protocol.Web3j -import org.web3j.protocol.http.HttpService as Web3jHttpService import org.web3j.tx.ClientTransactionManager import org.web3j.tx.TransactionManager import org.web3j.tx.gas.StaticGasProvider import snc.openchargingnetwork.contracts.Permissions -import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.contracts.Registry +import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.scheduledTasks.HubClientInfoStillAliveCheck import snc.openchargingnetwork.node.scheduledTasks.PlannedPartySearch +import java.util.concurrent.Executor +import org.web3j.protocol.http.HttpService as Web3jHttpService import snc.openchargingnetwork.node.services.HttpService as OcnHttpService diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index 9e80625..5ec01ad 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -114,3 +114,10 @@ enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) fun OcnAppPermission.matches(moduleID: ModuleID, interfaceRole: InterfaceRole): Boolean { return matches(BasicRequestType(moduleID, interfaceRole)) } + +data class OcnApp(val party: BasicRole, val permissions: List) + +enum class MessageType { + REQUEST, + RESPONSE +} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt new file mode 100644 index 0000000..b232364 --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -0,0 +1,61 @@ +package snc.openchargingnetwork.node.services + +import org.slf4j.LoggerFactory +import org.springframework.scheduling.annotation.Async +import org.springframework.stereotype.Service +import snc.openchargingnetwork.contracts.Permissions +import snc.openchargingnetwork.node.models.OcnAppPermission +import snc.openchargingnetwork.node.models.matches +import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import java.util.concurrent.CompletableFuture + +@Service +class AsyncTaskService(private val permissions: Permissions, + private val httpService: HttpService) { + + companion object { + private val logger = LoggerFactory.getLogger(AsyncTaskService::class.java) + } + + @Async + fun findLinkedApps(party: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): CompletableFuture> { + /** + * TODO: where to run most optimally? + * - runs once we have validated the sender AND receiver on the network AND signature + * - must use routing service to find out where app is located on network + * - request must be modified with new signature containing new recipient + * - fire and forget request + */ + + val recipients: MutableList = mutableListOf() + + val agreements = permissions + .getUserAgreementsByOcpi(party.country.toByteArray(), party.id.toByteArray()) + .sendAsync() + .get() + + for (agreement in agreements) { + val (countryCode, partyId, _, _, needs) = permissions.getApp(agreement as String).sendAsync().get() + + logger.info("needs=$needs") + +// Thread.sleep(5000L) + + for (need in needs) { + if (OcnAppPermission.getByIndex(need).matches(module, interfaceRole)) { + recipients.add(BasicRole( + id = partyId.toString(Charsets.UTF_8), + country = countryCode.toString(Charsets.UTF_8))) + logger.info("need $need matched") + break + } + logger.info("need $need not matched") + } + } + + return CompletableFuture.completedFuture(recipients) + } + +} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt index c7746e6..4ac212d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt @@ -16,10 +16,11 @@ package snc.openchargingnetwork.node.services +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.http.HttpHeaders import org.springframework.http.ResponseEntity import org.springframework.stereotype.Service -import org.web3j.crypto.Credentials import org.web3j.crypto.Keys import shareandcharge.openchargingnetwork.notary.Notary import shareandcharge.openchargingnetwork.notary.ValuesToSign @@ -46,6 +47,7 @@ class RequestHandlerBuilder(private val routingService: RoutingService, private val httpService: HttpService, private val walletService: WalletService, private val hubClientInfoService: HubClientInfoService, + private val asyncTaskService: AsyncTaskService, private val properties: NodeProperties) { /** @@ -93,6 +95,10 @@ class RequestHandler(private val request: OcpiRequestVariables, private val walletService: WalletService, private val properties: NodeProperties) { + companion object { + private var logger: Logger = LoggerFactory.getLogger(RequestHandler::class.java) + } + /** * HttpResponse object instantiated after forwarding a request. * TODO: could operate on response in dedicated ResponseHandler @@ -156,10 +162,13 @@ class RequestHandler(private val request: OcpiRequestVariables, */ fun forwardRequest(proxied: Boolean = false): RequestHandler { // TODO: move - routingService.getAdditionalRecipients(request.headers.sender, request.module, request.interfaceRole) + logger.info("dispatching getAdditionalRecipients async task") + val future = routingService.getAdditionalRecipients(request.headers.sender, request.module, request.interfaceRole) + logger.info("dispatched task: ${future.isDone}") response = when (routingService.validateReceiver(request.headers.receiver)) { Receiver.LOCAL -> { + logger.info("receiver is local") knownReceiver = true routingService.validateWhitelisted( sender = request.headers.sender, @@ -182,6 +191,10 @@ class RequestHandler(private val request: OcpiRequestVariables, httpService.postOcnMessage(url, headers, body) } } + logger.info("got response, sleeping") + Thread.sleep(2000L) + logger.info("woke up") + logger.info("future: ${future.isDone}") return this } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 86c4d0a..5f1d4a6 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -16,6 +16,8 @@ package snc.openchargingnetwork.node.services +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.data.repository.findByIdOrNull import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service @@ -45,12 +47,14 @@ class RoutingService(private val platformRepo: PlatformRepository, private val endpointRepo: EndpointRepository, private val proxyResourceRepo: ProxyResourceRepository, private val registry: Registry, - private val permissions: Permissions, private val httpService: HttpService, private val walletService: WalletService, private val ocnRulesService: OcnRulesService, private val properties: NodeProperties) { + companion object { + private var logger: Logger = LoggerFactory.getLogger(RoutingService::class.java) + } /** * serialize a data class (with @JsonProperty annotations) as a JSON string @@ -212,6 +216,8 @@ class RoutingService(private val platformRepo: PlatformRepository, */ fun prepareLocalPlatformRequest(request: OcpiRequestVariables, proxied: Boolean = false): Pair { + logger.info("preparing local platform request") + val platformID = getPlatformID(request.headers.receiver) val url = when { @@ -248,6 +254,8 @@ class RoutingService(private val platformRepo: PlatformRepository, val headers = request.headers.copy(authorization = "Token $tokenB", requestID = generateUUIDv4Token()) + logger.info("prepared local platform request") + return Pair(url, headers) } @@ -285,42 +293,6 @@ class RoutingService(private val platformRepo: PlatformRepository, } - /** - * Searches the Permissions contract for additional recipients a request/response could be forwarded to - */ - @Async - fun getAdditionalRecipients(party: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): CompletableFuture> { - val recipients: MutableList = mutableListOf() - - val agreements = permissions - .getUserAgreementsByOcpi(party.country.toByteArray(), party.id.toByteArray()) - .sendAsync() - .get() - - println("agreements=$agreements") - - for (agreement in agreements) { - val (countryCode, partyId, _, _, needs) = permissions.getApp(agreement as String).sendAsync().get() - - println("countryCode=$countryCode") - println("partyId=$partyId") - println("needs=$needs") - - for (need in needs) { - if (OcnAppPermission.getByIndex(need).matches(module, interfaceRole)) { - recipients.add(BasicRole(partyId.toString(), countryCode.toString())) - break - } - } - } - - println("recipients=$recipients") - // - iterate over agreements: match permissions to and - // - if match, add to additional recipient list - return CompletableFuture.completedFuture(recipients) - } - - /** * Get a generic proxy resource by its ID */ diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt index e7d7237..f4e8095 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -33,6 +33,11 @@ class AppInterfaceTest { cpo2.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) msp.server.agreeToAppPermissions(cpo2.address) msp.server.getLocation(cpo1.party) + Thread.sleep(1000L) + msp.server.getLocation(cpo1.party) + Thread.sleep(1000L) + msp.server.getLocation(cpo1.party) + } } \ No newline at end of file From 3c3e308e51c0b7a89a5534c29cfe44533da6364e Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Wed, 27 May 2020 14:11:41 +0200 Subject: [PATCH 04/26] move EnableAsync/Scheduling to Configuration bean; fix tests not building Signed-off-by: Adam Staveley --- .../snc/openchargingnetwork/node/Application.kt | 2 -- .../openchargingnetwork/node/config/NodeConfig.kt | 6 ++++-- .../node/services/RequestHandlerBuilder.kt | 15 +++++++++------ .../node/services/RequestHandlerTest.kt | 4 +++- .../node/services/RoutingServiceTest.kt | 2 -- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/Application.kt b/src/main/kotlin/snc/openchargingnetwork/node/Application.kt index 57078dc..2bfb139 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/Application.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/Application.kt @@ -25,8 +25,6 @@ import snc.openchargingnetwork.node.config.NodeProperties @SpringBootApplication @EnableConfigurationProperties(NodeProperties::class) -@EnableScheduling -@EnableAsync class Application fun main(args: Array) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt index 808159f..c73ec7a 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt @@ -19,7 +19,8 @@ package snc.openchargingnetwork.node.config import org.springframework.boot.ApplicationRunner import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor +import org.springframework.scheduling.annotation.EnableAsync +import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.scheduling.config.IntervalTask import org.web3j.protocol.Web3j import org.web3j.tx.ClientTransactionManager @@ -30,12 +31,13 @@ import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.scheduledTasks.HubClientInfoStillAliveCheck import snc.openchargingnetwork.node.scheduledTasks.PlannedPartySearch -import java.util.concurrent.Executor import org.web3j.protocol.http.HttpService as Web3jHttpService import snc.openchargingnetwork.node.services.HttpService as OcnHttpService @Configuration +@EnableScheduling +@EnableAsync open class NodeConfig(private val properties: NodeProperties) { private val web3: Web3j = Web3j.build(Web3jHttpService(properties.web3.provider)) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt index 4ac212d..f5b8149 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt @@ -54,7 +54,8 @@ class RequestHandlerBuilder(private val routingService: RoutingService, * Build a RequestHandler object from an OcpiRequestVariables object. */ fun build(requestVariables: OcpiRequestVariables): RequestHandler { - return RequestHandler(requestVariables, routingService, httpService, hubClientInfoService, walletService, properties) + return RequestHandler(requestVariables, routingService, httpService, hubClientInfoService, walletService, + asyncTaskService, properties) } /** @@ -62,7 +63,8 @@ class RequestHandlerBuilder(private val routingService: RoutingService, */ fun build(requestVariablesString: String): RequestHandler { val requestVariables = httpService.convertToRequestVariables(requestVariablesString) - return RequestHandler(requestVariables, routingService, httpService, hubClientInfoService, walletService, properties) + return RequestHandler(requestVariables, routingService, httpService, hubClientInfoService, walletService, + asyncTaskService, properties) } } @@ -93,6 +95,7 @@ class RequestHandler(private val request: OcpiRequestVariables, private val httpService: HttpService, private val hubClientInfoService: HubClientInfoService, private val walletService: WalletService, + private val asyncTaskService: AsyncTaskService, private val properties: NodeProperties) { companion object { @@ -162,9 +165,9 @@ class RequestHandler(private val request: OcpiRequestVariables, */ fun forwardRequest(proxied: Boolean = false): RequestHandler { // TODO: move - logger.info("dispatching getAdditionalRecipients async task") - val future = routingService.getAdditionalRecipients(request.headers.sender, request.module, request.interfaceRole) - logger.info("dispatched task: ${future.isDone}") +// logger.info("dispatching getAdditionalRecipients async task") +// val future = asyncTaskService.findLinkedApps(request.headers.sender, request.module, request.interfaceRole) +// logger.info("dispatched task: ${future.isDone}") response = when (routingService.validateReceiver(request.headers.receiver)) { Receiver.LOCAL -> { @@ -194,7 +197,7 @@ class RequestHandler(private val request: OcpiRequestVariables, logger.info("got response, sleeping") Thread.sleep(2000L) logger.info("woke up") - logger.info("future: ${future.isDone}") +// logger.info("future: ${future.isDone}") return this } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt index a4676f9..312d6c5 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt @@ -24,9 +24,11 @@ class RequestHandlerTest { private val httpService: HttpService = mockk() private val walletService: WalletService = mockk() private val hubClientInfoService: HubClientInfoService = mockk() + private val asyncTaskService: AsyncTaskService = mockk() private val properties: NodeProperties = mockk() - private val requestHandlerBuilder = RequestHandlerBuilder(routingService, httpService, walletService, hubClientInfoService, properties) + private val requestHandlerBuilder = RequestHandlerBuilder(routingService, httpService, walletService, + hubClientInfoService, asyncTaskService, properties) @Test fun validateSender() { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt index 4eca296..8f89b0f 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt @@ -26,7 +26,6 @@ class RoutingServiceTest { private val proxyResourceRepo: ProxyResourceRepository = mockk() private val httpService: HttpService = mockk() private val registry: Registry = mockk() - private val permissions: Permissions = mockk() private val walletService: WalletService = mockk() private val ocnRulesService: OcnRulesService = mockk() private val properties: NodeProperties = mockk() @@ -40,7 +39,6 @@ class RoutingServiceTest { endpointRepo, proxyResourceRepo, registry, - permissions, httpService, walletService, ocnRulesService, From 083dc868e925566982ddf5370750e805cab8046d Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Thu, 28 May 2020 11:20:13 +0200 Subject: [PATCH 05/26] bump version; upgrade notary to fix brotli errors on windows/darwin Signed-off-by: Adam Staveley --- build.gradle.kts | 4 ++-- infra/ganache.bat | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 infra/ganache.bat diff --git a/build.gradle.kts b/build.gradle.kts index deeb3d0..a3e0d40 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,7 +15,7 @@ plugins { } group = "snc.openchargingnetwork.node" -version = "1.1.0-rc0" +version = "1.1.0-rc1" java.sourceCompatibility = JavaVersion.VERSION_1_8 val snippetsDir = "build/generated-snippets" @@ -32,7 +32,7 @@ repositories { } dependencies { - implementation("shareandcharge.openchargingnetwork:notary:1.0.0") + implementation("shareandcharge.openchargingnetwork:notary:1.0.1-beta1") implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") diff --git a/infra/ganache.bat b/infra/ganache.bat new file mode 100644 index 0000000..b82bf65 --- /dev/null +++ b/infra/ganache.bat @@ -0,0 +1,7 @@ +@ECHO OFF +ganache-cli -m="candy maple cake sugar pudding cream honey rich smooth crumble sweet treat"^ + --port=8544^ + --accounts=20^ + --networkId=9^ + --gasLimit=10000000^ +PAUSE \ No newline at end of file From 3686c413281cc2b4cfbcadbc491997f8b65a0349 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Thu, 28 May 2020 15:24:54 +0200 Subject: [PATCH 06/26] provide more context to async tasks Signed-off-by: Adam Staveley --- .../openchargingnetwork/node/Application.kt | 2 + .../node/config/NodeConfig.kt | 13 ++++-- .../node/config/TaskConfig.kt | 9 ++++ .../scheduledTasks/HubClientInfoStillAlive.kt | 4 +- .../node/services/HubClientInfoService.kt | 18 +++++++- .../node/services/RoutingService.kt | 4 -- .../HubClientInfoIntegrationTest.kt | 14 +++--- .../HubClientInfoStillAliveTest.kt | 45 ++++++++++++------- 8 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/Application.kt b/src/main/kotlin/snc/openchargingnetwork/node/Application.kt index 2bfb139..57078dc 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/Application.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/Application.kt @@ -25,6 +25,8 @@ import snc.openchargingnetwork.node.config.NodeProperties @SpringBootApplication @EnableConfigurationProperties(NodeProperties::class) +@EnableScheduling +@EnableAsync class Application fun main(args: Array) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt index c73ec7a..97f0808 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt @@ -19,8 +19,6 @@ package snc.openchargingnetwork.node.config import org.springframework.boot.ApplicationRunner import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration -import org.springframework.scheduling.annotation.EnableAsync -import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.scheduling.config.IntervalTask import org.web3j.protocol.Web3j import org.web3j.tx.ClientTransactionManager @@ -36,8 +34,6 @@ import snc.openchargingnetwork.node.services.HttpService as OcnHttpService @Configuration -@EnableScheduling -@EnableAsync open class NodeConfig(private val properties: NodeProperties) { private val web3: Web3j = Web3j.build(Web3jHttpService(properties.web3.provider)) @@ -92,4 +88,13 @@ open class NodeConfig(private val properties: NodeProperties) { return taskList.toList() } +// // modify the default task executor (runs async tasks, not to be confused with scheduled tasks) +// @Bean +// fun taskExecutor(): TaskExecutor { +// val taskExecutor = ThreadPoolTaskExecutor() +// taskExecutor.corePoolSize = 100 +// taskExecutor.setThreadNamePrefix("worker-") +// return taskExecutor +// } + } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/TaskConfig.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/TaskConfig.kt index 8aa4101..89b6255 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/TaskConfig.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/TaskConfig.kt @@ -26,6 +26,15 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar class TaskConfig(private val scheduledTasks: List): SchedulingConfigurer { override fun configureTasks(taskRegistrar: ScheduledTaskRegistrar) { + // override default thread pool task scheduler as follows: + // note: not the same as the thread executor which runs async tasks (methods with with @Async annotations) + + // val threadPoolTaskScheduler = ThreadPoolTaskScheduler() + // threadPoolTaskScheduler.poolSize = 10 + // threadPoolTaskScheduler.setThreadNamePrefix("task-pool-") + // threadPoolTaskScheduler.initialize() + // taskRegistrar.setTaskScheduler(threadPoolTaskScheduler) + for (task in scheduledTasks) { taskRegistrar.addFixedRateTask(task) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/HubClientInfoStillAlive.kt b/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/HubClientInfoStillAlive.kt index 737963b..0634999 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/HubClientInfoStillAlive.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/HubClientInfoStillAlive.kt @@ -16,7 +16,6 @@ package snc.openchargingnetwork.node.scheduledTasks -import org.springframework.scheduling.annotation.Async import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.entities.PlatformEntity import snc.openchargingnetwork.node.models.exceptions.OcpiServerUnusableApiException @@ -43,7 +42,6 @@ class HubClientInfoStillAliveCheck(private val httpService: HttpService, /** * Update a client's connection status based on its availability if status is stale (i.e. if the platform hasn't been heard from in the set amount of time) */ - @Async private fun updateClientStatus(client: PlatformEntity, lastUpdatedCutoff: Instant, newUpdatedTime: Instant) { val clientLastUpdated = getInstant(client.lastUpdated) if (clientLastUpdated < lastUpdatedCutoff) { @@ -69,7 +67,7 @@ class HubClientInfoStillAliveCheck(private val httpService: HttpService, // If no exception thrown during versions request, assume that request was successful httpService.getVersions(client.versionsUrl!!, client.auth.tokenB!!) return true - } catch (e: OcpiServerUnusableApiException){ + } catch (e: OcpiServerUnusableApiException) { return false } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt index 1415099..302c677 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt @@ -16,6 +16,7 @@ package snc.openchargingnetwork.node.services +import org.slf4j.LoggerFactory import org.springframework.http.HttpMethod import org.springframework.stereotype.Service import snc.openchargingnetwork.node.models.OcnHeaders @@ -41,6 +42,10 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, private val walletService: WalletService, private val ocnRulesService: OcnRulesService) { + companion object { + private val logger = LoggerFactory.getLogger(HubClientInfoService::class.java) + } + /** * Get a HubClientInfo list of local and known network connections */ @@ -164,7 +169,12 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, urlPathVariables = "${changedClientInfo.countryCode}/${changedClientInfo.partyID}") val (url, headers) = routingService.prepareLocalPlatformRequest(requestVariables, proxied = false) - httpService.makeOcpiRequest(url, headers, requestVariables) + + try { + httpService.makeOcpiRequest(url, headers, requestVariables) + } catch (e: Exception) { // fire and forget; catch any error and log + logger.warn("Error notifying $receiver of client info change: ${e.message}") + } } } @@ -178,7 +188,11 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, val nodes = routingService.getNodesListedInRegistry(omitMine = true) for (node in nodes) { - httpService.putOcnClientInfo(node.url, signature, changedClientInfo) + try { + httpService.putOcnClientInfo(node.url, signature, changedClientInfo) + } catch (e: Exception) { // fire and forget; catch any error and log + logger.warn("Error notifying $node of client info change: ${e.message}") + } } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 5f1d4a6..0ac3e67 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -216,8 +216,6 @@ class RoutingService(private val platformRepo: PlatformRepository, */ fun prepareLocalPlatformRequest(request: OcpiRequestVariables, proxied: Boolean = false): Pair { - logger.info("preparing local platform request") - val platformID = getPlatformID(request.headers.receiver) val url = when { @@ -254,8 +252,6 @@ class RoutingService(private val platformRepo: PlatformRepository, val headers = request.headers.copy(authorization = "Token $tokenB", requestID = generateUUIDv4Token()) - logger.info("prepared local platform request") - return Pair(url, headers) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt index a188b39..8fbfb67 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoIntegrationTest.kt @@ -61,16 +61,20 @@ class HubClientInfoIntegrationTest { /** * Tests that HubClientInfo functionality can be disabled + * (MSP server is stopped and no OFFLINE update is broadcast) */ @Test fun hubClientInfo_stillAliveCanBeDisabled() { - assertThat(cpo1.server.hubClientInfoStatuses[msp.party]).isEqualTo(ConnectionStatus.CONNECTED) - assertThat(cpo2.server.hubClientInfoStatuses[msp.party]).isEqualTo(ConnectionStatus.CONNECTED) + fun mspIsConnected(): Boolean { + val cpo1notified = cpo1.server.hubClientInfoStatuses[msp.party] == ConnectionStatus.CONNECTED + val cpo2notified = cpo2.server.hubClientInfoStatuses[msp.party] == ConnectionStatus.CONNECTED + return cpo1notified && cpo2notified + } + + await().atMost(2, TimeUnit.SECONDS).until { mspIsConnected() } msp.server.stopServer() sleep(hubClientInfoParams.stillAlive.rate * 2) - assertThat(cpo1.server.hubClientInfoStatuses[msp.party]).isEqualTo(ConnectionStatus.CONNECTED) //Should still be connected as StillAliveCheck is disabled - assertThat(cpo2.server.hubClientInfoStatuses[msp.party]).isEqualTo(ConnectionStatus.CONNECTED) - + await().atMost(2, TimeUnit.SECONDS).until { mspIsConnected() } // should still be connected as still alive disabled } /** diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoStillAliveTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoStillAliveTest.kt index 5ea5c2b..ed9b69f 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoStillAliveTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/HubClientInfoStillAliveTest.kt @@ -1,12 +1,10 @@ package snc.openchargingnetwork.node.integration -import org.assertj.core.api.Assertions.assertThat import org.awaitility.Awaitility.* import org.junit.jupiter.api.* import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.OcnRulesListType import snc.openchargingnetwork.node.models.ocpi.* -import java.lang.Thread.sleep import java.util.concurrent.TimeUnit @@ -17,7 +15,8 @@ class HubClientInfoStillAliveTest { private lateinit var cpo2: TestCpo private lateinit var emsp: TestMsp - private val hubClientInfoParams = HubClientInfoParams(stillAlive = Scheduler(true)) + private val rate = 1000L + private val hubClientInfoParams = HubClientInfoParams(stillAlive = Scheduler(true, rate)) @BeforeEach fun bootStrap() { @@ -37,16 +36,23 @@ class HubClientInfoStillAliveTest { */ @Test fun hubClientInfo_stillAlivePutsOffline() { - assertThat(cpo1.server.hubClientInfoStatuses[emsp.party]).isEqualTo(ConnectionStatus.CONNECTED) - assertThat(cpo2.server.hubClientInfoStatuses[emsp.party]).isEqualTo(ConnectionStatus.CONNECTED) + fun mspIs(expectedStatus: ConnectionStatus): Boolean { + // ensure both cpo1 and cpo2 have seen the status update (tests both local and remote node connections) + val cpo1matches = cpo1.server.hubClientInfoStatuses[emsp.party] == expectedStatus + val cpo2matches = cpo2.server.hubClientInfoStatuses[emsp.party] == expectedStatus + return cpo1matches && cpo2matches + } + + // need to wait for initial emsp status to be broadcast to cpo2 + await().atMost(rate, TimeUnit.SECONDS).until { mspIs(ConnectionStatus.CONNECTED) } emsp.server.stopServer() - //TODO: ensure that test reliably works without factor of 2 to stillAliveRate - await().atMost(hubClientInfoParams.stillAlive.rate * 2, TimeUnit.MILLISECONDS).until{ - val seenByCpo1 = cpo1.server.hubClientInfoStatuses[emsp.party] == ConnectionStatus.OFFLINE - val seenByCpo2 = cpo2.server.hubClientInfoStatuses[emsp.party] == ConnectionStatus.OFFLINE - seenByCpo1 && seenByCpo2 + // TODO: ensure that test reliably works without factor of n to stillAliveRate + // - note: this is caused when the task fetches the disconnected party's Versions endpoint and must wait + // for a "Connection Refused" response, adding ~2 seconds to the test. + await().atMost(rate * 4, TimeUnit.MILLISECONDS).until { + mspIs(ConnectionStatus.OFFLINE) } } @@ -55,18 +61,25 @@ class HubClientInfoStillAliveTest { */ @Test fun hubClientInfo_stillAliveFilteredByWhitelist() { - assertThat(cpo1.server.hubClientInfoStatuses[emsp.party]).isEqualTo(ConnectionStatus.CONNECTED) - assertThat(cpo1.server.hubClientInfoStatuses[cpo2.party]).isEqualTo(ConnectionStatus.CONNECTED) + fun cpo1Has(emspStatus: ConnectionStatus, cpo2Status: ConnectionStatus): Boolean { + val emspSeen = cpo1.server.hubClientInfoStatuses[emsp.party] == emspStatus + val cpo2Seen = cpo1.server.hubClientInfoStatuses[cpo2.party] == cpo2Status + return emspSeen && cpo2Seen + } + + await().atMost(rate, TimeUnit.SECONDS).until { + cpo1Has(emspStatus = ConnectionStatus.CONNECTED, cpo2Status = ConnectionStatus.CONNECTED) + } cpo1.server.addToList(OcnRulesListType.WHITELIST, emsp.party) cpo2.server.stopServer() emsp.server.stopServer() - sleep(hubClientInfoParams.stillAlive.rate * 2) - - assertThat(cpo1.server.hubClientInfoStatuses[emsp.party]).isEqualTo(ConnectionStatus.OFFLINE) - assertThat(cpo1.server.hubClientInfoStatuses[cpo2.party]).isEqualTo(ConnectionStatus.CONNECTED) // has not received the updated status as not in whitelist + // see test above for timeout reasoning + await().atMost(rate * 4, TimeUnit.SECONDS).until { + cpo1Has(emspStatus = ConnectionStatus.OFFLINE, cpo2Status = ConnectionStatus.CONNECTED) + } } } \ No newline at end of file From e2c296190711ad22036af845500cd36a721306d6 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Thu, 28 May 2020 19:34:19 +0200 Subject: [PATCH 07/26] separate registry related methods from routing service; trigger async app forwarding task using valid message variable Signed-off-by: Adam Staveley --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../ocpi/v2_2/CredentialsController.kt | 5 +- .../ocpi/v2_2/HubClientInfoController.kt | 2 +- .../openchargingnetwork/node/models/Ocn.kt | 17 ++- .../node/scheduledTasks/PlannedPartySearch.kt | 1 + .../node/services/AsyncTaskService.kt | 60 +++------- .../node/services/HubClientInfoService.kt | 11 +- .../node/services/RegistryService.kt | 113 ++++++++++++++++++ .../node/services/RequestHandlerBuilder.kt | 55 +++++---- .../node/services/RoutingService.kt | 95 ++------------- .../ocpi/v2_2/CredentialsControllerTest.kt | 7 +- .../ocpi/v2_2/HubClientInfoControllerTest.kt | 2 +- .../node/integration/AppInterfaceTest.kt | 6 +- .../node/services/HubClientInfoServiceTest.kt | 4 +- .../node/services/RegistryServiceTest.kt | 73 +++++++++++ .../node/services/RequestHandlerTest.kt | 43 ++++--- .../node/services/RoutingServiceTest.kt | 87 +++----------- 17 files changed, 317 insertions(+), 266 deletions(-) create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt create mode 100644 src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4b4429..a4f0001 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt index 33fe25e..aee89ca 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt @@ -30,6 +30,7 @@ import snc.openchargingnetwork.node.models.exceptions.OcpiClientInvalidParameter import snc.openchargingnetwork.node.models.exceptions.OcpiServerNoMatchingEndpointsException import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.services.HttpService +import snc.openchargingnetwork.node.services.RegistryService import snc.openchargingnetwork.node.services.RoutingService import snc.openchargingnetwork.node.tools.* @@ -42,7 +43,7 @@ class CredentialsController(private val platformRepo: PlatformRepository, private val networkClientInfoRepository: NetworkClientInfoRepository, private val ocnRulesListRepo: OcnRulesListRepository, private val properties: NodeProperties, - private val routingService: RoutingService, + private val registryService: RegistryService, private val httpService: HttpService) { private fun myCredentials(token: String): Credentials { @@ -93,7 +94,7 @@ class CredentialsController(private val platformRepo: PlatformRepository, // ensure each role does not already exist; delete if planned for (role in body.roles) { val basicRole = BasicRole(role.partyID, role.countryCode) - if (!routingService.isRoleKnownOnNetwork(basicRole)) { + if (!registryService.isRoleKnown(basicRole)) { throw OcpiClientInvalidParametersException("Role with party_id=${basicRole.id} and country_code=${basicRole.country} not listed in OCN Registry with my node info!") } if (roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(basicRole.country, basicRole.id)) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoController.kt index bfab993..509a920 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoController.kt @@ -46,7 +46,7 @@ class HubClientInfoController(private val routingService: RoutingService, // for now we ignore requests to paginate, only responding with required "last page" pagination headers val sender = BasicRole(fromPartyID, fromCountryCode) - routingService.validateSender(authorization, sender) + routingService.checkSenderKnown(authorization, sender) // val params = PaginatedRequest(dateFrom, dateTo, offset, limit).encode() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index 5ec01ad..0737e5c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -96,6 +96,8 @@ data class RegistryPartyDetails(val party: BasicRole, val roles: List, val data class RegistryNode(val operator: String, val url: String) +data class OcnApp(val provider: BasicRole, val permissions: List) + data class BasicRequestType(val moduleID: ModuleID, val interfaceRole: InterfaceRole) // each enum value takes a "matcher" which tests a given module/interface @@ -105,8 +107,12 @@ enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) FORWARD_ALL_RECEIVER({it.interfaceRole == InterfaceRole.RECEIVER}); companion object { - fun getByIndex(index: BigInteger): OcnAppPermission { - return values()[index.intValueExact()] + fun getByIndex(index: BigInteger): OcnAppPermission? { + return try { + values()[index.intValueExact()] + } catch (e: ArrayIndexOutOfBoundsException) { + null + } } } } @@ -114,10 +120,3 @@ enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) fun OcnAppPermission.matches(moduleID: ModuleID, interfaceRole: InterfaceRole): Boolean { return matches(BasicRequestType(moduleID, interfaceRole)) } - -data class OcnApp(val party: BasicRole, val permissions: List) - -enum class MessageType { - REQUEST, - RESPONSE -} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt b/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt index 8bfc054..c3a6fb0 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/scheduledTasks/PlannedPartySearch.kt @@ -39,6 +39,7 @@ class PlannedPartySearch(private val registry: Registry, // registry.getParties() returns list of party ethereum addresses which can be used to get full party details val plannedParties = registry.parties.sendAsync().get() + .asSequence() .map { val (country, id, _, _, roles, operator, _) = registry.getPartyDetailsByAddress(it as String).sendAsync().get() RegistryPartyDetails( diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index b232364..281151a 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -3,59 +3,31 @@ package snc.openchargingnetwork.node.services import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service -import snc.openchargingnetwork.contracts.Permissions -import snc.openchargingnetwork.node.models.OcnAppPermission -import snc.openchargingnetwork.node.models.matches -import snc.openchargingnetwork.node.models.ocpi.BasicRole -import snc.openchargingnetwork.node.models.ocpi.InterfaceRole -import snc.openchargingnetwork.node.models.ocpi.ModuleID -import java.util.concurrent.CompletableFuture +import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables @Service -class AsyncTaskService(private val permissions: Permissions, - private val httpService: HttpService) { +class AsyncTaskService(private val registryService: RegistryService) { companion object { private val logger = LoggerFactory.getLogger(AsyncTaskService::class.java) } + /** + * Forward request to linked apps + * TODO: rename - e.g. findLinkedApps + */ @Async - fun findLinkedApps(party: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): CompletableFuture> { - /** - * TODO: where to run most optimally? - * - runs once we have validated the sender AND receiver on the network AND signature - * - must use routing service to find out where app is located on network - * - request must be modified with new signature containing new recipient - * - fire and forget request - */ - - val recipients: MutableList = mutableListOf() - - val agreements = permissions - .getUserAgreementsByOcpi(party.country.toByteArray(), party.id.toByteArray()) - .sendAsync() - .get() - - for (agreement in agreements) { - val (countryCode, partyId, _, _, needs) = permissions.getApp(agreement as String).sendAsync().get() - - logger.info("needs=$needs") - -// Thread.sleep(5000L) - - for (need in needs) { - if (OcnAppPermission.getByIndex(need).matches(module, interfaceRole)) { - recipients.add(BasicRole( - id = partyId.toString(Charsets.UTF_8), - country = countryCode.toString(Charsets.UTF_8))) - logger.info("need $need matched") - break + fun forwardToLinkedApps(request: OcpiRequestVariables) { + registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) + .forEach { + logger.info("forwarding request to ${it.provider}") + // TODO: send to app using requestHandlerBuilder (circular import - could use an event instead) + // e.g. forEach + // -> fire event containing original request and new recipient + // -> listener picks up event + // -> uses builder to create request handler + // -> forwards modifiable request to new sender (must modify recipient headers and re-sign) } - logger.info("need $need not matched") - } - } - - return CompletableFuture.completedFuture(recipients) } } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt index 302c677..194dbd0 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt @@ -18,6 +18,7 @@ package snc.openchargingnetwork.node.services import org.slf4j.LoggerFactory import org.springframework.http.HttpMethod +import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import snc.openchargingnetwork.node.models.OcnHeaders import snc.openchargingnetwork.node.models.entities.NetworkClientInfoEntity @@ -40,7 +41,8 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, private val httpService: HttpService, private val routingService: RoutingService, private val walletService: WalletService, - private val ocnRulesService: OcnRulesService) { + private val ocnRulesService: OcnRulesService, + private val registryService: RegistryService) { companion object { private val logger = LoggerFactory.getLogger(HubClientInfoService::class.java) @@ -185,7 +187,7 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, val requestBodyString = httpService.mapper.writeValueAsString(changedClientInfo) val signature = walletService.sign(requestBodyString) - val nodes = routingService.getNodesListedInRegistry(omitMine = true) + val nodes = registryService.getNodes(omitMine = true) for (node in nodes) { try { @@ -199,8 +201,11 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, /** * Confirm the online status of the client corresponding to a role */ + @Async fun renewClientConnection(sender: BasicRole) { - val role = roleRepo.findByCountryCodeAndPartyIDAllIgnoreCase(countryCode = sender.country, partyID = sender.id) ?: throw IllegalArgumentException("sender could not be found") + val role = roleRepo.findByCountryCodeAndPartyIDAllIgnoreCase(countryCode = sender.country, partyID = sender.id) + ?: throw IllegalArgumentException("sender could not be found") + val client = platformRepo.findById(role.platformID).get() client.renewConnection(Instant.now()) platformRepo.save(client) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt new file mode 100644 index 0000000..b6a590c --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt @@ -0,0 +1,113 @@ +package snc.openchargingnetwork.node.services + +import org.springframework.stereotype.Service +import org.web3j.crypto.Credentials +import org.web3j.crypto.Keys +import snc.openchargingnetwork.contracts.Permissions +import snc.openchargingnetwork.contracts.Registry +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.* +import snc.openchargingnetwork.node.models.exceptions.OcpiHubUnknownReceiverException +import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import snc.openchargingnetwork.node.tools.checksum + +/** + * Simplifies calls to the Registry (registry and permissions smart contracts) + * TODO: review tests + */ +@Service +class RegistryService(private val registry: Registry, + private val permissions: Permissions, + private val properties: NodeProperties) { + + /** + * Get nodes listed in registry + */ + fun getNodes(omitMine: Boolean = false): List { + val nodes = registry.nodeOperators.sendAsync().get() + .map { + val url = registry.getNode(it as String).sendAsync().get() + RegistryNode(operator = it.checksum(), url = url) + } + + return if (omitMine) { + val myAddress = Credentials.create(properties.privateKey).address.checksum() + nodes.filter { it.operator != myAddress } + } else { + nodes + } + } + + /** + * check OCN registry to see if basic role is registered + */ + fun isRoleKnown(role: BasicRole, belongsToMe: Boolean = true): Boolean { + val country = role.country.toByteArray() + val id = role.id.toByteArray() + + + val (operator, domain) = registry.getOperatorByOcpi(country, id).sendAsync().get() + if (belongsToMe) { + val myKey = Credentials.create(properties.privateKey).address + return domain == properties.url && Keys.toChecksumAddress(operator) == Keys.toChecksumAddress(myKey) + } + + return domain != "" + } + + /** + * get the OCN Node URL as registered by the basic role in the OCN Registry + */ + fun getRemoteNodeUrlOf(role: BasicRole): String { + val country = role.country.toByteArray() + val id = role.id.toByteArray() + + val (_, domain) = registry.getOperatorByOcpi(country, id).sendAsync().get() + if (domain == "") { + throw OcpiHubUnknownReceiverException("Recipient not registered on OCN") + } + return domain + } + + /** + * Returns basic party details (address and node operator) + */ + fun getPartyDetails(role: BasicRole): RegistryPartyDetailsBasic { + val country = role.country.toByteArray() + val id = role.id.toByteArray() + + val result = registry.getPartyDetailsByOcpi(country, id).sendAsync().get() + return RegistryPartyDetailsBasic(address = result.component1(), operator = result.component5()) + } + + /** + * Gets an OCN App's details (provider BasicRole) and required permissions by owner's identity (Eth public key) + */ + fun getOcnApp(provider: String): OcnApp { + val (countryCode, partyId, _, _, needs) = permissions.getApp(provider).sendAsync().get() + val role = BasicRole( + id = partyId.toString(Charsets.UTF_8), + country = countryCode.toString(Charsets.UTF_8)) + return OcnApp( + provider = role, + permissions = needs.mapNotNull { need -> OcnAppPermission.getByIndex(need) }) + } + + /** + * Gets OCN apps a given role has made agreements with, based on a module interface + */ + fun getAgreementsByInterface(role: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): Sequence { + val country = role.country.toByteArray() + val id = role.id.toByteArray() + + val agreements = permissions.getUserAgreementsByOcpi(country, id).sendAsync().get() + + return agreements + .asSequence() + .map { getOcnApp(it as String) } + .filter { it.permissions.any { permission -> permission.matches(module, interfaceRole) } } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt index f5b8149..88da0c6 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt @@ -44,6 +44,7 @@ import kotlin.properties.Delegates.observable */ @Service class RequestHandlerBuilder(private val routingService: RoutingService, + private val registryService: RegistryService, private val httpService: HttpService, private val walletService: WalletService, private val hubClientInfoService: HubClientInfoService, @@ -54,8 +55,8 @@ class RequestHandlerBuilder(private val routingService: RoutingService, * Build a RequestHandler object from an OcpiRequestVariables object. */ fun build(requestVariables: OcpiRequestVariables): RequestHandler { - return RequestHandler(requestVariables, routingService, httpService, hubClientInfoService, walletService, - asyncTaskService, properties) + return RequestHandler(requestVariables, routingService, registryService, httpService, hubClientInfoService, + walletService, asyncTaskService, properties) } /** @@ -63,8 +64,8 @@ class RequestHandlerBuilder(private val routingService: RoutingService, */ fun build(requestVariablesString: String): RequestHandler { val requestVariables = httpService.convertToRequestVariables(requestVariablesString) - return RequestHandler(requestVariables, routingService, httpService, hubClientInfoService, walletService, - asyncTaskService, properties) + return RequestHandler(requestVariables, routingService, registryService, httpService, hubClientInfoService, + walletService, asyncTaskService, properties) } } @@ -92,6 +93,7 @@ class RequestHandlerBuilder(private val routingService: RoutingService, */ class RequestHandler(private val request: OcpiRequestVariables, private val routingService: RoutingService, + private val registryService: RegistryService, private val httpService: HttpService, private val hubClientInfoService: HubClientInfoService, private val walletService: WalletService, @@ -121,15 +123,24 @@ class RequestHandler(private val request: OcpiRequestVariables, /** * Is sender/receiver known to this OCN Node? */ - private var knownReceiver: Boolean = false private var knownSender: Boolean = true + /** + * Sender's message is valid (sender, receiver and signature have all been validated) + * Once valid, we can run the async task to find ocn apps the request can be forwarded to + */ + private var requestIsValid: Boolean by observable(false) { _, validBefore, validNow -> + if (!validBefore && validNow) { // task should only run when requestIsValid becomes true for the first time + asyncTaskService.forwardToLinkedApps(request) + } + } + /** * Assert the sender is allowed to send OCPI requests to this OCN Node. * If message signing is required, or OCN-Signature header present, verifies the signature. */ fun validateSender(): RequestHandler { - routingService.validateSender(request.headers.authorization, request.headers.sender) + routingService.checkSenderKnown(request.headers.authorization, request.headers.sender) hubClientInfoService.renewClientConnection(request.headers.sender) return this } @@ -141,7 +152,7 @@ class RequestHandler(private val request: OcpiRequestVariables, */ fun validateOcnMessage(signature: String): RequestHandler { knownSender = false - if (!routingService.isRoleKnownOnNetwork(request.headers.sender, belongsToMe = false)) { + if (!registryService.isRoleKnown(request.headers.sender, belongsToMe = false)) { throw OcpiHubUnknownReceiverException("Sending party not registered on Open Charging Network") } @@ -164,40 +175,36 @@ class RequestHandler(private val request: OcpiRequestVariables, * saved by the OCN Node. */ fun forwardRequest(proxied: Boolean = false): RequestHandler { - // TODO: move -// logger.info("dispatching getAdditionalRecipients async task") -// val future = asyncTaskService.findLinkedApps(request.headers.sender, request.module, request.interfaceRole) -// logger.info("dispatched task: ${future.isDone}") + response = when (routingService.getReceiverType(request.headers.receiver)) { - response = when (routingService.validateReceiver(request.headers.receiver)) { Receiver.LOCAL -> { - logger.info("receiver is local") - knownReceiver = true - routingService.validateWhitelisted( + routingService.checkSenderWhitelisted( sender = request.headers.sender, receiver = request.headers.receiver, module = request.module) + validateOcnSignature( signature = request.headers.signature, signedValues = request.toSignedValues(), signer = request.headers.sender, receiver = request.headers.receiver) val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) + + requestIsValid = true // trigger side-effect (forward to linked apps) httpService.makeOcpiRequest(url, headers, request) } + Receiver.REMOTE -> { validateOcnSignature( signature = request.headers.signature, signedValues = request.toSignedValues(), signer = request.headers.sender) val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) + + requestIsValid = true // trigger side-effect (forward to linked apps) httpService.postOcnMessage(url, headers, body) } } - logger.info("got response, sleeping") - Thread.sleep(2000L) - logger.info("woke up") -// logger.info("future: ${future.isDone}") return this } @@ -215,11 +222,10 @@ class RequestHandler(private val request: OcpiRequestVariables, val proxyPath = "/ocpi/sender/2.2/commands/${request.urlPathVariables}" val rewriteFields = mapOf("$['body']['response_url']" to responseUrl) - response = when (routingService.validateReceiver(request.headers.receiver)) { + response = when (routingService.getReceiverType(request.headers.receiver)) { Receiver.LOCAL -> { - knownReceiver = true - routingService.validateWhitelisted( + routingService.checkSenderWhitelisted( sender = request.headers.sender, receiver = request.headers.receiver, module = request.module) @@ -237,11 +243,11 @@ class RequestHandler(private val request: OcpiRequestVariables, modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) // send the request with the modified body val (url, headers) = routingService.prepareLocalPlatformRequest(request) + requestIsValid = true // trigger side-effect (forward to linked apps) httpService.makeOcpiRequest(url, headers, modifiedRequest) } Receiver.REMOTE -> { - validateOcnSignature( signature = request.headers.signature, signedValues = request.toSignedValues(), @@ -258,6 +264,7 @@ class RequestHandler(private val request: OcpiRequestVariables, modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) } + requestIsValid = true // trigger side-effect (forward to linked apps) httpService.postOcnMessage(url, headers, body) } @@ -415,7 +422,7 @@ class RequestHandler(private val request: OcpiRequestVariables, !result.isValid -> throw OcpiClientInvalidParametersException("Invalid signature: ${result.error}") } - val party = routingService.getPartyDetails(signer) + val party = registryService.getPartyDetails(signer) val actualSignatory = Keys.toChecksumAddress(notary?.signatory) val signedByParty = actualSignatory == Keys.toChecksumAddress(party.address) val signedByOperator = actualSignatory == Keys.toChecksumAddress(party.operator) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 0ac3e67..5f043f5 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -19,13 +19,7 @@ package snc.openchargingnetwork.node.services import org.slf4j.Logger import org.slf4j.LoggerFactory import org.springframework.data.repository.findByIdOrNull -import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service -import org.web3j.crypto.Credentials -import org.web3j.crypto.Keys -import snc.openchargingnetwork.contracts.Permissions -import snc.openchargingnetwork.contracts.Registry -import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.entities.* import snc.openchargingnetwork.node.models.entities.OcnRules @@ -35,76 +29,28 @@ import snc.openchargingnetwork.node.models.ocpi.InterfaceRole import snc.openchargingnetwork.node.models.ocpi.ModuleID import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables import snc.openchargingnetwork.node.repositories.* -import snc.openchargingnetwork.node.tools.checksum import snc.openchargingnetwork.node.tools.extractToken import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.urlJoin -import java.util.concurrent.CompletableFuture @Service class RoutingService(private val platformRepo: PlatformRepository, private val roleRepo: RoleRepository, private val endpointRepo: EndpointRepository, private val proxyResourceRepo: ProxyResourceRepository, - private val registry: Registry, + private val registryService: RegistryService, private val httpService: HttpService, private val walletService: WalletService, - private val ocnRulesService: OcnRulesService, - private val properties: NodeProperties) { - - companion object { - private var logger: Logger = LoggerFactory.getLogger(RoutingService::class.java) - } - - /** - * serialize a data class (with @JsonProperty annotations) as a JSON string - */ - private fun stringify(body: Any): String { - return httpService.mapper.writeValueAsString(body) - } - - /** - * Get nodes listed in registry - */ - fun getNodesListedInRegistry(omitMine: Boolean = false): List { - val nodes = registry.nodeOperators.sendAsync().get() - .map { - val url = registry.getNode(it as String).sendAsync().get() - RegistryNode(operator = it.checksum(), url = url) - } - - return if (omitMine) { - val myAddress = Credentials.create(properties.privateKey).address.checksum() - nodes.filter { it.operator != myAddress } - } else { - nodes - } - } + private val ocnRulesService: OcnRulesService) { /** * check database to see if basic role is connected to the node */ fun isRoleKnown(role: BasicRole) = roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role.country, role.id) - - /** - * check OCN registry to see if basic role is registered - */ - fun isRoleKnownOnNetwork(role: BasicRole, belongsToMe: Boolean = true): Boolean { - val country = role.country.toByteArray() - val id = role.id.toByteArray() - - val (operator, domain) = registry.getOperatorByOcpi(country, id).sendAsync().get() - if (belongsToMe) { - val myKey = Credentials.create(properties.privateKey).address - return domain == properties.url && Keys.toChecksumAddress(operator) == Keys.toChecksumAddress(myKey) - } - - return domain != "" - } - /** * get platform by role + * TODO: review getPlatform* methods below */ fun getPlatform(role: BasicRole): PlatformEntity { val platformID = getPlatformID(role) @@ -139,31 +85,10 @@ class RoutingService(private val platformRepo: PlatformRepository, } - /** - * get the OCN Node URL as registered by the basic role in the OCN Registry - */ - fun getRemoteNodeUrl(receiver: BasicRole): String { - val (_, domain) = registry.getOperatorByOcpi(receiver.country.toByteArray(), receiver.id.toByteArray()).sendAsync().get() - if (domain == "") { - throw OcpiHubUnknownReceiverException("Recipient not registered on OCN") - } - return domain - } - - - /** - * Only returns party and operator addresses so far; add as needed - */ - fun getPartyDetails(party: BasicRole): RegistryPartyDetailsBasic { - val result = registry.getPartyDetailsByOcpi(party.country.toByteArray(), party.id.toByteArray()).sendAsync().get() - return RegistryPartyDetailsBasic(address = result.component1(), operator = result.component5()) - } - - /** * Check sender is known to this node using only the authorization header token */ - fun validateSender(authorization: String) { + fun checkSenderKnown(authorization: String) { if (!platformRepo.existsByAuth_TokenC(authorization.extractToken())) { throw OcpiClientInvalidParametersException("Invalid CREDENTIALS_TOKEN_C") } @@ -173,7 +98,7 @@ class RoutingService(private val platformRepo: PlatformRepository, /** * Check sender is known to this node using the authorization header token and role provided as sender */ - fun validateSender(authorization: String, sender: BasicRole) { + fun checkSenderKnown(authorization: String, sender: BasicRole) { // sender platform exists by auth token val senderPlatform = platformRepo.findByAuth_TokenC(authorization.extractToken()) @@ -190,10 +115,10 @@ class RoutingService(private val platformRepo: PlatformRepository, * Check receiver is registered on the Open Charging Network / known locally via database * @return Receiver - defines the whether receiver is LOCAL (on this node) or REMOTE (on different node) */ - fun validateReceiver(receiver: BasicRole): Receiver { + fun getReceiverType(receiver: BasicRole): Receiver { return when { isRoleKnown(receiver) -> Receiver.LOCAL - isRoleKnownOnNetwork(receiver, belongsToMe = false) -> Receiver.REMOTE + registryService.isRoleKnown(receiver, belongsToMe = false) -> Receiver.REMOTE else -> throw OcpiHubUnknownReceiverException("Receiver not registered on Open Charging Network") } } @@ -201,7 +126,7 @@ class RoutingService(private val platformRepo: PlatformRepository, /** * Check receiver has allowed sender to send them messages */ - fun validateWhitelisted(sender: BasicRole, receiver: BasicRole, module: ModuleID) { + fun checkSenderWhitelisted(sender: BasicRole, receiver: BasicRole, module: ModuleID) { val platform = getPlatform(receiver) val whitelisted = ocnRulesService.isWhitelisted(platform, sender, module) if (!whitelisted) { @@ -262,7 +187,7 @@ class RoutingService(private val platformRepo: PlatformRepository, */ fun prepareRemotePlatformRequest(request: OcpiRequestVariables, proxied: Boolean = false, alterBody: ((url: String) -> OcpiRequestVariables)? = null): Triple { - val url = getRemoteNodeUrl(request.headers.receiver) + val url = registryService.getRemoteNodeUrlOf(request.headers.receiver) var modifiedBody = request.copy() @@ -279,7 +204,7 @@ class RoutingService(private val platformRepo: PlatformRepository, // strip authorization modifiedBody = modifiedBody.copy(headers = modifiedBody.headers.copy(authorization = "")) - val bodyString = stringify(modifiedBody) + val bodyString = httpService.mapper.writeValueAsString(modifiedBody) val headers = OcnMessageHeaders( requestID = generateUUIDv4Token(), diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt index 036647b..13ed705 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt @@ -22,6 +22,7 @@ import snc.openchargingnetwork.node.models.ocpi.Role import snc.openchargingnetwork.node.models.ocpi.OcpiStatus import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.services.HttpService +import snc.openchargingnetwork.node.services.RegistryService import snc.openchargingnetwork.node.services.RoutingService @WebMvcTest(CredentialsController::class) @@ -46,7 +47,7 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { lateinit var properties: NodeProperties @MockkBean - lateinit var routingService: RoutingService + lateinit var registryService: RegistryService @MockkBean lateinit var httpService: HttpService @@ -106,11 +107,11 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { every { properties.url } returns "http://my.broker.com" every { properties.signatures } returns true - every { routingService.isRoleKnownOnNetwork(BasicRole(role1.partyID, role1.countryCode)) } returns true + every { registryService.isRoleKnown(BasicRole(role1.partyID, role1.countryCode)) } returns true every { networkClientInfoRepo.existsByPartyAndRole(BasicRole(role1.partyID, role1.countryCode), role1.role) } returns false every { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role1.countryCode, role1.partyID) } returns false - every { routingService.isRoleKnownOnNetwork(BasicRole(role2.partyID, role2.countryCode)) } returns true + every { registryService.isRoleKnown(BasicRole(role2.partyID, role2.countryCode)) } returns true every { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role2.countryCode, role2.partyID) } returns false every { networkClientInfoRepo.existsByPartyAndRole(BasicRole(role2.partyID, role2.countryCode), role2.role) } returns false diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt index ab323f8..799c2ac 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt @@ -51,7 +51,7 @@ class HubClientInfoControllerTest(@Autowired val mockMvc: MockMvc) { urlEncodedParams = mapOf("date_from" to dateFrom)) every { hubClientInfoService.getList(requestVariables.headers.authorization) } returns listOf(exampleClientInfo) - every { routingService.validateSender(requestVariables.headers.authorization, sender) } just Runs + every { routingService.checkSenderKnown(requestVariables.headers.authorization, sender) } just Runs mockMvc.perform(MockMvcRequestBuilders.get("/ocpi/2.2/hubclientinfo") .header("Authorization", "Token token-c") diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt index f4e8095..9e88a5b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -33,11 +33,7 @@ class AppInterfaceTest { cpo2.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) msp.server.agreeToAppPermissions(cpo2.address) msp.server.getLocation(cpo1.party) - Thread.sleep(1000L) - msp.server.getLocation(cpo1.party) - Thread.sleep(1000L) - msp.server.getLocation(cpo1.party) - + Thread.sleep(5000L) } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/HubClientInfoServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/HubClientInfoServiceTest.kt index a48cd7e..72e125a 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/HubClientInfoServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/HubClientInfoServiceTest.kt @@ -25,6 +25,7 @@ class HubClientInfoServiceTest { private val routingService: RoutingService = mockk() private val walletService: WalletService = mockk() private val ocnRulesService: OcnRulesService = mockk() + private val registryService: RegistryService = mockk() private val hubClientInfoService: HubClientInfoService @@ -37,7 +38,8 @@ class HubClientInfoServiceTest { httpService, routingService, walletService, - ocnRulesService) + ocnRulesService, + registryService) } @Test diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt new file mode 100644 index 0000000..cd3bd52 --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt @@ -0,0 +1,73 @@ +package snc.openchargingnetwork.node.services + +import io.mockk.every +import io.mockk.mockk +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.web3j.tuples.generated.Tuple2 +import snc.openchargingnetwork.contracts.Permissions +import snc.openchargingnetwork.contracts.Registry +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.RegistryNode +import snc.openchargingnetwork.node.models.ocpi.BasicRole + + +class RegistryServiceTest { + + private val registry: Registry = mockk() + private val permissions: Permissions = mockk() + private val properties: NodeProperties = mockk() + + private val registryService: RegistryService + + init { + registryService = RegistryService(registry, permissions, properties) + } + + @Test + fun getNodes() { + val nodes = listOf( + RegistryNode(operator = "0xf17f52151EbEF6C7334FAD080c5704D77216b732", url = "http://localhost:8080"), + RegistryNode(operator = "0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef", url = "http://localhost:8081")) + + every { registry.nodeOperators.sendAsync().get() } returns nodes.map { it.operator } + + for (node in nodes) { + every { registry.getNode(node.operator).sendAsync().get() } returns node.url + } + + every { properties.privateKey } returns "0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f" + + val actual = registryService.getNodes(omitMine = true) + assertThat(actual.size).isEqualTo(1) + assertThat(actual[0].operator).isEqualTo(nodes[1].operator) + assertThat(actual[0].url).isEqualTo(nodes[1].url) + } + + @Test + fun `isRoleKnown with belongsToMe flag returns true`() { + val role = BasicRole("XYZ", "CH") + val serverURL = "https://my.server.com" + val serverEthAddress = "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4" + every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2(serverEthAddress, serverURL) + every { properties.url } returns serverURL + every { properties.privateKey } returns "0x1c3e5453c0f9aa74a8eb0216310b2b013f017813a648fce364bf41dbc0b37647" + assertThat(registryService.isRoleKnown(role)).isEqualTo(true) + } + + + @Test + fun `isRoleKnown without belongsToMe flag returns true`() { + val role = BasicRole("XYZ", "CH") + val serverURL = "https://my.server.com" + every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2("", serverURL) + assertThat(registryService.isRoleKnown(role, belongsToMe = false)).isEqualTo(true) + } + + @Test + fun getRemoteNodeURLOf() { + val role = BasicRole("XXX", "NL") + every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2("", "https://some.node.com") + assertThat(registryService.getRemoteNodeUrlOf(role)).isEqualTo("https://some.node.com") + } +} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt index 312d6c5..30ad84c 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt @@ -21,13 +21,14 @@ import snc.openchargingnetwork.node.tools.generatePrivateKey class RequestHandlerTest { private val routingService: RoutingService = mockk() + private val registryService: RegistryService = mockk() private val httpService: HttpService = mockk() private val walletService: WalletService = mockk() private val hubClientInfoService: HubClientInfoService = mockk() private val asyncTaskService: AsyncTaskService = mockk() private val properties: NodeProperties = mockk() - private val requestHandlerBuilder = RequestHandlerBuilder(routingService, httpService, walletService, + private val requestHandlerBuilder = RequestHandlerBuilder(routingService, registryService, httpService, walletService, hubClientInfoService, asyncTaskService, properties) @Test @@ -43,7 +44,7 @@ class RequestHandlerTest { sender = BasicRole("ABC", "DE"), receiver = BasicRole("XYZ", "DE"))) - every { routingService.validateSender(variables.headers.authorization, variables.headers.sender) } just Runs + every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs val requestHandler = requestHandlerBuilder.build(variables) assertDoesNotThrow { requestHandler.validateSender() } } @@ -67,7 +68,7 @@ class RequestHandlerTest { val variablesString = jacksonObjectMapper().writeValueAsString(variables) - every { routingService.isRoleKnownOnNetwork(variables.headers.sender, false) } returns true + every { registryService.isRoleKnown(variables.headers.sender, false) } returns true every { routingService.isRoleKnown(variables.headers.receiver) } returns true every { httpService.mapper.writeValueAsString(variables) } returns variablesString every { walletService.verify(variablesString, signature, variables.headers.sender) } just Runs @@ -105,8 +106,8 @@ class RequestHandlerTest { headers = mapOf(), body = OcpiResponse(1000)) - every { routingService.validateReceiver(variables.headers.receiver) } returns Receiver.LOCAL - every { routingService.validateWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL + every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs every { properties.signatures } returns false every { routingService.getPlatformRules(any()) } returns OcnRules(signatures = false) every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) @@ -114,6 +115,8 @@ class RequestHandlerTest { every { routingService.isRoleKnown(variables.headers.receiver) } returns true every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { hubClientInfoService.renewClientConnection(variables.headers.receiver) } just Runs + every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { registryService.getAgreementsByInterface(variables.headers.sender, variables.module, variables.interfaceRole) } returns sequenceOf() val response = requestHandler.forwardRequest().getResponse() assertEquals(expectedResponse.statusCode, response.statusCodeValue) @@ -156,14 +159,17 @@ class RequestHandlerTest { val receiverSig = Notary().sign(expectedResponse.toSignedValues(), receiverKey) expectedResponse.body.signature = receiverSig.serialize() - every { routingService.validateReceiver(variables.headers.receiver) } returns Receiver.LOCAL - every { routingService.validateWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL + every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs every { properties.signatures } returns false every { routingService.getPlatformRules(variables.headers.receiver) } returns OcnRules(signatures = true) - every { routingService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic(signature.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") - every { routingService.getPartyDetails(variables.headers.receiver) } returns RegistryPartyDetailsBasic(receiverSig.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") + every { registryService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic( + signature.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") + every { registryService.getPartyDetails(variables.headers.receiver) } returns RegistryPartyDetailsBasic( + receiverSig.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) every { httpService.makeOcpiRequest(recipientUrl, outgoingHeaders, variables) } returns expectedResponse + every { asyncTaskService.forwardToLinkedApps(variables) } just Runs val response = requestHandler.forwardRequest().getResponse() assertEquals(expectedResponse.statusCode, response.statusCodeValue) @@ -196,12 +202,14 @@ class RequestHandlerTest { headers = mapOf(), body = OcpiResponse(1000)) - every { routingService.validateReceiver(variables.headers.receiver) } returns Receiver.REMOTE + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.REMOTE every { properties.signatures } returns false - every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple(recipientUrl, outgoingHeaders, outgoingBody) + every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( + recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { routingService.isRoleKnown(variables.headers.receiver) } returns false + every { asyncTaskService.forwardToLinkedApps(variables) } just Runs val response = requestHandler.forwardRequest().getResponse() assertEquals(expectedResponse.statusCode, response.statusCodeValue) @@ -242,12 +250,17 @@ class RequestHandlerTest { val receiverSig = Notary().sign(expectedResponse.toSignedValues(), receiverKey) expectedResponse.body.signature = receiverSig.serialize() - every { routingService.validateReceiver(variables.headers.receiver) } returns Receiver.REMOTE + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.REMOTE every { properties.signatures } returns true - every { routingService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic(signature.signatory, "0x7c514d15709fb091243a4dffb649361354a9b038") - every { routingService.getPartyDetails(variables.headers.receiver) } returns RegistryPartyDetailsBasic(receiverSig.signatory, "0xd49ead20b0ae060161c9ddea9b1bc46bb29b3c58") - every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple(recipientUrl, outgoingHeaders, outgoingBody) + every { registryService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic( + signature.signatory, "0x7c514d15709fb091243a4dffb649361354a9b038") + every { registryService.getPartyDetails(variables.headers.receiver) } returns RegistryPartyDetailsBasic( + receiverSig.signatory, "0xd49ead20b0ae060161c9ddea9b1bc46bb29b3c58") + every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( + recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse + every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + val response = requestHandler.forwardRequest().getResponse() assertEquals(expectedResponse.statusCode, response.statusCodeValue) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt index 8f89b0f..f92cc33 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt @@ -25,10 +25,9 @@ class RoutingServiceTest { private val endpointRepo: EndpointRepository = mockk() private val proxyResourceRepo: ProxyResourceRepository = mockk() private val httpService: HttpService = mockk() - private val registry: Registry = mockk() private val walletService: WalletService = mockk() private val ocnRulesService: OcnRulesService = mockk() - private val properties: NodeProperties = mockk() + private val registryService: RegistryService = mockk() private val routingService: RoutingService @@ -38,11 +37,10 @@ class RoutingServiceTest { roleRepo, endpointRepo, proxyResourceRepo, - registry, + registryService, httpService, walletService, - ocnRulesService, - properties) + ocnRulesService) } @Test @@ -158,9 +156,7 @@ class RoutingServiceTest { val modifiedRequest = request.copy(headers = request.headers.copy(authorization = "")) - every { registry.getOperatorByOcpi( - request.headers.receiver.country.toByteArray(), - request.headers.receiver.id.toByteArray()).sendAsync().get() } returns Tuple2("", "https://ocn.node.net") + every { registryService.getRemoteNodeUrlOf(request.headers.receiver) } returns "https://ocn.node.net" val jsonString = jacksonObjectMapper().writeValueAsString(modifiedRequest) every { httpService.mapper.writeValueAsString(modifiedRequest) } returns jsonString @@ -196,9 +192,7 @@ class RoutingServiceTest { headers = request.headers.copy(authorization = ""), proxyResource = "https://actual.cpo.com/ocpi/sender/2.2/sessions?limit=10&offset=50; rel =\"next\"") - every { registry.getOperatorByOcpi( - request.headers.receiver.country.toByteArray(), - request.headers.receiver.id.toByteArray()).sendAsync().get() } returns Tuple2("", "https://ocn-node.provider.net") + every { registryService.getRemoteNodeUrlOf(request.headers.receiver) } returns "https://ocn-node.provider.net" every { routingService.getProxyResource("45", request.headers.sender, request.headers.receiver) } returns "https://actual.cpo.com/ocpi/sender/2.2/sessions?limit=10&offset=50; rel =\"next\"" @@ -240,9 +234,7 @@ class RoutingServiceTest { val sig = "0x9955af11969a2d2a7f860cb00e6a00cfa7c581f5df2dbe8ea16700b33f4b4b9" + "b69f945012f7ea7d3febf11eb1b78e1adc2d1c14c2cf48b25000938cc1860c83e01" - every { registry.getOperatorByOcpi( - request.headers.receiver.country.toByteArray(), - request.headers.receiver.id.toByteArray()).sendAsync().get() } returns Tuple2("", "https://ocn-node.provider.net") + every { registryService.getRemoteNodeUrlOf(request.headers.receiver) } returns "https://ocn-node.provider.net" val expectedAlteredBody = request.copy( headers = request.headers.copy(authorization = ""), @@ -300,27 +292,6 @@ class RoutingServiceTest { } - @Test - fun `isRoleKnownOnNetwork with belongsToMe flag returns true`() { - val role = BasicRole("XYZ", "CH") - val serverURL = "https://my.server.com" - val serverEthAddress = "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4" - every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2(serverEthAddress, serverURL) - every { properties.url } returns serverURL - every { properties.privateKey } returns "0x1c3e5453c0f9aa74a8eb0216310b2b013f017813a648fce364bf41dbc0b37647" - assertThat(routingService.isRoleKnownOnNetwork(role)).isEqualTo(true) - } - - - @Test - fun `isRoleKnownOnNetwork without belongsToMe flag returns true`() { - val role = BasicRole("XYZ", "CH") - val serverURL = "https://my.server.com" - every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2("", serverURL) - assertThat(routingService.isRoleKnownOnNetwork(role, belongsToMe = false)).isEqualTo(true) - } - - @Test fun getPlatformID() { val role = RoleEntity(5L, Role.CPO, BusinessDetails("SENDER Co"), "SEN", "DE") @@ -344,64 +315,36 @@ class RoutingServiceTest { @Test - fun getRemoteNodeURL() { - val role = BasicRole("XXX", "NL") - every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2("", "https://some.node.com") - assertThat(routingService.getRemoteNodeUrl(role)).isEqualTo("https://some.node.com") - } - - - @Test - fun `validateSender with auth only`() { + fun `checkSenderKnown with auth only`() { every { platformRepo.existsByAuth_TokenC("0102030405") } returns true - routingService.validateSender("Token 0102030405") + routingService.checkSenderKnown("Token 0102030405") } @Test - fun `validateSender with auth and role`() { + fun `checkSenderKnown with auth and role`() { val role = BasicRole("YUT", "BE") val platform = PlatformEntity(id = 3L) every { platformRepo.findByAuth_TokenC("0102030405") } returns platform every { roleRepo.existsByPlatformIDAndCountryCodeAndPartyIDAllIgnoreCase(3L, role.country, role.id) } returns true - routingService.validateSender("Token 0102030405", role) + routingService.checkSenderKnown("Token 0102030405", role) } @Test - fun `validateReceiver should return LOCAL`() { + fun `getReceiverType should return LOCAL`() { val role = BasicRole("SNC", "DE") every { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role.country, role.id) } returns true - assertThat(routingService.validateReceiver(role)).isEqualTo(Receiver.LOCAL) + assertThat(routingService.getReceiverType(role)).isEqualTo(Receiver.LOCAL) } @Test - fun `validateReceiver should return REMOTE`() { + fun `getReceiverType should return REMOTE`() { val role = BasicRole("SNC", "DE") every { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role.country, role.id) } returns false - every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2("", "http://localhost:8080") - assertThat(routingService.validateReceiver(role)).isEqualTo(Receiver.REMOTE) - } - - @Test - fun getNodesListedInRegistry() { - val nodes = listOf( - RegistryNode(operator = "0xf17f52151EbEF6C7334FAD080c5704D77216b732", url = "http://localhost:8080"), - RegistryNode(operator = "0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef", url = "http://localhost:8081")) - - every { registry.nodeOperators.sendAsync().get() } returns nodes.map { it.operator } - - for (node in nodes) { - every { registry.getNode(node.operator).sendAsync().get() } returns node.url - } - - every { properties.privateKey } returns "0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f" - - val actual = routingService.getNodesListedInRegistry(omitMine = true) - assertThat(actual.size).isEqualTo(1) - assertThat(actual[0].operator).isEqualTo(nodes[1].operator) - assertThat(actual[0].url).isEqualTo(nodes[1].url) + every { registryService.isRoleKnown(role, false) } returns true + assertThat(routingService.getReceiverType(role)).isEqualTo(Receiver.REMOTE) } } \ No newline at end of file From 5551dc087be34a956e7b20c4d63fbc9f66483b7a Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Fri, 29 May 2020 13:04:35 +0200 Subject: [PATCH 08/26] split requestHandlerBuilder into separate request and response handlers Signed-off-by: Adam Staveley --- .../node/components/OcpiMessageHandler.kt | 90 ++++ .../node/components/OcpiRequestHandler.kt | 253 ++++++++++ .../node/components/OcpiResponseHandler.kt | 226 +++++++++ .../components/listeners/AppEventsListener.kt | 30 ++ .../listeners/HubClientInfoListener.kt | 2 +- .../node/controllers/ocn/MessageController.kt | 8 +- .../controllers/ocpi/v2_2/CdrsController.kt | 14 +- .../ocpi/v2_2/ChargingProfilesController.kt | 16 +- .../ocpi/v2_2/CommandsController.kt | 18 +- .../ocpi/v2_2/LocationsController.kt | 34 +- .../ocpi/v2_2/SessionsController.kt | 18 +- .../ocpi/v2_2/TariffsController.kt | 16 +- .../controllers/ocpi/v2_2/TokensController.kt | 18 +- .../node/models/events/AppEvents.kt | 7 + .../node/services/AsyncTaskService.kt | 31 +- .../node/services/RequestHandlerBuilder.kt | 477 ------------------ .../controllers/ocn/MessageControllerTest.kt | 8 +- .../ocpi/v2_2/CdrsControllerTest.kt | 14 +- .../v2_2/ChargingProfilesControllerTest.kt | 16 +- .../ocpi/v2_2/CommandsControllerTest.kt | 18 +- .../ocpi/v2_2/LocationsControllerTest.kt | 34 +- .../ocpi/v2_2/SessionsControllerTest.kt | 18 +- .../ocpi/v2_2/TariffsControllerTest.kt | 16 +- .../ocpi/v2_2/TokensControllerTest.kt | 18 +- .../node/services/RequestHandlerTest.kt | 47 +- 25 files changed, 797 insertions(+), 650 deletions(-) create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt rename src/main/kotlin/snc/openchargingnetwork/node/{ => components}/listeners/HubClientInfoListener.kt (98%) create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt delete mode 100644 src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt new file mode 100644 index 0000000..60bff7c --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt @@ -0,0 +1,90 @@ +package snc.openchargingnetwork.node.components + +import org.web3j.crypto.Keys +import shareandcharge.openchargingnetwork.notary.Notary +import shareandcharge.openchargingnetwork.notary.ValuesToSign +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.exceptions.OcpiClientInvalidParametersException +import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables +import snc.openchargingnetwork.node.services.RegistryService +import snc.openchargingnetwork.node.services.RoutingService + +open class OcpiMessageHandler(val request: OcpiRequestVariables, + val properties: NodeProperties, + val routingService: RoutingService, + val registryService: RegistryService) { + + /** + * Notary object instantiated after validating a request. + * Only if message signing property (signatures) set to true OR request contains "OCN-Signature" header. + */ + var notary: Notary? = null + + /** + * Check message signing is enabled. Can be enabled in the following ways: + * 1. ocn.node.signatures property is set to true + * 2. request contains a signature header + * 3. (optional) recipient requires it (overrides other settings) + * + */ + fun isSigningActive(recipient: BasicRole? = null): Boolean { + var active = properties.signatures || request.headers.signature != null + if (recipient != null) { + val recipientRules = routingService.getPlatformRules(recipient) + active = active || recipientRules.signatures + } + return active + } + + /** + * Use the OCN Notary to validate a request's "OCN-Signature" header. Only validated if signing is active. + * + * @param signature the OCN signature contained in the request header or response body + * @param signedValues the values signed by the sender + * @param signer expected signatory of the signature + * @param receiver optional receiver of message (checks their OcnRules for signature verification requirement) + */ + fun validateOcnSignature(signature: String?, signedValues: ValuesToSign<*>, signer: BasicRole, receiver: BasicRole? = null) { + + if (isSigningActive(receiver)) { + val result = signature?.let { + notary = Notary.deserialize(it) + notary?.verify(signedValues) + } + when { + result == null -> throw OcpiClientInvalidParametersException("Missing OCN Signature") + !result.isValid -> throw OcpiClientInvalidParametersException("Invalid signature: ${result.error}") + } + + val party = registryService.getPartyDetails(signer) + val actualSignatory = Keys.toChecksumAddress(notary?.signatory) + val signedByParty = actualSignatory == Keys.toChecksumAddress(party.address) + val signedByOperator = actualSignatory == Keys.toChecksumAddress(party.operator) + + if (!signedByParty && !signedByOperator) { + throw OcpiClientInvalidParametersException("Actual signatory ${notary?.signatory} differs from expected signatory ${party.address} (party) or ${party.operator} (operator)") + } + } + } + + /** + * Used by the OCN Node to "stash" and "rewrite" the signature of a request if it needs to modify values + */ + fun rewriteAndSign(valuesToSign: ValuesToSign<*>, rewriteFields: Map): String? { + if (isSigningActive()) { + val notary = validateNotary() + notary.stash(rewriteFields) + notary.sign(valuesToSign, properties.privateKey!!) + return notary.serialize() + } + return null + } + + /** + * Check notary exists. Throws UnsupportedOperationException if request has not yet been validated. + */ + private fun validateNotary(): Notary { + return notary ?: throw UnsupportedOperationException("Non-canonical method chaining: must call a validating method first") + } +} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt new file mode 100644 index 0000000..7521303 --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -0,0 +1,253 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.components + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Component +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.HttpResponse +import snc.openchargingnetwork.node.models.Receiver +import snc.openchargingnetwork.node.models.exceptions.OcpiHubUnknownReceiverException +import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables +import snc.openchargingnetwork.node.services.* +import snc.openchargingnetwork.node.tools.generateUUIDv4Token +import snc.openchargingnetwork.node.tools.urlJoin +import kotlin.properties.Delegates.observable + + +/** + * Spring boot service that instantiates RequestHandler objects. + */ +@Component +class OcpiRequestHandlerBuilder(private val routingService: RoutingService, + private val registryService: RegistryService, + private val httpService: HttpService, + private val walletService: WalletService, + private val hubClientInfoService: HubClientInfoService, + private val asyncTaskService: AsyncTaskService, + private val responseHandlerBuilder: OcpiResponseHandlerBuilder, + private val properties: NodeProperties) { + + /** + * Build a RequestHandler object from an OcpiRequestVariables object. + */ + fun build(requestVariables: OcpiRequestVariables): OcpiRequestHandler { + return OcpiRequestHandler(requestVariables, routingService, registryService, httpService, hubClientInfoService, + walletService, asyncTaskService, responseHandlerBuilder, properties) + } + + /** + * Build a RequestHandler object from a JSON-serialized string of an OcpiRequestVariables object. + */ + fun build(requestVariablesString: String): OcpiRequestHandler { + val requestVariables = httpService.convertToRequestVariables(requestVariablesString) + return OcpiRequestHandler(requestVariables, routingService, registryService, httpService, hubClientInfoService, + walletService, asyncTaskService, responseHandlerBuilder, properties) + } + +} + + +/** + * Handle an individual OCPI HTTP request. Instantiated with OcpiRequestVariables. The RequestHandler supports method + * chaining to cleanly handle incoming messages, however, this comes at the cost of needing to manage/know its state. + * + * There is a common way of handling core requests (peer-to-peer OCPI requests). + * 1. validate the request (authorization, sender, receiver, signature [optional]) + * 2. forward the request + * + * To avoid operating on responses that don't exist this order of operations needs to be respected. If not, an + * UnsupportedOperationException will be raised. + * + * E.g. + * + * requestHandler.validateSender().forwardRequest().getResponse() + * requestHandler.validateSender().forwardRequest(proxied = true).getResponseWithPaginatedHeaders() + * requestHandler.validateOcnMessage(signature).forwardRequest().getResponseWithAllHeaders() + * + * @property request the OCPI HTTP request as OcpiRequestVariables. + */ +class OcpiRequestHandler(request: OcpiRequestVariables, + routingService: RoutingService, + registryService: RegistryService, + private val httpService: HttpService, + private val hubClientInfoService: HubClientInfoService, + private val walletService: WalletService, + private val asyncTaskService: AsyncTaskService, + private val responseHandlerBuilder: OcpiResponseHandlerBuilder, + properties: NodeProperties): OcpiMessageHandler(request, properties, routingService, registryService) { + + companion object { + private var logger: Logger = LoggerFactory.getLogger(OcpiRequestHandler::class.java) + } + + /** + * Is sender/receiver known to this OCN Node? + */ + private var knownSender: Boolean = true + + /** + * Sender's message is valid (sender, receiver and signature have all been validated) + * Once valid, we can run the async task to find ocn apps the request can be forwarded to + */ + private var requestIsValid: Boolean by observable(false) { _, validBefore, validNow -> + if (!validBefore && validNow) { // task should only run when requestIsValid becomes true for the first time + asyncTaskService.forwardToLinkedApps(request) + } + } + + /** + * Assert the sender is allowed to send OCPI requests to this OCN Node. + * If message signing is required, or OCN-Signature header present, verifies the signature. + */ + fun validateSender(): OcpiRequestHandler { + routingService.checkSenderKnown(request.headers.authorization, request.headers.sender) + hubClientInfoService.renewClientConnection(request.headers.sender) + return this + } + + /** + * Asserts the sender exists in the Registry and is connected to the OCN Node which has sent the request. + * Asserts the receiver is connected to this OCN Node. + * If message signing is required, or OCN-Signature header present, verifies the signature. + */ + fun validateOcnMessage(signature: String): OcpiRequestHandler { + knownSender = false + if (!registryService.isRoleKnown(request.headers.sender, belongsToMe = false)) { + throw OcpiHubUnknownReceiverException("Sending party not registered on Open Charging Network") + } + + if (!routingService.isRoleKnown(request.headers.receiver)) { + throw OcpiHubUnknownReceiverException("Recipient unknown to OCN Node entered in Registry") + } + + val requestString = httpService.mapper.writeValueAsString(request) + walletService.verify(requestString, signature, request.headers.sender) + return this + } + + /** + * Forward the request to the receiver. + * Checks whether receiver is LOCAL or REMOTE. + * - If LOCAL, makes direct request to receiver. + * - If REMOTE, forwards request to receiver's OCN Node as written in the Registry. + * + * @param proxied tells the RequestHandler that this request requires a proxied resource that was previously + * saved by the OCN Node. + */ + fun forwardRequest(proxied: Boolean = false): OcpiResponseHandler { + val response: HttpResponse = when (routingService.getReceiverType(request.headers.receiver)) { + + Receiver.LOCAL -> { + routingService.checkSenderWhitelisted( + sender = request.headers.sender, + receiver = request.headers.receiver, + module = request.module) + + validateOcnSignature( + signature = request.headers.signature, + signedValues = request.toSignedValues(), + signer = request.headers.sender, + receiver = request.headers.receiver) + val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) + + requestIsValid = true // trigger side-effect (forward to linked apps) + httpService.makeOcpiRequest(url, headers, request) + } + + Receiver.REMOTE -> { + validateOcnSignature( + signature = request.headers.signature, + signedValues = request.toSignedValues(), + signer = request.headers.sender) + val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) + + requestIsValid = true // trigger side-effect (forward to linked apps) + httpService.postOcnMessage(url, headers, body) + } + } + + return responseHandlerBuilder.build(request, response) + } + + /** + * Used by the commands module's RECEIVER interface, which requires the modifying of the "response_url". + * Checks whether receiver is LOCAL or REMOTE. + * - If LOCAL, makes direct request to receiver. + * - If REMOTE, forwards request to receiver's OCN Node as written in the Registry. + * + * @param responseUrl the original response_url as defined by the sender + * @param modifyRequest callback which allows the request (OcpiRequestVariables) used by this RequestHandler to + * be modified with the new response_url which will be sent to the receiver. + */ + fun forwardModifiableRequest(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): OcpiResponseHandler { + val proxyPath = "/ocpi/sender/2.2/commands/${request.urlPathVariables}" + val rewriteFields = mapOf("$['body']['response_url']" to responseUrl) + + val response: HttpResponse = when (routingService.getReceiverType(request.headers.receiver)) { + + Receiver.LOCAL -> { + routingService.checkSenderWhitelisted( + sender = request.headers.sender, + receiver = request.headers.receiver, + module = request.module) + validateOcnSignature( + signature = request.headers.signature, + signedValues = request.toSignedValues(), + signer = request.headers.sender, + receiver = request.headers.receiver) + + // save the original resource (response_url), returning a uid pointing to its location + val resourceID = routingService.setProxyResource(responseUrl, request.headers.receiver, request.headers.sender) + // use the callback to modify the original request with the new response_url + val modifiedRequest = modifyRequest(urlJoin(properties.url, proxyPath, resourceID)) + // use the notary to securely modify the request signature + modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) + // send the request with the modified body + val (url, headers) = routingService.prepareLocalPlatformRequest(request) + requestIsValid = true // trigger side-effect (forward to linked apps) + httpService.makeOcpiRequest(url, headers, modifiedRequest) + } + + Receiver.REMOTE -> { + validateOcnSignature( + signature = request.headers.signature, + signedValues = request.toSignedValues(), + signer = request.headers.sender) + + val (url, headers, body) = routingService.prepareRemotePlatformRequest(request) { + // create a uid that will point to the original response_url + val proxyUID = generateUUIDv4Token() + // use the callback to modify the original request with the new response_url + val modifiedRequest = modifyRequest(urlJoin(it, proxyPath, proxyUID)) + // use the notary to securely modify the request signature + modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) + // add the proxy uid and resource to the outgoing body (read by the recipient's OCN node) + modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) + } + + requestIsValid = true // trigger side-effect (forward to linked apps) + httpService.postOcnMessage(url, headers, body) + } + + } + + return responseHandlerBuilder.build(request, response) + } + +} diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt new file mode 100644 index 0000000..dca3d75 --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt @@ -0,0 +1,226 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.components + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.http.HttpHeaders +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Component +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.HttpResponse +import snc.openchargingnetwork.node.models.exceptions.OcpiClientInvalidParametersException +import snc.openchargingnetwork.node.models.exceptions.OcpiServerGenericException +import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables +import snc.openchargingnetwork.node.models.ocpi.OcpiResponse +import snc.openchargingnetwork.node.services.HubClientInfoService +import snc.openchargingnetwork.node.services.RegistryService +import snc.openchargingnetwork.node.services.RoutingService +import snc.openchargingnetwork.node.tools.extractNextLink +import snc.openchargingnetwork.node.tools.urlJoin + + +/** + * Spring boot service that instantiates OcpiResponseHandler objects. + */ +@Component +class OcpiResponseHandlerBuilder(private val routingService: RoutingService, + private val registryService: RegistryService, + private val hubClientInfoService: HubClientInfoService, + private val properties: NodeProperties) { + + /** + * Build a RequestHandler object from an request (OcpiRequestVariables) and response (HttpResponse) object. + */ + fun build(request: OcpiRequestVariables, response: HttpResponse): OcpiResponseHandler { + return OcpiResponseHandler(request, response, routingService, registryService, properties, hubClientInfoService) + } + +} + + +/** + * Handle an individual OCPI HTTP request. Instantiated with OcpiRequestVariables. The RequestHandler supports method + * chaining to cleanly handle incoming messages, however, this comes at the cost of needing to manage/know its state. + * + * There is a common way of handling core requests (peer-to-peer OCPI requests). + * 1. validate the request (authorization, sender, receiver, signature [optional]) + * 2. forward the request + * 3. get the response + * + * To avoid operating on responses that don't exist this order of operations needs to be respected. If not, an + * UnsupportedOperationException will be raised. + * + * E.g. + * + * requestHandler.validateSender().forwardRequest().getResponse() + * requestHandler.validateSender().forwardRequest(proxied = true).getResponseWithPaginatedHeaders() + * requestHandler.validateOcnMessage(signature).forwardRequest().getResponseWithAllHeaders() + * + * @property request the OCPI HTTP request as OcpiRequestVariables. + */ +class OcpiResponseHandler(request: OcpiRequestVariables, + private val response: HttpResponse, + routingService: RoutingService, + registryService: RegistryService, + properties: NodeProperties, + hubClientInfoService: HubClientInfoService): OcpiMessageHandler(request, properties, routingService, registryService) { + + init { + validateResponse() + if (isOcpiSuccess() && routingService.isRoleKnown(request.headers.receiver)) { // only renew connection of known recipients + hubClientInfoService.renewClientConnection(request.headers.receiver) + } + } + + companion object { + private var logger: Logger = LoggerFactory.getLogger(OcpiResponseHandler::class.java) + } + + /** + * Is sender/receiver known to this OCN Node? + */ + private var knownSender: Boolean = true + + /** + * Get the ResponseEntity object after forwarding the request, expecting no headers from the receiver's response. + */ + fun getResponse(): ResponseEntity> { + val headers = HttpHeaders() + return ResponseEntity + .status(response.statusCode) + .headers(headers) + .body(response.body) + } + + /** + * Get the ResponseEntity object after forwarding the request, expecting pagination headers which must be proxied. + */ + fun getResponseWithPaginationHeaders(): ResponseEntity> { + return when (isOcpiSuccess()) { + true -> { + request.urlPathVariables?.let { + routingService.deleteProxyResource(it) + } + + val headers = HttpHeaders() + + response.headers["X-Total-Count"]?.let { headers["X-Total-Count"] = it } + response.headers["X-Limit"]?.let { headers["X-Limit"] = it } + response.headers["OCN-Signature"]?.let { headers["OCN-Signature"] = it } + + response.headers["Link"]?.let { + it.extractNextLink()?.let {next -> + + val id = routingService.setProxyResource(next, request.headers.sender, request.headers.receiver) + val proxyPaginationEndpoint = "/ocpi/${request.interfaceRole.id}/2.2/${request.module.id}/page" + val link = urlJoin(properties.url, proxyPaginationEndpoint, id) + headers["Link"] = "<$link>; rel=\"next\"" + + if (isSigningActive(request.headers.sender)) { + response.body.signature = null + val valuesToSign = response.copy(headers = headers.toSingleValueMap()).toSignedValues() + val rewriteFields = mapOf("$['headers']['link']" to it) + response.body.signature = rewriteAndSign(valuesToSign, rewriteFields) + } + } + } + + return ResponseEntity + .status(response.statusCode) + .headers(headers) + .body(response.body) + } + false -> getResponse() + } + } + + /** + * Get the ResponseEntity object after forwarding the request, expecting a Location link which must be proxied. + */ + fun getResponseWithLocationHeader(proxyPath: String): ResponseEntity> { + return when (isOcpiSuccess()) { + true -> { + val headers = HttpHeaders() + response.headers["Location"]?.let { + val resourceId = routingService.setProxyResource(it, request.headers.sender, request.headers.receiver) + val newLocation = urlJoin(properties.url, proxyPath, resourceId) + headers["Location"] = newLocation + + if (isSigningActive(request.headers.sender)) { + response.body.signature = null + val valuesToSign = response.copy(headers = headers.toSingleValueMap()) + val rewriteFields = mapOf("$['headers']['location']" to it) + response.body.signature = rewriteAndSign(valuesToSign.toSignedValues(), rewriteFields) + } + } + + ResponseEntity + .status(response.statusCode) + .headers(headers) + .body(response.body) + } + false -> getResponse() + } + } + + /** + * Used by the /ocn/message handler to forward all possible response headers. + */ + fun getResponseWithAllHeaders(): ResponseEntity> { + return when (isOcpiSuccess()) { + true -> { + val responseHeaders = HttpHeaders() + response.headers["location"]?.let { responseHeaders.set("Location", it) } + response.headers["Link"]?.let { responseHeaders.set("Link", it) } + response.headers["X-Total-Count"]?.let { responseHeaders.set("X-Total-Count", it) } + response.headers["X-Limit"]?.let { responseHeaders.set("X-Limit", it) } + ResponseEntity + .status(response.statusCode) + .headers(responseHeaders) + .body(response.body) + } + false -> getResponse() + } + } + + /** + * Check ocpi request was success (i.e. before operating on headers) + */ + private fun isOcpiSuccess(): Boolean { + return response.statusCode == 200 && response.body.statusCode == 1000 + } + + /** + * Check response exists. Throws UnsupportedOperationException if request has not yet been forwarded. + */ + private fun validateResponse() { + // set receiver based on if local/remote request + val receiver = if (knownSender) { request.headers.sender } else { null } + + try { + validateOcnSignature( + signature = response.body.signature, + signedValues = response.toSignedValues(), + signer = request.headers.receiver, + receiver = receiver) + } catch (e: OcpiClientInvalidParametersException) { + throw OcpiServerGenericException("Unable to verify response signature: ${e.message}") + } + } + +} diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt new file mode 100644 index 0000000..02e868c --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt @@ -0,0 +1,30 @@ +package snc.openchargingnetwork.node.components.listeners + +import org.slf4j.LoggerFactory +import org.springframework.context.event.EventListener +import org.springframework.scheduling.annotation.Async +import org.springframework.stereotype.Component +import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder + +@Component +class AppEventsListener(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { + + companion object { + private val logger = LoggerFactory.getLogger(AppEventsListener::class.java) + } + + @Async + @EventListener + fun handleAppRecipientFoundEvent(event: AppRecipientFoundEvent) { + logger.info("Got AppRecipientFoundEvent!") + // create a copy of the request with the new receiver headers (the app provider) + val modifiedRequest = event.request.copy(headers = event.request.headers.copy(receiver = event.recipient)) + // update the signature: use existing sig in notary, then stash and re-sign + // find recipient location (LOCAL/REMOTE) + // send! + val requestHandler = requestHandlerBuilder.build(modifiedRequest) + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/HubClientInfoListener.kt similarity index 98% rename from src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt rename to src/main/kotlin/snc/openchargingnetwork/node/components/listeners/HubClientInfoListener.kt index 06b5c7f..c33dd61 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/listeners/HubClientInfoListener.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/HubClientInfoListener.kt @@ -14,7 +14,7 @@ limitations under the License. */ -package snc.openchargingnetwork.node.listeners +package snc.openchargingnetwork.node.components.listeners import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt index e6911e9..7d61b85 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt @@ -18,14 +18,14 @@ package snc.openchargingnetwork.node.controllers.ocn import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.models.ocpi.OcpiResponse @RestController @RequestMapping("/ocn/message") -class MessageController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class MessageController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { @PostMapping @@ -33,7 +33,7 @@ class MessageController(private val requestHandlerBuilder: RequestHandlerBuilder @RequestHeader("OCN-Signature") signature: String, @RequestBody body: String): ResponseEntity> { - val request: RequestHandler = requestHandlerBuilder.build(body) + val request: OcpiRequestHandler = requestHandlerBuilder.build(body) return request .validateOcnMessage(signature) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt index 65efa30..4dec618 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt @@ -21,13 +21,13 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @RestController -class CdrsController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** @@ -60,7 +60,7 @@ class CdrsController(private val requestHandlerBuilder: RequestHandlerBuilder) { headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -89,7 +89,7 @@ class CdrsController(private val requestHandlerBuilder: RequestHandlerBuilder) { headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest(proxied = true) @@ -122,7 +122,7 @@ class CdrsController(private val requestHandlerBuilder: RequestHandlerBuilder) { headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = cdrID) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest(proxied = true) @@ -151,7 +151,7 @@ class CdrsController(private val requestHandlerBuilder: RequestHandlerBuilder) { headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt index daf4418..48f2614 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt @@ -21,12 +21,12 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.OcnHeaders import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @RestController -class ChargingProfilesController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** * SENDER INTERFACE @@ -55,7 +55,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: RequestHandl urlPathVariables = uid, body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest(proxied = true) @@ -85,7 +85,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: RequestHandl urlPathVariables = sessionId, body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -121,7 +121,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: RequestHandl urlPathVariables = sessionId, urlEncodedParams = mapOf("duration" to duration, "response_url" to responseUrl)) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(responseUrl) { @@ -153,7 +153,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: RequestHandl urlPathVariables = sessionId, body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(body.responseUrl) { @@ -185,7 +185,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: RequestHandl urlPathVariables = sessionId, urlEncodedParams = mapOf("response_url" to responseUrl)) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(responseUrl) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt index a8250b8..6fae333 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt @@ -22,11 +22,11 @@ import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @RestController -class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilder, +class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder, private val properties: NodeProperties) { @@ -58,7 +58,7 @@ class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = uid, body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest(proxied = true) @@ -93,7 +93,7 @@ class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "CANCEL_RESERVATION", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } @@ -123,7 +123,7 @@ class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "RESERVE_NOW", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } @@ -153,7 +153,7 @@ class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "START_SESSION", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } @@ -183,7 +183,7 @@ class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "STOP_SESSION", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } @@ -213,7 +213,7 @@ class CommandsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "UNLOCK_CONNECTOR", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt index a3d804b..e0712d5 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt @@ -21,13 +21,13 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @RestController -class LocationsController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** @@ -60,7 +60,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -88,7 +88,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest(proxied = true) @@ -116,7 +116,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = locationID) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -145,7 +145,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$locationID/$evseUID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -175,7 +175,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$locationID/$evseUID/$connectorID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -210,7 +210,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$locationID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -241,7 +241,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -273,7 +273,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -305,7 +305,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild urlPathVariables = "/$countryCode/$partyID/$locationID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -338,7 +338,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -372,7 +372,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -404,7 +404,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild urlPathVariables = "/$countryCode/$partyID/$locationID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -437,7 +437,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -471,7 +471,7 @@ class LocationsController(private val requestHandlerBuilder: RequestHandlerBuild urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt index 25ef3a9..a12958d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt @@ -21,13 +21,13 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @RestController -class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** @@ -60,7 +60,7 @@ class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -88,7 +88,7 @@ class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -118,7 +118,7 @@ class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "/$sessionID/charging_preferences", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -153,7 +153,7 @@ class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$sessionID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -185,7 +185,7 @@ class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "/$countryCode/$partyID/$sessionID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -217,7 +217,7 @@ class SessionsController(private val requestHandlerBuilder: RequestHandlerBuilde urlPathVariables = "/$countryCode/$partyID/$sessionID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt index f1cc857..09d3732 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt @@ -21,13 +21,13 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @RestController -class TariffsController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** @@ -60,7 +60,7 @@ class TariffsController(private val requestHandlerBuilder: RequestHandlerBuilder headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -88,7 +88,7 @@ class TariffsController(private val requestHandlerBuilder: RequestHandlerBuilder headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -123,7 +123,7 @@ class TariffsController(private val requestHandlerBuilder: RequestHandlerBuilder headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$tariffID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -156,7 +156,7 @@ class TariffsController(private val requestHandlerBuilder: RequestHandlerBuilder urlPathVariables = "/$countryCode/$partyID/$tariffID", body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -187,7 +187,7 @@ class TariffsController(private val requestHandlerBuilder: RequestHandlerBuilder headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$tariffID") - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt index 38b1b63..0db42e9 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt @@ -21,12 +21,12 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @RestController -class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) { +class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** @@ -59,7 +59,7 @@ class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -87,7 +87,7 @@ class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: RequestHandler> = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -120,7 +120,7 @@ class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) urlEncodedParams = mapOf("type" to type), body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -157,7 +157,7 @@ class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) urlPathVariables = "/$countryCode/$partyID/$tokenUID", urlEncodedParams = mapOf("type" to type)) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -191,7 +191,7 @@ class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) urlEncodedParams = mapOf("type" to type), body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() @@ -225,7 +225,7 @@ class TokensController(private val requestHandlerBuilder: RequestHandlerBuilder) urlEncodedParams = mapOf("type" to type), body = body) - val request: RequestHandler = requestHandlerBuilder.build(requestVariables) + val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) return request .validateSender() .forwardRequest() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt new file mode 100644 index 0000000..1652479 --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt @@ -0,0 +1,7 @@ +package snc.openchargingnetwork.node.models.events + +import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables + + +class AppRecipientFoundEvent(val recipient: BasicRole, val request: OcpiRequestVariables) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index 281151a..63baf79 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -1,12 +1,32 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.services import org.slf4j.LoggerFactory +import org.springframework.context.ApplicationEventPublisher +import org.springframework.data.domain.AbstractAggregateRoot import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service +import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables @Service -class AsyncTaskService(private val registryService: RegistryService) { +class AsyncTaskService(private val registryService: RegistryService, + private val applicationEventPublisher: ApplicationEventPublisher) { companion object { private val logger = LoggerFactory.getLogger(AsyncTaskService::class.java) @@ -20,13 +40,8 @@ class AsyncTaskService(private val registryService: RegistryService) { fun forwardToLinkedApps(request: OcpiRequestVariables) { registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach { - logger.info("forwarding request to ${it.provider}") - // TODO: send to app using requestHandlerBuilder (circular import - could use an event instead) - // e.g. forEach - // -> fire event containing original request and new recipient - // -> listener picks up event - // -> uses builder to create request handler - // -> forwards modifiable request to new sender (must modify recipient headers and re-sign) + logger.info("publishing forward request to ${it.provider}") + applicationEventPublisher.publishEvent(AppRecipientFoundEvent(it.provider, request)) } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt deleted file mode 100644 index 88da0c6..0000000 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RequestHandlerBuilder.kt +++ /dev/null @@ -1,477 +0,0 @@ -/* - Copyright 2019-2020 eMobilify GmbH - - 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 snc.openchargingnetwork.node.services - -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.springframework.http.HttpHeaders -import org.springframework.http.ResponseEntity -import org.springframework.stereotype.Service -import org.web3j.crypto.Keys -import shareandcharge.openchargingnetwork.notary.Notary -import shareandcharge.openchargingnetwork.notary.ValuesToSign -import snc.openchargingnetwork.node.config.NodeProperties -import snc.openchargingnetwork.node.models.HttpResponse -import snc.openchargingnetwork.node.models.Receiver -import snc.openchargingnetwork.node.models.exceptions.OcpiClientInvalidParametersException -import snc.openchargingnetwork.node.models.exceptions.OcpiHubUnknownReceiverException -import snc.openchargingnetwork.node.models.exceptions.OcpiServerGenericException -import snc.openchargingnetwork.node.models.ocpi.BasicRole -import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables -import snc.openchargingnetwork.node.models.ocpi.OcpiResponse -import snc.openchargingnetwork.node.tools.extractNextLink -import snc.openchargingnetwork.node.tools.generateUUIDv4Token -import snc.openchargingnetwork.node.tools.urlJoin -import kotlin.properties.Delegates.observable - - -/** - * Spring boot service that instantiates RequestHandler objects. - */ -@Service -class RequestHandlerBuilder(private val routingService: RoutingService, - private val registryService: RegistryService, - private val httpService: HttpService, - private val walletService: WalletService, - private val hubClientInfoService: HubClientInfoService, - private val asyncTaskService: AsyncTaskService, - private val properties: NodeProperties) { - - /** - * Build a RequestHandler object from an OcpiRequestVariables object. - */ - fun build(requestVariables: OcpiRequestVariables): RequestHandler { - return RequestHandler(requestVariables, routingService, registryService, httpService, hubClientInfoService, - walletService, asyncTaskService, properties) - } - - /** - * Build a RequestHandler object from a JSON-serialized string of an OcpiRequestVariables object. - */ - fun build(requestVariablesString: String): RequestHandler { - val requestVariables = httpService.convertToRequestVariables(requestVariablesString) - return RequestHandler(requestVariables, routingService, registryService, httpService, hubClientInfoService, - walletService, asyncTaskService, properties) - } - -} - - -/** - * Handle an individual OCPI HTTP request. Instantiated with OcpiRequestVariables. The RequestHandler supports method - * chaining to cleanly handle incoming messages, however, this comes at the cost of needing to manage/know its state. - * - * There is a common way of handling core requests (peer-to-peer OCPI requests). - * 1. validate the request (authorization, sender, receiver, signature [optional]) - * 2. forward the request - * 3. get the response - * - * To avoid operating on responses that don't exist this order of operations needs to be respected. If not, an - * UnsupportedOperationException will be raised. - * - * E.g. - * - * requestHandler.validateSender().forwardRequest().getResponse() - * requestHandler.validateSender().forwardRequest(proxied = true).getResponseWithPaginatedHeaders() - * requestHandler.validateOcnMessage(signature).forwardRequest().getResponseWithAllHeaders() - * - * @property request the OCPI HTTP request as OcpiRequestVariables. - */ -class RequestHandler(private val request: OcpiRequestVariables, - private val routingService: RoutingService, - private val registryService: RegistryService, - private val httpService: HttpService, - private val hubClientInfoService: HubClientInfoService, - private val walletService: WalletService, - private val asyncTaskService: AsyncTaskService, - private val properties: NodeProperties) { - - companion object { - private var logger: Logger = LoggerFactory.getLogger(RequestHandler::class.java) - } - - /** - * HttpResponse object instantiated after forwarding a request. - * TODO: could operate on response in dedicated ResponseHandler - */ - private var response: HttpResponse? by observable?>(null) {_, _, _ -> - if (isOcpiSuccess() && routingService.isRoleKnown(request.headers.receiver)) { // only renew connection of known recipients - hubClientInfoService.renewClientConnection(request.headers.receiver) - } - } - - /** - * Notary object instantiated after validating a request. - * Only if message signing property (signatures) set to true OR request contains "OCN-Signature" header. - */ - private var notary: Notary? = null - - /** - * Is sender/receiver known to this OCN Node? - */ - private var knownSender: Boolean = true - - /** - * Sender's message is valid (sender, receiver and signature have all been validated) - * Once valid, we can run the async task to find ocn apps the request can be forwarded to - */ - private var requestIsValid: Boolean by observable(false) { _, validBefore, validNow -> - if (!validBefore && validNow) { // task should only run when requestIsValid becomes true for the first time - asyncTaskService.forwardToLinkedApps(request) - } - } - - /** - * Assert the sender is allowed to send OCPI requests to this OCN Node. - * If message signing is required, or OCN-Signature header present, verifies the signature. - */ - fun validateSender(): RequestHandler { - routingService.checkSenderKnown(request.headers.authorization, request.headers.sender) - hubClientInfoService.renewClientConnection(request.headers.sender) - return this - } - - /** - * Asserts the sender exists in the Registry and is connected to the OCN Node which has sent the request. - * Asserts the receiver is connected to this OCN Node. - * If message signing is required, or OCN-Signature header present, verifies the signature. - */ - fun validateOcnMessage(signature: String): RequestHandler { - knownSender = false - if (!registryService.isRoleKnown(request.headers.sender, belongsToMe = false)) { - throw OcpiHubUnknownReceiverException("Sending party not registered on Open Charging Network") - } - - if (!routingService.isRoleKnown(request.headers.receiver)) { - throw OcpiHubUnknownReceiverException("Recipient unknown to OCN Node entered in Registry") - } - - val requestString = httpService.mapper.writeValueAsString(request) - walletService.verify(requestString, signature, request.headers.sender) - return this - } - - /** - * Forward the request to the receiver. - * Checks whether receiver is LOCAL or REMOTE. - * - If LOCAL, makes direct request to receiver. - * - If REMOTE, forwards request to receiver's OCN Node as written in the Registry. - * - * @param proxied tells the RequestHandler that this request requires a proxied resource that was previously - * saved by the OCN Node. - */ - fun forwardRequest(proxied: Boolean = false): RequestHandler { - response = when (routingService.getReceiverType(request.headers.receiver)) { - - Receiver.LOCAL -> { - routingService.checkSenderWhitelisted( - sender = request.headers.sender, - receiver = request.headers.receiver, - module = request.module) - - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender, - receiver = request.headers.receiver) - val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) - - requestIsValid = true // trigger side-effect (forward to linked apps) - httpService.makeOcpiRequest(url, headers, request) - } - - Receiver.REMOTE -> { - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender) - val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) - - requestIsValid = true // trigger side-effect (forward to linked apps) - httpService.postOcnMessage(url, headers, body) - } - } - return this - } - - /** - * Used by the commands module's RECEIVER interface, which requires the modifying of the "response_url". - * Checks whether receiver is LOCAL or REMOTE. - * - If LOCAL, makes direct request to receiver. - * - If REMOTE, forwards request to receiver's OCN Node as written in the Registry. - * - * @param responseUrl the original response_url as defined by the sender - * @param modifyRequest callback which allows the request (OcpiRequestVariables) used by this RequestHandler to - * be modified with the new response_url which will be sent to the receiver. - */ - fun forwardModifiableRequest(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): RequestHandler { - val proxyPath = "/ocpi/sender/2.2/commands/${request.urlPathVariables}" - val rewriteFields = mapOf("$['body']['response_url']" to responseUrl) - - response = when (routingService.getReceiverType(request.headers.receiver)) { - - Receiver.LOCAL -> { - routingService.checkSenderWhitelisted( - sender = request.headers.sender, - receiver = request.headers.receiver, - module = request.module) - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender, - receiver = request.headers.receiver) - - // save the original resource (response_url), returning a uid pointing to its location - val resourceID = routingService.setProxyResource(responseUrl, request.headers.receiver, request.headers.sender) - // use the callback to modify the original request with the new response_url - val modifiedRequest = modifyRequest(urlJoin(properties.url, proxyPath, resourceID)) - // use the notary to securely modify the request signature - modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) - // send the request with the modified body - val (url, headers) = routingService.prepareLocalPlatformRequest(request) - requestIsValid = true // trigger side-effect (forward to linked apps) - httpService.makeOcpiRequest(url, headers, modifiedRequest) - } - - Receiver.REMOTE -> { - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender) - - val (url, headers, body) = routingService.prepareRemotePlatformRequest(request) { - // create a uid that will point to the original response_url - val proxyUID = generateUUIDv4Token() - // use the callback to modify the original request with the new response_url - val modifiedRequest = modifyRequest(urlJoin(it, proxyPath, proxyUID)) - // use the notary to securely modify the request signature - modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) - // add the proxy uid and resource to the outgoing body (read by the recipient's OCN node) - modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) - } - - requestIsValid = true // trigger side-effect (forward to linked apps) - httpService.postOcnMessage(url, headers, body) - } - - } - - return this - } - - /** - * Get the ResponseEntity object after forwarding the request, expecting no headers from the receiver's response. - */ - fun getResponse(): ResponseEntity> { - val response = validateResponse() - val headers = HttpHeaders() - return ResponseEntity - .status(response.statusCode) - .headers(headers) - .body(response.body) - } - - /** - * Get the ResponseEntity object after forwarding the request, expecting pagination headers which must be proxied. - */ - fun getResponseWithPaginationHeaders(): ResponseEntity> { - val response = validateResponse() - return when (isOcpiSuccess()) { - true -> { - request.urlPathVariables?.let { - routingService.deleteProxyResource(it) - } - - val headers = HttpHeaders() - - response.headers["X-Total-Count"]?.let { headers["X-Total-Count"] = it } - response.headers["X-Limit"]?.let { headers["X-Limit"] = it } - response.headers["OCN-Signature"]?.let { headers["OCN-Signature"] = it } - - response.headers["Link"]?.let { - it.extractNextLink()?.let {next -> - - val id = routingService.setProxyResource(next, request.headers.sender, request.headers.receiver) - val proxyPaginationEndpoint = "/ocpi/${request.interfaceRole.id}/2.2/${request.module.id}/page" - val link = urlJoin(properties.url, proxyPaginationEndpoint, id) - headers["Link"] = "<$link>; rel=\"next\"" - - if (isSigningActive(request.headers.sender)) { - response.body.signature = null - val valuesToSign = response.copy(headers = headers.toSingleValueMap()).toSignedValues() - val rewriteFields = mapOf("$['headers']['link']" to it) - response.body.signature = rewriteAndSign(valuesToSign, rewriteFields) - } - } - } - - return ResponseEntity - .status(response.statusCode) - .headers(headers) - .body(response.body) - } - false -> getResponse() - } - } - - /** - * Get the ResponseEntity object after forwarding the request, expecting a Location link which must be proxied. - */ - fun getResponseWithLocationHeader(proxyPath: String): ResponseEntity> { - val response = validateResponse() - return when (isOcpiSuccess()) { - true -> { - val headers = HttpHeaders() - response.headers["Location"]?.let { - val resourceId = routingService.setProxyResource(it, request.headers.sender, request.headers.receiver) - val newLocation = urlJoin(properties.url, proxyPath, resourceId) - headers["Location"] = newLocation - - if (isSigningActive(request.headers.sender)) { - response.body.signature = null - val valuesToSign = response.copy(headers = headers.toSingleValueMap()) - val rewriteFields = mapOf("$['headers']['location']" to it) - response.body.signature = rewriteAndSign(valuesToSign.toSignedValues(), rewriteFields) - } - } - - ResponseEntity - .status(response.statusCode) - .headers(headers) - .body(response.body) - } - false -> getResponse() - } - } - - /** - * Used by the /ocn/message handler to forward all possible response headers. - */ - fun getResponseWithAllHeaders(): ResponseEntity> { - val response = validateResponse() - return when (isOcpiSuccess()) { - true -> { - val responseHeaders = HttpHeaders() - response.headers["location"]?.let { responseHeaders.set("Location", it) } - response.headers["Link"]?.let { responseHeaders.set("Link", it) } - response.headers["X-Total-Count"]?.let { responseHeaders.set("X-Total-Count", it) } - response.headers["X-Limit"]?.let { responseHeaders.set("X-Limit", it) } - ResponseEntity - .status(response.statusCode) - .headers(responseHeaders) - .body(response.body) - } - false -> getResponse() - } - } - - /** - * Check ocpi request was success (i.e. before operating on headers) - */ - private fun isOcpiSuccess(): Boolean { - return response!!.statusCode == 200 && response!!.body.statusCode == 1000 - } - - /** - * Check message signing is enabled. Can be enabled in the following ways: - * 1. ocn.node.signatures property is set to true - * 2. request contains a signature header - * 3. (optional) recipient requires it (overrides other settings) - * - */ - private fun isSigningActive(recipient: BasicRole? = null): Boolean { - var active = properties.signatures || request.headers.signature != null - if (recipient != null) { - val recipientRules = routingService.getPlatformRules(recipient) - active = active || recipientRules.signatures - } - return active - } - - /** - * Use the OCN Notary to validate a request's "OCN-Signature" header. Only validated if signing is active. - * - * @param signature the OCN signature contained in the request header or response body - * @param signedValues the values signed by the sender - * @param signer expected signatory of the signature - * @param receiver optional receiver of message (checks their OcnRules for signature verification requirement) - */ - private fun validateOcnSignature(signature: String?, signedValues: ValuesToSign<*>, signer: BasicRole, receiver: BasicRole? = null) { - - if (isSigningActive(receiver)) { - val result = signature?.let { - notary = Notary.deserialize(it) - notary?.verify(signedValues) - } - when { - result == null -> throw OcpiClientInvalidParametersException("Missing OCN Signature") - !result.isValid -> throw OcpiClientInvalidParametersException("Invalid signature: ${result.error}") - } - - val party = registryService.getPartyDetails(signer) - val actualSignatory = Keys.toChecksumAddress(notary?.signatory) - val signedByParty = actualSignatory == Keys.toChecksumAddress(party.address) - val signedByOperator = actualSignatory == Keys.toChecksumAddress(party.operator) - - if (!signedByParty && !signedByOperator) { - throw OcpiClientInvalidParametersException("Actual signatory ${notary?.signatory} differs from expected signatory ${party.address} (party) or ${party.operator} (operator)") - } - } - } - - /** - * Used by the OCN Node to "stash" and "rewrite" the signature of a request if it needs to modify values - */ - private fun rewriteAndSign(valuesToSign: ValuesToSign<*>, rewriteFields: Map): String? { - if (isSigningActive()) { - val notary = validateNotary() - notary.stash(rewriteFields) - notary.sign(valuesToSign, properties.privateKey!!) - return notary.serialize() - } - return null - } - - /** - * Check response exists. Throws UnsupportedOperationException if request has not yet been forwarded. - */ - private fun validateResponse(): HttpResponse { - return response?.let { - // set receiver based on if local/remote request - val receiver = if (knownSender) { request.headers.sender } else { null } - - try { - validateOcnSignature( - signature = it.body.signature, - signedValues = it.toSignedValues(), - signer = request.headers.receiver, - receiver = receiver) - } catch (e: OcpiClientInvalidParametersException) { - throw OcpiServerGenericException("Unable to verify response signature: ${e.message}") - } - it - } ?: throw UnsupportedOperationException("Non-canonical method chaining: must call a forwarding method first") - } - - /** - * Check notary exists. Throws UnsupportedOperationException if request has not yet been validated. - */ - private fun validateNotary(): Notary { - return notary ?: throw UnsupportedOperationException("Non-canonical method chaining: must call a validating method first") - } - -} diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt index 4873d54..f61c0e6 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt @@ -18,14 +18,14 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import snc.openchargingnetwork.node.data.exampleLocation2 import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @WebMvcTest(MessageController::class) class MessageControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test fun `When POST OCN message should forward the request to local recipient and return their OCPI response`() { @@ -47,7 +47,7 @@ class MessageControllerTest(@Autowired val mockMvc: MockMvc) { val requestVariablesString = jacksonObjectMapper().writeValueAsString(requestVariables) - val mockkRequestHandler = mockk>() + val mockkRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariablesString) } returns mockkRequestHandler diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt index 2728b1b..871dad8 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt @@ -22,15 +22,15 @@ import org.springframework.http.ResponseEntity import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @WebMvcTest(CdrsController::class) class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -51,7 +51,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlEncodedParams = mapOf("limit" to 100)) - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://node.ocn.co/ocpi/sender/2.2/cdrs/page/43; rel=\"next\"" @@ -104,7 +104,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "67") - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://node.ocn.co/ocpi/sender/2.2/cdrs/page/68; rel=\"next\"" @@ -157,7 +157,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "6534") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -200,7 +200,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), body = exampleCDR) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() val responseHeaders = HttpHeaders() responseHeaders["Location"] = "https://super.hub.net/ocpi/receiver/2.2/cdrs/6545" diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt index ace6ee5..92f40ba 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt @@ -16,8 +16,8 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* import org.springframework.test.web.servlet.result.MockMvcResultMatchers import snc.openchargingnetwork.node.models.OcnHeaders import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.getTimestamp @@ -25,7 +25,7 @@ import snc.openchargingnetwork.node.tools.getTimestamp class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - private lateinit var requestHandlerBuilder: RequestHandlerBuilder + private lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -43,7 +43,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "12345", body = GenericChargingProfileResult(result = ChargingProfileResultType.ACCEPTED)) - val requestHandler: RequestHandler = mockk() + val requestHandler: OcpiRequestHandler = mockk() val response = ResponseEntity.status(HttpStatus.ACCEPTED).body(OcpiResponse(statusCode = 1000)) @@ -89,7 +89,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { startPeriod = 0, limit = 64f))))) - val requestHandler: RequestHandler = mockk() + val requestHandler: OcpiRequestHandler = mockk() val response = ResponseEntity.status(HttpStatus.ACCEPTED).body(OcpiResponse(statusCode = 1000)) @@ -127,7 +127,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "0987654321", urlEncodedParams = mapOf("duration" to 30, "response_url" to "https://server.com/profiles/1")) - val requestHandler: RequestHandler = mockk() + val requestHandler: OcpiRequestHandler = mockk() every { requestHandlerBuilder.build(request) } returns requestHandler every { @@ -173,7 +173,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { responseUrl = "https://smart.charging.net/profiles/0102030405" )) - val requestHandler: RequestHandler = mockk() + val requestHandler: OcpiRequestHandler = mockk() every { requestHandlerBuilder.build(request) } returns requestHandler every { @@ -216,7 +216,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "333666999", urlEncodedParams = mapOf("response_url" to "https://scsp.io/ocpi/callback/369")) - val requestHandler: RequestHandler = mockk() + val requestHandler: OcpiRequestHandler = mockk() every { requestHandlerBuilder.build(request) } returns requestHandler every { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt index 08f2c0d..097900b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt @@ -16,8 +16,8 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import snc.openchargingnetwork.node.data.exampleToken import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.getTimestamp @@ -26,7 +26,7 @@ import snc.openchargingnetwork.node.tools.getTimestamp class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -51,7 +51,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = uid, body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -98,7 +98,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "CANCEL_RESERVATION", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -154,7 +154,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "RESERVE_NOW", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -208,7 +208,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "START_SESSION", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -261,7 +261,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "STOP_SESSION", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -316,7 +316,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "UNLOCK_CONNECTOR", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt index 5d1b6b1..af5ec2d 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt @@ -21,8 +21,8 @@ import snc.openchargingnetwork.node.data.exampleLocation1 import snc.openchargingnetwork.node.data.exampleLocation2 import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.getTimestamp @@ -31,7 +31,7 @@ import snc.openchargingnetwork.node.tools.getTimestamp class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -54,7 +54,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlEncodedParams = mapOf("date_from" to dateFrom)) - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://node.ocn.co/ocpi/sender/2.2/locations/page/189; rel=\"next\"" @@ -113,7 +113,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "67") - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://node.ocn.co/ocpi/sender/2.2/locations/page/68; rel=\"next\"" @@ -170,7 +170,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = locationID) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -218,7 +218,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/$locationID/$evseUID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -267,7 +267,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/$locationID/$evseUID/$connectorID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -314,7 +314,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/${sender.country}/${sender.id}/$locationID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -362,7 +362,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -411,7 +411,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -460,7 +460,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$locationID", body = exampleLocation2) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -509,7 +509,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -559,7 +559,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -607,7 +607,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$locationID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -657,7 +657,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -708,7 +708,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt index c43b5b3..b2aae97 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt @@ -20,8 +20,8 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* import snc.openchargingnetwork.node.data.exampleSession import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.getTimestamp @@ -30,7 +30,7 @@ import snc.openchargingnetwork.node.tools.getTimestamp class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -53,7 +53,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlEncodedParams = mapOf("date_from" to dateFrom, "limit" to 20)) - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://client.ocn.co/ocpi/sender/2.2/sessions/page/2247; rel=\"next\"" @@ -109,7 +109,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "2247") - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://client.ocn.co/ocpi/sender/2.2/sessions/page/2248; rel=\"next\"" @@ -166,7 +166,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/2247/charging_preferences", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -212,7 +212,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/${sender.country}/${sender.id}/$sessionID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -259,7 +259,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$sessionID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -307,7 +307,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$sessionID", body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt index 6d18d18..add7c0a 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt @@ -20,8 +20,8 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* import snc.openchargingnetwork.node.data.exampleTariff import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.generateUUIDv4Token @@ -29,7 +29,7 @@ import snc.openchargingnetwork.node.tools.generateUUIDv4Token class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -50,7 +50,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlEncodedParams = mapOf("limit" to 10)) - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://node.ocn.co/ocpi/sender/2.2/tariffs/page/39; rel=\"next\"" @@ -105,7 +105,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "39") - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://client.ocn.co/ocpi/sender/2.2/tariffs/page/40; rel=\"next\"" @@ -160,7 +160,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/${sender.country}/${sender.id}/$tariffID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -206,7 +206,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$tariffID", body = exampleTariff) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -252,7 +252,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = "/${sender.country}/${sender.id}/$tariffID") - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt index 6ce69d0..97be2a4 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt @@ -18,8 +18,8 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* import snc.openchargingnetwork.node.data.exampleToken import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.services.RequestHandler -import snc.openchargingnetwork.node.services.RequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.generateUUIDv4Token @@ -27,7 +27,7 @@ import snc.openchargingnetwork.node.tools.generateUUIDv4Token class TokensControllerTest(@Autowired val mockMvc: MockMvc) { @MockkBean - lateinit var requestHandlerBuilder: RequestHandlerBuilder + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder @Test @@ -48,7 +48,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlEncodedParams = mapOf("limit" to 50)) - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://node.ocn.co/ocpi/sender/2.2/tariffs/page/935432; rel=\"next\"" @@ -106,7 +106,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { receiver = receiver), urlPathVariables = uid) - val mockRequestHandler = mockk>>() + val mockRequestHandler = mockk>>() val responseHeaders = HttpHeaders() responseHeaders["Link"] = "https://client.ocn.co/ocpi/sender/2.2/tokens/page/935433; rel=\"next\"" @@ -164,7 +164,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { urlEncodedParams = mapOf("type" to TokenType.RFID), body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -215,7 +215,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { urlPathVariables = "/${sender.country}/${sender.id}/$tokenUID", urlEncodedParams = mapOf("type" to TokenType.RFID)) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -263,7 +263,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { urlEncodedParams = mapOf("type" to TokenType.APP_USER), body = exampleToken) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler @@ -315,7 +315,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { urlEncodedParams = mapOf("type" to TokenType.APP_USER), body = body) - val mockRequestHandler = mockk>() + val mockRequestHandler = mockk>() every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt index 30ad84c..943d549 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt @@ -13,6 +13,8 @@ import org.springframework.http.HttpMethod import shareandcharge.openchargingnetwork.notary.Notary import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.data.exampleLocation1 +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder +import snc.openchargingnetwork.node.components.OcpiResponseHandlerBuilder import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.entities.OcnRules import snc.openchargingnetwork.node.models.ocpi.* @@ -27,9 +29,10 @@ class RequestHandlerTest { private val hubClientInfoService: HubClientInfoService = mockk() private val asyncTaskService: AsyncTaskService = mockk() private val properties: NodeProperties = mockk() + private val responseHandlerBuilder: OcpiResponseHandlerBuilder = mockk() - private val requestHandlerBuilder = RequestHandlerBuilder(routingService, registryService, httpService, walletService, - hubClientInfoService, asyncTaskService, properties) + private val requestHandlerBuilder = OcpiRequestHandlerBuilder(routingService, registryService, httpService, walletService, + hubClientInfoService, asyncTaskService, responseHandlerBuilder, properties) @Test fun validateSender() { @@ -266,25 +269,25 @@ class RequestHandlerTest { assertEquals(expectedResponse.statusCode, response.statusCodeValue) } - @Test - fun validateResponse() { - val variables = OcpiRequestVariables( - module = ModuleID.LOCATIONS, - interfaceRole = InterfaceRole.RECEIVER, - method = HttpMethod.GET, - headers = OcnHeaders( - authorization = "", - requestID = "123", - correlationID = "456", - sender = BasicRole("ABC", "DE"), - receiver = BasicRole("XYZ", "DE"))) - - val requestHandler = requestHandlerBuilder.build(variables) - - assertThrows { requestHandler.getResponse() } - assertThrows { requestHandler.getResponseWithPaginationHeaders() } - assertThrows { requestHandler.getResponseWithLocationHeader("/proxy") } - assertThrows { requestHandler.getResponseWithAllHeaders() } - } +// @Test +// fun validateResponse() { +// val variables = OcpiRequestVariables( +// module = ModuleID.LOCATIONS, +// interfaceRole = InterfaceRole.RECEIVER, +// method = HttpMethod.GET, +// headers = OcnHeaders( +// authorization = "", +// requestID = "123", +// correlationID = "456", +// sender = BasicRole("ABC", "DE"), +// receiver = BasicRole("XYZ", "DE"))) +// +// val requestHandler = requestHandlerBuilder.build(variables) +// +// assertThrows { requestHandler.getResponse() } +// assertThrows { requestHandler.getResponseWithPaginationHeaders() } +// assertThrows { requestHandler.getResponseWithLocationHeader("/proxy") } +// assertThrows { requestHandler.getResponseWithAllHeaders() } +// } } \ No newline at end of file From 47c6d140efe1a17bfd19fa6598c18258c036ded6 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Fri, 29 May 2020 14:08:43 +0200 Subject: [PATCH 09/26] convert request handler service test to separate component tests; add missing licenses Signed-off-by: Adam Staveley --- .../node/components/OcpiMessageHandler.kt | 16 ++++++ .../node/components/OcpiResponseHandler.kt | 4 +- .../components/listeners/AppEventsListener.kt | 16 ++++++ .../node/config/Verification.kt | 16 ++++++ .../node/models/events/AppEvents.kt | 16 ++++++ .../OcpiRequestHandlerTest.kt} | 57 ++++++++----------- .../components/OcpiResponseHandlerTest.kt | 56 ++++++++++++++++++ 7 files changed, 147 insertions(+), 34 deletions(-) rename src/test/kotlin/snc/openchargingnetwork/node/{services/RequestHandlerTest.kt => components/OcpiRequestHandlerTest.kt} (88%) create mode 100644 src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt index 60bff7c..a851cda 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt @@ -1,3 +1,19 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.components import org.web3j.crypto.Keys diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt index dca3d75..0e87f20 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt @@ -81,7 +81,7 @@ class OcpiResponseHandler(request: OcpiRequestVariables, hubClientInfoService: HubClientInfoService): OcpiMessageHandler(request, properties, routingService, registryService) { init { - validateResponse() + validateResponseSignature() if (isOcpiSuccess() && routingService.isRoleKnown(request.headers.receiver)) { // only renew connection of known recipients hubClientInfoService.renewClientConnection(request.headers.receiver) } @@ -208,7 +208,7 @@ class OcpiResponseHandler(request: OcpiRequestVariables, /** * Check response exists. Throws UnsupportedOperationException if request has not yet been forwarded. */ - private fun validateResponse() { + private fun validateResponseSignature() { // set receiver based on if local/remote request val receiver = if (knownSender) { request.headers.sender } else { null } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt index 02e868c..fd5c95a 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt @@ -1,3 +1,19 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.components.listeners import org.slf4j.LoggerFactory diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt index 469b06b..d0fc73c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/Verification.kt @@ -1,3 +1,19 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.config import org.springframework.boot.context.event.ApplicationReadyEvent diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt index 1652479..5cde3f6 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt @@ -1,3 +1,19 @@ +/* + Copyright 2019-2020 eMobilify GmbH + + 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 snc.openchargingnetwork.node.models.events import snc.openchargingnetwork.node.models.ocpi.BasicRole diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt similarity index 88% rename from src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt rename to src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index 943d549..f5391ab 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -1,26 +1,25 @@ -package snc.openchargingnetwork.node.services +package snc.openchargingnetwork.node.components import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import io.mockk.Runs import io.mockk.every import io.mockk.just import io.mockk.mockk -import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow -import org.junit.jupiter.api.assertThrows import org.springframework.http.HttpMethod +import org.springframework.http.ResponseEntity import shareandcharge.openchargingnetwork.notary.Notary import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.data.exampleLocation1 -import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder -import snc.openchargingnetwork.node.components.OcpiResponseHandlerBuilder import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.entities.OcnRules import snc.openchargingnetwork.node.models.ocpi.* +import snc.openchargingnetwork.node.services.* import snc.openchargingnetwork.node.tools.generatePrivateKey -class RequestHandlerTest { +class OcpiRequestHandlerTest { private val routingService: RoutingService = mockk() private val registryService: RegistryService = mockk() @@ -109,6 +108,8 @@ class RequestHandlerTest { headers = mapOf(), body = OcpiResponse(1000)) + val responseHandler: OcpiResponseHandler = mockk() + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs every { properties.signatures } returns false @@ -120,9 +121,11 @@ class RequestHandlerTest { every { hubClientInfoService.renewClientConnection(variables.headers.receiver) } just Runs every { asyncTaskService.forwardToLinkedApps(variables) } just Runs every { registryService.getAgreementsByInterface(variables.headers.sender, variables.module, variables.interfaceRole) } returns sequenceOf() + every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler + every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) val response = requestHandler.forwardRequest().getResponse() - assertEquals(expectedResponse.statusCode, response.statusCodeValue) + Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @Test @@ -162,6 +165,8 @@ class RequestHandlerTest { val receiverSig = Notary().sign(expectedResponse.toSignedValues(), receiverKey) expectedResponse.body.signature = receiverSig.serialize() + val responseHandler: OcpiResponseHandler = mockk() + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs every { properties.signatures } returns false @@ -173,9 +178,11 @@ class RequestHandlerTest { every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) every { httpService.makeOcpiRequest(recipientUrl, outgoingHeaders, variables) } returns expectedResponse every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler + every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) val response = requestHandler.forwardRequest().getResponse() - assertEquals(expectedResponse.statusCode, response.statusCodeValue) + Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @Test @@ -205,6 +212,8 @@ class RequestHandlerTest { headers = mapOf(), body = OcpiResponse(1000)) + val responseHandler: OcpiResponseHandler = mockk() + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.REMOTE every { properties.signatures } returns false every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( @@ -213,9 +222,11 @@ class RequestHandlerTest { every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { routingService.isRoleKnown(variables.headers.receiver) } returns false every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler + every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) val response = requestHandler.forwardRequest().getResponse() - assertEquals(expectedResponse.statusCode, response.statusCodeValue) + Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @Test @@ -253,6 +264,8 @@ class RequestHandlerTest { val receiverSig = Notary().sign(expectedResponse.toSignedValues(), receiverKey) expectedResponse.body.signature = receiverSig.serialize() + val responseHandler: OcpiResponseHandler = mockk() + every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.REMOTE every { properties.signatures } returns true every { registryService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic( @@ -263,31 +276,11 @@ class RequestHandlerTest { recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse every { asyncTaskService.forwardToLinkedApps(variables) } just Runs - + every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler + every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) val response = requestHandler.forwardRequest().getResponse() - assertEquals(expectedResponse.statusCode, response.statusCodeValue) + Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } -// @Test -// fun validateResponse() { -// val variables = OcpiRequestVariables( -// module = ModuleID.LOCATIONS, -// interfaceRole = InterfaceRole.RECEIVER, -// method = HttpMethod.GET, -// headers = OcnHeaders( -// authorization = "", -// requestID = "123", -// correlationID = "456", -// sender = BasicRole("ABC", "DE"), -// receiver = BasicRole("XYZ", "DE"))) -// -// val requestHandler = requestHandlerBuilder.build(variables) -// -// assertThrows { requestHandler.getResponse() } -// assertThrows { requestHandler.getResponseWithPaginationHeaders() } -// assertThrows { requestHandler.getResponseWithLocationHeader("/proxy") } -// assertThrows { requestHandler.getResponseWithAllHeaders() } -// } - } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt new file mode 100644 index 0000000..28b43a1 --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt @@ -0,0 +1,56 @@ +package snc.openchargingnetwork.node.components + +import io.mockk.every +import io.mockk.mockk +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.springframework.http.HttpHeaders +import org.springframework.http.HttpMethod +import org.springframework.http.ResponseEntity +import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.HttpResponse +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.ocpi.* +import snc.openchargingnetwork.node.services.* + +class OcpiResponseHandlerTest { + + private val routingService: RoutingService = mockk() + private val registryService: RegistryService = mockk() + private val hubClientInfoService: HubClientInfoService = mockk() + private val properties: NodeProperties = mockk() + + private val responseHandlerBuilder = OcpiResponseHandlerBuilder(routingService, registryService, + hubClientInfoService, properties) + + @Test + fun getResponse() { + val request = OcpiRequestVariables( + module = ModuleID.LOCATIONS, + interfaceRole = InterfaceRole.RECEIVER, + method = HttpMethod.GET, + headers = OcnHeaders( + authorization = "", + requestID = "123", + correlationID = "456", + sender = BasicRole("ABC", "DE"), + receiver = BasicRole("XYZ", "DE"))) + + val response = HttpResponse( + statusCode = 200, + headers = mapOf(), + body = OcpiResponse(statusCode = 1000)) + + every { properties.signatures } returns false + every { routingService.isRoleKnown(request.headers.receiver) } returns false + + val responseHandler = responseHandlerBuilder.build(request, response) + val actual = responseHandler.getResponse() + assertEquals(actual.statusCodeValue, response.statusCode) + assertEquals(actual.headers.count(), response.headers.count()) + assertEquals(actual.body, response.body) + + } + +} \ No newline at end of file From 7ee3326a063d40911755044113603731a878b569 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Fri, 29 May 2020 17:19:42 +0200 Subject: [PATCH 10/26] modify request handler api and rework tests Signed-off-by: Adam Staveley --- .../node/components/OcpiMessageHandler.kt | 1 - .../node/components/OcpiRequestHandler.kt | 199 +++++++++--------- .../node/components/OcpiResponseHandler.kt | 50 ++--- .../node/controllers/ocn/MessageController.kt | 8 +- .../controllers/ocpi/v2_2/CdrsController.kt | 32 ++- .../ocpi/v2_2/ChargingProfilesController.kt | 35 ++- .../ocpi/v2_2/CommandsController.kt | 57 ++--- .../ocpi/v2_2/LocationsController.kt | 101 ++++----- .../ocpi/v2_2/SessionsController.kt | 46 ++-- .../ocpi/v2_2/TariffsController.kt | 37 ++-- .../controllers/ocpi/v2_2/TokensController.kt | 44 ++-- .../node/models/ocpi/Common.kt | 2 +- .../node/services/AsyncTaskService.kt | 7 +- .../node/services/RoutingService.kt | 2 - .../openchargingnetwork/node/tools/Tools.kt | 3 - .../node/components/OcpiRequestHandlerTest.kt | 66 ++---- .../components/OcpiResponseHandlerTest.kt | 5 +- .../controllers/ocn/MessageControllerTest.kt | 3 +- .../ocpi/v2_2/CdrsControllerTest.kt | 8 +- .../v2_2/ChargingProfilesControllerTest.kt | 10 +- .../ocpi/v2_2/CommandsControllerTest.kt | 17 +- .../ocpi/v2_2/LocationsControllerTest.kt | 28 +-- .../ocpi/v2_2/SessionsControllerTest.kt | 12 +- .../ocpi/v2_2/TariffsControllerTest.kt | 10 +- .../ocpi/v2_2/TokensControllerTest.kt | 12 +- .../node/tools/ToolsTests.kt | 1 - 26 files changed, 338 insertions(+), 458 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt index a851cda..d79fc45 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiMessageHandler.kt @@ -62,7 +62,6 @@ open class OcpiMessageHandler(val request: OcpiRequestVariables, * @param receiver optional receiver of message (checks their OcnRules for signature verification requirement) */ fun validateOcnSignature(signature: String?, signedValues: ValuesToSign<*>, signer: BasicRole, receiver: BasicRole? = null) { - if (isSigningActive(receiver)) { val result = signature?.let { notary = Notary.deserialize(it) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt index 7521303..4037265 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -27,11 +27,10 @@ import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables import snc.openchargingnetwork.node.services.* import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.urlJoin -import kotlin.properties.Delegates.observable /** - * Spring boot service that instantiates RequestHandler objects. + * Spring boot component that instantiates RequestHandler objects. */ @Component class OcpiRequestHandlerBuilder(private val routingService: RoutingService, @@ -64,23 +63,23 @@ class OcpiRequestHandlerBuilder(private val routingService: RoutingService, /** - * Handle an individual OCPI HTTP request. Instantiated with OcpiRequestVariables. The RequestHandler supports method - * chaining to cleanly handle incoming messages, however, this comes at the cost of needing to manage/know its state. + * Handle an individual OCPI HTTP request, instantiated with OcpiRequestVariables. The RequestHandler supports method + * chaining to cleanly handle incoming messages. * - * There is a common way of handling core requests (peer-to-peer OCPI requests). - * 1. validate the request (authorization, sender, receiver, signature [optional]) - * 2. forward the request + * Generally speaking, all incoming peer-to-peer OCPI requests follow a very similar trajectory: * - * To avoid operating on responses that don't exist this order of operations needs to be respected. If not, an - * UnsupportedOperationException will be raised. + * 1. validate the message: + * - sender is registered on this node + * - [optional] sender's signature is valid * - * E.g. + * 2. find location of sender on network * - * requestHandler.validateSender().forwardRequest().getResponse() - * requestHandler.validateSender().forwardRequest(proxied = true).getResponseWithPaginatedHeaders() - * requestHandler.validateOcnMessage(signature).forwardRequest().getResponseWithAllHeaders() + * 3. [optional] modify request (i.e. if containing a response_url which the recipient will be unable to read) * - * @property request the OCPI HTTP request as OcpiRequestVariables. + * 4. forward the request + * + * On a successful forwarding of the request, the handler will return an OcpiResponseHandler which can be used to + * validate and extract the response. */ class OcpiRequestHandler(request: OcpiRequestVariables, routingService: RoutingService, @@ -96,121 +95,56 @@ class OcpiRequestHandler(request: OcpiRequestVariables, private var logger: Logger = LoggerFactory.getLogger(OcpiRequestHandler::class.java) } - /** - * Is sender/receiver known to this OCN Node? - */ - private var knownSender: Boolean = true - - /** - * Sender's message is valid (sender, receiver and signature have all been validated) - * Once valid, we can run the async task to find ocn apps the request can be forwarded to - */ - private var requestIsValid: Boolean by observable(false) { _, validBefore, validNow -> - if (!validBefore && validNow) { // task should only run when requestIsValid becomes true for the first time - asyncTaskService.forwardToLinkedApps(request) - } - } - - /** - * Assert the sender is allowed to send OCPI requests to this OCN Node. - * If message signing is required, or OCN-Signature header present, verifies the signature. - */ - fun validateSender(): OcpiRequestHandler { - routingService.checkSenderKnown(request.headers.authorization, request.headers.sender) - hubClientInfoService.renewClientConnection(request.headers.sender) - return this - } - - /** - * Asserts the sender exists in the Registry and is connected to the OCN Node which has sent the request. - * Asserts the receiver is connected to this OCN Node. - * If message signing is required, or OCN-Signature header present, verifies the signature. - */ - fun validateOcnMessage(signature: String): OcpiRequestHandler { - knownSender = false - if (!registryService.isRoleKnown(request.headers.sender, belongsToMe = false)) { - throw OcpiHubUnknownReceiverException("Sending party not registered on Open Charging Network") - } - - if (!routingService.isRoleKnown(request.headers.receiver)) { - throw OcpiHubUnknownReceiverException("Recipient unknown to OCN Node entered in Registry") - } - - val requestString = httpService.mapper.writeValueAsString(request) - walletService.verify(requestString, signature, request.headers.sender) - return this - } - /** * Forward the request to the receiver. - * Checks whether receiver is LOCAL or REMOTE. - * - If LOCAL, makes direct request to receiver. - * - If REMOTE, forwards request to receiver's OCN Node as written in the Registry. - * * @param proxied tells the RequestHandler that this request requires a proxied resource that was previously - * saved by the OCN Node. + * saved by the OCN Node (e.g. a paginated "Link" response header). */ - fun forwardRequest(proxied: Boolean = false): OcpiResponseHandler { + fun forward(proxied: Boolean = false, fromLocalPlatform: Boolean = true): OcpiResponseHandler { + if (fromLocalPlatform) { + assertSenderValid() + } + val response: HttpResponse = when (routingService.getReceiverType(request.headers.receiver)) { Receiver.LOCAL -> { - routingService.checkSenderWhitelisted( - sender = request.headers.sender, - receiver = request.headers.receiver, - module = request.module) - - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender, - receiver = request.headers.receiver) + assertWhitelisted() + assertValidSignature() val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) - requestIsValid = true // trigger side-effect (forward to linked apps) + asyncTaskService.findLinkedApps(request) httpService.makeOcpiRequest(url, headers, request) } Receiver.REMOTE -> { - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender) + assertValidSignature(false) val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) - requestIsValid = true // trigger side-effect (forward to linked apps) + asyncTaskService.findLinkedApps(request) httpService.postOcnMessage(url, headers, body) } } - return responseHandlerBuilder.build(request, response) + return responseHandlerBuilder.build(request, response, knownSender = fromLocalPlatform) } /** - * Used by the commands module's RECEIVER interface, which requires the modifying of the "response_url". - * Checks whether receiver is LOCAL or REMOTE. - * - If LOCAL, makes direct request to receiver. - * - If REMOTE, forwards request to receiver's OCN Node as written in the Registry. - * + * Used by the module interfaces which require the modifying of a "response_url". * @param responseUrl the original response_url as defined by the sender * @param modifyRequest callback which allows the request (OcpiRequestVariables) used by this RequestHandler to * be modified with the new response_url which will be sent to the receiver. */ - fun forwardModifiableRequest(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): OcpiResponseHandler { - val proxyPath = "/ocpi/sender/2.2/commands/${request.urlPathVariables}" + fun forward(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): OcpiResponseHandler { + assertSenderValid() + + val proxyPath = "/ocpi/sender/2.2/${request.module.id}/${request.urlPathVariables}" val rewriteFields = mapOf("$['body']['response_url']" to responseUrl) val response: HttpResponse = when (routingService.getReceiverType(request.headers.receiver)) { Receiver.LOCAL -> { - routingService.checkSenderWhitelisted( - sender = request.headers.sender, - receiver = request.headers.receiver, - module = request.module) - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender, - receiver = request.headers.receiver) + assertWhitelisted() + assertValidSignature() // save the original resource (response_url), returning a uid pointing to its location val resourceID = routingService.setProxyResource(responseUrl, request.headers.receiver, request.headers.sender) @@ -220,15 +154,13 @@ class OcpiRequestHandler(request: OcpiRequestVariables, modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) // send the request with the modified body val (url, headers) = routingService.prepareLocalPlatformRequest(request) - requestIsValid = true // trigger side-effect (forward to linked apps) + + asyncTaskService.findLinkedApps(request) httpService.makeOcpiRequest(url, headers, modifiedRequest) } Receiver.REMOTE -> { - validateOcnSignature( - signature = request.headers.signature, - signedValues = request.toSignedValues(), - signer = request.headers.sender) + assertValidSignature(false) val (url, headers, body) = routingService.prepareRemotePlatformRequest(request) { // create a uid that will point to the original response_url @@ -241,7 +173,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) } - requestIsValid = true // trigger side-effect (forward to linked apps) + asyncTaskService.findLinkedApps(request) httpService.postOcnMessage(url, headers, body) } @@ -250,4 +182,63 @@ class OcpiRequestHandler(request: OcpiRequestVariables, return responseHandlerBuilder.build(request, response) } + /** + * Forwards a message received over the network (containing an "OCN-Signature" from the sending node) + * @param signature the OCN-Signature header received from the sending node + */ + fun forward(signature: String): OcpiResponseHandler { + validateOcnMessage(signature) + return forward(fromLocalPlatform = false) + } + + /** + * Assert the sender is allowed to send OCPI requests to this OCN Node. + */ + private fun assertSenderValid() { + routingService.checkSenderKnown(request.headers.authorization, request.headers.sender) + hubClientInfoService.renewClientConnection(request.headers.sender) + } + + /** + * Wrapper around RoutingService.checkSenderWhitelisted (asserts sender is allowed to send messages to receiver) + */ + private fun assertWhitelisted() { + routingService.checkSenderWhitelisted( + sender = request.headers.sender, + receiver = request.headers.receiver, + module = request.module) + } + + /** + * Wrapper around OcpiMessageHandler.validateOcnSignature (asserts signature is valid) + * @param knownReceiver set to true if the request is to a local platform - this will also check if the receiver + * platform requires a signature too. + */ + private fun assertValidSignature(knownReceiver: Boolean = true) { + val receiver = if (knownReceiver) { request.headers.receiver } else { null } + validateOcnSignature( + signature = request.headers.signature, + signedValues = request.toSignedValues(), + signer = request.headers.sender, + receiver = receiver) + } + + /** + * Asserts the sender exists in the Registry and is connected to the OCN Node which has sent the request. + * Asserts the receiver is connected to this OCN Node. + */ + private fun validateOcnMessage(signature: String): OcpiRequestHandler { + if (!registryService.isRoleKnown(request.headers.sender, belongsToMe = false)) { + throw OcpiHubUnknownReceiverException("Sending party not registered on Open Charging Network") + } + + if (!routingService.isRoleKnown(request.headers.receiver)) { + throw OcpiHubUnknownReceiverException("Recipient unknown to OCN Node entered in Registry") + } + + val requestString = httpService.mapper.writeValueAsString(request) + walletService.verify(requestString, signature, request.headers.sender) + return this + } + } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt index 0e87f20..e75fc33 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt @@ -44,41 +44,38 @@ class OcpiResponseHandlerBuilder(private val routingService: RoutingService, private val properties: NodeProperties) { /** - * Build a RequestHandler object from an request (OcpiRequestVariables) and response (HttpResponse) object. + * Build a ResponseHandler object from an request (OcpiRequestVariables) and response (HttpResponse) object. + * + * @param request the OCPI HTTP request as OcpiRequestVariables. + * @param response the OCPI HTTP response as HttpResponse + * @param knownSender is the sender of the request known to this node? */ - fun build(request: OcpiRequestVariables, response: HttpResponse): OcpiResponseHandler { - return OcpiResponseHandler(request, response, routingService, registryService, properties, hubClientInfoService) + fun build(request: OcpiRequestVariables, + response: HttpResponse, + knownSender: Boolean = true): OcpiResponseHandler { + return OcpiResponseHandler(request, response, knownSender, routingService, registryService, properties, + hubClientInfoService) } } /** - * Handle an individual OCPI HTTP request. Instantiated with OcpiRequestVariables. The RequestHandler supports method - * chaining to cleanly handle incoming messages, however, this comes at the cost of needing to manage/know its state. - * - * There is a common way of handling core requests (peer-to-peer OCPI requests). - * 1. validate the request (authorization, sender, receiver, signature [optional]) - * 2. forward the request - * 3. get the response - * - * To avoid operating on responses that don't exist this order of operations needs to be respected. If not, an - * UnsupportedOperationException will be raised. - * - * E.g. - * - * requestHandler.validateSender().forwardRequest().getResponse() - * requestHandler.validateSender().forwardRequest(proxied = true).getResponseWithPaginatedHeaders() - * requestHandler.validateOcnMessage(signature).forwardRequest().getResponseWithAllHeaders() - * - * @property request the OCPI HTTP request as OcpiRequestVariables. + * Handle an individual OCPI HTTP response, for example after having an OCPI Request Handler forward a request and + * received the response. Can be created easily using the above OcpiResponseHandlerBuilder. */ class OcpiResponseHandler(request: OcpiRequestVariables, private val response: HttpResponse, + private val knownSender: Boolean, routingService: RoutingService, registryService: RegistryService, properties: NodeProperties, - hubClientInfoService: HubClientInfoService): OcpiMessageHandler(request, properties, routingService, registryService) { + hubClientInfoService: HubClientInfoService): + OcpiMessageHandler(request, properties, routingService, registryService) { + + companion object { + private var logger: Logger = LoggerFactory.getLogger(OcpiResponseHandler::class.java) + } init { validateResponseSignature() @@ -87,15 +84,6 @@ class OcpiResponseHandler(request: OcpiRequestVariables, } } - companion object { - private var logger: Logger = LoggerFactory.getLogger(OcpiResponseHandler::class.java) - } - - /** - * Is sender/receiver known to this OCN Node? - */ - private var knownSender: Boolean = true - /** * Get the ResponseEntity object after forwarding the request, expecting no headers from the receiver's response. */ diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt index 7d61b85..12b0f0e 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt @@ -33,11 +33,9 @@ class MessageController(private val requestHandlerBuilder: OcpiRequestHandlerBui @RequestHeader("OCN-Signature") signature: String, @RequestBody body: String): ResponseEntity> { - val request: OcpiRequestHandler = requestHandlerBuilder.build(body) - - return request - .validateOcnMessage(signature) - .forwardRequest() + return requestHandlerBuilder + .build(body) + .forward(signature) .getResponseWithAllHeaders() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt index 4dec618..d963cd2 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt @@ -60,12 +60,11 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() - .getResponseWithPaginationHeaders() // TODO: all pagination response header links should contain original url-encoded parameters - + // TODO: all pagination response header links should contain original url-encoded parameters + return requestHandlerBuilder + .build>(requestVariables) + .forward() + .getResponseWithPaginationHeaders() // proxies the Link response header } @GetMapping("/ocpi/sender/2.2/cdrs/page/{uid}") @@ -89,10 +88,9 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest(proxied = true) + return requestHandlerBuilder + .build>(requestVariables) + .forward(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -122,10 +120,9 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = cdrID) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest(proxied = true) + return requestHandlerBuilder + .build(requestVariables) + .forward(proxied = true) // retrieves proxied Location response header .getResponse() } @@ -151,10 +148,9 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponseWithLocationHeader("/ocpi/receiver/2.2/cdrs") } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt index 48f2614..d89e809 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt @@ -55,10 +55,9 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH urlPathVariables = uid, body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest(proxied = true) + return requestHandlerBuilder + .build(requestVariables) + .forward(proxied = true) // retrieves proxied response_url .getResponse() } @@ -85,10 +84,9 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH urlPathVariables = sessionId, body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -121,10 +119,9 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH urlPathVariables = sessionId, urlEncodedParams = mapOf("duration" to duration, "response_url" to responseUrl)) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(responseUrl) { + return requestHandlerBuilder + .build(requestVariables) + .forward(responseUrl) { requestVariables.copy(urlEncodedParams = mapOf("duration" to duration, "response_url" to it)) } .getResponse() @@ -153,10 +150,9 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH urlPathVariables = sessionId, body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(body.responseUrl) { + return requestHandlerBuilder + .build(requestVariables) + .forward(body.responseUrl) { requestVariables.copy(body = body.copy(responseUrl = it)) } .getResponse() @@ -185,10 +181,9 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH urlPathVariables = sessionId, urlEncodedParams = mapOf("response_url" to responseUrl)) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(responseUrl) { + return requestHandlerBuilder + .build(requestVariables) + .forward(responseUrl) { requestVariables.copy(urlEncodedParams = mapOf("response_url" to it)) } .getResponse() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt index 6fae333..246f250 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt @@ -19,15 +19,12 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @RestController -class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder, - private val properties: NodeProperties) { +class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { /** @@ -58,10 +55,9 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = uid, body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest(proxied = true) + return requestHandlerBuilder + .build(requestVariables) + .forward(proxied = true) // retrieves proxied response_url .getResponse() } @@ -93,10 +89,11 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "CANCEL_RESERVATION", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } + return requestHandlerBuilder + .build(requestVariables) + .forward(body.responseURL) { + requestVariables.copy(body = body.copy(responseURL = it)) + } .getResponse() } @@ -123,10 +120,11 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "RESERVE_NOW", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } + return requestHandlerBuilder + .build(requestVariables) + .forward(body.responseURL) { + requestVariables.copy(body = body.copy(responseURL = it)) + } .getResponse() } @@ -153,10 +151,11 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "START_SESSION", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } + return requestHandlerBuilder + .build(requestVariables) + .forward(body.responseURL) { + requestVariables.copy(body = body.copy(responseURL = it)) + } .getResponse() } @@ -183,10 +182,11 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "STOP_SESSION", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } + return requestHandlerBuilder + .build(requestVariables) + .forward(body.responseURL) { + requestVariables.copy(body = body.copy(responseURL = it)) + } .getResponse() } @@ -213,10 +213,11 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "UNLOCK_CONNECTOR", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardModifiableRequest(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } + return requestHandlerBuilder + .build(requestVariables) + .forward(body.responseURL) { + requestVariables.copy(body = body.copy(responseURL = it)) + } .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt index e0712d5..ff329f3 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt @@ -21,7 +21,6 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @@ -60,11 +59,10 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() - .getResponseWithPaginationHeaders() + return requestHandlerBuilder + .build>(requestVariables) + .forward() + .getResponseWithPaginationHeaders() // proxies Link response header } @GetMapping("/ocpi/sender/2.2/locations/page/{uid}") @@ -88,10 +86,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest(proxied = true) + return requestHandlerBuilder + .build>(requestVariables) + .forward(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -116,10 +113,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = locationID) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -145,10 +141,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$locationID/$evseUID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -175,10 +170,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$locationID/$evseUID/$connectorID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -210,10 +204,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$locationID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -241,10 +234,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -273,10 +265,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -305,10 +296,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB urlPathVariables = "/$countryCode/$partyID/$locationID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -338,10 +328,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -372,10 +361,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -404,10 +392,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB urlPathVariables = "/$countryCode/$partyID/$locationID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -437,10 +424,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -471,10 +457,9 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt index a12958d..eee2153 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt @@ -21,7 +21,6 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.models.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.tools.filterNull @@ -60,11 +59,10 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() - .getResponseWithPaginationHeaders() + return requestHandlerBuilder + .build>(requestVariables) + .forward() + .getResponseWithPaginationHeaders() // proxies Link response header } @GetMapping("/ocpi/sender/2.2/sessions/page/{uid}") @@ -88,10 +86,10 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + + return requestHandlerBuilder + .build>(requestVariables) + .forward(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -118,10 +116,9 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "/$sessionID/charging_preferences", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -153,10 +150,9 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$sessionID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -185,10 +181,9 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "/$countryCode/$partyID/$sessionID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -217,10 +212,9 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu urlPathVariables = "/$countryCode/$partyID/$sessionID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt index 09d3732..ad6e78b 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt @@ -60,11 +60,10 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() - .getResponseWithPaginationHeaders() + return requestHandlerBuilder + .build>(requestVariables) + .forward() + .getResponseWithPaginationHeaders() // proxies Link response header } @GetMapping("/ocpi/sender/2.2/tariffs/page/{uid}") @@ -88,10 +87,9 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build>(requestVariables) + .forward(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -123,10 +121,9 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$tariffID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -156,10 +153,9 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui urlPathVariables = "/$countryCode/$partyID/$tariffID", body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -187,10 +183,9 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = "/$countryCode/$partyID/$tariffID") - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt index 0db42e9..4c47a27 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt @@ -59,11 +59,10 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlEncodedParams = params) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() - .getResponseWithPaginationHeaders() + return requestHandlerBuilder + .build>(requestVariables) + .forward() + .getResponseWithPaginationHeaders() // proxies Link response header } @GetMapping("/ocpi/sender/2.2/tokens/page/{uid}") @@ -87,10 +86,9 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), urlPathVariables = uid) - val request: OcpiRequestHandler> = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build>(requestVariables) + .forward(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -120,10 +118,9 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil urlEncodedParams = mapOf("type" to type), body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -157,10 +154,9 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil urlPathVariables = "/$countryCode/$partyID/$tokenUID", urlEncodedParams = mapOf("type" to type)) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -191,10 +187,9 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil urlEncodedParams = mapOf("type" to type), body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } @@ -225,10 +220,9 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil urlEncodedParams = mapOf("type" to type), body = body) - val request: OcpiRequestHandler = requestHandlerBuilder.build(requestVariables) - return request - .validateSender() - .forwardRequest() + return requestHandlerBuilder + .build(requestVariables) + .forward() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt index 1086352..184b19e 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt @@ -29,7 +29,7 @@ import javax.persistence.Embeddable import javax.persistence.Embedded -// TODO: rename to avoid confusion +// TODO: rename to avoid confusion? // BasicParty may be a better description @Embeddable data class BasicRole(@JsonProperty("party_id") final val id: String, diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index 63baf79..cc89659 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -18,7 +18,6 @@ package snc.openchargingnetwork.node.services import org.slf4j.LoggerFactory import org.springframework.context.ApplicationEventPublisher -import org.springframework.data.domain.AbstractAggregateRoot import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent @@ -33,11 +32,11 @@ class AsyncTaskService(private val registryService: RegistryService, } /** - * Forward request to linked apps - * TODO: rename - e.g. findLinkedApps + * Finds all apps, linked to a sender, with permissions that grant them access to a given request type. + * Once apps have been found, triggers an AppRecipientFoundEvent. */ @Async - fun forwardToLinkedApps(request: OcpiRequestVariables) { + fun findLinkedApps(request: OcpiRequestVariables) { registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach { logger.info("publishing forward request to ${it.provider}") diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 5f043f5..40fdf72 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -16,8 +16,6 @@ package snc.openchargingnetwork.node.services -import org.slf4j.Logger -import org.slf4j.LoggerFactory import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import snc.openchargingnetwork.node.models.* diff --git a/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt b/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt index 3b242bf..157cd2c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/tools/Tools.kt @@ -17,9 +17,6 @@ package snc.openchargingnetwork.node.tools import org.web3j.crypto.Keys -import snc.openchargingnetwork.node.models.HttpResponse -import snc.openchargingnetwork.node.models.ocpi.CommandResponse -import snc.openchargingnetwork.node.models.ocpi.CommandResponseType import java.time.Instant import java.time.format.DateTimeFormatter import java.util.UUID diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index f5391ab..acd0bef 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -33,52 +33,6 @@ class OcpiRequestHandlerTest { private val requestHandlerBuilder = OcpiRequestHandlerBuilder(routingService, registryService, httpService, walletService, hubClientInfoService, asyncTaskService, responseHandlerBuilder, properties) - @Test - fun validateSender() { - val variables = OcpiRequestVariables( - module = ModuleID.LOCATIONS, - interfaceRole = InterfaceRole.RECEIVER, - method = HttpMethod.GET, - headers = OcnHeaders( - authorization = "", - requestID = "123", - correlationID = "456", - sender = BasicRole("ABC", "DE"), - receiver = BasicRole("XYZ", "DE"))) - - every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs - val requestHandler = requestHandlerBuilder.build(variables) - assertDoesNotThrow { requestHandler.validateSender() } - } - - @Test - fun validateOcnMessage() { - val signature = "0x12345" - - val variables = OcpiRequestVariables( - module = ModuleID.LOCATIONS, - interfaceRole = InterfaceRole.RECEIVER, - method = HttpMethod.GET, - headers = OcnHeaders( - authorization = "", - requestID = "123", - correlationID = "456", - sender = BasicRole("ABC", "DE"), - receiver = BasicRole("XYZ", "DE"))) - - val requestHandler = requestHandlerBuilder.build(variables) - - val variablesString = jacksonObjectMapper().writeValueAsString(variables) - - every { registryService.isRoleKnown(variables.headers.sender, false) } returns true - every { routingService.isRoleKnown(variables.headers.receiver) } returns true - every { httpService.mapper.writeValueAsString(variables) } returns variablesString - every { walletService.verify(variablesString, signature, variables.headers.sender) } just Runs - every { properties.signatures } returns false - - assertDoesNotThrow { requestHandler.validateOcnMessage(signature) } - } - @Test fun forwardRequest_local() { val variables = OcpiRequestVariables( @@ -110,6 +64,7 @@ class OcpiRequestHandlerTest { val responseHandler: OcpiResponseHandler = mockk() + every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs every { properties.signatures } returns false @@ -119,12 +74,12 @@ class OcpiRequestHandlerTest { every { routingService.isRoleKnown(variables.headers.receiver) } returns true every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { hubClientInfoService.renewClientConnection(variables.headers.receiver) } just Runs - every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(variables) } just Runs every { registryService.getAgreementsByInterface(variables.headers.sender, variables.module, variables.interfaceRole) } returns sequenceOf() every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forwardRequest().getResponse() + val response = requestHandler.forward().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @@ -167,6 +122,7 @@ class OcpiRequestHandlerTest { val responseHandler: OcpiResponseHandler = mockk() + every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs every { properties.signatures } returns false @@ -177,11 +133,11 @@ class OcpiRequestHandlerTest { receiverSig.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) every { httpService.makeOcpiRequest(recipientUrl, outgoingHeaders, variables) } returns expectedResponse - every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(variables) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forwardRequest().getResponse() + val response = requestHandler.forward().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @@ -214,6 +170,7 @@ class OcpiRequestHandlerTest { val responseHandler: OcpiResponseHandler = mockk() + every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.REMOTE every { properties.signatures } returns false every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( @@ -221,11 +178,11 @@ class OcpiRequestHandlerTest { every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { routingService.isRoleKnown(variables.headers.receiver) } returns false - every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(variables) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forwardRequest().getResponse() + val response = requestHandler.forward().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @@ -266,6 +223,7 @@ class OcpiRequestHandlerTest { val responseHandler: OcpiResponseHandler = mockk() + every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.REMOTE every { properties.signatures } returns true every { registryService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic( @@ -275,11 +233,11 @@ class OcpiRequestHandlerTest { every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse - every { asyncTaskService.forwardToLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(variables) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forwardRequest().getResponse() + val response = requestHandler.forward().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt index 28b43a1..800aa21 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandlerTest.kt @@ -4,13 +4,11 @@ import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.springframework.http.HttpHeaders import org.springframework.http.HttpMethod -import org.springframework.http.ResponseEntity import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.HttpResponse import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.entities.OcnRules import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.services.* @@ -44,6 +42,7 @@ class OcpiResponseHandlerTest { every { properties.signatures } returns false every { routingService.isRoleKnown(request.headers.receiver) } returns false + every { routingService.getPlatformRules(request.headers.sender) } returns OcnRules() val responseHandler = responseHandlerBuilder.build(request, response) val actual = responseHandler.getResponse() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt index f61c0e6..bddca21 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt @@ -53,8 +53,7 @@ class MessageControllerTest(@Autowired val mockMvc: MockMvc) { every { mockkRequestHandler - .validateOcnMessage("0x1234") - .forwardRequest() + .forward("0x1234") .getResponseWithAllHeaders() } returns ResponseEntity .status(200) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt index 871dad8..4fcef5c 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt @@ -59,7 +59,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleCDR))) @@ -112,7 +112,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleCDR))) @@ -161,7 +161,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest(true).getResponse() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleCDR)) @@ -207,7 +207,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithLocationHeader("/ocpi/receiver/2.2/cdrs") } returns ResponseEntity + every { mockRequestHandler.forward().getResponseWithLocationHeader("/ocpi/receiver/2.2/cdrs") } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt index 92f40ba..161fcec 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt @@ -48,7 +48,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { val response = ResponseEntity.status(HttpStatus.ACCEPTED).body(OcpiResponse(statusCode = 1000)) every { requestHandlerBuilder.build(request) } returns requestHandler - every { requestHandler.validateSender().forwardRequest(true).getResponse() } returns response + every { requestHandler.forward(true).getResponse() } returns response mockMvc.perform(post("/ocpi/2.2/sender/chargingprofiles/result/12345") .header("Authorization", "Token token-c") @@ -94,7 +94,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { val response = ResponseEntity.status(HttpStatus.ACCEPTED).body(OcpiResponse(statusCode = 1000)) every { requestHandlerBuilder.build(request) } returns requestHandler - every { requestHandler.validateSender().forwardRequest().getResponse() } returns response + every { requestHandler.forward().getResponse() } returns response mockMvc.perform(put("/ocpi/2.2/sender/chargingprofiles/1234567890") .header("Authorization", "Token token-c") @@ -131,7 +131,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(request) } returns requestHandler every { - requestHandler.validateSender().forwardModifiableRequest("https://server.com/profiles/1", any()).getResponse() + requestHandler.forward("https://server.com/profiles/1", any()).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -177,7 +177,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(request) } returns requestHandler every { - requestHandler.validateSender().forwardModifiableRequest("https://smart.charging.net/profiles/0102030405", any()).getResponse() + requestHandler.forward("https://smart.charging.net/profiles/0102030405", any()).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -220,7 +220,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(request) } returns requestHandler every { - requestHandler.validateSender().forwardModifiableRequest("https://scsp.io/ocpi/callback/369", any()).getResponse() + requestHandler.forward("https://scsp.io/ocpi/callback/369", any()).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt index 097900b..6f0a89b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt @@ -55,7 +55,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest(true).getResponse() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -103,8 +103,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .validateSender() - .forwardModifiableRequest(body.responseURL, any()) + .forward(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -159,8 +158,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .validateSender() - .forwardModifiableRequest(body.responseURL, any()) + .forward(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -213,8 +211,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .validateSender() - .forwardModifiableRequest(body.responseURL, any()) + .forward(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -266,8 +263,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .validateSender() - .forwardModifiableRequest(body.responseURL, any()) + .forward(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -321,8 +317,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .validateSender() - .forwardModifiableRequest(body.responseURL, any()) + .forward(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt index af5ec2d..cc089dd 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt @@ -63,7 +63,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse( @@ -121,7 +121,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse( @@ -174,7 +174,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -222,7 +222,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -271,7 +271,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -318,7 +318,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -366,7 +366,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -415,7 +415,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -464,7 +464,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -513,7 +513,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -563,7 +563,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -611,7 +611,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -661,7 +661,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -712,7 +712,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt index b2aae97..d2c96ef 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt @@ -62,7 +62,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleSession))) @@ -118,7 +118,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleSession))) @@ -170,7 +170,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = ChargingPreferencesResponse.NOT_POSSIBLE)) @@ -216,7 +216,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleSession)) @@ -263,7 +263,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -311,7 +311,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt index add7c0a..1b0bac7 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt @@ -59,7 +59,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleTariff))) @@ -113,7 +113,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleTariff))) @@ -164,7 +164,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleTariff)) @@ -210,7 +210,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -256,7 +256,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt index 97be2a4..a50628e 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt @@ -57,7 +57,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleToken))) @@ -114,7 +114,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleToken))) @@ -168,7 +168,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = AuthorizationInfo( allowed = Allowed.ALLOWED, @@ -219,7 +219,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleToken)) @@ -267,7 +267,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -319,7 +319,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.validateSender().forwardRequest().getResponse() } returns ResponseEntity + every { mockRequestHandler.forward().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/tools/ToolsTests.kt b/src/test/kotlin/snc/openchargingnetwork/node/tools/ToolsTests.kt index 43a7178..4a4cde6 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/tools/ToolsTests.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/tools/ToolsTests.kt @@ -2,7 +2,6 @@ package snc.openchargingnetwork.node.tools import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import org.web3j.crypto.Keys class ToolsTests { From 467df80319ff15ec504edb2e1522347adcc3e625 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Fri, 29 May 2020 17:31:05 +0200 Subject: [PATCH 11/26] cleanup including organisation of imports Signed-off-by: Adam Staveley --- build.gradle.kts | 2 +- .../node/config/NodeConfig.kt | 2 +- .../controllers/ocpi/v2_2/CdrsController.kt | 5 ++--- .../ocpi/v2_2/ChargingProfilesController.kt | 3 +-- .../ocpi/v2_2/CredentialsController.kt | 12 ++++++------ .../ocpi/v2_2/TariffsController.kt | 5 ++--- .../controllers/ocpi/v2_2/TokensController.kt | 5 ++--- .../node/services/OcnRulesService.kt | 10 +++++----- .../node/components/OcpiRequestHandlerTest.kt | 1 - .../node/controllers/AdminControllerTest.kt | 2 +- .../controllers/ocn/RegistryControllerTest.kt | 1 - .../ocpi/v2_2/CredentialsControllerTest.kt | 19 ++++++++----------- .../node/integration/parties/CpoServer.kt | 8 ++++---- .../node/integration/parties/MspServer.kt | 8 ++++---- .../node/services/HttpServiceTest.kt | 3 +-- .../node/services/RoutingServiceTest.kt | 14 +++++++------- 16 files changed, 45 insertions(+), 55 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a3e0d40..a5c8818 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -20,7 +20,7 @@ java.sourceCompatibility = JavaVersion.VERSION_1_8 val snippetsDir = "build/generated-snippets" -val developmentOnly by configurations.creating +val developmentOnly: Configuration by configurations.creating configurations { runtimeClasspath { extendsFrom(developmentOnly) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt index 97f0808..781e2c5 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeConfig.kt @@ -34,7 +34,7 @@ import snc.openchargingnetwork.node.services.HttpService as OcnHttpService @Configuration -open class NodeConfig(private val properties: NodeProperties) { +class NodeConfig(private val properties: NodeProperties) { private val web3: Web3j = Web3j.build(Web3jHttpService(properties.web3.provider)) private val txManager: TransactionManager = ClientTransactionManager(web3, null) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt index d963cd2..508ca52 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt @@ -19,10 +19,9 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.models.* -import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.tools.filterNull diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt index d89e809..9f03c85 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt @@ -19,10 +19,9 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.models.OcnHeaders import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler -import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @RestController diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt index aee89ca..b390854 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsController.kt @@ -18,21 +18,21 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.transaction.annotation.Transactional import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.config.NodeProperties -import snc.openchargingnetwork.node.models.ocpi.ConnectionStatus -import snc.openchargingnetwork.node.models.ocpi.Role -import snc.openchargingnetwork.node.models.ocpi.OcpiStatus import snc.openchargingnetwork.node.models.entities.Auth import snc.openchargingnetwork.node.models.entities.EndpointEntity import snc.openchargingnetwork.node.models.entities.RoleEntity import snc.openchargingnetwork.node.models.exceptions.OcpiClientInvalidParametersException import snc.openchargingnetwork.node.models.exceptions.OcpiServerNoMatchingEndpointsException import snc.openchargingnetwork.node.models.ocpi.* +import snc.openchargingnetwork.node.models.ocpi.Role +import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.services.HttpService import snc.openchargingnetwork.node.services.RegistryService -import snc.openchargingnetwork.node.services.RoutingService -import snc.openchargingnetwork.node.tools.* +import snc.openchargingnetwork.node.tools.extractToken +import snc.openchargingnetwork.node.tools.generateUUIDv4Token +import snc.openchargingnetwork.node.tools.getTimestamp +import snc.openchargingnetwork.node.tools.urlJoin @RestController diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt index ad6e78b..5fa3e88 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt @@ -19,10 +19,9 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.models.* -import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.tools.filterNull diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt index 4c47a27..3ff7389 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt @@ -19,10 +19,9 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.models.* -import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.tools.filterNull @RestController diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt index 936905a..1d9d5ce 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt @@ -116,7 +116,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, */ fun blockAll(authorization: String) { // 1. check C / find platform - val platform = findPlatform(authorization); + val platform = findPlatform(authorization) // 2. determine whether whitelist is active assertListNotActive(platform, OcnRulesListType.BLACKLIST) @@ -361,7 +361,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, } private fun checkModule(modules: List) { - val result = modules.any{ it.isNullOrEmpty() } + val result = modules.any{ it.isEmpty() } if(result) { throw OcpiClientGenericException("Module list is empty") @@ -377,7 +377,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, } // 2. check each element of module is empty or not - result = parties.any { it -> it.modules.any { it.isNullOrEmpty() } } + result = parties.any { it -> it.modules.any { it.isEmpty() } } if(result) { throw OcpiClientGenericException("One of the element of module list is empty") @@ -445,7 +445,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, else -> return false } } - return false; + return false } /** @@ -499,7 +499,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, else -> return false } } - return false; + return false } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index acd0bef..13ac56a 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -7,7 +7,6 @@ import io.mockk.just import io.mockk.mockk import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertDoesNotThrow import org.springframework.http.HttpMethod import org.springframework.http.ResponseEntity import shareandcharge.openchargingnetwork.notary.Notary diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/AdminControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/AdminControllerTest.kt index 093d31a..1fbcfa8 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/AdminControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/AdminControllerTest.kt @@ -55,7 +55,7 @@ class AdminControllerTest { every { properties.apikey } returns "1234567890" every { properties.url } returns "https://node.ocn.org" every { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role.country, role.id) } returns false - every { platformRepo.save(any()) } returns platform + every { platformRepo.save(any()) } returns platform mockMvc.perform(post("/admin/generate-registration-token") .header("Authorization", "Token 1234567890") .contentType(MediaType.APPLICATION_JSON) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/RegistryControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/RegistryControllerTest.kt index 24cc849..affb633 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/RegistryControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/RegistryControllerTest.kt @@ -16,7 +16,6 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPat import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.web.context.WebApplicationContext -import org.web3j.crypto.Keys import org.web3j.tuples.generated.Tuple2 import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.config.NodeProperties diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt index 13ed705..3921d8e 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CredentialsControllerTest.kt @@ -14,16 +14,13 @@ import org.springframework.http.MediaType import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.* import org.springframework.test.web.servlet.result.MockMvcResultMatchers.* -import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.entities.* -import snc.openchargingnetwork.node.models.ocpi.InterfaceRole -import snc.openchargingnetwork.node.models.ocpi.Role -import snc.openchargingnetwork.node.models.ocpi.OcpiStatus import snc.openchargingnetwork.node.models.ocpi.* +import snc.openchargingnetwork.node.models.ocpi.Role +import snc.openchargingnetwork.node.repositories.* import snc.openchargingnetwork.node.services.HttpService import snc.openchargingnetwork.node.services.RegistryService -import snc.openchargingnetwork.node.services.RoutingService @WebMvcTest(CredentialsController::class) class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { @@ -115,8 +112,8 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { every { roleRepo.existsByCountryCodeAndPartyIDAllIgnoreCase(role2.countryCode, role2.partyID) } returns false every { networkClientInfoRepo.existsByPartyAndRole(BasicRole(role2.partyID, role2.countryCode), role2.role) } returns false - every { platformRepo.save(any()) } returns platform - every { endpointRepo.save(any()) } returns mockk() + every { platformRepo.save(any()) } returns platform + every { endpointRepo.save(any()) } returns mockk() every { roleRepo.saveAll(any>())} returns mockk() mockMvc.perform(post("/ocpi/2.2/credentials") @@ -172,10 +169,10 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { Endpoint("commands", InterfaceRole.RECEIVER, "https://org.charging.net/commands"))) every { properties.url } returns "http://my.broker.com" every { properties.signatures } returns false - every { platformRepo.save(any()) } returns platform + every { platformRepo.save(any()) } returns platform every { endpointRepo.deleteByPlatformID(platform.id) } returns mockk() - every { endpointRepo.save(any()) } returns mockk() - every { roleRepo.findAllByPlatformID(platform.id) } returns listOf() + every { endpointRepo.save(any()) } returns mockk() + every { roleRepo.findAllByPlatformID(platform.id) } returns listOf() every { roleRepo.deleteByPlatformID(platform.id) } returns mockk() every { roleRepo.saveAll(any>())} returns mockk() @@ -207,7 +204,7 @@ class CredentialsControllerTest(@Autowired val mockMvc: MockMvc) { every { platformRepo.findByAuth_TokenC(platform.auth.tokenC) } returns platform every { platformRepo.deleteById(platform.id!!) } just Runs every { platformRepo.save(platform) } returns platform - every { roleRepo.findAllByPlatformID(platform.id) } returns listOf() + every { roleRepo.findAllByPlatformID(platform.id) } returns listOf() every { roleRepo.deleteByPlatformID(platform.id) } just Runs every { endpointRepo.deleteByPlatformID(platform.id) } just Runs every { ocnRulesListRepo.deleteByPlatformID(platform.id) } just Runs diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt index d600984..875d49d 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt @@ -2,13 +2,13 @@ package snc.openchargingnetwork.node.integration.parties import com.fasterxml.jackson.module.kotlin.readValue import khttp.responses.Response -import shareandcharge.openchargingnetwork.notary.Notary import shareandcharge.openchargingnetwork.notary.SignableHeaders -import shareandcharge.openchargingnetwork.notary.ValuesToSign import snc.openchargingnetwork.node.data.exampleCDR -import org.web3j.crypto.Credentials as KeyPair import snc.openchargingnetwork.node.data.exampleLocation1 -import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.integration.utils.OcnContracts +import snc.openchargingnetwork.node.integration.utils.PartyDefinition +import snc.openchargingnetwork.node.integration.utils.objectMapper +import snc.openchargingnetwork.node.integration.utils.toMap import snc.openchargingnetwork.node.models.ocpi.* class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(config, contracts) { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt index 98bde8e..b261c7b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt @@ -2,14 +2,14 @@ package snc.openchargingnetwork.node.integration.parties import com.fasterxml.jackson.module.kotlin.readValue import khttp.responses.Response -import org.web3j.crypto.Credentials as KeyPair -import shareandcharge.openchargingnetwork.notary.Notary import shareandcharge.openchargingnetwork.notary.SignableHeaders -import shareandcharge.openchargingnetwork.notary.ValuesToSign import snc.openchargingnetwork.node.data.exampleCDR import snc.openchargingnetwork.node.data.exampleLocation1 import snc.openchargingnetwork.node.data.exampleToken -import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.integration.utils.OcnContracts +import snc.openchargingnetwork.node.integration.utils.PartyDefinition +import snc.openchargingnetwork.node.integration.utils.objectMapper +import snc.openchargingnetwork.node.integration.utils.toMap import snc.openchargingnetwork.node.models.ocpi.* class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(config, contracts) { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/HttpServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/HttpServiceTest.kt index 7d27955..00978d2 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/HttpServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/HttpServiceTest.kt @@ -6,7 +6,6 @@ import io.mockk.mockkStatic import khttp.responses.Response import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test -import snc.openchargingnetwork.node.models.* class HttpServiceTest { @@ -34,7 +33,7 @@ class HttpServiceTest { every { mockResponse.statusCode } returns 200 mockkStatic("khttp.KHttp") - every { khttp.get(any(), any>()) } returns mockResponse + every { khttp.get(any(), any()) } returns mockResponse val versions = httpService.getVersions("https://www.example.com/ocpi/cpo/versions", "authToken") assertThat(versions.count()).isEqualTo(1) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt index f92cc33..2ffe5ca 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt @@ -6,16 +6,16 @@ import io.mockk.mockk import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.http.HttpMethod -import org.web3j.tuples.generated.Tuple2 -import snc.openchargingnetwork.contracts.Permissions -import snc.openchargingnetwork.node.config.NodeProperties -import snc.openchargingnetwork.node.models.* +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.Receiver import snc.openchargingnetwork.node.models.entities.* import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.repositories.* +import snc.openchargingnetwork.node.repositories.EndpointRepository +import snc.openchargingnetwork.node.repositories.PlatformRepository +import snc.openchargingnetwork.node.repositories.ProxyResourceRepository +import snc.openchargingnetwork.node.repositories.RoleRepository import snc.openchargingnetwork.node.tools.generateUUIDv4Token import snc.openchargingnetwork.node.tools.urlJoin -import snc.openchargingnetwork.contracts.Registry class RoutingServiceTest { @@ -275,7 +275,7 @@ class RoutingServiceTest { val resource = "https://some.co/ocpi/tokens?limit=10; rel=\"next\"" val sender = BasicRole("SNC", "DE") val receiver = BasicRole("DIY", "UK") - every { proxyResourceRepo.save(any()) } returns ProxyResourceEntity( + every { proxyResourceRepo.save(any()) } returns ProxyResourceEntity( resource = resource, sender = sender, receiver = receiver, From b3784354874471da54e9720caf910c4d4d3725ca Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Tue, 2 Jun 2020 14:42:21 +0200 Subject: [PATCH 12/26] rename OcpiRequestHandler.forward overrides; implement forwardAgain method Signed-off-by: Adam Staveley --- .../node/components/OcpiRequestHandler.kt | 55 +++++++++++++++---- .../components/listeners/AppEventsListener.kt | 7 +-- .../node/controllers/ocn/MessageController.kt | 3 +- .../controllers/ocpi/v2_2/CdrsController.kt | 8 +-- .../ocpi/v2_2/ChargingProfilesController.kt | 10 ++-- .../ocpi/v2_2/CommandsController.kt | 12 ++-- .../ocpi/v2_2/LocationsController.kt | 28 +++++----- .../ocpi/v2_2/SessionsController.kt | 12 ++-- .../ocpi/v2_2/TariffsController.kt | 10 ++-- .../controllers/ocpi/v2_2/TokensController.kt | 12 ++-- .../node/models/events/AppEvents.kt | 4 +- .../node/services/AsyncTaskService.kt | 7 ++- .../node/components/OcpiRequestHandlerTest.kt | 16 +++--- .../controllers/ocn/MessageControllerTest.kt | 2 +- .../ocpi/v2_2/CdrsControllerTest.kt | 8 +-- .../v2_2/ChargingProfilesControllerTest.kt | 10 ++-- .../ocpi/v2_2/CommandsControllerTest.kt | 12 ++-- .../ocpi/v2_2/LocationsControllerTest.kt | 28 +++++----- .../ocpi/v2_2/SessionsControllerTest.kt | 12 ++-- .../ocpi/v2_2/TariffsControllerTest.kt | 10 ++-- .../ocpi/v2_2/TokensControllerTest.kt | 12 ++-- 21 files changed, 153 insertions(+), 125 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt index 4037265..6c367a4 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -23,6 +23,7 @@ import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.HttpResponse import snc.openchargingnetwork.node.models.Receiver import snc.openchargingnetwork.node.models.exceptions.OcpiHubUnknownReceiverException +import snc.openchargingnetwork.node.models.ocpi.BasicRole import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables import snc.openchargingnetwork.node.services.* import snc.openchargingnetwork.node.tools.generateUUIDv4Token @@ -96,11 +97,11 @@ class OcpiRequestHandler(request: OcpiRequestVariables, } /** - * Forward the request to the receiver. + * Forward an incoming request to the specified receiver. * @param proxied tells the RequestHandler that this request requires a proxied resource that was previously * saved by the OCN Node (e.g. a paginated "Link" response header). */ - fun forward(proxied: Boolean = false, fromLocalPlatform: Boolean = true): OcpiResponseHandler { + fun forwardDefault(proxied: Boolean = false, fromLocalPlatform: Boolean = true): OcpiResponseHandler { if (fromLocalPlatform) { assertSenderValid() } @@ -112,7 +113,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, assertValidSignature() val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) - asyncTaskService.findLinkedApps(request) + asyncTaskService.findLinkedApps(this) httpService.makeOcpiRequest(url, headers, request) } @@ -120,7 +121,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, assertValidSignature(false) val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) - asyncTaskService.findLinkedApps(request) + asyncTaskService.findLinkedApps(this) httpService.postOcnMessage(url, headers, body) } } @@ -129,12 +130,13 @@ class OcpiRequestHandler(request: OcpiRequestVariables, } /** - * Used by the module interfaces which require the modifying of a "response_url". + * Forward requests from module interfaces which require the modifying of a "response_url" (i.e. commands, charging + * profiles). * @param responseUrl the original response_url as defined by the sender * @param modifyRequest callback which allows the request (OcpiRequestVariables) used by this RequestHandler to * be modified with the new response_url which will be sent to the receiver. */ - fun forward(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): OcpiResponseHandler { + fun forwardAsync(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): OcpiResponseHandler { assertSenderValid() val proxyPath = "/ocpi/sender/2.2/${request.module.id}/${request.urlPathVariables}" @@ -155,7 +157,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, // send the request with the modified body val (url, headers) = routingService.prepareLocalPlatformRequest(request) - asyncTaskService.findLinkedApps(request) + asyncTaskService.findLinkedApps(this) httpService.makeOcpiRequest(url, headers, modifiedRequest) } @@ -173,7 +175,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) } - asyncTaskService.findLinkedApps(request) + asyncTaskService.findLinkedApps(this) httpService.postOcnMessage(url, headers, body) } @@ -184,11 +186,40 @@ class OcpiRequestHandler(request: OcpiRequestVariables, /** * Forwards a message received over the network (containing an "OCN-Signature" from the sending node) - * @param signature the OCN-Signature header received from the sending node + * @param sendingNodeSignature the OCN-Signature header received from the sending node */ - fun forward(signature: String): OcpiResponseHandler { - validateOcnMessage(signature) - return forward(fromLocalPlatform = false) + fun forwardFromOcn(sendingNodeSignature: String): OcpiResponseHandler { + validateOcnMessage(sendingNodeSignature) + return forwardDefault(fromLocalPlatform = false) + } + + /** + * Forwards a message to another recipient (i.e. an App with the appropriate permissions). + * @param newRecipient country_code and party_id of the App + */ + fun forwardAgain(newRecipient: BasicRole): OcpiResponseHandler { + val modifiedRequest = request.copy(headers = request.headers.copy(receiver = newRecipient)) + val rewriteFields = mapOf( + "$['headers']['ocpi-to-country-code']" to modifiedRequest.headers.receiver.country, + "$['headers']['ocpi-to-party-id']" to modifiedRequest.headers.receiver.id) + + // TODO: stash and re-sign + modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) + + val response: HttpResponse = when (routingService.getReceiverType(newRecipient)) { + Receiver.LOCAL -> { + // TODO: prepare local platform request + val (url, headers) = routingService.prepareLocalPlatformRequest(modifiedRequest) + httpService.makeOcpiRequest(url, headers, modifiedRequest) + } + Receiver.REMOTE -> { + // TODO: prepare remote platform request + val (url, headers, body) = routingService.prepareRemotePlatformRequest(modifiedRequest) + httpService.postOcnMessage(url, headers, body) + } + } + + return responseHandlerBuilder.build(modifiedRequest, response) } /** diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt index fd5c95a..4bcffe6 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt @@ -21,10 +21,9 @@ import org.springframework.context.event.EventListener import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Component import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent -import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder @Component -class AppEventsListener(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { +class AppEventsListener { companion object { private val logger = LoggerFactory.getLogger(AppEventsListener::class.java) @@ -35,12 +34,10 @@ class AppEventsListener(private val requestHandlerBuilder: OcpiRequestHandlerBui fun handleAppRecipientFoundEvent(event: AppRecipientFoundEvent) { logger.info("Got AppRecipientFoundEvent!") // create a copy of the request with the new receiver headers (the app provider) - val modifiedRequest = event.request.copy(headers = event.request.headers.copy(receiver = event.recipient)) // update the signature: use existing sig in notary, then stash and re-sign // find recipient location (LOCAL/REMOTE) // send! - val requestHandler = requestHandlerBuilder.build(modifiedRequest) - + event.requestHandler.forwardAgain(event.appRecipient) } } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt index 12b0f0e..dff02c1 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt @@ -18,7 +18,6 @@ package snc.openchargingnetwork.node.controllers.ocn import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* -import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.models.ocpi.OcpiResponse @@ -35,7 +34,7 @@ class MessageController(private val requestHandlerBuilder: OcpiRequestHandlerBui return requestHandlerBuilder .build(body) - .forward(signature) + .forwardFromOcn(signature) .getResponseWithAllHeaders() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt index 508ca52..ffdb69f 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt @@ -62,7 +62,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde // TODO: all pagination response header links should contain original url-encoded parameters return requestHandlerBuilder .build>(requestVariables) - .forward() + .forwardDefault() .getResponseWithPaginationHeaders() // proxies the Link response header } @@ -89,7 +89,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde return requestHandlerBuilder .build>(requestVariables) - .forward(proxied = true) // retrieves proxied Link response header + .forwardDefault(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -121,7 +121,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde return requestHandlerBuilder .build(requestVariables) - .forward(proxied = true) // retrieves proxied Location response header + .forwardDefault(proxied = true) // retrieves proxied Location response header .getResponse() } @@ -149,7 +149,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponseWithLocationHeader("/ocpi/receiver/2.2/cdrs") } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt index 9f03c85..05a6246 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt @@ -56,7 +56,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH return requestHandlerBuilder .build(requestVariables) - .forward(proxied = true) // retrieves proxied response_url + .forwardDefault(proxied = true) // retrieves proxied response_url .getResponse() } @@ -85,7 +85,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -120,7 +120,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH return requestHandlerBuilder .build(requestVariables) - .forward(responseUrl) { + .forwardAsync(responseUrl) { requestVariables.copy(urlEncodedParams = mapOf("duration" to duration, "response_url" to it)) } .getResponse() @@ -151,7 +151,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH return requestHandlerBuilder .build(requestVariables) - .forward(body.responseUrl) { + .forwardAsync(body.responseUrl) { requestVariables.copy(body = body.copy(responseUrl = it)) } .getResponse() @@ -182,7 +182,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH return requestHandlerBuilder .build(requestVariables) - .forward(responseUrl) { + .forwardAsync(responseUrl) { requestVariables.copy(urlEncodedParams = mapOf("response_url" to it)) } .getResponse() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt index 246f250..51b2535 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt @@ -57,7 +57,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward(proxied = true) // retrieves proxied response_url + .forwardDefault(proxied = true) // retrieves proxied response_url .getResponse() } @@ -91,7 +91,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward(body.responseURL) { + .forwardAsync(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } .getResponse() @@ -122,7 +122,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward(body.responseURL) { + .forwardAsync(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } .getResponse() @@ -153,7 +153,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward(body.responseURL) { + .forwardAsync(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } .getResponse() @@ -184,7 +184,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward(body.responseURL) { + .forwardAsync(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } .getResponse() @@ -215,7 +215,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward(body.responseURL) { + .forwardAsync(body.responseURL) { requestVariables.copy(body = body.copy(responseURL = it)) } .getResponse() diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt index ff329f3..e89aa20 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt @@ -61,7 +61,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build>(requestVariables) - .forward() + .forwardDefault() .getResponseWithPaginationHeaders() // proxies Link response header } @@ -88,7 +88,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build>(requestVariables) - .forward(proxied = true) // retrieves proxied Link response header + .forwardDefault(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -115,7 +115,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -143,7 +143,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -172,7 +172,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -206,7 +206,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -236,7 +236,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -267,7 +267,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -298,7 +298,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -330,7 +330,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -363,7 +363,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -394,7 +394,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -426,7 +426,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -459,7 +459,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt index eee2153..82dcf2d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt @@ -61,7 +61,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build>(requestVariables) - .forward() + .forwardDefault() .getResponseWithPaginationHeaders() // proxies Link response header } @@ -89,7 +89,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build>(requestVariables) - .forward(proxied = true) // retrieves proxied Link response header + .forwardDefault(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -118,7 +118,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -152,7 +152,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -183,7 +183,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -214,7 +214,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt index 5fa3e88..7e25c91 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt @@ -61,7 +61,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui return requestHandlerBuilder .build>(requestVariables) - .forward() + .forwardDefault() .getResponseWithPaginationHeaders() // proxies Link response header } @@ -88,7 +88,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui return requestHandlerBuilder .build>(requestVariables) - .forward(proxied = true) // retrieves proxied Link response header + .forwardDefault(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -122,7 +122,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -154,7 +154,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -184,7 +184,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt index 3ff7389..a77f270 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt @@ -60,7 +60,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil return requestHandlerBuilder .build>(requestVariables) - .forward() + .forwardDefault() .getResponseWithPaginationHeaders() // proxies Link response header } @@ -87,7 +87,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil return requestHandlerBuilder .build>(requestVariables) - .forward(proxied = true) // retrieves proxied Link response header + .forwardDefault(proxied = true) // retrieves proxied Link response header .getResponseWithPaginationHeaders() } @@ -119,7 +119,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -155,7 +155,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -188,7 +188,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } @@ -221,7 +221,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil return requestHandlerBuilder .build(requestVariables) - .forward() + .forwardDefault() .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt index 5cde3f6..930400d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt @@ -16,8 +16,8 @@ package snc.openchargingnetwork.node.models.events +import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.models.ocpi.BasicRole -import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables -class AppRecipientFoundEvent(val recipient: BasicRole, val request: OcpiRequestVariables) +class AppRecipientFoundEvent(val requestHandler: OcpiRequestHandler<*>, val appRecipient: BasicRole) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index cc89659..e0bbe1d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -20,8 +20,8 @@ import org.slf4j.LoggerFactory import org.springframework.context.ApplicationEventPublisher import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service +import snc.openchargingnetwork.node.components.OcpiRequestHandler import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent -import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables @Service class AsyncTaskService(private val registryService: RegistryService, @@ -36,11 +36,12 @@ class AsyncTaskService(private val registryService: RegistryService, * Once apps have been found, triggers an AppRecipientFoundEvent. */ @Async - fun findLinkedApps(request: OcpiRequestVariables) { + fun findLinkedApps(requestHandler: OcpiRequestHandler<*>) { + val request = requestHandler.request registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach { logger.info("publishing forward request to ${it.provider}") - applicationEventPublisher.publishEvent(AppRecipientFoundEvent(it.provider, request)) + applicationEventPublisher.publishEvent(AppRecipientFoundEvent(requestHandler, it.provider)) } } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index 13ac56a..74b4ee5 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -73,12 +73,12 @@ class OcpiRequestHandlerTest { every { routingService.isRoleKnown(variables.headers.receiver) } returns true every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { hubClientInfoService.renewClientConnection(variables.headers.receiver) } just Runs - every { asyncTaskService.findLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(requestHandler) } just Runs every { registryService.getAgreementsByInterface(variables.headers.sender, variables.module, variables.interfaceRole) } returns sequenceOf() every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forward().getResponse() + val response = requestHandler.forwardDefault().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @@ -132,11 +132,11 @@ class OcpiRequestHandlerTest { receiverSig.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) every { httpService.makeOcpiRequest(recipientUrl, outgoingHeaders, variables) } returns expectedResponse - every { asyncTaskService.findLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forward().getResponse() + val response = requestHandler.forwardDefault().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @@ -177,11 +177,11 @@ class OcpiRequestHandlerTest { every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { routingService.isRoleKnown(variables.headers.receiver) } returns false - every { asyncTaskService.findLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forward().getResponse() + val response = requestHandler.forwardDefault().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } @@ -232,11 +232,11 @@ class OcpiRequestHandlerTest { every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse - every { asyncTaskService.findLinkedApps(variables) } just Runs + every { asyncTaskService.findLinkedApps(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) - val response = requestHandler.forward().getResponse() + val response = requestHandler.forwardDefault().getResponse() Assertions.assertEquals(expectedResponse.statusCode, response.statusCodeValue) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt index bddca21..a973538 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt @@ -53,7 +53,7 @@ class MessageControllerTest(@Autowired val mockMvc: MockMvc) { every { mockkRequestHandler - .forward("0x1234") + .forwardFromOcn("0x1234") .getResponseWithAllHeaders() } returns ResponseEntity .status(200) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt index 4fcef5c..9d2cd36 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt @@ -59,7 +59,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleCDR))) @@ -112,7 +112,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleCDR))) @@ -161,7 +161,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleCDR)) @@ -207,7 +207,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponseWithLocationHeader("/ocpi/receiver/2.2/cdrs") } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponseWithLocationHeader("/ocpi/receiver/2.2/cdrs") } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt index 161fcec..d446861 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt @@ -48,7 +48,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { val response = ResponseEntity.status(HttpStatus.ACCEPTED).body(OcpiResponse(statusCode = 1000)) every { requestHandlerBuilder.build(request) } returns requestHandler - every { requestHandler.forward(true).getResponse() } returns response + every { requestHandler.forwardDefault(true).getResponse() } returns response mockMvc.perform(post("/ocpi/2.2/sender/chargingprofiles/result/12345") .header("Authorization", "Token token-c") @@ -94,7 +94,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { val response = ResponseEntity.status(HttpStatus.ACCEPTED).body(OcpiResponse(statusCode = 1000)) every { requestHandlerBuilder.build(request) } returns requestHandler - every { requestHandler.forward().getResponse() } returns response + every { requestHandler.forwardDefault().getResponse() } returns response mockMvc.perform(put("/ocpi/2.2/sender/chargingprofiles/1234567890") .header("Authorization", "Token token-c") @@ -131,7 +131,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(request) } returns requestHandler every { - requestHandler.forward("https://server.com/profiles/1", any()).getResponse() + requestHandler.forwardAsync("https://server.com/profiles/1", any()).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -177,7 +177,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(request) } returns requestHandler every { - requestHandler.forward("https://smart.charging.net/profiles/0102030405", any()).getResponse() + requestHandler.forwardAsync("https://smart.charging.net/profiles/0102030405", any()).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -220,7 +220,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(request) } returns requestHandler every { - requestHandler.forward("https://scsp.io/ocpi/callback/369", any()).getResponse() + requestHandler.forwardAsync("https://scsp.io/ocpi/callback/369", any()).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt index 6f0a89b..e97e355 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt @@ -55,7 +55,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -103,7 +103,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .forward(body.responseURL, any()) + .forwardAsync(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -158,7 +158,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .forward(body.responseURL, any()) + .forwardAsync(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -211,7 +211,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .forward(body.responseURL, any()) + .forwardAsync(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -263,7 +263,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .forward(body.responseURL, any()) + .forwardAsync(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( @@ -317,7 +317,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler every { mockRequestHandler - .forward(body.responseURL, any()) + .forwardAsync(body.responseURL, any()) .getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt index cc089dd..6d4635b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt @@ -63,7 +63,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse( @@ -121,7 +121,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse( @@ -174,7 +174,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -222,7 +222,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -271,7 +271,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -318,7 +318,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -366,7 +366,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -415,7 +415,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse( statusCode = 1000, @@ -464,7 +464,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -513,7 +513,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -563,7 +563,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -611,7 +611,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -661,7 +661,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -712,7 +712,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt index d2c96ef..eb4904e 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt @@ -62,7 +62,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleSession))) @@ -118,7 +118,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleSession))) @@ -170,7 +170,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = ChargingPreferencesResponse.NOT_POSSIBLE)) @@ -216,7 +216,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleSession)) @@ -263,7 +263,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -311,7 +311,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt index 1b0bac7..4ca3917 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt @@ -59,7 +59,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleTariff))) @@ -113,7 +113,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleTariff))) @@ -164,7 +164,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleTariff)) @@ -210,7 +210,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -256,7 +256,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt index a50628e..85639da 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt @@ -57,7 +57,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleToken))) @@ -114,7 +114,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build>(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward(true).getResponseWithPaginationHeaders() } returns ResponseEntity + every { mockRequestHandler.forwardDefault(true).getResponseWithPaginationHeaders() } returns ResponseEntity .status(200) .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleToken))) @@ -168,7 +168,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = AuthorizationInfo( allowed = Allowed.ALLOWED, @@ -219,7 +219,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleToken)) @@ -267,7 +267,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) @@ -319,7 +319,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandlerBuilder.build(requestVariables) } returns mockRequestHandler - every { mockRequestHandler.forward().getResponse() } returns ResponseEntity + every { mockRequestHandler.forwardDefault().getResponse() } returns ResponseEntity .status(200) .body(OcpiResponse(statusCode = 1000)) From cd969ea9c8fcb19d2e3e189d859e5f30e28b0eb3 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Tue, 2 Jun 2020 16:56:13 +0200 Subject: [PATCH 13/26] replace event listener with forwarding in the same thread Signed-off-by: Adam Staveley --- .../node/components/OcpiRequestHandler.kt | 15 +++---- .../components/listeners/AppEventsListener.kt | 43 ------------------- .../node/models/events/AppEvents.kt | 23 ---------- .../node/services/AsyncTaskService.kt | 31 +++++++------ .../node/components/OcpiRequestHandlerTest.kt | 8 ++-- .../node/integration/AppInterfaceTest.kt | 3 ++ 6 files changed, 32 insertions(+), 91 deletions(-) delete mode 100644 src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt delete mode 100644 src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt index 6c367a4..08c840c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -113,7 +113,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, assertValidSignature() val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) - asyncTaskService.findLinkedApps(this) + asyncTaskService.forwardOcpiRequestToLinkedApps(this, fromLocalPlatform) httpService.makeOcpiRequest(url, headers, request) } @@ -121,7 +121,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, assertValidSignature(false) val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) - asyncTaskService.findLinkedApps(this) + asyncTaskService.forwardOcpiRequestToLinkedApps(this, fromLocalPlatform) httpService.postOcnMessage(url, headers, body) } } @@ -157,7 +157,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, // send the request with the modified body val (url, headers) = routingService.prepareLocalPlatformRequest(request) - asyncTaskService.findLinkedApps(this) + asyncTaskService.forwardOcpiRequestToLinkedApps(this) httpService.makeOcpiRequest(url, headers, modifiedRequest) } @@ -175,7 +175,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) } - asyncTaskService.findLinkedApps(this) + asyncTaskService.forwardOcpiRequestToLinkedApps(this) httpService.postOcnMessage(url, headers, body) } @@ -200,20 +200,17 @@ class OcpiRequestHandler(request: OcpiRequestVariables, fun forwardAgain(newRecipient: BasicRole): OcpiResponseHandler { val modifiedRequest = request.copy(headers = request.headers.copy(receiver = newRecipient)) val rewriteFields = mapOf( - "$['headers']['ocpi-to-country-code']" to modifiedRequest.headers.receiver.country, - "$['headers']['ocpi-to-party-id']" to modifiedRequest.headers.receiver.id) + "$['headers']['ocpi-to-country-code']" to request.headers.receiver.country, + "$['headers']['ocpi-to-party-id']" to request.headers.receiver.id) - // TODO: stash and re-sign modifiedRequest.headers.signature = rewriteAndSign(modifiedRequest.toSignedValues(), rewriteFields) val response: HttpResponse = when (routingService.getReceiverType(newRecipient)) { Receiver.LOCAL -> { - // TODO: prepare local platform request val (url, headers) = routingService.prepareLocalPlatformRequest(modifiedRequest) httpService.makeOcpiRequest(url, headers, modifiedRequest) } Receiver.REMOTE -> { - // TODO: prepare remote platform request val (url, headers, body) = routingService.prepareRemotePlatformRequest(modifiedRequest) httpService.postOcnMessage(url, headers, body) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt deleted file mode 100644 index 4bcffe6..0000000 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/listeners/AppEventsListener.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright 2019-2020 eMobilify GmbH - - 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 snc.openchargingnetwork.node.components.listeners - -import org.slf4j.LoggerFactory -import org.springframework.context.event.EventListener -import org.springframework.scheduling.annotation.Async -import org.springframework.stereotype.Component -import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent - -@Component -class AppEventsListener { - - companion object { - private val logger = LoggerFactory.getLogger(AppEventsListener::class.java) - } - - @Async - @EventListener - fun handleAppRecipientFoundEvent(event: AppRecipientFoundEvent) { - logger.info("Got AppRecipientFoundEvent!") - // create a copy of the request with the new receiver headers (the app provider) - // update the signature: use existing sig in notary, then stash and re-sign - // find recipient location (LOCAL/REMOTE) - // send! - event.requestHandler.forwardAgain(event.appRecipient) - } - -} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt deleted file mode 100644 index 930400d..0000000 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/events/AppEvents.kt +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2019-2020 eMobilify GmbH - - 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 snc.openchargingnetwork.node.models.events - -import snc.openchargingnetwork.node.components.OcpiRequestHandler -import snc.openchargingnetwork.node.models.ocpi.BasicRole - - -class AppRecipientFoundEvent(val requestHandler: OcpiRequestHandler<*>, val appRecipient: BasicRole) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index e0bbe1d..b7d0a8d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -17,15 +17,12 @@ package snc.openchargingnetwork.node.services import org.slf4j.LoggerFactory -import org.springframework.context.ApplicationEventPublisher import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import snc.openchargingnetwork.node.components.OcpiRequestHandler -import snc.openchargingnetwork.node.models.events.AppRecipientFoundEvent @Service -class AsyncTaskService(private val registryService: RegistryService, - private val applicationEventPublisher: ApplicationEventPublisher) { +class AsyncTaskService(private val registryService: RegistryService) { companion object { private val logger = LoggerFactory.getLogger(AsyncTaskService::class.java) @@ -33,16 +30,26 @@ class AsyncTaskService(private val registryService: RegistryService, /** * Finds all apps, linked to a sender, with permissions that grant them access to a given request type. - * Once apps have been found, triggers an AppRecipientFoundEvent. + * Once apps have been found, sends via provided request handler. */ @Async - fun findLinkedApps(requestHandler: OcpiRequestHandler<*>) { - val request = requestHandler.request - registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) - .forEach { - logger.info("publishing forward request to ${it.provider}") - applicationEventPublisher.publishEvent(AppRecipientFoundEvent(requestHandler, it.provider)) - } + fun forwardOcpiRequestToLinkedApps(requestHandler: OcpiRequestHandler<*>, fromLocalPlatform: Boolean = true) { + if (fromLocalPlatform) { + val request = requestHandler.request + registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) + .forEach { + try { + requestHandler.forwardAgain(it.provider) + } catch (e: Exception) { + // fire and forget + logger.warn("Error forwarding request to app ${it.provider}: ${e.message}") + } + } + + // TODO: add tests + // - AppInterfaceTest + // - OcpiRequestHandlerTest.forwardAgain + } } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index 74b4ee5..317596a 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -73,7 +73,7 @@ class OcpiRequestHandlerTest { every { routingService.isRoleKnown(variables.headers.receiver) } returns true every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { hubClientInfoService.renewClientConnection(variables.headers.receiver) } just Runs - every { asyncTaskService.findLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs every { registryService.getAgreementsByInterface(variables.headers.sender, variables.module, variables.interfaceRole) } returns sequenceOf() every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) @@ -132,7 +132,7 @@ class OcpiRequestHandlerTest { receiverSig.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) every { httpService.makeOcpiRequest(recipientUrl, outgoingHeaders, variables) } returns expectedResponse - every { asyncTaskService.findLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) @@ -177,7 +177,7 @@ class OcpiRequestHandlerTest { every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { routingService.isRoleKnown(variables.headers.receiver) } returns false - every { asyncTaskService.findLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) @@ -232,7 +232,7 @@ class OcpiRequestHandlerTest { every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse - every { asyncTaskService.findLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt index 9e88a5b..e8ebd05 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -30,8 +30,11 @@ class AppInterfaceTest { @Test fun fowardsRequestToApp() { + println("CPO2 ${cpo2.party} is the App Provider") cpo2.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) + println("MSP ${msp.party} is the App User") msp.server.agreeToAppPermissions(cpo2.address) + println("MSP fetches CPO1 ${cpo1.party} location, thereby forwarding to CPO2") msp.server.getLocation(cpo1.party) Thread.sleep(5000L) } From fc2f5b20e0e5a05a5fe31f148219d608f613fd3f Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Wed, 3 Jun 2020 11:02:57 +0200 Subject: [PATCH 14/26] test app interface with local and remote message forwarding Signed-off-by: Adam Staveley --- .../node/integration/AppInterfaceTest.kt | 40 ++++++++++++++++--- .../node/integration/parties/CpoServer.kt | 14 +++++-- .../node/integration/parties/PartyServer.kt | 4 +- .../node/integration/utils/Types.kt | 5 +++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt index e8ebd05..1192735 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -1,10 +1,15 @@ package snc.openchargingnetwork.node.integration +import org.awaitility.kotlin.await import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.springframework.http.HttpMethod import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.OcnAppPermission +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import java.util.concurrent.TimeUnit class AppInterfaceTest { @@ -28,15 +33,40 @@ class AppInterfaceTest { stopPartyServers(networkComponents) } + private fun seenByBothCpos(): Boolean { + val message = ReceivedMessage( + module = ModuleID.LOCATIONS, + interfaceRole = InterfaceRole.SENDER, + method = HttpMethod.GET, + sender = msp.party) + val cpo1Seen = cpo1.server.messageStore.contains(message) + val cpo2Seen = cpo2.server.messageStore.contains(message) + return cpo1Seen && cpo2Seen + } + + @Test + fun fowardsRequestToApp_Local() { + // CPO1 is the App Provider + cpo1.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) + // MSP is the App User + msp.server.agreeToAppPermissions(cpo1.address) + // MSP sends request to CPO2 which should also be forwarded to CPO1 + msp.server.getLocation(cpo2.party) + + await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } + + } + @Test - fun fowardsRequestToApp() { - println("CPO2 ${cpo2.party} is the App Provider") + fun fowardsRequestToApp_Remote() { + // CPO2 is the App Provider cpo2.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) - println("MSP ${msp.party} is the App User") + // MSP is the App User msp.server.agreeToAppPermissions(cpo2.address) - println("MSP fetches CPO1 ${cpo1.party} location, thereby forwarding to CPO2") + // MSP sends request to CPO1 which should also be forwarded to CPO2 msp.server.getLocation(cpo1.party) - Thread.sleep(5000L) + + await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt index 875d49d..b9c6c20 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt @@ -2,13 +2,11 @@ package snc.openchargingnetwork.node.integration.parties import com.fasterxml.jackson.module.kotlin.readValue import khttp.responses.Response +import org.springframework.http.HttpMethod import shareandcharge.openchargingnetwork.notary.SignableHeaders import snc.openchargingnetwork.node.data.exampleCDR import snc.openchargingnetwork.node.data.exampleLocation1 -import snc.openchargingnetwork.node.integration.utils.OcnContracts -import snc.openchargingnetwork.node.integration.utils.PartyDefinition -import snc.openchargingnetwork.node.integration.utils.objectMapper -import snc.openchargingnetwork.node.integration.utils.toMap +import snc.openchargingnetwork.node.integration.utils.* import snc.openchargingnetwork.node.models.ocpi.* class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(config, contracts) { @@ -37,6 +35,14 @@ class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c } app.get("/ocpi/cpo/2.2/locations/1") { + messageStore.add(ReceivedMessage( + module = ModuleID.LOCATIONS, + interfaceRole = InterfaceRole.SENDER, + method = HttpMethod.GET, + sender = BasicRole( + id = it.req.getHeader("ocpi-from-party-id"), + country = it.req.getHeader("ocpi-from-country-code")))) + val body = OcpiResponse(statusCode = 1000, data = exampleLocation1) body.signature = sign(body = body) it.json(body) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt index 6c76d64..2ac9a60 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt @@ -20,7 +20,9 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra lateinit var tokenC: String lateinit var node: String - val hubClientInfoStatuses = mutableMapOf() + val hubClientInfoStatuses = mutableMapOf() // store received hubclientinfo updates + + val messageStore = mutableListOf() // store any received messages // replace deployed contract instances with own party's transaction manager private val txManager = ClientTransactionManager(web3, config.credentials.address) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt index 23522ac..8a4cd06 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/utils/Types.kt @@ -1,12 +1,15 @@ package snc.openchargingnetwork.node.integration.utils import org.springframework.context.ConfigurableApplicationContext +import org.springframework.http.HttpMethod import org.web3j.crypto.Credentials import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.integration.parties.CpoServer import snc.openchargingnetwork.node.integration.parties.MspServer import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID class JavalinException(val httpCode: Int = 200, val ocpiCode: Int = 2001, message: String): Exception(message) @@ -34,3 +37,5 @@ data class Scheduler(val enabled: Boolean, val rate: Long = 2000) data class HubClientInfoParams(val stillAlive: Scheduler = Scheduler(false), val plannedPartySearch: Scheduler = Scheduler(false)) + +data class ReceivedMessage(val module: ModuleID, val interfaceRole: InterfaceRole, val method: HttpMethod, val sender: BasicRole) \ No newline at end of file From 2c21a14b1633e699cd5e3bc371dd1a6e8f35eba7 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Wed, 3 Jun 2020 11:37:12 +0200 Subject: [PATCH 15/26] add tests for OcnAppPermissions enum matcher methods and RegistryService.getAgreementsByInterface() Signed-off-by: Adam Staveley --- .../node/services/AsyncTaskService.kt | 4 -- .../openchargingnetwork/node/models/Ocn.kt | 47 +++++++++++++++++++ .../node/services/RegistryServiceTest.kt | 33 +++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index b7d0a8d..416514e 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -45,10 +45,6 @@ class AsyncTaskService(private val registryService: RegistryService) { logger.warn("Error forwarding request to app ${it.provider}: ${e.message}") } } - - // TODO: add tests - // - AppInterfaceTest - // - OcpiRequestHandlerTest.forwardAgain } } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt new file mode 100644 index 0000000..21ecaf1 --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -0,0 +1,47 @@ +package snc.openchargingnetwork.node.models + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import java.util.stream.Stream + +data class PermissionsMatcherTestCase(val request: BasicRequestType, val permission: OcnAppPermission, val expected: Boolean) + +class OcnAppPermissionsTest { + + private fun permissionsTestSources(): Stream { + return Stream.of( + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_ALL, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_ALL_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_ALL_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_ALL_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_ALL_SENDER, + expected = false)) + ) + } + + @ParameterizedTest + @MethodSource("permissionsTestSources") + fun permissionsMatchers(testCase: PermissionsMatcherTestCase) { + val actual = testCase.permission.matches(testCase.request) + assertThat(actual).isEqualTo(testCase.expected) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt index cd3bd52..d7cb0af 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt @@ -5,11 +5,17 @@ import io.mockk.mockk import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.web3j.tuples.generated.Tuple2 +import org.web3j.tuples.generated.Tuple5 import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.config.NodeProperties +import snc.openchargingnetwork.node.models.OcnApp +import snc.openchargingnetwork.node.models.OcnAppPermission import snc.openchargingnetwork.node.models.RegistryNode import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import java.math.BigInteger class RegistryServiceTest { @@ -70,4 +76,31 @@ class RegistryServiceTest { every { registry.getOperatorByOcpi(role.country.toByteArray(), role.id.toByteArray()).sendAsync().get() } returns Tuple2("", "https://some.node.com") assertThat(registryService.getRemoteNodeUrlOf(role)).isEqualTo("https://some.node.com") } + + @Test + fun getAgreementsByInterface() { + val user = BasicRole(id = "HEY", country = "YA") + val provider = BasicRole(id = "OOO", country = "AH") + + every { + permissions.getUserAgreementsByOcpi(user.country.toByteArray(), user.id.toByteArray()).sendAsync().get() + } returns listOf("0x059a44557cF9Bd2b446d72fC772254F0E487BACf") + + every { + permissions.getApp("0x059a44557cF9Bd2b446d72fC772254F0E487BACf").sendAsync().get() + } returns Tuple5( + provider.country.toByteArray(), + provider.id.toByteArray(), + "Hungry Hippo Charging", + "https://hungry-hip.pos.io", + listOf(BigInteger.TWO) + ) + + val actual = registryService.getAgreementsByInterface(user, ModuleID.TARIFFS, InterfaceRole.RECEIVER) + + assertThat(actual.count()).isEqualTo(1) + assertThat(actual.iterator().next()).isEqualTo( + OcnApp(provider = provider, permissions = listOf(OcnAppPermission.FORWARD_ALL_RECEIVER)) + ) + } } \ No newline at end of file From f9ab2a5df1a8d6b121f8134316266ced832b87a6 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Wed, 3 Jun 2020 11:58:30 +0200 Subject: [PATCH 16/26] fix BigInteger instantiation in registyr test case and update changelog --- CHANGELOG.md | 15 ++++++++++++--- .../node/services/RegistryServiceTest.kt | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2fc4b2..e349f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,23 @@ # OCN Node Changelog -## 1.1.0-rc0 +## 1.1.0-rc1 +### Upcoming -Includes initial hubclientinfo OCPI module implementation +Adds the ability for requests to be forwarded to "Ocn Apps" with matching permissions. The +Ocn App Interface, using the new Permissions contract in the OCN Registry, allows data to be +shared and accessed using a permission system. + +## 1.1.0-rc0 +### Apr 28, 2020 + +Includes initial hubclientinfo OCPI module implementation. - Optional "Still-Alive" check requests connected parties versions endpoint at regular intervals. - Optional "Planned Party" search scans registry for newly planned parties at regular intervals. - New configuration properties under `ocn.node`: `stillAliveEnabled`, `stillAliveRate`, `plannedPartySearchEnabled`, `plannedPartySearchRate`. -## 1.0.0 +## 1.0.0 +### Mar 03, 2020 Initial release of the Open Charging Network Node. - All OCPI modules included, except for hubclientinfo. diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt index d7cb0af..675d9fb 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt @@ -93,7 +93,7 @@ class RegistryServiceTest { provider.id.toByteArray(), "Hungry Hippo Charging", "https://hungry-hip.pos.io", - listOf(BigInteger.TWO) + listOf(BigInteger("2")) ) val actual = registryService.getAgreementsByInterface(user, ModuleID.TARIFFS, InterfaceRole.RECEIVER) From 4871a1758cc4f6daacf0aa15f3f1fa0638a5a348 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Thu, 4 Jun 2020 16:06:15 +0200 Subject: [PATCH 17/26] intial custom modules controller and unit test Signed-off-by: Adam Staveley --- .../ocpi/v2_2/CustomModulesController.kt | 63 +++++++++++++++++++ .../node/models/ocpi/Common.kt | 16 ++++- .../ocpi/v2_2/CustomModulesControllerTest.kt | 28 +++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt create mode 100644 src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt new file mode 100644 index 0000000..d3eb50f --- /dev/null +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt @@ -0,0 +1,63 @@ +package snc.openchargingnetwork.node.controllers.ocpi.v2_2 + +import org.springframework.http.HttpMethod +import org.springframework.web.bind.annotation.* +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.ocpi.BasicRole +import snc.openchargingnetwork.node.models.ocpi.InterfaceRole +import snc.openchargingnetwork.node.models.ocpi.ModuleID +import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables +import javax.servlet.http.HttpServletRequest + +fun String.toQueryMap(): Map { + val queryMap = mutableMapOf() + split("&").forEach { + val (key, value) = it.split("=") + queryMap[key] = value + } + return queryMap +} + +@RestController +@RequestMapping("/ocpi/custom") +class CustomModulesController { + + @RequestMapping("/{module}/{interfaceRole}/**/*") + fun customModuleMapping(@RequestHeader("authorization") authorization: String, + @RequestHeader("OCN-Signature") signature: String? = null, + @RequestHeader("X-Request-ID") requestID: String, + @RequestHeader("X-Correlation-ID") correlationID: String, + @RequestHeader("OCPI-from-country-code") fromCountryCode: String, + @RequestHeader("OCPI-from-party-id") fromPartyID: String, + @RequestHeader("OCPI-to-country-code") toCountryCode: String, + @RequestHeader("OCPI-to-party-id") toPartyID: String, + @PathVariable module: String, + @PathVariable interfaceRole: String, + @RequestBody body: String?, + request: HttpServletRequest): String { + // TODO: + // - sender is registered with this node + // - recipient is found (local/remote) + // - local recipient supports this module/interface + // - send to... + + val sender = BasicRole(fromPartyID, fromCountryCode).toUpperCase() + val receiver = BasicRole(toPartyID, toCountryCode).toUpperCase() + + val pathWildcards = request.pathInfo.replace("/ocpi/custom/${module}/${interfaceRole}/", "") + + val requestVariables = OcpiRequestVariables( + module = ModuleID.CUSTOM, + interfaceRole = InterfaceRole.resolve(interfaceRole), + method = HttpMethod.valueOf(request.method), + headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), + urlPathVariables = pathWildcards, + urlEncodedParams = request.queryString.toQueryMap(), + body = body) + + + + return "OK" + } + +} \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt index 1086352..d8088db 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt @@ -138,13 +138,25 @@ enum class ModuleID(val id: String) { LOCATIONS("locations"), SESSIONS("sessions"), TARIFFS("tariffs"), - TOKENS("tokens") + TOKENS("tokens"), + CUSTOM("custom") } enum class InterfaceRole(val id: String) { SENDER(id = "sender"), - RECEIVER(id = "receiver") + RECEIVER(id = "receiver"); + + companion object { + fun values(): List { + return listOf(SENDER, RECEIVER) + } + fun resolve(role: String): InterfaceRole { + val values = values() + return values.find { it.id.toLowerCase() == role } + ?: throw OcpiClientInvalidParametersException("No interface $role found. Expected one of $values.") + } + } } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt new file mode 100644 index 0000000..165346d --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt @@ -0,0 +1,28 @@ +package snc.openchargingnetwork.node.controllers.ocpi.v2_2 + +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +@WebMvcTest(CustomModulesController::class) +class CustomModulesControllerTest(@Autowired val mockMvc: MockMvc) { + + @Test + fun providesCustomModuleEndpoint() { + mockMvc.perform(get("/ocpi/custom/my-module/sender/path/1/2/3/5/6/7?hello=world&foo=bar") + .header("authorization", "Token abc123") + .header("x-request-id", "1") + .header("x-correlation-id", "1") + .header("ocpi-from-country-code", "de") + .header("ocpi-from-party-id", "abc") + .header("ocpi-to-country-code", "gb") + .header("ocpi-to-party-id", "lon") + .contentType("application/json") + .content("{\"foo\": \"bar\"}")) + .andExpect(status().isOk) + } + +} \ No newline at end of file From fb34b300888c3ab9ed4be62bb2b670046fd13b9f Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Thu, 4 Jun 2020 17:27:39 +0200 Subject: [PATCH 18/26] start integrating custom modules into request handling flow Signed-off-by: Adam Staveley --- .../ocpi/v2_2/CustomModulesController.kt | 34 ++++++------------- .../node/models/ocpi/Common.kt | 10 ++++++ .../node/services/AsyncTaskService.kt | 7 +++- .../node/services/RoutingService.kt | 7 ++-- .../node/tools/Extensions.kt | 9 +++++ 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt index d3eb50f..5084867 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt @@ -1,26 +1,17 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 import org.springframework.http.HttpMethod +import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.models.OcnHeaders -import snc.openchargingnetwork.node.models.ocpi.BasicRole -import snc.openchargingnetwork.node.models.ocpi.InterfaceRole -import snc.openchargingnetwork.node.models.ocpi.ModuleID -import snc.openchargingnetwork.node.models.ocpi.OcpiRequestVariables +import snc.openchargingnetwork.node.models.ocpi.* +import snc.openchargingnetwork.node.tools.toQueryMap import javax.servlet.http.HttpServletRequest -fun String.toQueryMap(): Map { - val queryMap = mutableMapOf() - split("&").forEach { - val (key, value) = it.split("=") - queryMap[key] = value - } - return queryMap -} - @RestController @RequestMapping("/ocpi/custom") -class CustomModulesController { +class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { @RequestMapping("/{module}/{interfaceRole}/**/*") fun customModuleMapping(@RequestHeader("authorization") authorization: String, @@ -34,12 +25,7 @@ class CustomModulesController { @PathVariable module: String, @PathVariable interfaceRole: String, @RequestBody body: String?, - request: HttpServletRequest): String { - // TODO: - // - sender is registered with this node - // - recipient is found (local/remote) - // - local recipient supports this module/interface - // - send to... + request: HttpServletRequest): ResponseEntity> { val sender = BasicRole(fromPartyID, fromCountryCode).toUpperCase() val receiver = BasicRole(toPartyID, toCountryCode).toUpperCase() @@ -48,6 +34,7 @@ class CustomModulesController { val requestVariables = OcpiRequestVariables( module = ModuleID.CUSTOM, + customModuleId = module, interfaceRole = InterfaceRole.resolve(interfaceRole), method = HttpMethod.valueOf(request.method), headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), @@ -55,9 +42,10 @@ class CustomModulesController { urlEncodedParams = request.queryString.toQueryMap(), body = body) - - - return "OK" + return requestHandlerBuilder + .build(requestVariables) + .forwardDefault() + .getResponseWithAllHeaders() } } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt index ad09d60..bf5c770 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt @@ -59,8 +59,18 @@ data class OcpiRequestVariables(@JsonProperty("module") val module: ModuleID, @JsonProperty("url_encoded_params") val urlEncodedParams: Map? = null, @JsonProperty("proxy_uid") val proxyUID: String? = null, @JsonProperty("proxy_resource") val proxyResource: String? = null, + @JsonProperty("custom_module_id") val customModuleId: String? = null, @JsonProperty("body") val body: Any? = null) { + init { + if (module != ModuleID.CUSTOM && customModuleId != null) { + throw IllegalStateException("customModuleId defined but module is not CUSTOM") + } + if (module == ModuleID.CUSTOM && customModuleId == null) { + throw IllegalStateException("customModuleId not defined but module is CUSTOM") + } + } + fun toSignedValues(): ValuesToSign<*> { return ValuesToSign( headers = headers.toSignedHeaders(), diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index 416514e..dc164b1 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -20,6 +20,7 @@ import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.models.ocpi.ModuleID @Service class AsyncTaskService(private val registryService: RegistryService) { @@ -34,7 +35,11 @@ class AsyncTaskService(private val registryService: RegistryService) { */ @Async fun forwardOcpiRequestToLinkedApps(requestHandler: OcpiRequestHandler<*>, fromLocalPlatform: Boolean = true) { - if (fromLocalPlatform) { + // we only want to forward to apps if the module is one of the default OCPI modules, + // and only if the sender is a local platform (to avoid repeat forwarding on the recipient node) + val isDefaultModule = requestHandler.request.module != ModuleID.CUSTOM + + if (isDefaultModule && fromLocalPlatform) { val request = requestHandler.request registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 40fdf72..fb58ccc 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -77,8 +77,9 @@ class RoutingService(private val platformRepo: PlatformRepository, /** * get OCPI platform endpoint information using platform ID (from above) */ - fun getPlatformEndpoint(platformID: Long?, module: ModuleID, interfaceRole: InterfaceRole): EndpointEntity { - return endpointRepo.findByPlatformIDAndIdentifierAndRole(platformID, module.id, interfaceRole) + fun getPlatformEndpoint(platformID: Long?, module: ModuleID, interfaceRole: InterfaceRole, customModuleId: String? = null): EndpointEntity { + val moduleId = if (customModuleId !== null) { customModuleId } else { module.id } + return endpointRepo.findByPlatformIDAndIdentifierAndRole(platformID, moduleId, interfaceRole) ?: throw OcpiClientInvalidParametersException("Receiver does not support the requested module") } @@ -165,7 +166,7 @@ class RoutingService(private val platformRepo: PlatformRepository, // return standard OCPI module URL of recipient else -> { - val endpoint = getPlatformEndpoint(platformID, request.module, request.interfaceRole) + val endpoint = getPlatformEndpoint(platformID, request.module, request.interfaceRole, request.customModuleId) // could replace with request.resolveModuleId()... urlJoin(endpoint.url, request.urlPathVariables) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt b/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt index 628ab5d..4db7f19 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt @@ -40,3 +40,12 @@ fun Map.filterNull(): Map { fun String.checksum(): String { return Keys.toChecksumAddress(this) } + +fun String.toQueryMap(): Map { + val queryMap = mutableMapOf() + split("&").forEach { + val (key, value) = it.split("=") + queryMap[key] = value + } + return queryMap +} \ No newline at end of file From 760d55d03dc83430a051d2af38d1cd41b46890a5 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Fri, 5 Jun 2020 10:44:42 +0200 Subject: [PATCH 19/26] custom modules controller test; further custom module id integration; rename ocpi request variable properties Signed-off-by: Adam Staveley --- .../node/components/OcpiRequestHandler.kt | 2 +- .../node/components/OcpiResponseHandler.kt | 2 +- .../controllers/ocpi/VersionsController.kt | 42 ++++++++------- .../controllers/ocpi/v2_2/CdrsController.kt | 6 +-- .../ocpi/v2_2/ChargingProfilesController.kt | 18 +++---- .../ocpi/v2_2/CommandsController.kt | 12 ++--- .../ocpi/v2_2/CustomModulesController.kt | 12 ++--- .../ocpi/v2_2/LocationsController.kt | 28 +++++----- .../ocpi/v2_2/SessionsController.kt | 12 ++--- .../ocpi/v2_2/TariffsController.kt | 10 ++-- .../controllers/ocpi/v2_2/TokensController.kt | 20 +++---- .../node/models/ocpi/Common.kt | 15 ++++-- .../node/services/HttpService.kt | 2 +- .../node/services/HubClientInfoService.kt | 2 +- .../node/services/RoutingService.kt | 17 +++--- .../node/tools/Extensions.kt | 9 ---- .../controllers/ocn/MessageControllerTest.kt | 2 +- .../ocpi/VersionsControllerTest.kt | 2 +- .../ocpi/v2_2/CdrsControllerTest.kt | 10 ++-- .../v2_2/ChargingProfilesControllerTest.kt | 14 ++--- .../ocpi/v2_2/CommandsControllerTest.kt | 12 ++--- .../ocpi/v2_2/CustomModulesControllerTest.kt | 53 +++++++++++++++---- .../ocpi/v2_2/HubClientInfoControllerTest.kt | 2 +- .../ocpi/v2_2/LocationsControllerTest.kt | 30 +++++------ .../ocpi/v2_2/SessionsControllerTest.kt | 14 ++--- .../ocpi/v2_2/TariffsControllerTest.kt | 12 ++--- .../ocpi/v2_2/TokensControllerTest.kt | 20 +++---- .../node/services/RoutingServiceTest.kt | 18 +++---- 28 files changed, 216 insertions(+), 182 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt index 08c840c..577e28b 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -139,7 +139,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, fun forwardAsync(responseUrl: String, modifyRequest: (newResponseUrl: String) -> OcpiRequestVariables): OcpiResponseHandler { assertSenderValid() - val proxyPath = "/ocpi/sender/2.2/${request.module.id}/${request.urlPathVariables}" + val proxyPath = "/ocpi/sender/2.2/${request.module.id}/${request.urlPath}" val rewriteFields = mapOf("$['body']['response_url']" to responseUrl) val response: HttpResponse = when (routingService.getReceiverType(request.headers.receiver)) { diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt index e75fc33..f1df55e 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiResponseHandler.kt @@ -101,7 +101,7 @@ class OcpiResponseHandler(request: OcpiRequestVariables, fun getResponseWithPaginationHeaders(): ResponseEntity> { return when (isOcpiSuccess()) { true -> { - request.urlPathVariables?.let { + request.urlPath?.let { routingService.deleteProxyResource(it) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt index 8183d55..fec86c3 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt @@ -54,7 +54,7 @@ class VersionsController(private val repository: PlatformRepository, fun getVersionsDetail(@RequestHeader("Authorization") authorization: String): OcpiResponse { val token = authorization.extractToken() - val endpoints = this.getEndpoints() + val endpoints = this.getAllEndpoints() val response = OcpiResponse( OcpiStatus.SUCCESS.code, data = VersionDetail("2.2", endpoints)) @@ -66,37 +66,39 @@ class VersionsController(private val repository: PlatformRepository, } } + private fun getModuleEndpoints(module: ModuleID): List { + return InterfaceRole.values().map { + val paths = if (module == ModuleID.CUSTOM) { + "/ocpi/custom" + } else { + "/ocpi/${it.id}/2.2/${module.id}" + } + Endpoint( + identifier = module.id, + role = it, + url = urlJoin(properties.url, paths) + ) + } + } - private fun getEndpoints(): List { + private fun getAllEndpoints(): List { val endpoints = mutableListOf() + val senderOnlyInterfaces = listOf(ModuleID.CREDENTIALS, ModuleID.HUB_CLIENT_INFO) for (module in ModuleID.values()) { - if (module.id == "hubclientinfo") { - continue - } - - if (module.id == "credentials" /*|| /module.id == "hubclientinfo"*/) { - // these modules have only SENDER endpoint (the broker/hub) - endpoints.add(Endpoint( + if (senderOnlyInterfaces.contains(module)) { + // these modules have only SENDER endpoint (the node/hub) + endpoints.add(Endpoint( identifier = module.id, role = InterfaceRole.SENDER, url = urlJoin(properties.url, "/ocpi/2.2/${module.id}"))) } else { - // remaining modules have both interfaces implemented - endpoints.addAll(listOf( - Endpoint( - identifier = module.id, - role = InterfaceRole.SENDER, - url = urlJoin(properties.url, "/ocpi/sender/2.2/${module.id}")), - Endpoint( - identifier = module.id, - role = InterfaceRole.RECEIVER, - url = urlJoin(properties.url, "/ocpi/receiver/2.2/${module.id}")))) + endpoints.addAll(getModuleEndpoints(module)) } } - // custom module + // add custom OcnRules module endpoint endpoints.add(Endpoint( identifier = "ocnrules", role = InterfaceRole.RECEIVER, diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt index ffdb69f..b29d512 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsController.kt @@ -57,7 +57,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlEncodedParams = params) + queryParams = params) // TODO: all pagination response header links should contain original url-encoded parameters return requestHandlerBuilder @@ -85,7 +85,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid) + urlPath = uid) return requestHandlerBuilder .build>(requestVariables) @@ -117,7 +117,7 @@ class CdrsController(private val requestHandlerBuilder: OcpiRequestHandlerBuilde interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = cdrID) + urlPath = cdrID) return requestHandlerBuilder .build(requestVariables) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt index 05a6246..55a5884 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesController.kt @@ -51,7 +51,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH interfaceRole = InterfaceRole.SENDER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid, + urlPath = uid, body = body) return requestHandlerBuilder @@ -80,7 +80,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH interfaceRole = InterfaceRole.SENDER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = sessionId, + urlPath = sessionId, body = body) return requestHandlerBuilder @@ -115,13 +115,13 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = sessionId, - urlEncodedParams = mapOf("duration" to duration, "response_url" to responseUrl)) + urlPath = sessionId, + queryParams = mapOf("duration" to duration, "response_url" to responseUrl)) return requestHandlerBuilder .build(requestVariables) .forwardAsync(responseUrl) { - requestVariables.copy(urlEncodedParams = mapOf("duration" to duration, "response_url" to it)) + requestVariables.copy(queryParams = mapOf("duration" to duration, "response_url" to it)) } .getResponse() } @@ -146,7 +146,7 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = sessionId, + urlPath = sessionId, body = body) return requestHandlerBuilder @@ -177,13 +177,13 @@ class ChargingProfilesController(private val requestHandlerBuilder: OcpiRequestH interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.DELETE, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = sessionId, - urlEncodedParams = mapOf("response_url" to responseUrl)) + urlPath = sessionId, + queryParams = mapOf("response_url" to responseUrl)) return requestHandlerBuilder .build(requestVariables) .forwardAsync(responseUrl) { - requestVariables.copy(urlEncodedParams = mapOf("response_url" to it)) + requestVariables.copy(queryParams = mapOf("response_url" to it)) } .getResponse() } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt index 51b2535..942167c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsController.kt @@ -52,7 +52,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.SENDER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid, + urlPath = uid, body = body) return requestHandlerBuilder @@ -86,7 +86,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "CANCEL_RESERVATION", + urlPath = "CANCEL_RESERVATION", body = body) return requestHandlerBuilder @@ -117,7 +117,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "RESERVE_NOW", + urlPath = "RESERVE_NOW", body = body) return requestHandlerBuilder @@ -148,7 +148,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "START_SESSION", + urlPath = "START_SESSION", body = body) return requestHandlerBuilder @@ -179,7 +179,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "STOP_SESSION", + urlPath = "STOP_SESSION", body = body) return requestHandlerBuilder @@ -210,7 +210,7 @@ class CommandsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "UNLOCK_CONNECTOR", + urlPath = "UNLOCK_CONNECTOR", body = body) return requestHandlerBuilder diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt index 5084867..c79a18c 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt @@ -6,7 +6,6 @@ import org.springframework.web.bind.annotation.* import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder import snc.openchargingnetwork.node.models.OcnHeaders import snc.openchargingnetwork.node.models.ocpi.* -import snc.openchargingnetwork.node.tools.toQueryMap import javax.servlet.http.HttpServletRequest @RestController @@ -24,13 +23,14 @@ class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHand @RequestHeader("OCPI-to-party-id") toPartyID: String, @PathVariable module: String, @PathVariable interfaceRole: String, + @RequestParam params: Map, @RequestBody body: String?, request: HttpServletRequest): ResponseEntity> { - val sender = BasicRole(fromPartyID, fromCountryCode).toUpperCase() - val receiver = BasicRole(toPartyID, toCountryCode).toUpperCase() + val sender = BasicRole(fromPartyID, fromCountryCode) + val receiver = BasicRole(toPartyID, toCountryCode) - val pathWildcards = request.pathInfo.replace("/ocpi/custom/${module}/${interfaceRole}/", "") + val pathWildcards = request.pathInfo.replace("/ocpi/custom/${module}/${interfaceRole}", "") val requestVariables = OcpiRequestVariables( module = ModuleID.CUSTOM, @@ -38,8 +38,8 @@ class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHand interfaceRole = InterfaceRole.resolve(interfaceRole), method = HttpMethod.valueOf(request.method), headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = pathWildcards, - urlEncodedParams = request.queryString.toQueryMap(), + urlPath = pathWildcards, + queryParams = params, body = body) return requestHandlerBuilder diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt index e89aa20..738bc17 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsController.kt @@ -57,7 +57,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlEncodedParams = params) + queryParams = params) return requestHandlerBuilder .build>(requestVariables) @@ -84,7 +84,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid) + urlPath = uid) return requestHandlerBuilder .build>(requestVariables) @@ -111,7 +111,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = locationID) + urlPath = locationID) return requestHandlerBuilder .build(requestVariables) @@ -139,7 +139,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$locationID/$evseUID") + urlPath = "/$locationID/$evseUID") return requestHandlerBuilder .build(requestVariables) @@ -168,7 +168,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$locationID/$evseUID/$connectorID") + urlPath = "/$locationID/$evseUID/$connectorID") return requestHandlerBuilder .build(requestVariables) @@ -202,7 +202,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID") + urlPath = "/$countryCode/$partyID/$locationID") return requestHandlerBuilder .build(requestVariables) @@ -232,7 +232,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID") + urlPath = "/$countryCode/$partyID/$locationID/$evseUID") return requestHandlerBuilder .build(requestVariables) @@ -263,7 +263,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID") + urlPath = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID") return requestHandlerBuilder .build(requestVariables) @@ -293,7 +293,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID", + urlPath = "/$countryCode/$partyID/$locationID", body = body) return requestHandlerBuilder @@ -325,7 +325,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID", + urlPath = "/$countryCode/$partyID/$locationID/$evseUID", body = body) return requestHandlerBuilder @@ -358,7 +358,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", + urlPath = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", body = body) return requestHandlerBuilder @@ -389,7 +389,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PATCH, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID", + urlPath = "/$countryCode/$partyID/$locationID", body = body) return requestHandlerBuilder @@ -421,7 +421,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PATCH, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID", + urlPath = "/$countryCode/$partyID/$locationID/$evseUID", body = body) return requestHandlerBuilder @@ -454,7 +454,7 @@ class LocationsController(private val requestHandlerBuilder: OcpiRequestHandlerB interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PATCH, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", + urlPath = "/$countryCode/$partyID/$locationID/$evseUID/$connectorID", body = body) return requestHandlerBuilder diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt index 82dcf2d..46e8b1b 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsController.kt @@ -57,7 +57,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlEncodedParams = params) + queryParams = params) return requestHandlerBuilder .build>(requestVariables) @@ -84,7 +84,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid) + urlPath = uid) return requestHandlerBuilder @@ -113,7 +113,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.SENDER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$sessionID/charging_preferences", + urlPath = "/$sessionID/charging_preferences", body = body) return requestHandlerBuilder @@ -148,7 +148,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$sessionID") + urlPath = "/$countryCode/$partyID/$sessionID") return requestHandlerBuilder .build(requestVariables) @@ -178,7 +178,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$sessionID", + urlPath = "/$countryCode/$partyID/$sessionID", body = body) return requestHandlerBuilder @@ -209,7 +209,7 @@ class SessionsController(private val requestHandlerBuilder: OcpiRequestHandlerBu interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PATCH, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$sessionID", + urlPath = "/$countryCode/$partyID/$sessionID", body = body) return requestHandlerBuilder diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt index 7e25c91..48ff911 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsController.kt @@ -57,7 +57,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlEncodedParams = params) + queryParams = params) return requestHandlerBuilder .build>(requestVariables) @@ -84,7 +84,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid) + urlPath = uid) return requestHandlerBuilder .build>(requestVariables) @@ -118,7 +118,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$tariffID") + urlPath = "/$countryCode/$partyID/$tariffID") return requestHandlerBuilder .build(requestVariables) @@ -149,7 +149,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$tariffID", + urlPath = "/$countryCode/$partyID/$tariffID", body = body) return requestHandlerBuilder @@ -180,7 +180,7 @@ class TariffsController(private val requestHandlerBuilder: OcpiRequestHandlerBui interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.DELETE, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$tariffID") + urlPath = "/$countryCode/$partyID/$tariffID") return requestHandlerBuilder .build(requestVariables) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt index a77f270..84addc4 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensController.kt @@ -56,7 +56,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlEncodedParams = params) + queryParams = params) return requestHandlerBuilder .build>(requestVariables) @@ -83,7 +83,7 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil interfaceRole = InterfaceRole.SENDER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = uid) + urlPath = uid) return requestHandlerBuilder .build>(requestVariables) @@ -113,8 +113,8 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil interfaceRole = InterfaceRole.SENDER, method = HttpMethod.POST, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "$tokenUID/authorize", - urlEncodedParams = mapOf("type" to type), + urlPath = "$tokenUID/authorize", + queryParams = mapOf("type" to type), body = body) return requestHandlerBuilder @@ -150,8 +150,8 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.GET, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$tokenUID", - urlEncodedParams = mapOf("type" to type)) + urlPath = "/$countryCode/$partyID/$tokenUID", + queryParams = mapOf("type" to type)) return requestHandlerBuilder .build(requestVariables) @@ -182,8 +182,8 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PUT, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$tokenUID", - urlEncodedParams = mapOf("type" to type), + urlPath = "/$countryCode/$partyID/$tokenUID", + queryParams = mapOf("type" to type), body = body) return requestHandlerBuilder @@ -215,8 +215,8 @@ class TokensController(private val requestHandlerBuilder: OcpiRequestHandlerBuil interfaceRole = InterfaceRole.RECEIVER, method = HttpMethod.PATCH, headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPathVariables = "/$countryCode/$partyID/$tokenUID", - urlEncodedParams = mapOf("type" to type), + urlPath = "/$countryCode/$partyID/$tokenUID", + queryParams = mapOf("type" to type), body = body) return requestHandlerBuilder diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt index bf5c770..2c467a6 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/ocpi/Common.kt @@ -55,8 +55,8 @@ data class OcpiRequestVariables(@JsonProperty("module") val module: ModuleID, @JsonProperty("interface_role") val interfaceRole: InterfaceRole, @JsonProperty("method") val method: HttpMethod, @JsonProperty("headers") val headers: OcnHeaders, - @JsonProperty("url_path_variables") val urlPathVariables: String? = null, - @JsonProperty("url_encoded_params") val urlEncodedParams: Map? = null, + @JsonProperty("url_path") val urlPath: String? = null, + @JsonProperty("query_params") val queryParams: Map? = null, @JsonProperty("proxy_uid") val proxyUID: String? = null, @JsonProperty("proxy_resource") val proxyResource: String? = null, @JsonProperty("custom_module_id") val customModuleId: String? = null, @@ -74,9 +74,18 @@ data class OcpiRequestVariables(@JsonProperty("module") val module: ModuleID, fun toSignedValues(): ValuesToSign<*> { return ValuesToSign( headers = headers.toSignedHeaders(), - params = urlEncodedParams, + params = queryParams, body = body) + } + + fun resolveModuleId(): String { + return if (module != ModuleID.CUSTOM) { + module.id + } else { + customModuleId!! } + } + } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt index 8f2b750..76e017e 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/HttpService.kt @@ -84,7 +84,7 @@ class HttpService { method = requestVariables.method, url = url, headers = headersMap, - params = requestVariables.urlEncodedParams, + params = requestVariables.queryParams, json = jsonBody) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt index 194dbd0..3873f1a 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/HubClientInfoService.kt @@ -168,7 +168,7 @@ class HubClientInfoService(private val platformRepo: PlatformRepository, sender = sender, receiver = receiver), body = changedClientInfo, - urlPathVariables = "${changedClientInfo.countryCode}/${changedClientInfo.partyID}") + urlPath = "${changedClientInfo.countryCode}/${changedClientInfo.partyID}") val (url, headers) = routingService.prepareLocalPlatformRequest(requestVariables, proxied = false) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index fb58ccc..2442233 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -77,9 +77,8 @@ class RoutingService(private val platformRepo: PlatformRepository, /** * get OCPI platform endpoint information using platform ID (from above) */ - fun getPlatformEndpoint(platformID: Long?, module: ModuleID, interfaceRole: InterfaceRole, customModuleId: String? = null): EndpointEntity { - val moduleId = if (customModuleId !== null) { customModuleId } else { module.id } - return endpointRepo.findByPlatformIDAndIdentifierAndRole(platformID, moduleId, interfaceRole) + fun getPlatformEndpoint(platformID: Long?, moduleID: String, interfaceRole: InterfaceRole): EndpointEntity { + return endpointRepo.findByPlatformIDAndIdentifierAndRole(platformID, moduleID, interfaceRole) ?: throw OcpiClientInvalidParametersException("Receiver does not support the requested module") } @@ -146,7 +145,7 @@ class RoutingService(private val platformRepo: PlatformRepository, // local sender is requesting a resource/url via a proxy // returns the resource behind the proxy - proxied -> getProxyResource(request.urlPathVariables, request.headers.sender, request.headers.receiver) + proxied -> getProxyResource(request.urlPath, request.headers.sender, request.headers.receiver) // remote sender is requesting a resource/url via a proxy // return the proxied resource as defined by the sender @@ -160,14 +159,14 @@ class RoutingService(private val platformRepo: PlatformRepository, sender = request.headers.receiver, receiver = request.headers.sender, alternativeUID = request.proxyUID) - val endpoint = getPlatformEndpoint(platformID, request.module, request.interfaceRole) - urlJoin(endpoint.url, request.urlPathVariables) + val endpoint = getPlatformEndpoint(platformID, request.resolveModuleId(), request.interfaceRole) + urlJoin(endpoint.url, request.urlPath) } // return standard OCPI module URL of recipient else -> { - val endpoint = getPlatformEndpoint(platformID, request.module, request.interfaceRole, request.customModuleId) // could replace with request.resolveModuleId()... - urlJoin(endpoint.url, request.urlPathVariables) + val endpoint = getPlatformEndpoint(platformID, request.resolveModuleId(), request.interfaceRole) // could replace with request.resolveModuleId()... + urlJoin(endpoint.url, request.urlPath) } } @@ -196,7 +195,7 @@ class RoutingService(private val platformRepo: PlatformRepository, // if proxied request, add the proxy resource to the body if (proxied) { modifiedBody = modifiedBody.run { - copy(proxyResource = getProxyResource(urlPathVariables, headers.sender, headers.receiver)) + copy(proxyResource = getProxyResource(urlPath, headers.sender, headers.receiver)) } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt b/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt index 4db7f19..628ab5d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/tools/Extensions.kt @@ -40,12 +40,3 @@ fun Map.filterNull(): Map { fun String.checksum(): String { return Keys.toChecksumAddress(this) } - -fun String.toQueryMap(): Map { - val queryMap = mutableMapOf() - split("&").forEach { - val (key, value) = it.split("=") - queryMap[key] = value - } - return queryMap -} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt index a973538..6f37fde 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageControllerTest.kt @@ -37,7 +37,7 @@ class MessageControllerTest(@Autowired val mockMvc: MockMvc) { method = HttpMethod.GET, module = ModuleID.LOCATIONS, interfaceRole = InterfaceRole.SENDER, - urlPathVariables = "LOC2", + urlPath = "LOC2", headers = OcnHeaders( authorization = "", requestID = "123", diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsControllerTest.kt index 92828ed..c6a0587 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsControllerTest.kt @@ -54,7 +54,7 @@ class VersionsControllerTest(@Autowired val mockMvc: MockMvc) { .andExpect(jsonPath("\$.status_message").doesNotExist()) .andExpect(jsonPath("\$.timestamp").isString) .andExpect(jsonPath("\$.data.version").value("2.2")) - .andExpect(jsonPath("\$.data.endpoints", hasSize>(16))) + .andExpect(jsonPath("\$.data.endpoints", hasSize>(19))) .andExpect(jsonPath("\$.data.endpoints[0].identifier").value("cdrs")) .andExpect(jsonPath("\$.data.endpoints[0].role").value("SENDER")) .andExpect(jsonPath("\$.data.endpoints[0].url").value("https://broker.provider.com/ocpi/sender/2.2/cdrs")) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt index 9d2cd36..f39fe82 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CdrsControllerTest.kt @@ -49,7 +49,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlEncodedParams = mapOf("limit" to 100)) + queryParams = mapOf("limit" to 100)) val mockRequestHandler = mockk>>() @@ -102,7 +102,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "67") + urlPath = "67") val mockRequestHandler = mockk>>() @@ -117,7 +117,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleCDR))) - mockMvc.perform(get("/ocpi/sender/2.2/cdrs/page/${requestVariables.urlPathVariables}") + mockMvc.perform(get("/ocpi/sender/2.2/cdrs/page/${requestVariables.urlPath}") .header("Authorization", "Token token-c") .header("X-Request-ID", requestVariables.headers.requestID) .header("X-Correlation-ID", requestVariables.headers.correlationID) @@ -155,7 +155,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "6534") + urlPath = "6534") val mockRequestHandler = mockk>() @@ -165,7 +165,7 @@ class CdrsControllerTest(@Autowired val mockMvc: MockMvc) { .status(200) .body(OcpiResponse(statusCode = 1000, data = exampleCDR)) - mockMvc.perform(get("/ocpi/receiver/2.2/cdrs/${requestVariables.urlPathVariables}") + mockMvc.perform(get("/ocpi/receiver/2.2/cdrs/${requestVariables.urlPath}") .header("Authorization", "Token token-c") .header("X-Request-ID", requestVariables.headers.requestID) .header("X-Correlation-ID", requestVariables.headers.correlationID) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt index d446861..29f1ab6 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/ChargingProfilesControllerTest.kt @@ -40,7 +40,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = "666", sender = BasicRole("SNC", "CH"), receiver = BasicRole("ABC", "DE")), - urlPathVariables = "12345", + urlPath = "12345", body = GenericChargingProfileResult(result = ChargingProfileResultType.ACCEPTED)) val requestHandler: OcpiRequestHandler = mockk() @@ -78,7 +78,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = "666", sender = BasicRole("SNC", "CH"), receiver = BasicRole("ABC", "DE")), - urlPathVariables = "1234567890", + urlPath = "1234567890", body = ActiveChargingProfile( startDateTime = getTimestamp(), chargingProfile = ChargingProfile( @@ -124,8 +124,8 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = "666", sender = BasicRole("SNC", "CH"), receiver = BasicRole("ABC", "DE")), - urlPathVariables = "0987654321", - urlEncodedParams = mapOf("duration" to 30, "response_url" to "https://server.com/profiles/1")) + urlPath = "0987654321", + queryParams = mapOf("duration" to 30, "response_url" to "https://server.com/profiles/1")) val requestHandler: OcpiRequestHandler = mockk() @@ -167,7 +167,7 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = "666", sender = BasicRole("SNC", "CH"), receiver = BasicRole("ABC", "DE")), - urlPathVariables = "0102030405", + urlPath = "0102030405", body = SetChargingProfile( chargingProfile = ChargingProfile(startDateTime = getTimestamp(), chargingRateUnit = ChargingRateUnit.W), responseUrl = "https://smart.charging.net/profiles/0102030405" @@ -213,8 +213,8 @@ class ChargingProfilesControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = "666", sender = BasicRole("SNC", "CH"), receiver = BasicRole("ABC", "DE")), - urlPathVariables = "333666999", - urlEncodedParams = mapOf("response_url" to "https://scsp.io/ocpi/callback/369")) + urlPath = "333666999", + queryParams = mapOf("response_url" to "https://scsp.io/ocpi/callback/369")) val requestHandler: OcpiRequestHandler = mockk() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt index e97e355..397ec44 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CommandsControllerTest.kt @@ -48,7 +48,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = uid, + urlPath = uid, body = body) val mockRequestHandler = mockk>() @@ -95,7 +95,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "CANCEL_RESERVATION", + urlPath = "CANCEL_RESERVATION", body = body) val mockRequestHandler = mockk>() @@ -150,7 +150,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "RESERVE_NOW", + urlPath = "RESERVE_NOW", body = body) val mockRequestHandler = mockk>() @@ -203,7 +203,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "START_SESSION", + urlPath = "START_SESSION", body = body) val mockRequestHandler = mockk>() @@ -255,7 +255,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "STOP_SESSION", + urlPath = "STOP_SESSION", body = body) val mockRequestHandler = mockk>() @@ -309,7 +309,7 @@ class CommandsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "UNLOCK_CONNECTOR", + urlPath = "UNLOCK_CONNECTOR", body = body) val mockRequestHandler = mockk>() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt index 165346d..e71de11 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt @@ -1,28 +1,61 @@ package snc.openchargingnetwork.node.controllers.ocpi.v2_2 +import com.ninjasquad.springmockk.MockkBean +import io.mockk.every +import io.mockk.mockk import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest +import org.springframework.http.HttpMethod +import org.springframework.http.ResponseEntity import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status +import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.components.OcpiRequestHandlerBuilder +import snc.openchargingnetwork.node.models.OcnHeaders +import snc.openchargingnetwork.node.models.ocpi.* @WebMvcTest(CustomModulesController::class) class CustomModulesControllerTest(@Autowired val mockMvc: MockMvc) { + @MockkBean + lateinit var requestHandlerBuilder: OcpiRequestHandlerBuilder + @Test fun providesCustomModuleEndpoint() { - mockMvc.perform(get("/ocpi/custom/my-module/sender/path/1/2/3/5/6/7?hello=world&foo=bar") - .header("authorization", "Token abc123") - .header("x-request-id", "1") - .header("x-correlation-id", "1") - .header("ocpi-from-country-code", "de") - .header("ocpi-from-party-id", "abc") - .header("ocpi-to-country-code", "gb") - .header("ocpi-to-party-id", "lon") + val request = OcpiRequestVariables( + module = ModuleID.CUSTOM, + customModuleId = "my-module", + interfaceRole = InterfaceRole.SENDER, + method = HttpMethod.POST, + headers = OcnHeaders("Token token", null, "1", "1", + BasicRole("abc", "de"), BasicRole("lon", "gb")), + urlPath = "/path/1/2/3/4/5/6/7", + queryParams = mapOf("hello" to "world", "foo" to "bar"), + body = "{\"foo\": \"bar\"}") + + val requestHandler: OcpiRequestHandler = mockk() + + every { requestHandlerBuilder.build(request) } returns requestHandler + every { requestHandler.forwardDefault().getResponseWithAllHeaders() } returns ResponseEntity + .ok(OcpiResponse(statusCode = 1000)) + + mockMvc.perform(post("/ocpi/custom/${request.customModuleId}/${request.interfaceRole.id}${request.urlPath}") + .queryParam("hello", "world") + .queryParam("foo", "bar") + .header("authorization", request.headers.authorization) + .header("x-request-id", request.headers.requestID) + .header("x-correlation-id", request.headers.correlationID) + .header("ocpi-from-country-code", request.headers.sender.country) + .header("ocpi-from-party-id", request.headers.sender.id) + .header("ocpi-to-country-code", request.headers.receiver.country) + .header("ocpi-to-party-id", request.headers.receiver.id) .contentType("application/json") - .content("{\"foo\": \"bar\"}")) + .content(request.body.toString())) .andExpect(status().isOk) + .andExpect(jsonPath("\$.status_code").value(1000)) } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt index 799c2ac..aae0dd8 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/HubClientInfoControllerTest.kt @@ -48,7 +48,7 @@ class HubClientInfoControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlEncodedParams = mapOf("date_from" to dateFrom)) + queryParams = mapOf("date_from" to dateFrom)) every { hubClientInfoService.getList(requestVariables.headers.authorization) } returns listOf(exampleClientInfo) every { routingService.checkSenderKnown(requestVariables.headers.authorization, sender) } just Runs diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt index 6d4635b..58a0035 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/LocationsControllerTest.kt @@ -52,7 +52,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlEncodedParams = mapOf("date_from" to dateFrom)) + queryParams = mapOf("date_from" to dateFrom)) val mockRequestHandler = mockk>>() @@ -111,7 +111,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "67") + urlPath = "67") val mockRequestHandler = mockk>>() @@ -128,7 +128,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { statusCode = 1000, data = arrayOf(exampleLocation2))) - mockMvc.perform(get("/ocpi/sender/2.2/locations/page/${requestVariables.urlPathVariables}") + mockMvc.perform(get("/ocpi/sender/2.2/locations/page/${requestVariables.urlPath}") .header("Authorization", "Token token-c") .header("X-Request-ID", requestVariables.headers.requestID) .header("X-Correlation-ID", requestVariables.headers.correlationID) @@ -168,7 +168,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = locationID) + urlPath = locationID) val mockRequestHandler = mockk>() @@ -216,7 +216,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/$locationID/$evseUID") + urlPath = "/$locationID/$evseUID") val mockRequestHandler = mockk>() @@ -265,7 +265,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/$locationID/$evseUID/$connectorID") + urlPath = "/$locationID/$evseUID/$connectorID") val mockRequestHandler = mockk>() @@ -312,7 +312,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID") + urlPath = "/${sender.country}/${sender.id}/$locationID") val mockRequestHandler = mockk>() @@ -360,7 +360,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID") + urlPath = "/${sender.country}/${sender.id}/$locationID/$evseUID") val mockRequestHandler = mockk>() @@ -409,7 +409,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID") + urlPath = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID") val mockRequestHandler = mockk>() @@ -457,7 +457,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID", + urlPath = "/${sender.country}/${sender.id}/$locationID", body = exampleLocation2) val mockRequestHandler = mockk>() @@ -506,7 +506,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID", + urlPath = "/${sender.country}/${sender.id}/$locationID/$evseUID", body = body) val mockRequestHandler = mockk>() @@ -556,7 +556,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID", + urlPath = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID", body = body) val mockRequestHandler = mockk>() @@ -604,7 +604,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID", + urlPath = "/${sender.country}/${sender.id}/$locationID", body = body) val mockRequestHandler = mockk>() @@ -654,7 +654,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID", + urlPath = "/${sender.country}/${sender.id}/$locationID/$evseUID", body = body) val mockRequestHandler = mockk>() @@ -705,7 +705,7 @@ class LocationsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID", + urlPath = "/${sender.country}/${sender.id}/$locationID/$evseUID/$connectorID", body = body) val mockRequestHandler = mockk>() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt index eb4904e..879294b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/SessionsControllerTest.kt @@ -51,7 +51,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlEncodedParams = mapOf("date_from" to dateFrom, "limit" to 20)) + queryParams = mapOf("date_from" to dateFrom, "limit" to 20)) val mockRequestHandler = mockk>>() @@ -107,7 +107,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "2247") + urlPath = "2247") val mockRequestHandler = mockk>>() @@ -123,7 +123,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleSession))) - mockMvc.perform(get("/ocpi/sender/2.2/sessions/page/${requestVariables.urlPathVariables}") + mockMvc.perform(get("/ocpi/sender/2.2/sessions/page/${requestVariables.urlPath}") .header("Authorization", "Token token-c") .header("X-Request-ID", requestVariables.headers.requestID) .header("X-Correlation-ID", requestVariables.headers.correlationID) @@ -163,7 +163,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/2247/charging_preferences", + urlPath = "/2247/charging_preferences", body = body) val mockRequestHandler = mockk>() @@ -210,7 +210,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$sessionID") + urlPath = "/${sender.country}/${sender.id}/$sessionID") val mockRequestHandler = mockk>() @@ -256,7 +256,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$sessionID", + urlPath = "/${sender.country}/${sender.id}/$sessionID", body = body) val mockRequestHandler = mockk>() @@ -304,7 +304,7 @@ class SessionsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$sessionID", + urlPath = "/${sender.country}/${sender.id}/$sessionID", body = body) val mockRequestHandler = mockk>() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt index 4ca3917..f75ccd1 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TariffsControllerTest.kt @@ -48,7 +48,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlEncodedParams = mapOf("limit" to 10)) + queryParams = mapOf("limit" to 10)) val mockRequestHandler = mockk>>() @@ -103,7 +103,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "39") + urlPath = "39") val mockRequestHandler = mockk>>() @@ -118,7 +118,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { .headers(responseHeaders) .body(OcpiResponse(statusCode = 1000, data = arrayOf(exampleTariff))) - mockMvc.perform(get("/ocpi/sender/2.2/tariffs/page/${requestVariables.urlPathVariables}") + mockMvc.perform(get("/ocpi/sender/2.2/tariffs/page/${requestVariables.urlPath}") .header("Authorization", "Token token-c") .header("X-Request-ID", requestVariables.headers.requestID) .header("X-Correlation-ID", requestVariables.headers.correlationID) @@ -158,7 +158,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$tariffID") + urlPath = "/${sender.country}/${sender.id}/$tariffID") val mockRequestHandler = mockk>() @@ -203,7 +203,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$tariffID", + urlPath = "/${sender.country}/${sender.id}/$tariffID", body = exampleTariff) val mockRequestHandler = mockk>() @@ -250,7 +250,7 @@ class TariffsControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$tariffID") + urlPath = "/${sender.country}/${sender.id}/$tariffID") val mockRequestHandler = mockk>() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt index 85639da..55f515e 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/TokensControllerTest.kt @@ -46,7 +46,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlEncodedParams = mapOf("limit" to 50)) + queryParams = mapOf("limit" to 50)) val mockRequestHandler = mockk>>() @@ -104,7 +104,7 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = uid) + urlPath = uid) val mockRequestHandler = mockk>>() @@ -160,8 +160,8 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "1234567890/authorize", - urlEncodedParams = mapOf("type" to TokenType.RFID), + urlPath = "1234567890/authorize", + queryParams = mapOf("type" to TokenType.RFID), body = body) val mockRequestHandler = mockk>() @@ -212,8 +212,8 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$tokenUID", - urlEncodedParams = mapOf("type" to TokenType.RFID)) + urlPath = "/${sender.country}/${sender.id}/$tokenUID", + queryParams = mapOf("type" to TokenType.RFID)) val mockRequestHandler = mockk>() @@ -259,8 +259,8 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$tokenUID", - urlEncodedParams = mapOf("type" to TokenType.APP_USER), + urlPath = "/${sender.country}/${sender.id}/$tokenUID", + queryParams = mapOf("type" to TokenType.APP_USER), body = exampleToken) val mockRequestHandler = mockk>() @@ -311,8 +311,8 @@ class TokensControllerTest(@Autowired val mockMvc: MockMvc) { correlationID = generateUUIDv4Token(), sender = sender, receiver = receiver), - urlPathVariables = "/${sender.country}/${sender.id}/$tokenUID", - urlEncodedParams = mapOf("type" to TokenType.APP_USER), + urlPath = "/${sender.country}/${sender.id}/$tokenUID", + queryParams = mapOf("type" to TokenType.APP_USER), body = body) val mockRequestHandler = mockk>() diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt index 2ffe5ca..e55c584 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RoutingServiceTest.kt @@ -55,8 +55,8 @@ class RoutingServiceTest { correlationID = generateUUIDv4Token(), sender = BasicRole("SNC", "DE"), receiver = BasicRole("ABC", "CH")), - urlPathVariables = "DE/SNC/abc123", - urlEncodedParams = mapOf("type" to TokenType.APP_USER)) + urlPath = "DE/SNC/abc123", + queryParams = mapOf("type" to TokenType.APP_USER)) every { roleRepo.findAllByCountryCodeAndPartyIDAllIgnoreCase("CH", "ABC") } returns listOf( RoleEntity( @@ -67,7 +67,7 @@ class RoutingServiceTest { partyID = "CH")) every { routingService.getPlatformEndpoint( platformID = 6L, - module = request.module, + moduleID = request.module.id, interfaceRole = request.interfaceRole) } returns EndpointEntity( platformID = 6L, @@ -104,7 +104,7 @@ class RoutingServiceTest { correlationID = generateUUIDv4Token(), sender = BasicRole("SNC", "DE"), receiver = BasicRole("ABC", "CH")), - urlPathVariables = "67") + urlPath = "67") every { roleRepo.findAllByCountryCodeAndPartyIDAllIgnoreCase("CH", "ABC") } returns listOf( RoleEntity( @@ -148,8 +148,8 @@ class RoutingServiceTest { correlationID = generateUUIDv4Token(), sender = BasicRole("SNC", "DE"), receiver = BasicRole("ABC", "CH")), - urlPathVariables = "DE/SNC/abc123", - urlEncodedParams = mapOf("type" to TokenType.APP_USER)) + urlPath = "DE/SNC/abc123", + queryParams = mapOf("type" to TokenType.APP_USER)) val sig = "0x9955af11969a2d2a7f860cb00e6a00cfa7c581f5df2dbe8ea16700b33f4b4b9" + "b69f945012f7ea7d3febf11eb1b78e1adc2d1c14c2cf48b25000938cc1860c83e01" @@ -183,7 +183,7 @@ class RoutingServiceTest { correlationID = generateUUIDv4Token(), sender = BasicRole("SNC", "DE"), receiver = BasicRole("ABC", "CH")), - urlPathVariables = "45") + urlPath = "45") val sig = "0x9955af11969a2d2a7f860cb00e6a00cfa7c581f5df2dbe8ea16700b33f4b4b9" + "b69f945012f7ea7d3febf11eb1b78e1adc2d1c14c2cf48b25000938cc1860c83e01" @@ -226,7 +226,7 @@ class RoutingServiceTest { correlationID = "456", sender = BasicRole("EMY", "DE"), receiver = BasicRole("ING", "DE")), - urlPathVariables = "CANCEL_RESERVATION", + urlPath = "CANCEL_RESERVATION", proxyUID = "128", proxyResource = body.responseURL, body = body) @@ -309,7 +309,7 @@ class RoutingServiceTest { Role = InterfaceRole.SENDER) } returns endpoint assertThat(routingService.getPlatformEndpoint( platformID = endpoint.platformID, - module = ModuleID.TOKENS, + moduleID = ModuleID.TOKENS.id, interfaceRole = InterfaceRole.SENDER).url).isEqualTo(endpoint.url) } From aa550a5c91cf4373988bb62d61aacb834dd17848 Mon Sep 17 00:00:00 2001 From: Arzon Barua Date: Fri, 5 Jun 2020 09:33:19 +0000 Subject: [PATCH 20/26] Merged in feature/app-give-permissions (pull request #29) give the permission of 4-17 Signed-off-by: Arzon Approved-by: Adam Staveley --- .../openchargingnetwork/node/models/Ocn.kt | 17 ++- .../openchargingnetwork/node/models/Ocn.kt | 112 ++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index 0737e5c..ace0193 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -104,7 +104,22 @@ data class BasicRequestType(val moduleID: ModuleID, val interfaceRole: Interface enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) { FORWARD_ALL({true}), FORWARD_ALL_SENDER({it.interfaceRole == InterfaceRole.SENDER}), - FORWARD_ALL_RECEIVER({it.interfaceRole == InterfaceRole.RECEIVER}); + FORWARD_ALL_RECEIVER({it.interfaceRole == InterfaceRole.RECEIVER}), + FORWARD_MODULE_LOCATIONS_SENDER({it.moduleID == ModuleID.LOCATIONS && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_LOCATIONS_RECEIVER({it.moduleID == ModuleID.LOCATIONS && it.interfaceRole == InterfaceRole.RECEIVER}), + FORWARD_MODULE_SESSIONS_SENDER({it.moduleID == ModuleID.SESSIONS && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_SESSIONS_RECEIVER({it.moduleID == ModuleID.SESSIONS && it.interfaceRole == InterfaceRole.RECEIVER }), + FORWARD_MODULE_CDRS_SENDER({it.moduleID == ModuleID.CDRS && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_CDRS_RECEIVER({it.moduleID == ModuleID.CDRS && it.interfaceRole == InterfaceRole.RECEIVER}), + FORWARD_MODULE_TARIFFS_SENDER({it.moduleID == ModuleID.TARIFFS && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_TARIFFS_RECEIVER({it.moduleID == ModuleID.TARIFFS && it.interfaceRole == InterfaceRole.RECEIVER}), + FORWARD_MODULE_TOKENS_SENDER({it.moduleID == ModuleID.TOKENS && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_TOKENS_RECEIVER({it.moduleID == ModuleID.TOKENS && it.interfaceRole == InterfaceRole.RECEIVER}), + FORWARD_MODULE_COMMANDS_SENDER({it.moduleID == ModuleID.COMMANDS && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_COMMANDS_RECEIVER({it.moduleID == ModuleID.COMMANDS && it.interfaceRole == InterfaceRole.RECEIVER}), + FORWARD_MODULE_CHARGINGPROFILES_SENDER({it.moduleID == ModuleID.CHARGING_PROFILES && it.interfaceRole == InterfaceRole.SENDER}), + FORWARD_MODULE_CHARGINGPROFILES_RECEIVER({it.moduleID == ModuleID.CHARGING_PROFILES && it.interfaceRole == InterfaceRole.RECEIVER}); + companion object { fun getByIndex(index: BigInteger): OcnAppPermission? { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index 21ecaf1..0962e20 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -33,6 +33,118 @@ class OcnAppPermissionsTest { Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), permission = OcnAppPermission.FORWARD_ALL_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CDRS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_CDRS_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CDRS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_CDRS_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CDRS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_CDRS_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CDRS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_CDRS_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_TOKENS_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_TOKENS_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_TOKENS_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_TOKENS_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_RECEIVER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_SENDER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_SENDER, + expected = false)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.RECEIVER), + permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_RECEIVER, + expected = true)), + Arguments.of(PermissionsMatcherTestCase( + request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.SENDER), + permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_RECEIVER, expected = false)) ) } From 38dea86aff5aa737d9980e553085adb15ffcc81b Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Fri, 5 Jun 2020 17:38:29 +0200 Subject: [PATCH 21/26] integrate custom modules into ocnrules Signed-off-by: Adam Staveley --- .../node/components/OcpiRequestHandler.kt | 2 +- .../node/controllers/ocn/MessageController.kt | 1 - .../ocpi/v2_2/CustomModulesController.kt | 14 +- .../node/models/entities/Entities.kt | 9 +- .../models/exceptions/ExceptionHandler.kt | 160 +++++++------- .../node/services/OcnRulesService.kt | 196 +++--------------- .../node/services/RoutingService.kt | 4 +- .../node/components/OcpiRequestHandlerTest.kt | 4 +- .../node/integration/AppInterfaceTest.kt | 25 +-- .../CustomModulesIntegrationTest.kt | 74 +++++++ .../node/integration/parties/CpoServer.kt | 21 +- .../node/integration/parties/MspServer.kt | 16 +- .../node/integration/parties/PartyServer.kt | 13 +- .../node/services/OcnRulesServiceTest.kt | 3 +- 14 files changed, 252 insertions(+), 290 deletions(-) create mode 100644 src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt index 577e28b..ec20408 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -234,7 +234,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, routingService.checkSenderWhitelisted( sender = request.headers.sender, receiver = request.headers.receiver, - module = request.module) + moduleID = request.resolveModuleId()) } /** diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt index dff02c1..6c8db08 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocn/MessageController.kt @@ -26,7 +26,6 @@ import snc.openchargingnetwork.node.models.ocpi.OcpiResponse @RequestMapping("/ocn/message") class MessageController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { - @PostMapping fun postMessage(@RequestHeader("X-Request-ID") requestID: String, @RequestHeader("OCN-Signature") signature: String, diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt index c79a18c..40e4478 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt @@ -12,7 +12,7 @@ import javax.servlet.http.HttpServletRequest @RequestMapping("/ocpi/custom") class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { - @RequestMapping("/{module}/{interfaceRole}/**/*") + @RequestMapping("/{module}/{interfaceRole}", "/{module}/{interfaceRole}/**/*") fun customModuleMapping(@RequestHeader("authorization") authorization: String, @RequestHeader("OCN-Signature") signature: String? = null, @RequestHeader("X-Request-ID") requestID: String, @@ -23,14 +23,18 @@ class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHand @RequestHeader("OCPI-to-party-id") toPartyID: String, @PathVariable module: String, @PathVariable interfaceRole: String, - @RequestParam params: Map, + @RequestParam queryParams: Map, @RequestBody body: String?, request: HttpServletRequest): ResponseEntity> { val sender = BasicRole(fromPartyID, fromCountryCode) val receiver = BasicRole(toPartyID, toCountryCode) - val pathWildcards = request.pathInfo.replace("/ocpi/custom/${module}/${interfaceRole}", "") + val urlPath = try { + request.pathInfo.replace("/ocpi/custom/${module}/${interfaceRole}", "") + } catch (e: IllegalStateException) { // catch IllegalStateException: request.pathInfo must not be null + null + } val requestVariables = OcpiRequestVariables( module = ModuleID.CUSTOM, @@ -38,8 +42,8 @@ class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHand interfaceRole = InterfaceRole.resolve(interfaceRole), method = HttpMethod.valueOf(request.method), headers = OcnHeaders(authorization, signature, requestID, correlationID, sender, receiver), - urlPath = pathWildcards, - queryParams = params, + urlPath = urlPath, + queryParams = queryParams, body = body) return requestHandlerBuilder diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/entities/Entities.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/entities/Entities.kt index dda8386..ea644dd 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/entities/Entities.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/entities/Entities.kt @@ -145,14 +145,7 @@ class OcnRulesListEntity( @Embedded val counterparty: BasicRole, - @Column(columnDefinition = "boolean default false") var cdrs: Boolean = false, - @Column(columnDefinition = "boolean default false") var chargingprofiles: Boolean = false, - @Column(columnDefinition = "boolean default false") var commands: Boolean = false, - @Column(columnDefinition = "boolean default false") var sessions: Boolean = false, - @Column(columnDefinition = "boolean default false") var locations: Boolean = false, - @Column(columnDefinition = "boolean default false") var tariffs: Boolean = false, - @Column(columnDefinition = "boolean default false") var tokens: Boolean = false, - + @ElementCollection(fetch = FetchType.EAGER) val modules: List = listOf(), @Id @GeneratedValue val id: Long? = null ) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/exceptions/ExceptionHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/exceptions/ExceptionHandler.kt index 0e2a878..cbaa860 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/exceptions/ExceptionHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/exceptions/ExceptionHandler.kt @@ -28,6 +28,9 @@ import org.springframework.web.bind.annotation.ControllerAdvice import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.context.request.WebRequest import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler +import shareandcharge.openchargingnetwork.notary.Notary +import shareandcharge.openchargingnetwork.notary.ValuesToSign +import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.ocpi.OcpiStatus import snc.openchargingnetwork.node.models.ocpi.OcpiResponse import java.net.ConnectException @@ -35,121 +38,134 @@ import java.net.SocketTimeoutException @Order(Ordered.HIGHEST_PRECEDENCE) @ControllerAdvice -class ExceptionHandler: ResponseEntityExceptionHandler() { +class ExceptionHandler(private val properties: NodeProperties): ResponseEntityExceptionHandler() { - override fun handleHttpMessageNotReadable(e: HttpMessageNotReadableException, headers: HttpHeaders, status: HttpStatus, request: WebRequest): ResponseEntity { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(OcpiResponse( - statusCode = OcpiStatus.CLIENT_INVALID_PARAMETERS.code, - statusMessage = e.message)) + private fun signError(body: OcpiResponse): String { + return Notary().sign(ValuesToSign(body = body), properties.privateKey!!).serialize() + } + + /** + * GENERIC EXCEPTIONS + */ + + override fun handleHttpMessageNotReadable(e: HttpMessageNotReadableException, + headers: HttpHeaders, + status: HttpStatus, + request: WebRequest): ResponseEntity { + val body = OcpiResponse(statusCode = OcpiStatus.CLIENT_INVALID_PARAMETERS.code, statusMessage = e.message) + body.signature = signError(body) + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body) } override fun handleMissingServletRequestParameter(e: MissingServletRequestParameterException, headers: HttpHeaders, status: HttpStatus, request: WebRequest): ResponseEntity { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(OcpiResponse( - statusCode = OcpiStatus.CLIENT_INVALID_PARAMETERS.code, - statusMessage = e.message)) + val body = OcpiResponse(statusCode = OcpiStatus.CLIENT_INVALID_PARAMETERS.code, statusMessage = e.message) + body.signature = signError(body) + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body) } @ExceptionHandler(MissingRequestHeaderException::class) - fun handleMissingRequestHeaderException(e: MissingRequestHeaderException): ResponseEntity> { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(OcpiResponse( + fun handleMissingRequestHeaderException(e: MissingRequestHeaderException): ResponseEntity> { + val body = OcpiResponse( statusCode = OcpiStatus.CLIENT_INVALID_PARAMETERS.code, - statusMessage = e.message)) + statusMessage = e.message) + body.signature = signError(body) + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(body) } @ExceptionHandler(SocketTimeoutException::class) - fun handleSocketTimeoutException(e: SocketTimeoutException): ResponseEntity> { - return ResponseEntity.status(HttpStatus.OK).body(OcpiResponse( + fun handleSocketTimeoutException(e: SocketTimeoutException): ResponseEntity> { + val body = OcpiResponse( statusCode = OcpiStatus.HUB_REQUEST_TIMEOUT.code, - statusMessage = e.message)) + statusMessage = e.message) + body.signature = signError(body) + return ResponseEntity.status(HttpStatus.OK).body(body) } @ExceptionHandler(ConnectException::class) - fun handleConnectException(e: ConnectException): ResponseEntity> { - return ResponseEntity.status(HttpStatus.OK).body(OcpiResponse( + fun handleConnectException(e: ConnectException): ResponseEntity> { + val body = OcpiResponse( statusCode = OcpiStatus.HUB_CONNECTION_PROBLEM.code, - statusMessage = e.message)) + statusMessage = e.message) + body.signature = signError(body) + return ResponseEntity.status(HttpStatus.OK).body(body) } /** * OCPI EXCEPTIONS */ - @ExceptionHandler(OcpiClientGenericException::class) - fun handleOcpiClientGenericException(e: OcpiClientGenericException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) + private fun ocpiErrorToResponseEntity(httpStatus: HttpStatus, ocpiStatus: OcpiStatus, message: String?) + : ResponseEntity> { + val body = OcpiResponse(statusCode = ocpiStatus.code, statusMessage = message) + body.signature = signError(body) + return ResponseEntity.status(httpStatus).body(body) } + @ExceptionHandler(OcpiClientGenericException::class) + fun handleOcpiClientGenericException(e: OcpiClientGenericException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) + @ExceptionHandler(OcpiClientInvalidParametersException::class) - fun handleOcpiClientInvalidParametersException(e: OcpiClientInvalidParametersException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiClientInvalidParametersException(e: OcpiClientInvalidParametersException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiClientNotEnoughInformationException::class) - fun handleOcpiClientNotEnoughInformationException(e: OcpiClientNotEnoughInformationException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiClientNotEnoughInformationException(e: OcpiClientNotEnoughInformationException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiClientUnknownLocationException::class) - fun handleOcpiClientUnknownLocationException(e: OcpiClientUnknownLocationException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiClientUnknownLocationException(e: OcpiClientUnknownLocationException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiServerGenericException::class) - fun handleOcpiServerGenericException(e: OcpiServerGenericException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiServerGenericException(e: OcpiServerGenericException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiServerUnusableApiException::class) - fun handleOcpiServerUnusableApiException(e: OcpiServerUnusableApiException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiServerUnusableApiException(e: OcpiServerUnusableApiException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiServerNoMatchingEndpointsException::class) - fun handleOcpiServerNoMatchingEndpointsException(e: OcpiServerNoMatchingEndpointsException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiServerNoMatchingEndpointsException(e: OcpiServerNoMatchingEndpointsException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiServerUnsupportedVersionException::class) - fun handleOcpiServerUnsupportedVersionException(e: OcpiServerUnsupportedVersionException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiServerUnsupportedVersionException(e: OcpiServerUnsupportedVersionException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiHubConnectionProblemException::class) - fun handleOcpiHubConnectionProblemException(e: OcpiHubConnectionProblemException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiHubConnectionProblemException(e: OcpiHubConnectionProblemException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiHubTimeoutOnRequestException::class) - fun handleOcpiHubTimeoutOnRequestException(e: OcpiHubTimeoutOnRequestException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiHubTimeoutOnRequestException(e: OcpiHubTimeoutOnRequestException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) @ExceptionHandler(OcpiHubUnknownReceiverException::class) - fun handleOcpiHubUnknownReceiverException(e: OcpiHubUnknownReceiverException): ResponseEntity> { - return ResponseEntity.status(e.httpStatus).body(OcpiResponse( - statusCode = e.ocpiStatus.code, - statusMessage = e.message)) - } + fun handleOcpiHubUnknownReceiverException(e: OcpiHubUnknownReceiverException) = ocpiErrorToResponseEntity( + httpStatus = e.httpStatus, + ocpiStatus = e.ocpiStatus, + message = e.message) /** * OCN Exceptions diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt index 1d9d5ce..dc3cfe5 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/OcnRulesService.kt @@ -42,7 +42,12 @@ class OcnRulesService(private val platformRepo: PlatformRepository, fun getRules(authorization: String): OcnRules { val platform = findPlatform(authorization) - val rulesList = ocnRulesListRepo.findAllByPlatformID(platform.id).map { getModules(it) } + val rulesList = ocnRulesListRepo.findAllByPlatformID(platform.id) + .map { OcnRulesListParty( + id = it.counterparty.id, + country = it.counterparty.country, + modules = it.modules) + } return OcnRules( signatures = platform.rules.signatures, @@ -60,48 +65,6 @@ class OcnRulesService(private val platformRepo: PlatformRepository, })) } - /** - * Get list of modules? - */ - private fun getModules(ocnRulesListEntity: OcnRulesListEntity): OcnRulesListParty { - val basicRole = ocnRulesListEntity.counterparty - val modules = mutableListOf() - - if(ocnRulesListEntity.cdrs) { - modules.add("cdrs") - } - - if(ocnRulesListEntity.chargingprofiles) { - modules.add("chargingprofiles") - } - - if(ocnRulesListEntity.commands) { - modules.add("commands") - } - - if(ocnRulesListEntity.locations) { - modules.add("locations") - } - - if(ocnRulesListEntity.sessions) { - modules.add("sessions") - } - - if(ocnRulesListEntity.tariffs) { - modules.add("tariffs") - } - - if(ocnRulesListEntity.tokens) { - modules.add("tokens") - } - - return OcnRulesListParty( - id = basicRole.id, - country = basicRole.country, - modules = modules - ) - } - /** * OcnRules PUT receiver interface to update signature setting */ @@ -160,14 +123,8 @@ class OcnRulesService(private val platformRepo: PlatformRepository, ocnRulesListRepo.saveAll(parties.map { OcnRulesListEntity( platformID = platform.id!!, counterparty = BasicRole(it.id, it.country).toUpperCase(), - cdrs = it.modules.contains("cdrs"), - chargingprofiles = it.modules.contains("chargingprofiles"), - commands = it.modules.contains("commands"), - sessions = it.modules.contains("sessions"), - locations= it.modules.contains("locations"), - tariffs = it.modules.contains("tariffs"), - tokens = it.modules.contains("tokens") - )}) + modules = it.modules) + }) } /** @@ -201,14 +158,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, ocnRulesListRepo.saveAll(parties.map { OcnRulesListEntity( platformID = platform.id!!, counterparty = BasicRole(it.id, it.country).toUpperCase(), - cdrs = it.modules.contains("cdrs"), - chargingprofiles = it.modules.contains("chargingprofiles"), - commands = it.modules.contains("commands"), - sessions = it.modules.contains("sessions"), - locations= it.modules.contains("locations"), - tariffs = it.modules.contains("tariffs"), - tokens = it.modules.contains("tokens") - )}) + modules = it.modules)}) } /** @@ -239,14 +189,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, ocnRulesListRepo.save(OcnRulesListEntity( platformID = platform.id!!, counterparty = BasicRole( id = body.id, country = body.country).toUpperCase(), - cdrs = body.modules.contains("cdrs"), - chargingprofiles = body.modules.contains("chargingprofiles"), - commands = body.modules.contains("commands"), - sessions = body.modules.contains("sessions"), - locations = body.modules.contains("locations"), - tariffs = body.modules.contains("tariffs"), - tokens = body.modules.contains("tokens") - )) + modules = body.modules)) } /** @@ -277,14 +220,7 @@ class OcnRulesService(private val platformRepo: PlatformRepository, ocnRulesListRepo.save(OcnRulesListEntity( platformID = platform.id!!, counterparty = BasicRole( id = body.id, country = body.country).toUpperCase(), - cdrs = body.modules.contains("cdrs"), - chargingprofiles = body.modules.contains("chargingprofiles"), - commands = body.modules.contains("commands"), - sessions = body.modules.contains("sessions"), - locations = body.modules.contains("locations"), - tariffs = body.modules.contains("tariffs"), - tokens = body.modules.contains("tokens") - )) + modules = body.modules)) } /** @@ -345,12 +281,12 @@ class OcnRulesService(private val platformRepo: PlatformRepository, /** * Checks a counter-party has been whitelisted with specific module allowed by a connected platform */ - fun isWhitelisted(platform: PlatformEntity, counterParty: BasicRole, module: ModuleID): Boolean { + fun isWhitelisted(platform: PlatformEntity, counterParty: BasicRole, moduleID: String): Boolean { val rulesList = ocnRulesListRepo.findAllByPlatformID(platform.id) return when { - platform.rules.whitelist -> rulesList.any { validateWhiteListWithModule(it, counterParty, module) } - platform.rules.blacklist -> rulesList.none { validateBlackListWithModule(it, counterParty, module) } + platform.rules.whitelist -> rulesList.any { validateWhiteListWithModule(it, counterParty, moduleID) } + platform.rules.blacklist -> rulesList.none { validateBlackListWithModule(it, counterParty, moduleID) } else -> true } } @@ -397,53 +333,12 @@ class OcnRulesService(private val platformRepo: PlatformRepository, /** * For whitelist check receiver has allowed the module of sender to send them message */ - private fun validateWhiteListWithModule (it: OcnRulesListEntity, sender: BasicRole, module: ModuleID): Boolean { - if(it.counterparty == sender){ - when(module) { - ModuleID.CDRS -> { - if( !it.cdrs ) { - throw OcpiClientGenericException("CDRS Module is blocked") - } - return true - } - ModuleID.CHARGING_PROFILES -> { - if( !it.chargingprofiles ) { - throw OcpiClientGenericException("Charging Profiles Module is blocked") - } - return true - } - ModuleID.COMMANDS -> { - if( !it.commands ) { - throw OcpiClientGenericException("Commands Module is blocked") - } - return true - } - ModuleID.LOCATIONS -> { - if( !it.locations ) { - throw OcpiClientGenericException("Locations Module is blocked") - } - return true - } - ModuleID.SESSIONS -> { - if( !it.sessions ) { - throw OcpiClientGenericException("Session Module is blocked") - } - return true - } - ModuleID.TARIFFS -> { - if( !it.tariffs ) { - throw OcpiClientGenericException("Tariffs Module is blocked") - } - return true - } - ModuleID.TOKENS -> { - if( !it.tokens ) { - throw OcpiClientGenericException("Token Module is blocked") - } - return true - } - else -> return false + private fun validateWhiteListWithModule (rule: OcnRulesListEntity, sender: BasicRole, moduleID: String): Boolean { + if (rule.counterparty == sender) { + if (!rule.modules.contains(moduleID)) { + throw OcpiClientGenericException("Sender not whitelisted to request $moduleID from receiver.") } + return true } return false } @@ -451,53 +346,14 @@ class OcnRulesService(private val platformRepo: PlatformRepository, /** * For blacklist check receiver has allowed the module of sender to send them message */ - private fun validateBlackListWithModule (it: OcnRulesListEntity, sender: BasicRole, module: ModuleID): Boolean { - if(it.counterparty == sender){ - when(module) { - ModuleID.CDRS -> { - if( it.cdrs ) { - throw OcpiClientGenericException("CDRS Module is blocked") - } - return false - } - ModuleID.CHARGING_PROFILES -> { - if( it.chargingprofiles ) { - throw OcpiClientGenericException("Charging Profiles Module is blocked") - } - return false - } - ModuleID.COMMANDS -> { - if( it.commands ) { - throw OcpiClientGenericException("Commands Module is blocked") - } - return false - } - ModuleID.LOCATIONS -> { - if( it.locations ) { - throw OcpiClientGenericException("Locations Module is blocked") - } - return false - } - ModuleID.SESSIONS -> { - if( it.sessions ) { - throw OcpiClientGenericException("Session Module is blocked") - } - return false - } - ModuleID.TARIFFS -> { - if( it.tariffs ) { - throw OcpiClientGenericException("Tariffs Module is blocked") - } - return false - } - ModuleID.TOKENS -> { - if( it.tokens ) { - throw OcpiClientGenericException("Token Module is blocked") - } - return false - } - else -> return false + private fun validateBlackListWithModule (rule: OcnRulesListEntity, sender: BasicRole, moduleID: String): Boolean { + println("sender = $sender") + println("rule = ${rule.counterparty} ${rule.modules}}") + if (rule.counterparty == sender) { + if (rule.modules.contains(moduleID)) { + throw OcpiClientGenericException("Sender not whitelisted to request $moduleID from receiver.") } + return true } return false } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt index 2442233..04fd6fc 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RoutingService.kt @@ -124,9 +124,9 @@ class RoutingService(private val platformRepo: PlatformRepository, /** * Check receiver has allowed sender to send them messages */ - fun checkSenderWhitelisted(sender: BasicRole, receiver: BasicRole, module: ModuleID) { + fun checkSenderWhitelisted(sender: BasicRole, receiver: BasicRole, moduleID: String) { val platform = getPlatform(receiver) - val whitelisted = ocnRulesService.isWhitelisted(platform, sender, module) + val whitelisted = ocnRulesService.isWhitelisted(platform, sender, moduleID) if (!whitelisted) { throw OcpiClientGenericException("Message receiver not in sender's whitelist.") } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index 317596a..95d3260 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -65,7 +65,7 @@ class OcpiRequestHandlerTest { every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL - every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs + every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.resolveModuleId()) } just Runs every { properties.signatures } returns false every { routingService.getPlatformRules(any()) } returns OcnRules(signatures = false) every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) @@ -123,7 +123,7 @@ class OcpiRequestHandlerTest { every { routingService.checkSenderKnown(variables.headers.authorization, variables.headers.sender) } just Runs every { routingService.getReceiverType(variables.headers.receiver) } returns Receiver.LOCAL - every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.module) } just Runs + every { routingService.checkSenderWhitelisted(variables.headers.sender, variables.headers.receiver, variables.resolveModuleId()) } just Runs every { properties.signatures } returns false every { routingService.getPlatformRules(variables.headers.receiver) } returns OcnRules(signatures = true) every { registryService.getPartyDetails(variables.headers.sender) } returns RegistryPartyDetailsBasic( diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt index 1192735..6006117 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -44,29 +44,22 @@ class AppInterfaceTest { return cpo1Seen && cpo2Seen } + private fun testForwarding(recipient: TestCpo, app: TestCpo) { + app.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) + msp.server.agreeToAppPermissions(app.address) + msp.server.getLocation(recipient.party) + await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } + } + @Test fun fowardsRequestToApp_Local() { - // CPO1 is the App Provider - cpo1.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) - // MSP is the App User - msp.server.agreeToAppPermissions(cpo1.address) - // MSP sends request to CPO2 which should also be forwarded to CPO1 - msp.server.getLocation(cpo2.party) - - await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } + testForwarding(recipient = cpo2, app = cpo1) } @Test fun fowardsRequestToApp_Remote() { - // CPO2 is the App Provider - cpo2.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) - // MSP is the App User - msp.server.agreeToAppPermissions(cpo2.address) - // MSP sends request to CPO1 which should also be forwarded to CPO2 - msp.server.getLocation(cpo1.party) - - await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } + testForwarding(recipient = cpo1, app = cpo2) } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt new file mode 100644 index 0000000..7786333 --- /dev/null +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/CustomModulesIntegrationTest.kt @@ -0,0 +1,74 @@ +package snc.openchargingnetwork.node.integration + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.* +import snc.openchargingnetwork.node.integration.utils.* +import snc.openchargingnetwork.node.models.OcnRulesListType + +class CustomModulesIntegrationTest { + + private lateinit var networkComponents: NetworkComponents + private lateinit var cpo1: TestCpo + private lateinit var cpo2: TestCpo + private lateinit var msp: TestMsp + + @BeforeEach + fun bootStrap() { + networkComponents = setupNetwork(HubClientInfoParams()) + cpo1 = networkComponents.cpos[0] + cpo2 = networkComponents.cpos[1] + msp = networkComponents.msps.first() + } + + @AfterEach + fun stopTestParties() { + stopPartyServers(networkComponents) + } + + private fun testMessageReceived(cpo: TestCpo) { + val response = msp.server.sendCustomModuleRequest(cpo.party) + val json = response.jsonObject + val data = json.getJSONArray("data").getJSONObject(0) + + assertThat(response.statusCode).isEqualTo(200) + assertThat(json.get("status_code")).isEqualTo(1000) + assertThat(data.get("id")).isEqualTo("999") + assertThat(data.get("overall_free")).isEqualTo(true) + assertThat(data.getJSONObject("evses").get("free")).isEqualTo(1) + assertThat(data.getJSONObject("evses").get("total")).isEqualTo(2) + assertThat(data.getJSONArray("coordinates").get(0)).isEqualTo("50.387") + assertThat(data.getJSONArray("coordinates").get(1)).isEqualTo("1.651") + } + + private fun testBlocked(cpo: TestCpo) { + cpo.server.addToList(OcnRulesListType.BLACKLIST, msp.party, listOf("lite-locations")) + + val response = msp.server.sendCustomModuleRequest(cpo.party) + val json = response.jsonObject + + assertThat(response.statusCode).isEqualTo(400) + assertThat(json.get("status_code")).isEqualTo(2000) + assertThat(json.get("status_message")).isEqualTo("Sender not whitelisted to request lite-locations from receiver.") + } + + @Test + fun `can send and receive custom module messages locally`() { + testMessageReceived(cpo1) + } + + @Test + fun `can send and receive custom module messages remotely`() { + testMessageReceived(cpo2) + } + + @Test + fun `can be blocked locally`() { + testBlocked(cpo1) + } + + @Test + fun `can be blocked remotely`() { + testBlocked(cpo2) + } + +} \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt index b9c6c20..5567d35 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/CpoServer.kt @@ -27,11 +27,16 @@ class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c Endpoint( identifier = "hubclientinfo", role = InterfaceRole.RECEIVER, - url = urlBuilder("/ocpi/cpo/2.2/clientinfo")), + url = urlBuilder("/ocpi/2.2/clientinfo")), Endpoint( identifier = "commands", role = InterfaceRole.RECEIVER, - url = urlBuilder("/ocpi/cpo/2.2/commands")))))) + url = urlBuilder("/ocpi/cpo/2.2/commands")), + Endpoint( + identifier = "lite-locations", + role = InterfaceRole.SENDER, + url = urlBuilder("/ocpi/cpo/2.2/lite-locations")) + )))) } app.get("/ocpi/cpo/2.2/locations/1") { @@ -83,6 +88,18 @@ class CpoServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c it.json(body) } + app.get("/ocpi/cpo/2.2/lite-locations") { + val body = OcpiResponse(statusCode = 1000, data = listOf( + mapOf( + "id" to "999", + "overall_free" to true, + "evses" to mapOf("free" to 1, "total" to 2), + "coordinates" to listOf("50.387", "1.651")) + )) + body.signature = sign(body = body) + it.json(body) + } + } fun sendCdr(to: BasicRole): Response { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt index b261c7b..a5b01ef 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt @@ -33,11 +33,17 @@ class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c Endpoint( identifier = "hubclientinfo", role = InterfaceRole.RECEIVER, - url = urlBuilder("/ocpi/cpo/2.2/clientinfo")), + url = urlBuilder("/ocpi/2.2/clientinfo")), Endpoint( identifier = "commands", role = InterfaceRole.SENDER, - url = urlBuilder("/ocpi/msp/2.2/commands")))))) + url = urlBuilder("/ocpi/msp/2.2/commands")), + Endpoint( + identifier = "enriched-tariffs", + role = InterfaceRole.RECEIVER, + url = urlBuilder("/ocpi/msp/2.2/enriched-tariffs") + ) + )))) } app.get("/ocpi/msp/2.2/cdrs/1") { @@ -99,4 +105,10 @@ class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c return khttp.post("$node/ocpi/receiver/2.2/commands/START_SESSION", headers = headers.toMap(tokenC, signature), json = json) } + fun sendCustomModuleRequest(to: BasicRole): Response { // TODO: test with different parameters (path, query, json) + val headers = getSignableHeaders(to) + val signature = sign(headers = headers) + return khttp.get("$node/ocpi/custom/lite-locations/sender", headers = headers.toMap(tokenC, signature)) + } + } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt index 2ac9a60..48bed7f 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt @@ -48,7 +48,7 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra )) } - app.put("ocpi/cpo/2.2/clientinfo/:countryCode/:partyID") { + app.put("ocpi/2.2/clientinfo/:countryCode/:partyID") { this.hubClientInfoStatuses[BasicRole(id = it.pathParam("partyID"), country = it.pathParam("countryCode"))] = it.body().status val body = OcpiResponse(statusCode = 1000, data = "") body.signature = sign(body = body) @@ -80,6 +80,7 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra } fun registerCredentials() { + // TODO: could also request versions and store endpoints in memory val tokenA = getTokenA(node, listOf(config.party)) val response = khttp.post("$node/ocpi/2.2/credentials", headers = mapOf("Authorization" to "Token $tokenA"), @@ -112,14 +113,10 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra toPartyId = to.id) } - fun addToList(type: OcnRulesListType, party: BasicRole) { - val typeString = when (type) { - OcnRulesListType.WHITELIST -> "whitelist" - OcnRulesListType.BLACKLIST -> "blacklist" - } - khttp.post("$node/ocpi/receiver/2.2/ocnrules/$typeString", + fun addToList(type: OcnRulesListType, party: BasicRole, modules: List? = listOf()) { + khttp.post("$node/ocpi/receiver/2.2/ocnrules/${type.toString().toLowerCase()}", headers = mapOf("Authorization" to "Token $tokenC"), - json = mapOf("country_code" to party.country, "party_id" to party.id, "modules" to listOf())) + json = mapOf("country_code" to party.country, "party_id" to party.id, "modules" to modules)) } fun stopServer() { diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/OcnRulesServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/OcnRulesServiceTest.kt index 1ada626..897e775 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/OcnRulesServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/OcnRulesServiceTest.kt @@ -61,7 +61,8 @@ class OcnRulesServiceTest { every { ocnRulesListRepo.findAllByPlatformID(platform.id) } returns listOf( OcnRulesListEntity( platformID = platform.id!!, - counterparty = case.rulesListCounterParty)) + counterparty = case.rulesListCounterParty, + modules = listOf())) val actual = ocnRulesService.isWhitelisted(platform, case.testCounterParty) assertThat(actual).isEqualTo(case.expectedResult) From 5dde8ce70f46f85cba65838bb923d9590ea1f713 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Mon, 8 Jun 2020 11:35:18 +0200 Subject: [PATCH 22/26] change custom module path placement and update asciidoc documentation Signed-off-by: Adam Staveley --- src/docs/asciidoc/index.adoc | 60 +++++++++++++++++-- .../controllers/ocpi/VersionsController.kt | 2 +- .../ocpi/v2_2/CustomModulesController.kt | 4 +- .../ocpi/v2_2/CustomModulesControllerTest.kt | 2 +- .../node/integration/parties/MspServer.kt | 6 +- 5 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 6222cb6..5295b1f 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -87,12 +87,60 @@ uses this as it's primary means of connecting such parties. It is important to h API beforehand, in order to connect to and use an OCN Node. This comprises, as a bare minimum, the credentials module. Documentation for version 2.2 can be found on https://github.com/ocpi/ocpi[OCPI @ *GitHub*]. -The node strives to provide all the functionality of OCPI. As this software is in alpha stage, there are currently -some features of OCPI missing: - -- `hubclientinfo` module is only partly implemented. -- Hub-specific features such as broadcast push and intelligent routing are missing. - +The node strives to provide all the functionality of OCPI, though there are currently some features missing: + +- The `hubclientinfo` module is still work in progress. +- Hub-specific features such as broadcast push and intelligent routing are not included. + +See the https://bitbucket.org/shareandcharge/ocn-node/issues[issues page] on Bitbucket for more. + +== Pagination and Proxying Resources + +The OCN handles OCPI pagination requests in a special way. When sending a paginated request, for example, +to get the first 50 locations from a Charge Point Operator, the CPO will respond with a `Link` header which +only their OCN Node will be able to access. + +Because of this, the OCN Node must proxy this header. The sender will receive from their OCN Node a `Link` +header such as `https://some.ocn-node.net/ocpi/sender/2.2/locations/page/2` which will correspond to the +original CPO resource, for example, `https://some.cpo.com/ocpi/cpo/2.2/locations?offset=100&limit=100`. + +Other examples of resources which require proxying include the `Location` header returned from the POST method +of the cdrs receiver interface, as well as any `response_url` used in the commands and charging profiles +modules. + +== Custom OCPI modules + +The OCN supports the use of custom OCPI modules. Such modules can be registered with an OCN Node during +the credentials handshake. + +When requesting version details from the node, the response will contain the following endpoints: + +.... +{ + "identifier": "custom", + "role": "sender", + "endpoint": "https://some.ocn-node.net/ocpi/custom/sender" +}, +{ + "identifier": "custom", + "role": "receiver", + "endpoint": "https://some.ocn-node.net/ocpi/custom/receiver" +} +.... + +This acts as a wildcard for _all_ custom OCPI modules. Here's how it works: + +1. CPO completes credentials handshake with OCN Node + a. CPO fetches OCN Node versions, saves endpoint details for wildcard custom module + b. OCN Node fetches CPO versions, saves endpoint details for `enriched-locations` custom module +2. CPO sends their custom module request to the OCN Node + a. requests any HTTP method + b. places module name as first path variable, followed by any additional variables required by the +custom module: `https://some.ocn-node.net/ocpi/custom/receiver/enriched-locations/location1` + c. appends url-encoded queries and json body if necessary +3. OCN Node routes the message to recipient's OCN Node + a. recipient's OCN Node looks up `enriched-locations` in recipient's endpoints + b. recipient's OCN Node forwards the request to `https://recipient.msp.com/ocpi/emsp/2.2/enriched-locations/location1` == _OcnRules_ module diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt index fec86c3..3ef5899 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/VersionsController.kt @@ -69,7 +69,7 @@ class VersionsController(private val repository: PlatformRepository, private fun getModuleEndpoints(module: ModuleID): List { return InterfaceRole.values().map { val paths = if (module == ModuleID.CUSTOM) { - "/ocpi/custom" + "/ocpi/custom/${it.id}" } else { "/ocpi/${it.id}/2.2/${module.id}" } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt index 40e4478..379fd47 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt @@ -12,7 +12,7 @@ import javax.servlet.http.HttpServletRequest @RequestMapping("/ocpi/custom") class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { - @RequestMapping("/{module}/{interfaceRole}", "/{module}/{interfaceRole}/**/*") + @RequestMapping("/{interfaceRole}/{module}/", "/{interfaceRole}/{module}/**/*") fun customModuleMapping(@RequestHeader("authorization") authorization: String, @RequestHeader("OCN-Signature") signature: String? = null, @RequestHeader("X-Request-ID") requestID: String, @@ -31,7 +31,7 @@ class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHand val receiver = BasicRole(toPartyID, toCountryCode) val urlPath = try { - request.pathInfo.replace("/ocpi/custom/${module}/${interfaceRole}", "") + request.pathInfo.replace("/ocpi/custom/${interfaceRole}/${module}", "") } catch (e: IllegalStateException) { // catch IllegalStateException: request.pathInfo must not be null null } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt index e71de11..6943aa8 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesControllerTest.kt @@ -42,7 +42,7 @@ class CustomModulesControllerTest(@Autowired val mockMvc: MockMvc) { every { requestHandler.forwardDefault().getResponseWithAllHeaders() } returns ResponseEntity .ok(OcpiResponse(statusCode = 1000)) - mockMvc.perform(post("/ocpi/custom/${request.customModuleId}/${request.interfaceRole.id}${request.urlPath}") + mockMvc.perform(post("/ocpi/custom/${request.interfaceRole.id}/${request.customModuleId}/${request.urlPath}") .queryParam("hello", "world") .queryParam("foo", "bar") .header("authorization", request.headers.authorization) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt index a5b01ef..0ca032c 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/MspServer.kt @@ -39,9 +39,9 @@ class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c role = InterfaceRole.SENDER, url = urlBuilder("/ocpi/msp/2.2/commands")), Endpoint( - identifier = "enriched-tariffs", + identifier = "enriched-locations", role = InterfaceRole.RECEIVER, - url = urlBuilder("/ocpi/msp/2.2/enriched-tariffs") + url = urlBuilder("/ocpi/msp/2.2/enriched-locations") ) )))) } @@ -108,7 +108,7 @@ class MspServer(config: PartyDefinition, contracts: OcnContracts): PartyServer(c fun sendCustomModuleRequest(to: BasicRole): Response { // TODO: test with different parameters (path, query, json) val headers = getSignableHeaders(to) val signature = sign(headers = headers) - return khttp.get("$node/ocpi/custom/lite-locations/sender", headers = headers.toMap(tokenC, signature)) + return khttp.get("$node/ocpi/custom/sender/lite-locations", headers = headers.toMap(tokenC, signature)) } } \ No newline at end of file From 1c055f669a9bd201dccb6eac787c26bcd9abd0cd Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Mon, 8 Jun 2020 11:53:41 +0200 Subject: [PATCH 23/26] clean docs and fix custom module mapping matching Signed-off-by: Adam Staveley --- src/docs/asciidoc/index.adoc | 8 ++++---- .../node/controllers/ocpi/v2_2/CustomModulesController.kt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 5295b1f..b5b50db 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -101,8 +101,8 @@ to get the first 50 locations from a Charge Point Operator, the CPO will respond only their OCN Node will be able to access. Because of this, the OCN Node must proxy this header. The sender will receive from their OCN Node a `Link` -header such as `https://some.ocn-node.net/ocpi/sender/2.2/locations/page/2` which will correspond to the -original CPO resource, for example, `https://some.cpo.com/ocpi/cpo/2.2/locations?offset=100&limit=100`. +header such as https://some.ocn-node.net/ocpi/sender/2.2/locations/page/2 which will correspond to the +original CPO resource, for example, https://some.cpo.com/ocpi/cpo/2.2/locations?offset=100&limit=100. Other examples of resources which require proxying include the `Location` header returned from the POST method of the cdrs receiver interface, as well as any `response_url` used in the commands and charging profiles @@ -136,11 +136,11 @@ This acts as a wildcard for _all_ custom OCPI modules. Here's how it works: 2. CPO sends their custom module request to the OCN Node a. requests any HTTP method b. places module name as first path variable, followed by any additional variables required by the -custom module: `https://some.ocn-node.net/ocpi/custom/receiver/enriched-locations/location1` +custom module: https://some.ocn-node.net/ocpi/custom/receiver/enriched-locations/location1 c. appends url-encoded queries and json body if necessary 3. OCN Node routes the message to recipient's OCN Node a. recipient's OCN Node looks up `enriched-locations` in recipient's endpoints - b. recipient's OCN Node forwards the request to `https://recipient.msp.com/ocpi/emsp/2.2/enriched-locations/location1` + b. recipient's OCN Node forwards the request to https://recipient.msp.com/ocpi/emsp/2.2/enriched-locations/location1 == _OcnRules_ module diff --git a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt index 379fd47..fa0efbc 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/controllers/ocpi/v2_2/CustomModulesController.kt @@ -12,7 +12,7 @@ import javax.servlet.http.HttpServletRequest @RequestMapping("/ocpi/custom") class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHandlerBuilder) { - @RequestMapping("/{interfaceRole}/{module}/", "/{interfaceRole}/{module}/**/*") + @RequestMapping("/{interfaceRole}/{module}", "/{interfaceRole}/{module}/**/*") fun customModuleMapping(@RequestHeader("authorization") authorization: String, @RequestHeader("OCN-Signature") signature: String? = null, @RequestHeader("X-Request-ID") requestID: String, @@ -21,8 +21,8 @@ class CustomModulesController(private val requestHandlerBuilder: OcpiRequestHand @RequestHeader("OCPI-from-party-id") fromPartyID: String, @RequestHeader("OCPI-to-country-code") toCountryCode: String, @RequestHeader("OCPI-to-party-id") toPartyID: String, - @PathVariable module: String, @PathVariable interfaceRole: String, + @PathVariable module: String, @RequestParam queryParams: Map, @RequestBody body: String?, request: HttpServletRequest): ResponseEntity> { From fbe2b6c72c8388698e9683ad42d39b32e4833074 Mon Sep 17 00:00:00 2001 From: Adam Staveley Date: Tue, 16 Jun 2020 10:36:49 +0200 Subject: [PATCH 24/26] rename app to service --- CHANGELOG.md | 4 +- .../contracts/Permissions.java | 92 +++++++++---------- .../node/components/OcpiRequestHandler.kt | 12 +-- .../openchargingnetwork/node/models/Ocn.kt | 8 +- .../node/services/AsyncTaskService.kt | 10 +- .../node/services/RegistryService.kt | 14 +-- .../node/components/OcpiRequestHandlerTest.kt | 8 +- .../node/integration/AppInterfaceTest.kt | 18 ++-- .../node/integration/parties/PartyServer.kt | 12 +-- .../openchargingnetwork/node/models/Ocn.kt | 70 +++++++------- .../node/services/RegistryServiceTest.kt | 8 +- 11 files changed, 128 insertions(+), 128 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e349f40..26da60f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,8 @@ ## 1.1.0-rc1 ### Upcoming -Adds the ability for requests to be forwarded to "Ocn Apps" with matching permissions. The -Ocn App Interface, using the new Permissions contract in the OCN Registry, allows data to be +Adds the ability for requests to be forwarded to "Ocn Services" with matching permissions. The +Ocn ServiceInterface, using the new Permissions contract in the OCN Registry, allows data to be shared and accessed using a permission system. ## 1.1.0-rc0 diff --git a/src/main/java/snc/openchargingnetwork/contracts/Permissions.java b/src/main/java/snc/openchargingnetwork/contracts/Permissions.java index 273e9f1..8db1d55 100644 --- a/src/main/java/snc/openchargingnetwork/contracts/Permissions.java +++ b/src/main/java/snc/openchargingnetwork/contracts/Permissions.java @@ -46,19 +46,19 @@ */ @SuppressWarnings("rawtypes") public class Permissions extends Contract { - public static final String BINARY = "0x608060405234801561001057600080fd5b50604051613c39380380613c398339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050613ba5806100946000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80635c0f93bb11610097578063c13489ca11610066578063c13489ca14610780578063cdebf96e14610966578063dc3acebe146109aa578063edc922a914610bab576100f5565b80635c0f93bb1461058d578063674a730d146105f2578063884eb949146106c0578063b5d9f61f14610704576100f5565b8063340f1e7c116100d3578063340f1e7c146103bc57806338f7e0e1146104215780633fd62464146104ba57806350f3fc811461051f576100f5565b8063099e9762146100fa5780631cf939261461030157806323db5e8c1461030b575b600080fd5b6102ff600480360360c081101561011057600080fd5b810190808035906020019064010000000081111561012d57600080fd5b82018360208201111561013f57600080fd5b8035906020019184600183028401116401000000008311171561016157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156101c457600080fd5b8201836020820111156101d657600080fd5b803590602001918460018302840111640100000000831117156101f857600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561025b57600080fd5b82018360208201111561026d57600080fd5b8035906020019184602083028401116401000000008311171561028f57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050610c0a565b005b610309610e37565b005b6103a26004803603606081101561032157600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610e42565b604051808215151515815260200191505060405180910390f35b61041f600480360360808110156103d257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506112ae565b005b6104636004803603602081101561043757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611433565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156104a657808201518184015260208101905061048b565b505050509050019250505060405180910390f35b61051d600480360360808110156104d057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611500565b005b61054b6004803603602081101561053557600080fd5b8101908080359060200190929190505050611684565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6105f0600480360360808110156105a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff16906020019092919080359060200190929190803590602001909291905050506116c0565b005b6106696004803603604081101561060857600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050611845565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156106ac578082015181840152602081019050610691565b505050509050019250505060405180910390f35b610702600480360360208110156106d657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611ceb565b005b6107666004803603604081101561071a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611cf8565b604051808215151515815260200191505060405180910390f35b6109646004803603606081101561079657600080fd5b81019080803590602001906401000000008111156107b357600080fd5b8201836020820111156107c557600080fd5b803590602001918460018302840111640100000000831117156107e757600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561084a57600080fd5b82018360208201111561085c57600080fd5b8035906020019184600183028401116401000000008311171561087e57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156108e157600080fd5b8201836020820111156108f357600080fd5b8035906020019184602083028401116401000000008311171561091557600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290505050611d8c565b005b6109a86004803603602081101561097c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611d9d565b005b6109ec600480360360208110156109c057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611daa565b60405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019080838360005b83811015610ac2578082015181840152602081019050610aa7565b50505050905090810190601f168015610aef5780820380516001836020036101000a031916815260200191505b50848103835286818151815260200191508051906020019080838360005b83811015610b28578082015181840152602081019050610b0d565b50505050905090810190601f168015610b555780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019060200280838360005b83811015610b91578082015181840152602081019050610b76565b505050509050019850505050505050505060405180910390f35b610bb361238d565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610bf6578082015181840152602081019050610bdb565b505050509050019250505060405180910390f35b60008686866040516020018084805190602001908083835b60208310610c455780518252602082019150602081019050602083039250610c22565b6001836020036101000a03801982511681845116808217855250505050505090500183805190602001908083835b60208310610c965780518252602082019150602081019050602083039250610c73565b6001836020036101000a038019825116818451168082178552505050505050905001828051906020019060200280838360005b83811015610ce4578082015181840152602081019050610cc9565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310610d795780518252602082019150602081019050602083039250610d56565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610e15573d6000803e3d6000fd5b505050602060405103519050610e2d8189898961241b565b5050505050505050565b610e4033612a17565b565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a5b5ffbb86866040518363ffffffff1660e01b815260040180837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001827cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019250505060006040518083038186803b158015610f4157600080fd5b505afa158015610f55573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060c0811015610f7f57600080fd5b810190808051906020019092919080516040519392919084640100000000821115610fa957600080fd5b83820191506020820185811115610fbf57600080fd5b8251866020820283011164010000000082111715610fdc57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611013578082015181840152602081019050610ff8565b505050509050016040526020018051604051939291908464010000000082111561103c57600080fd5b8382019150602082018581111561105257600080fd5b825186602082028301116401000000008211171561106f57600080fd5b8083526020830192505050908051906020019060200280838360005b838110156110a657808201518184015260208101905061108b565b50505050905001604052602001805160405193929190846401000000008211156110cf57600080fd5b838201915060208201858111156110e557600080fd5b825186602082028301116401000000008211171561110257600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561113957808201518184015260208101905061111e565b50505050905001604052602001805190602001909291908051604051939291908464010000000082111561116c57600080fd5b8382019150602082018581111561118257600080fd5b825186600182028301116401000000008211171561119f57600080fd5b8083526020830192505050908051906020019080838360005b838110156111d35780820151818401526020810190506111b8565b50505050905090810190601f1680156112005780820380516001836020036101000a031916815260200191505b5060405250505090919293509091925090915090505080915050600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169150509392505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106113795780518252602082019150602081019050602083039250611356565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611415573d6000803e3d6000fd5b50505060206040510351905061142b8187612e56565b505050505050565b6060600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156114f457602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116114aa575b50505050509050919050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106115cb57805182526020820191506020810190506020830392506115a8565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611667573d6000803e3d6000fd5b50505060206040510351905061167c81612a17565b505050505050565b6001818154811061169157fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b6020831061178b5780518252602082019150602081019050602083039250611768565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611827573d6000803e3d6000fd5b50505060206040510351905061183d8187613402565b505050505050565b606060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a5b5ffbb85856040518363ffffffff1660e01b815260040180837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001827cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019250505060006040518083038186803b15801561194557600080fd5b505afa158015611959573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060c081101561198357600080fd5b8101908080519060200190929190805160405193929190846401000000008211156119ad57600080fd5b838201915060208201858111156119c357600080fd5b82518660208202830111640100000000821117156119e057600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611a175780820151818401526020810190506119fc565b5050505090500160405260200180516040519392919084640100000000821115611a4057600080fd5b83820191506020820185811115611a5657600080fd5b8251866020820283011164010000000082111715611a7357600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611aaa578082015181840152602081019050611a8f565b5050505090500160405260200180516040519392919084640100000000821115611ad357600080fd5b83820191506020820185811115611ae957600080fd5b8251866020820283011164010000000082111715611b0657600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611b3d578082015181840152602081019050611b22565b505050509050016040526020018051906020019092919080516040519392919084640100000000821115611b7057600080fd5b83820191506020820185811115611b8657600080fd5b8251866001820283011164010000000082111715611ba357600080fd5b8083526020830192505050908051906020019080838360005b83811015611bd7578082015181840152602081019050611bbc565b50505050905090810190601f168015611c045780820380516001836020036101000a031916815260200191505b5060405250505090919293509091925090915090505080915050600560008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015611cdd57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611c93575b505050505091505092915050565b611cf53382612e56565b50565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b611d983384848461241b565b505050565b611da73382613402565b50565b60008060608060606000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663df516128876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b158015611e5057600080fd5b505afa158015611e64573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060e0811015611e8e57600080fd5b81019080805190602001909291908051906020019092919080516040519392919084640100000000821115611ec257600080fd5b83820191506020820185811115611ed857600080fd5b8251866020820283011164010000000082111715611ef557600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611f2c578082015181840152602081019050611f11565b5050505090500160405260200180516040519392919084640100000000821115611f5557600080fd5b83820191506020820185811115611f6b57600080fd5b8251866020820283011164010000000082111715611f8857600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611fbf578082015181840152602081019050611fa4565b5050505090500160405260200180516040519392919084640100000000821115611fe857600080fd5b83820191506020820185811115611ffe57600080fd5b825186602082028301116401000000008211171561201b57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015612052578082015181840152602081019050612037565b50505050905001604052602001805190602001909291908051604051939291908464010000000082111561208557600080fd5b8382019150602082018581111561209b57600080fd5b82518660018202830111640100000000821117156120b857600080fd5b8083526020830192505050908051906020019080838360005b838110156120ec5780820151818401526020810190506120d1565b50505050905090810190601f1680156121195780820380516001836020036101000a031916815260200191505b506040525050509091929350909192509091509050508095508196505050600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561220d5780601f106121e25761010080835404028352916020019161220d565b820191906000526020600020905b8154815290600101906020018083116121f057829003601f168201915b50505050509250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156122ea5780601f106122bf576101008083540402835291602001916122ea565b820191906000526020600020905b8154815290600101906020018083116122cd57829003601f168201915b50505050509150600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060020180548060200260200160405190810160405280929190818152602001828054801561237d57602002820191906000526020600020905b815481526020019060010190808311612369575b5050505050905091939590929450565b6060600180548060200260200160405190810160405280929190818152602001828054801561241157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116123c7575b5050505050905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b1580156124bb57600080fd5b505afa1580156124cf573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060408110156124f957600080fd5b81019080805190602001909291908051604051939291908464010000000082111561252357600080fd5b8382019150602082018581111561253957600080fd5b825186600182028301116401000000008211171561255657600080fd5b8083526020830192505050908051906020019080838360005b8381101561258a57808201518184015260208101905061256f565b50505050905090810190601f1680156125b75780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612647576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603c815260200180613b35603c913960400191505060405180910390fd5b60008251116126be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f207065726d697373696f6e7320676976656e2e000000000000000000000081525060200191505060405180910390fd5b604051806060016040528085815260200184815260200183815250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082015181600001908051906020019061273492919061390b565b50602082015181600101908051906020019061275192919061390b565b50604082015181600201908051906020019061276e92919061398b565b5090505060001515600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515141561288a5760018590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b7f5217645fd1a5d80a7a561ee8df6541e94bff9b1151d8c1281e17c1a304d9d6fa84848488604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845288818151815260200191508051906020019080838360005b8381101561292957808201518184015260208101905061290e565b50505050905090810190601f1680156129565780820380516001836020036101000a031916815260200191505b50848103835287818151815260200191508051906020019080838360005b8381101561298f578082015181840152602081019050612974565b50505050905090810190601f1680156129bc5780820380516001836020036101000a031916815260200191505b50848103825286818151815260200191508051906020019060200280838360005b838110156129f85780820151818401526020810190506129dd565b5050505090500197505050505050505060405180910390a15050505050565b60011515600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514612ac0576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526026815260200180613ab26026913960400191505060405180910390fd5b612ac86139d8565b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051806060016040529081600082018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612bab5780601f10612b8057610100808354040283529160200191612bab565b820191906000526020600020905b815481529060010190602001808311612b8e57829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015612c4d5780601f10612c2257610100808354040283529160200191612c4d565b820191906000526020600020905b815481529060010190602001808311612c3057829003601f168201915b5050505050815260200160028201805480602002602001604051908101604052809291908181526020018280548015612ca557602002820191906000526020600020905b815481526020019060010190808311612c91575b5050505050815250509050600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008082016000612d0091906139f9565b600182016000612d1091906139f9565b600282016000612d209190613a41565b50506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f5217645fd1a5d80a7a561ee8df6541e94bff9b1151d8c1281e17c1a304d9d6fa816040015183604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845260008152602001602001848103835260008152602001602001848103825286818151815260200191508051906020019060200280838360005b83811015612e3c578082015181840152602081019050612e21565b505050509050019550505050505060405180910390a15050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b158015612ef657600080fd5b505afa158015612f0a573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506040811015612f3457600080fd5b810190808051906020019092919080516040519392919084640100000000821115612f5e57600080fd5b83820191506020820185811115612f7457600080fd5b8251866001820283011164010000000082111715612f9157600080fd5b8083526020830192505050908051906020019080838360005b83811015612fc5578082015181840152602081019050612faa565b50505050905090810190601f168015612ff25780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613082576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613a88602a913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514613148576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f50726f766964657220686173206e6f2072656769737465726564204170702e0081525060200191505060405180910390fd5b60001515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151461322e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180613ad86031913960400191505060405180910390fd5b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f59c05fe4657968f976c095206514e77e8ed37c2fe56e7614cd8f245dfbb008298383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b1580156134a257600080fd5b505afa1580156134b6573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060408110156134e057600080fd5b81019080805190602001909291908051604051939291908464010000000082111561350a57600080fd5b8382019150602082018581111561352057600080fd5b825186600182028301116401000000008211171561353d57600080fd5b8083526020830192505050908051906020019080838360005b83811015613571578082015181840152602081019050613556565b50505050905090810190601f16801561359e5780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561362e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613a88602a913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146136f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601f8152602001807f50726f766964657220686173206e6f2072656769737465726564204170702e0081525060200191505060405180910390fd5b60011515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146137da576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613b09602c913960400191505060405180910390fd5b6000600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f59c05fe4657968f976c095206514e77e8ed37c2fe56e7614cd8f245dfbb008298383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061394c57805160ff191683800117855561397a565b8280016001018555821561397a579182015b8281111561397957825182559160200191906001019061395e565b5b5090506139879190613a62565b5090565b8280548282559060005260206000209081019282156139c7579160200282015b828111156139c65782518255916020019190600101906139ab565b5b5090506139d49190613a62565b5090565b60405180606001604052806060815260200160608152602001606081525090565b50805460018160011615610100020316600290046000825580601f10613a1f5750613a3e565b601f016020900490600052602060002090810190613a3d9190613a62565b5b50565b5080546000825590600052602060002090810190613a5f9190613a62565b50565b613a8491905b80821115613a80576000816000905550600101613a68565b5090565b9056fe417070207573657220686173206e6f207061727479206c697374696e6720696e2052656769737472792e43616e6e6f742064656c65746520617070207468617420646f6573206e6f742065786973742e41677265656d656e7420616c7265616479206d616465206265747765656e207573657220616e642070726f76696465722e4e6f2041677265656d656e74206d616465206265747765656e207573657220616e642070726f76696465722e547279696e6720746f20726567697374657220616e2061707020776974686f7574207061727479206c697374696e6720696e2052656769737472792ea265627a7a7231582022ad71273afc23e7112660d5d26832ec62d291dd2e9f14b7f6a1701451f9940964736f6c634300050f0032"; + public static final String BINARY = "0x608060405234801561001057600080fd5b50604051613c2d380380613c2d8339818101604052602081101561003357600080fd5b8101908080519060200190929190505050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050613b99806100946000396000f3fe608060405234801561001057600080fd5b50600436106100f55760003560e01c80635c0f93bb11610097578063a45469d111610066578063a45469d114610905578063b5d9f61f14610aeb578063cdebf96e14610b67578063edc922a914610bab576100f5565b80635c0f93bb146107845780635fce7138146107e9578063674a730d146107f3578063884eb949146108c1576100f5565b8063340f1e7c116100d3578063340f1e7c146105b357806338f7e0e11461061857806350f3fc81146106b15780635a9da7f11461071f576100f5565b806315a52302146100fa57806323db5e8c146102fb57806329cc2b23146103ac575b600080fd5b61013c6004803603602081101561011057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c0a565b60405180867dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001857cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001806020018060200180602001848103845287818151815260200191508051906020019080838360005b838110156102125780820151818401526020810190506101f7565b50505050905090810190601f16801561023f5780820380516001836020036101000a031916815260200191505b50848103835286818151815260200191508051906020019080838360005b8381101561027857808201518184015260208101905061025d565b50505050905090810190601f1680156102a55780820380516001836020036101000a031916815260200191505b50848103825285818151815260200191508051906020019060200280838360005b838110156102e15780820151818401526020810190506102c6565b505050509050019850505050505050505060405180910390f35b6103926004803603606081101561031157600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506111ed565b604051808215151515815260200191505060405180910390f35b6105b1600480360360c08110156103c257600080fd5b81019080803590602001906401000000008111156103df57600080fd5b8201836020820111156103f157600080fd5b8035906020019184600183028401116401000000008311171561041357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561047657600080fd5b82018360208201111561048857600080fd5b803590602001918460018302840111640100000000831117156104aa57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561050d57600080fd5b82018360208201111561051f57600080fd5b8035906020019184602083028401116401000000008311171561054157600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f820116905080830192505050505050509192919290803560ff1690602001909291908035906020019092919080359060200190929190505050611659565b005b610616600480360360808110156105c957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611886565b005b61065a6004803603602081101561062e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050611a0b565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561069d578082015181840152602081019050610682565b505050509050019250505060405180910390f35b6106dd600480360360208110156106c757600080fd5b8101908080359060200190929190505050611ad8565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6107826004803603608081101561073557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611b14565b005b6107e76004803603608081101561079a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803560ff1690602001909291908035906020019092919080359060200190929190505050611c98565b005b6107f1611e1d565b005b61086a6004803603604081101561080957600080fd5b8101908080357dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916906020019092919080357cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050611e28565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b838110156108ad578082015181840152602081019050610892565b505050509050019250505060405180910390f35b610903600480360360208110156108d757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506122ce565b005b610ae96004803603606081101561091b57600080fd5b810190808035906020019064010000000081111561093857600080fd5b82018360208201111561094a57600080fd5b8035906020019184600183028401116401000000008311171561096c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290803590602001906401000000008111156109cf57600080fd5b8201836020820111156109e157600080fd5b80359060200191846001830284011164010000000083111715610a0357600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929080359060200190640100000000811115610a6657600080fd5b820183602082011115610a7857600080fd5b80359060200191846020830284011164010000000083111715610a9a57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600081840152601f19601f8201169050808301925050505050505091929192905050506122db565b005b610b4d60048036036040811015610b0157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506122ec565b604051808215151515815260200191505060405180910390f35b610ba960048036036020811015610b7d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050612380565b005b610bb361238d565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610bf6578082015181840152602081019050610bdb565b505050509050019250505060405180910390f35b60008060608060606000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663df516128876040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b158015610cb057600080fd5b505afa158015610cc4573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060e0811015610cee57600080fd5b81019080805190602001909291908051906020019092919080516040519392919084640100000000821115610d2257600080fd5b83820191506020820185811115610d3857600080fd5b8251866020820283011164010000000082111715610d5557600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610d8c578082015181840152602081019050610d71565b5050505090500160405260200180516040519392919084640100000000821115610db557600080fd5b83820191506020820185811115610dcb57600080fd5b8251866020820283011164010000000082111715610de857600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610e1f578082015181840152602081019050610e04565b5050505090500160405260200180516040519392919084640100000000821115610e4857600080fd5b83820191506020820185811115610e5e57600080fd5b8251866020820283011164010000000082111715610e7b57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610eb2578082015181840152602081019050610e97565b505050509050016040526020018051906020019092919080516040519392919084640100000000821115610ee557600080fd5b83820191506020820185811115610efb57600080fd5b8251866001820283011164010000000082111715610f1857600080fd5b8083526020830192505050908051906020019080838360005b83811015610f4c578082015181840152602081019050610f31565b50505050905090810190601f168015610f795780820380516001836020036101000a031916815260200191505b506040525050509091929350909192509091509050508095508196505050600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561106d5780601f106110425761010080835404028352916020019161106d565b820191906000526020600020905b81548152906001019060200180831161105057829003601f168201915b50505050509250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206001018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561114a5780601f1061111f5761010080835404028352916020019161114a565b820191906000526020600020905b81548152906001019060200180831161112d57829003601f168201915b50505050509150600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206002018054806020026020016040519081016040528092919081815260200182805480156111dd57602002820191906000526020600020905b8154815260200190600101908083116111c9575b5050505050905091939590929450565b6000806000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a5b5ffbb86866040518363ffffffff1660e01b815260040180837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001827cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019250505060006040518083038186803b1580156112ec57600080fd5b505afa158015611300573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060c081101561132a57600080fd5b81019080805190602001909291908051604051939291908464010000000082111561135457600080fd5b8382019150602082018581111561136a57600080fd5b825186602082028301116401000000008211171561138757600080fd5b8083526020830192505050908051906020019060200280838360005b838110156113be5780820151818401526020810190506113a3565b50505050905001604052602001805160405193929190846401000000008211156113e757600080fd5b838201915060208201858111156113fd57600080fd5b825186602082028301116401000000008211171561141a57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611451578082015181840152602081019050611436565b505050509050016040526020018051604051939291908464010000000082111561147a57600080fd5b8382019150602082018581111561149057600080fd5b82518660208202830111640100000000821117156114ad57600080fd5b8083526020830192505050908051906020019060200280838360005b838110156114e45780820151818401526020810190506114c9565b50505050905001604052602001805190602001909291908051604051939291908464010000000082111561151757600080fd5b8382019150602082018581111561152d57600080fd5b825186600182028301116401000000008211171561154a57600080fd5b8083526020830192505050908051906020019080838360005b8381101561157e578082015181840152602081019050611563565b50505050905090810190601f1680156115ab5780820380516001836020036101000a031916815260200191505b5060405250505090919293509091925090915090505080915050600460008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff169150509392505050565b60008686866040516020018084805190602001908083835b602083106116945780518252602082019150602081019050602083039250611671565b6001836020036101000a03801982511681845116808217855250505050505090500183805190602001908083835b602083106116e557805182526020820191506020810190506020830392506116c2565b6001836020036101000a038019825116818451168082178552505050505050905001828051906020019060200280838360005b83811015611733578082015181840152602081019050611718565b505050509050019350505050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b602083106117c857805182526020820191506020810190506020830392506117a5565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611864573d6000803e3d6000fd5b50505060206040510351905061187c8189898961241b565b5050505050505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310611951578051825260208201915060208101905060208303925061192e565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156119ed573d6000803e3d6000fd5b505050602060405103519050611a038187612a17565b505050505050565b6060600560008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015611acc57602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311611a82575b50505050509050919050565b60018181548110611ae557fe5b906000526020600020016000915054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310611bdf5780518252602082019150602081019050602083039250611bbc565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611c7b573d6000803e3d6000fd5b505050602060405103519050611c9081612fa6565b505050505050565b600084604051602001808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1660601b8152601401915050604051602081830303815290604052805190602001209050600060016040518060400160405280601c81526020017f19457468657265756d205369676e6564204d6573736167653a0a333200000000815250836040516020018083805190602001908083835b60208310611d635780518252602082019150602081019050602083039250611d40565b6001836020036101000a038019825116818451168082178552505050505050905001828152602001925050506040516020818303038152906040528051906020012086868660405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015611dff573d6000803e3d6000fd5b505050602060405103519050611e1581876133e5565b505050505050565b611e2633612fa6565b565b606060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663a5b5ffbb85856040518363ffffffff1660e01b815260040180837dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001827cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019250505060006040518083038186803b158015611f2857600080fd5b505afa158015611f3c573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060c0811015611f6657600080fd5b810190808051906020019092919080516040519392919084640100000000821115611f9057600080fd5b83820191506020820185811115611fa657600080fd5b8251866020820283011164010000000082111715611fc357600080fd5b8083526020830192505050908051906020019060200280838360005b83811015611ffa578082015181840152602081019050611fdf565b505050509050016040526020018051604051939291908464010000000082111561202357600080fd5b8382019150602082018581111561203957600080fd5b825186602082028301116401000000008211171561205657600080fd5b8083526020830192505050908051906020019060200280838360005b8381101561208d578082015181840152602081019050612072565b50505050905001604052602001805160405193929190846401000000008211156120b657600080fd5b838201915060208201858111156120cc57600080fd5b82518660208202830111640100000000821117156120e957600080fd5b8083526020830192505050908051906020019060200280838360005b83811015612120578082015181840152602081019050612105565b50505050905001604052602001805190602001909291908051604051939291908464010000000082111561215357600080fd5b8382019150602082018581111561216957600080fd5b825186600182028301116401000000008211171561218657600080fd5b8083526020830192505050908051906020019080838360005b838110156121ba57808201518184015260208101905061219f565b50505050905090810190601f1680156121e75780820380516001836020036101000a031916815260200191505b5060405250505090919293509091925090915090505080915050600560008273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156122c057602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019060010190808311612276575b505050505091505092915050565b6122d83382612a17565b50565b6122e73384848461241b565b505050565b6000600460008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16905092915050565b61238a33826133e5565b50565b6060600180548060200260200160405190810160405280929190818152602001828054801561241157602002820191906000526020600020905b8160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190600101908083116123c7575b5050505050905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0866040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b1580156124bb57600080fd5b505afa1580156124cf573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060408110156124f957600080fd5b81019080805190602001909291908051604051939291908464010000000082111561252357600080fd5b8382019150602082018581111561253957600080fd5b825186600182028301116401000000008211171561255657600080fd5b8083526020830192505050908051906020019080838360005b8381101561258a57808201518184015260208101905061256f565b50505050905090810190601f1680156125b75780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612647576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603f815260200180613b26603f913960400191505060405180910390fd5b60008251116126be576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f4e6f207065726d697373696f6e7320676976656e2e000000000000000000000081525060200191505060405180910390fd5b604051806060016040528085815260200184815260200183815250600360008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000190805190602001906127349291906138d1565b5060208201518160010190805190602001906127519291906138d1565b50604082015181600201908051906020019061276e929190613951565b5090505060001515600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515141561288a5760018590806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506001600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055505b7f533987f16b883a1fe0783ff9c6c6cedcf97aee0eaf60cc491c7a3e48c9f1b5d784848488604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845288818151815260200191508051906020019080838360005b8381101561292957808201518184015260208101905061290e565b50505050905090810190601f1680156129565780820380516001836020036101000a031916815260200191505b50848103835287818151815260200191508051906020019080838360005b8381101561298f578082015181840152602081019050612974565b50505050905090810190601f1680156129bc5780820380516001836020036101000a031916815260200191505b50848103825286818151815260200191508051906020019060200280838360005b838110156129f85780820151818401526020810190506129dd565b5050505090500197505050505050505060405180910390a15050505050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b158015612ab757600080fd5b505afa158015612acb573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506040811015612af557600080fd5b810190808051906020019092919080516040519392919084640100000000821115612b1f57600080fd5b83820191506020820185811115612b3557600080fd5b8251866001820283011164010000000082111715612b5257600080fd5b8083526020830192505050908051906020019080838360005b83811015612b86578082015181840152602081019050612b6b565b50505050905090810190601f168015612bb35780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415612c43576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613a9b602e913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514612cec576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180613a4e6023913960400191505060405180910390fd5b60001515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff16151514612dd2576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526031815260200180613ac96031913960400191505060405180910390fd5b6001600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208290806001815401808255809150509060018203906000526020600020016000909192909190916101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550507f91aba333be281959a9130cefbc0e18d32a4f1c6bc3a922c7fdb084c1179f413a8383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b60011515600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1615151461304f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602a815260200180613a71602a913960400191505060405180910390fd5b61305761399e565b600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020604051806060016040529081600082018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561313a5780601f1061310f5761010080835404028352916020019161313a565b820191906000526020600020905b81548152906001019060200180831161311d57829003601f168201915b50505050508152602001600182018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156131dc5780601f106131b1576101008083540402835291602001916131dc565b820191906000526020600020905b8154815290600101906020018083116131bf57829003601f168201915b505050505081526020016002820180548060200260200160405190810160405280929190818152602001828054801561323457602002820191906000526020600020905b815481526020019060010190808311613220575b5050505050815250509050600360008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000808201600061328f91906139bf565b60018201600061329f91906139bf565b6002820160006132af9190613a07565b50506000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f533987f16b883a1fe0783ff9c6c6cedcf97aee0eaf60cc491c7a3e48c9f1b5d7816040015183604051808060200180602001806020018573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848103845260008152602001602001848103835260008152602001602001848103825286818151815260200191508051906020019060200280838360005b838110156133cb5780820151818401526020810190506133b0565b505050509050019550505050505060405180910390a15050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166388cf72a0846040518263ffffffff1660e01b8152600401808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060006040518083038186803b15801561348557600080fd5b505afa158015613499573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f8201168201806040525060408110156134c357600080fd5b8101908080519060200190929190805160405193929190846401000000008211156134ed57600080fd5b8382019150602082018581111561350357600080fd5b825186600182028301116401000000008211171561352057600080fd5b8083526020830192505050908051906020019080838360005b83811015613554578082015181840152602081019050613539565b50505050905090810190601f1680156135815780820380516001836020036101000a031916815260200191505b50604052505050509050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415613611576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602e815260200180613a9b602e913960400191505060405180910390fd5b60011515600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146136ba576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526023815260200180613a4e6023913960400191505060405180910390fd5b60011515600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff161515146137a0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602c815260200180613afa602c913960400191505060405180910390fd5b6000600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff0219169083151502179055507f91aba333be281959a9130cefbc0e18d32a4f1c6bc3a922c7fdb084c1179f413a8383604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a1505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061391257805160ff1916838001178555613940565b82800160010185558215613940579182015b8281111561393f578251825591602001919060010190613924565b5b50905061394d9190613a28565b5090565b82805482825590600052602060002090810192821561398d579160200282015b8281111561398c578251825591602001919060010190613971565b5b50905061399a9190613a28565b5090565b60405180606001604052806060815260200160608152602001606081525090565b50805460018160011615610100020316600290046000825580601f106139e55750613a04565b601f016020900490600052602060002090810190613a039190613a28565b5b50565b5080546000825590600052602060002090810190613a259190613a28565b50565b613a4a91905b80821115613a46576000816000905550600101613a2e565b5090565b9056fe50726f766964657220686173206e6f207265676973746572656420536572766963652e43616e6e6f742064656c6574652073657276696365207468617420646f6573206e6f742065786973742e53657276696365207573657220686173206e6f207061727479206c697374696e6720696e2052656769737472792e41677265656d656e7420616c7265616479206d616465206265747765656e207573657220616e642070726f76696465722e4e6f2041677265656d656e74206d616465206265747765656e207573657220616e642070726f76696465722e547279696e6720746f2072656769737465722061207365727669636520776974686f7574207061727479206c697374696e6720696e2052656769737472792ea265627a7a723158207a1ae39705dcaf020cf6abf6f43eb996d9647cbe073a901e49c5f0761617e14c64736f6c634300050f0032"; public static final String FUNC_PROVIDERS = "providers"; - public static final String FUNC_SETAPP = "setApp"; + public static final String FUNC_SETSERVICE = "setService"; - public static final String FUNC_SETAPPRAW = "setAppRaw"; + public static final String FUNC_SETSERVICERAW = "setServiceRaw"; - public static final String FUNC_GETAPP = "getApp"; + public static final String FUNC_GETSERVICE = "getService"; - public static final String FUNC_DELETEAPP = "deleteApp"; + public static final String FUNC_DELETESERVICE = "deleteService"; - public static final String FUNC_DELETEAPPRAW = "deleteAppRaw"; + public static final String FUNC_DELETESERVICERAW = "deleteServiceRaw"; public static final String FUNC_GETPROVIDERS = "getProviders"; @@ -78,11 +78,11 @@ public class Permissions extends Contract { public static final String FUNC_HASUSERAGREEMENTBYOCPI = "hasUserAgreementByOcpi"; - public static final Event APPAGREEMENT_EVENT = new Event("AppAgreement", + public static final Event SERVICEAGREEMENT_EVENT = new Event("ServiceAgreement", Arrays.>asList(new TypeReference
() {}, new TypeReference
() {})); ; - public static final Event APPUPDATE_EVENT = new Event("AppUpdate", + public static final Event SERVICEUPDATE_EVENT = new Event("ServiceUpdate", Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference>() {}, new TypeReference
() {})); ; @@ -90,8 +90,8 @@ public class Permissions extends Contract { static { _addresses = new HashMap(); - _addresses.put("73799", "0xB02f333b9E32EFCdE0394373C39f5d59fBDB2A0F"); - _addresses.put("9", "0x75c35C980C0d37ef46DF04d31A140b65503c0eEd"); + _addresses.put("73799", "0xa72479612436D1f9df5FA975baFB9ae74529aB0E"); + _addresses.put("9", "0xf25186B5081Ff5cE73482AD761DB0eB0d25abfBF"); } @Deprecated @@ -112,11 +112,11 @@ protected Permissions(String contractAddress, Web3j web3j, TransactionManager tr super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider); } - public List getAppAgreementEvents(TransactionReceipt transactionReceipt) { - List valueList = extractEventParametersWithLog(APPAGREEMENT_EVENT, transactionReceipt); - ArrayList responses = new ArrayList(valueList.size()); + public List getServiceAgreementEvents(TransactionReceipt transactionReceipt) { + List valueList = extractEventParametersWithLog(SERVICEAGREEMENT_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); for (Contract.EventValuesWithLog eventValues : valueList) { - AppAgreementEventResponse typedResponse = new AppAgreementEventResponse(); + ServiceAgreementEventResponse typedResponse = new ServiceAgreementEventResponse(); typedResponse.log = eventValues.getLog(); typedResponse.user = (String) eventValues.getNonIndexedValues().get(0).getValue(); typedResponse.provider = (String) eventValues.getNonIndexedValues().get(1).getValue(); @@ -125,12 +125,12 @@ public List getAppAgreementEvents(TransactionReceipt return responses; } - public Flowable appAgreementEventFlowable(EthFilter filter) { - return web3j.ethLogFlowable(filter).map(new Function() { + public Flowable serviceAgreementEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(new Function() { @Override - public AppAgreementEventResponse apply(Log log) { - Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(APPAGREEMENT_EVENT, log); - AppAgreementEventResponse typedResponse = new AppAgreementEventResponse(); + public ServiceAgreementEventResponse apply(Log log) { + Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(SERVICEAGREEMENT_EVENT, log); + ServiceAgreementEventResponse typedResponse = new ServiceAgreementEventResponse(); typedResponse.log = log; typedResponse.user = (String) eventValues.getNonIndexedValues().get(0).getValue(); typedResponse.provider = (String) eventValues.getNonIndexedValues().get(1).getValue(); @@ -139,17 +139,17 @@ public AppAgreementEventResponse apply(Log log) { }); } - public Flowable appAgreementEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + public Flowable serviceAgreementEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); - filter.addSingleTopic(EventEncoder.encode(APPAGREEMENT_EVENT)); - return appAgreementEventFlowable(filter); + filter.addSingleTopic(EventEncoder.encode(SERVICEAGREEMENT_EVENT)); + return serviceAgreementEventFlowable(filter); } - public List getAppUpdateEvents(TransactionReceipt transactionReceipt) { - List valueList = extractEventParametersWithLog(APPUPDATE_EVENT, transactionReceipt); - ArrayList responses = new ArrayList(valueList.size()); + public List getServiceUpdateEvents(TransactionReceipt transactionReceipt) { + List valueList = extractEventParametersWithLog(SERVICEUPDATE_EVENT, transactionReceipt); + ArrayList responses = new ArrayList(valueList.size()); for (Contract.EventValuesWithLog eventValues : valueList) { - AppUpdateEventResponse typedResponse = new AppUpdateEventResponse(); + ServiceUpdateEventResponse typedResponse = new ServiceUpdateEventResponse(); typedResponse.log = eventValues.getLog(); typedResponse.name = (String) eventValues.getNonIndexedValues().get(0).getValue(); typedResponse.url = (String) eventValues.getNonIndexedValues().get(1).getValue(); @@ -160,12 +160,12 @@ public List getAppUpdateEvents(TransactionReceipt transa return responses; } - public Flowable appUpdateEventFlowable(EthFilter filter) { - return web3j.ethLogFlowable(filter).map(new Function() { + public Flowable serviceUpdateEventFlowable(EthFilter filter) { + return web3j.ethLogFlowable(filter).map(new Function() { @Override - public AppUpdateEventResponse apply(Log log) { - Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(APPUPDATE_EVENT, log); - AppUpdateEventResponse typedResponse = new AppUpdateEventResponse(); + public ServiceUpdateEventResponse apply(Log log) { + Contract.EventValuesWithLog eventValues = extractEventParametersWithLog(SERVICEUPDATE_EVENT, log); + ServiceUpdateEventResponse typedResponse = new ServiceUpdateEventResponse(); typedResponse.log = log; typedResponse.name = (String) eventValues.getNonIndexedValues().get(0).getValue(); typedResponse.url = (String) eventValues.getNonIndexedValues().get(1).getValue(); @@ -176,10 +176,10 @@ public AppUpdateEventResponse apply(Log log) { }); } - public Flowable appUpdateEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { + public Flowable serviceUpdateEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) { EthFilter filter = new EthFilter(startBlock, endBlock, getContractAddress()); - filter.addSingleTopic(EventEncoder.encode(APPUPDATE_EVENT)); - return appUpdateEventFlowable(filter); + filter.addSingleTopic(EventEncoder.encode(SERVICEUPDATE_EVENT)); + return serviceUpdateEventFlowable(filter); } public RemoteFunctionCall providers(BigInteger param0) { @@ -189,9 +189,9 @@ public RemoteFunctionCall providers(BigInteger param0) { return executeRemoteCallSingleValueReturn(function, String.class); } - public RemoteFunctionCall setApp(String name, String url, List permissions) { + public RemoteFunctionCall setService(String name, String url, List permissions) { final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( - FUNC_SETAPP, + FUNC_SETSERVICE, Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name), new org.web3j.abi.datatypes.Utf8String(url), new org.web3j.abi.datatypes.DynamicArray( @@ -201,9 +201,9 @@ public RemoteFunctionCall setApp(String name, String url, Li return executeRemoteCallTransaction(function); } - public RemoteFunctionCall setAppRaw(String name, String url, List permissions, BigInteger v, byte[] r, byte[] s) { + public RemoteFunctionCall setServiceRaw(String name, String url, List permissions, BigInteger v, byte[] r, byte[] s) { final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( - FUNC_SETAPPRAW, + FUNC_SETSERVICERAW, Arrays.asList(new org.web3j.abi.datatypes.Utf8String(name), new org.web3j.abi.datatypes.Utf8String(url), new org.web3j.abi.datatypes.DynamicArray( @@ -216,8 +216,8 @@ public RemoteFunctionCall setAppRaw(String name, String url, return executeRemoteCallTransaction(function); } - public RemoteFunctionCall>> getApp(String provider) { - final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETAPP, + public RemoteFunctionCall>> getService(String provider) { + final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function(FUNC_GETSERVICE, Arrays.asList(new org.web3j.abi.datatypes.Address(provider)), Arrays.>asList(new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference() {}, new TypeReference>() {})); return new RemoteFunctionCall>>(function, @@ -235,17 +235,17 @@ public Tuple5> call() throws Ex }); } - public RemoteFunctionCall deleteApp() { + public RemoteFunctionCall deleteService() { final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( - FUNC_DELETEAPP, + FUNC_DELETESERVICE, Arrays.asList(), Collections.>emptyList()); return executeRemoteCallTransaction(function); } - public RemoteFunctionCall deleteAppRaw(String provider, BigInteger v, byte[] r, byte[] s) { + public RemoteFunctionCall deleteServiceRaw(String provider, BigInteger v, byte[] r, byte[] s) { final org.web3j.abi.datatypes.Function function = new org.web3j.abi.datatypes.Function( - FUNC_DELETEAPPRAW, + FUNC_DELETESERVICERAW, Arrays.asList(new org.web3j.abi.datatypes.Address(provider), new org.web3j.abi.datatypes.generated.Uint8(v), new org.web3j.abi.datatypes.generated.Bytes32(r), @@ -403,13 +403,13 @@ public static String getPreviouslyDeployedAddress(String networkId) { return _addresses.get(networkId); } - public static class AppAgreementEventResponse extends BaseEventResponse { + public static class ServiceAgreementEventResponse extends BaseEventResponse { public String user; public String provider; } - public static class AppUpdateEventResponse extends BaseEventResponse { + public static class ServiceUpdateEventResponse extends BaseEventResponse { public String name; public String url; diff --git a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt index ec20408..e4ebee0 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandler.kt @@ -113,7 +113,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, assertValidSignature() val (url, headers) = routingService.prepareLocalPlatformRequest(request, proxied) - asyncTaskService.forwardOcpiRequestToLinkedApps(this, fromLocalPlatform) + asyncTaskService.forwardOcpiRequestToLinkedServices(this, fromLocalPlatform) httpService.makeOcpiRequest(url, headers, request) } @@ -121,7 +121,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, assertValidSignature(false) val (url, headers, body) = routingService.prepareRemotePlatformRequest(request, proxied) - asyncTaskService.forwardOcpiRequestToLinkedApps(this, fromLocalPlatform) + asyncTaskService.forwardOcpiRequestToLinkedServices(this, fromLocalPlatform) httpService.postOcnMessage(url, headers, body) } } @@ -157,7 +157,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, // send the request with the modified body val (url, headers) = routingService.prepareLocalPlatformRequest(request) - asyncTaskService.forwardOcpiRequestToLinkedApps(this) + asyncTaskService.forwardOcpiRequestToLinkedServices(this) httpService.makeOcpiRequest(url, headers, modifiedRequest) } @@ -175,7 +175,7 @@ class OcpiRequestHandler(request: OcpiRequestVariables, modifiedRequest.copy(proxyUID = proxyUID, proxyResource = responseUrl) } - asyncTaskService.forwardOcpiRequestToLinkedApps(this) + asyncTaskService.forwardOcpiRequestToLinkedServices(this) httpService.postOcnMessage(url, headers, body) } @@ -194,8 +194,8 @@ class OcpiRequestHandler(request: OcpiRequestVariables, } /** - * Forwards a message to another recipient (i.e. an App with the appropriate permissions). - * @param newRecipient country_code and party_id of the App + * Forwards a message to another recipient (i.e. a Service with the appropriate permissions). + * @param newRecipient country_code and party_id of the Service */ fun forwardAgain(newRecipient: BasicRole): OcpiResponseHandler { val modifiedRequest = request.copy(headers = request.headers.copy(receiver = newRecipient)) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index ace0193..b653b78 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -96,12 +96,12 @@ data class RegistryPartyDetails(val party: BasicRole, val roles: List, val data class RegistryNode(val operator: String, val url: String) -data class OcnApp(val provider: BasicRole, val permissions: List) +data class OcnService(val provider: BasicRole, val permissions: List) data class BasicRequestType(val moduleID: ModuleID, val interfaceRole: InterfaceRole) // each enum value takes a "matcher" which tests a given module/interface -enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) { +enum class OcnServicePermission(val matches: (request: BasicRequestType) -> Boolean) { FORWARD_ALL({true}), FORWARD_ALL_SENDER({it.interfaceRole == InterfaceRole.SENDER}), FORWARD_ALL_RECEIVER({it.interfaceRole == InterfaceRole.RECEIVER}), @@ -122,7 +122,7 @@ enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) companion object { - fun getByIndex(index: BigInteger): OcnAppPermission? { + fun getByIndex(index: BigInteger): OcnServicePermission? { return try { values()[index.intValueExact()] } catch (e: ArrayIndexOutOfBoundsException) { @@ -132,6 +132,6 @@ enum class OcnAppPermission(val matches: (request: BasicRequestType) -> Boolean) } } -fun OcnAppPermission.matches(moduleID: ModuleID, interfaceRole: InterfaceRole): Boolean { +fun OcnServicePermission.matches(moduleID: ModuleID, interfaceRole: InterfaceRole): Boolean { return matches(BasicRequestType(moduleID, interfaceRole)) } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index dc164b1..d10ac30 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -30,12 +30,12 @@ class AsyncTaskService(private val registryService: RegistryService) { } /** - * Finds all apps, linked to a sender, with permissions that grant them access to a given request type. - * Once apps have been found, sends via provided request handler. + * Finds all services, linked to a sender, with permissions that grant them access to a given request type. + * Once services have been found, sends via provided request handler. */ @Async - fun forwardOcpiRequestToLinkedApps(requestHandler: OcpiRequestHandler<*>, fromLocalPlatform: Boolean = true) { - // we only want to forward to apps if the module is one of the default OCPI modules, + fun forwardOcpiRequestToLinkedServices(requestHandler: OcpiRequestHandler<*>, fromLocalPlatform: Boolean = true) { + // we only want to forward to services if the module is one of the default OCPI modules, // and only if the sender is a local platform (to avoid repeat forwarding on the recipient node) val isDefaultModule = requestHandler.request.module != ModuleID.CUSTOM @@ -47,7 +47,7 @@ class AsyncTaskService(private val registryService: RegistryService) { requestHandler.forwardAgain(it.provider) } catch (e: Exception) { // fire and forget - logger.warn("Error forwarding request to app ${it.provider}: ${e.message}") + logger.warn("Error forwarding request to service ${it.provider}: ${e.message}") } } } diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt index b6a590c..972a836 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/RegistryService.kt @@ -83,22 +83,22 @@ class RegistryService(private val registry: Registry, } /** - * Gets an OCN App's details (provider BasicRole) and required permissions by owner's identity (Eth public key) + * Gets an OCN Service's details (provider BasicRole) and required permissions by owner's identity (Eth public key) */ - fun getOcnApp(provider: String): OcnApp { - val (countryCode, partyId, _, _, needs) = permissions.getApp(provider).sendAsync().get() + fun getOcnService(provider: String): OcnService{ + val (countryCode, partyId, _, _, needs) = permissions.getService(provider).sendAsync().get() val role = BasicRole( id = partyId.toString(Charsets.UTF_8), country = countryCode.toString(Charsets.UTF_8)) - return OcnApp( + return OcnService( provider = role, - permissions = needs.mapNotNull { need -> OcnAppPermission.getByIndex(need) }) + permissions = needs.mapNotNull { need -> OcnServicePermission.getByIndex(need) }) } /** * Gets OCN apps a given role has made agreements with, based on a module interface */ - fun getAgreementsByInterface(role: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): Sequence { + fun getAgreementsByInterface(role: BasicRole, module: ModuleID, interfaceRole: InterfaceRole): Sequence { val country = role.country.toByteArray() val id = role.id.toByteArray() @@ -106,7 +106,7 @@ class RegistryService(private val registry: Registry, return agreements .asSequence() - .map { getOcnApp(it as String) } + .map { getOcnService(it as String) } .filter { it.permissions.any { permission -> permission.matches(module, interfaceRole) } } } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt index 95d3260..dd5732c 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/components/OcpiRequestHandlerTest.kt @@ -73,7 +73,7 @@ class OcpiRequestHandlerTest { every { routingService.isRoleKnown(variables.headers.receiver) } returns true every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { hubClientInfoService.renewClientConnection(variables.headers.receiver) } just Runs - every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedServices(requestHandler) } just Runs every { registryService.getAgreementsByInterface(variables.headers.sender, variables.module, variables.interfaceRole) } returns sequenceOf() every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) @@ -132,7 +132,7 @@ class OcpiRequestHandlerTest { receiverSig.signatory, "0x9bC1169Ca09555bf2721A5C9eC6D69c8073bfeB4") every { routingService.prepareLocalPlatformRequest(variables, false) } returns Pair(recipientUrl, outgoingHeaders) every { httpService.makeOcpiRequest(recipientUrl, outgoingHeaders, variables) } returns expectedResponse - every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedServices(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) @@ -177,7 +177,7 @@ class OcpiRequestHandlerTest { every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse every { hubClientInfoService.renewClientConnection(variables.headers.sender) } just Runs every { routingService.isRoleKnown(variables.headers.receiver) } returns false - every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedServices(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) @@ -232,7 +232,7 @@ class OcpiRequestHandlerTest { every { routingService.prepareRemotePlatformRequest(variables, false) } returns Triple( recipientUrl, outgoingHeaders, outgoingBody) every { httpService.postOcnMessage(recipientUrl, outgoingHeaders, outgoingBody) } returns expectedResponse - every { asyncTaskService.forwardOcpiRequestToLinkedApps(requestHandler) } just Runs + every { asyncTaskService.forwardOcpiRequestToLinkedServices(requestHandler) } just Runs every { responseHandlerBuilder.build(variables, expectedResponse) } returns responseHandler every { responseHandler.getResponse() } returns ResponseEntity.ok(expectedResponse.body) diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt index 6006117..d9ca29b 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/AppInterfaceTest.kt @@ -6,12 +6,12 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.http.HttpMethod import snc.openchargingnetwork.node.integration.utils.* -import snc.openchargingnetwork.node.models.OcnAppPermission +import snc.openchargingnetwork.node.models.OcnServicePermission import snc.openchargingnetwork.node.models.ocpi.InterfaceRole import snc.openchargingnetwork.node.models.ocpi.ModuleID import java.util.concurrent.TimeUnit -class AppInterfaceTest { +class ServiceInterfaceTest { // TODO: integration test setup could be in an inheritable class private lateinit var networkComponents: NetworkComponents @@ -44,22 +44,22 @@ class AppInterfaceTest { return cpo1Seen && cpo2Seen } - private fun testForwarding(recipient: TestCpo, app: TestCpo) { - app.server.setAppPermissions(listOf(OcnAppPermission.FORWARD_ALL)) - msp.server.agreeToAppPermissions(app.address) + private fun testForwarding(recipient: TestCpo, service: TestCpo) { + service.server.setServicePermissions(listOf(OcnServicePermission.FORWARD_ALL)) + msp.server.agreeToServicePermissions(service.address) msp.server.getLocation(recipient.party) await.atMost(2L, TimeUnit.SECONDS).until { seenByBothCpos() } } @Test - fun fowardsRequestToApp_Local() { - testForwarding(recipient = cpo2, app = cpo1) + fun fowardsRequestToService_Local() { + testForwarding(recipient = cpo2, service = cpo1) } @Test - fun fowardsRequestToApp_Remote() { - testForwarding(recipient = cpo1, app = cpo2) + fun fowardsRequestToService_Remote() { + testForwarding(recipient = cpo1, service = cpo2) } } \ No newline at end of file diff --git a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt index 48bed7f..5a5de77 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/integration/parties/PartyServer.kt @@ -8,7 +8,7 @@ import shareandcharge.openchargingnetwork.notary.ValuesToSign import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.integration.utils.* -import snc.openchargingnetwork.node.models.OcnAppPermission +import snc.openchargingnetwork.node.models.OcnServicePermission import snc.openchargingnetwork.node.models.OcnRulesListType import snc.openchargingnetwork.node.models.ocpi.* import snc.openchargingnetwork.node.tools.generateUUIDv4Token @@ -68,14 +68,14 @@ open class PartyServer(val config: PartyDefinition, deployedContracts: OcnContra node = contracts.registry.getNode(operator).sendAsync().get() } - fun setAppPermissions(permissions: List) { - val name = "Test App" // optional name - val url = "https://test.app" // optional public url + fun setServicePermissions(permissions: List) { + val name = "Test Service" // optional name + val url = "https://test.Service" // optional public url val permissionsIntList = permissions.map { it.ordinal.toBigInteger() } - contracts.permissions.setApp(name, url, permissionsIntList).sendAsync().get() + contracts.permissions.setService(name, url, permissionsIntList).sendAsync().get() } - fun agreeToAppPermissions(provider: String) { + fun agreeToServicePermissions(provider: String) { contracts.permissions.createAgreement(provider).sendAsync().get() } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt b/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt index 0962e20..770fc1a 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/models/Ocn.kt @@ -8,143 +8,143 @@ import snc.openchargingnetwork.node.models.ocpi.InterfaceRole import snc.openchargingnetwork.node.models.ocpi.ModuleID import java.util.stream.Stream -data class PermissionsMatcherTestCase(val request: BasicRequestType, val permission: OcnAppPermission, val expected: Boolean) +data class PermissionsMatcherTestCase(val request: BasicRequestType, val permission: OcnServicePermission, val expected: Boolean) -class OcnAppPermissionsTest { +class OcnServicePermissionsTest { private fun permissionsTestSources(): Stream { return Stream.of( Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_ALL, + permission = OcnServicePermission.FORWARD_ALL, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_ALL_RECEIVER, + permission = OcnServicePermission.FORWARD_ALL_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_ALL_RECEIVER, + permission = OcnServicePermission.FORWARD_ALL_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_ALL_SENDER, + permission = OcnServicePermission.FORWARD_ALL_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_ALL_SENDER, + permission = OcnServicePermission.FORWARD_ALL_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_LOCATIONS_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_LOCATIONS_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_LOCATIONS_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.LOCATIONS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_LOCATIONS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_LOCATIONS_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_SESSIONS_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_SESSIONS_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_SESSIONS_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.SESSIONS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_SESSIONS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_SESSIONS_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CDRS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_CDRS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_CDRS_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CDRS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_CDRS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_CDRS_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CDRS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_CDRS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_CDRS_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CDRS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_CDRS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_CDRS_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_TARIFFS_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_TARIFFS_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_TARIFFS_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TARIFFS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_TARIFFS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_TARIFFS_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_TOKENS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_TOKENS_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_TOKENS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_TOKENS_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_TOKENS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_TOKENS_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.TOKENS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_TOKENS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_TOKENS_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_COMMANDS_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_COMMANDS_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_COMMANDS_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.COMMANDS, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_COMMANDS_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_COMMANDS_RECEIVER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_CHARGINGPROFILES_SENDER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_SENDER, + permission = OcnServicePermission.FORWARD_MODULE_CHARGINGPROFILES_SENDER, expected = false)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.RECEIVER), - permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_CHARGINGPROFILES_RECEIVER, expected = true)), Arguments.of(PermissionsMatcherTestCase( request = BasicRequestType(ModuleID.CHARGING_PROFILES, InterfaceRole.SENDER), - permission = OcnAppPermission.FORWARD_MODULE_CHARGINGPROFILES_RECEIVER, + permission = OcnServicePermission.FORWARD_MODULE_CHARGINGPROFILES_RECEIVER, expected = false)) ) } diff --git a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt index 675d9fb..32d4f00 100644 --- a/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt +++ b/src/test/kotlin/snc/openchargingnetwork/node/services/RegistryServiceTest.kt @@ -9,8 +9,8 @@ import org.web3j.tuples.generated.Tuple5 import snc.openchargingnetwork.contracts.Permissions import snc.openchargingnetwork.contracts.Registry import snc.openchargingnetwork.node.config.NodeProperties -import snc.openchargingnetwork.node.models.OcnApp -import snc.openchargingnetwork.node.models.OcnAppPermission +import snc.openchargingnetwork.node.models.OcnService +import snc.openchargingnetwork.node.models.OcnServicePermission import snc.openchargingnetwork.node.models.RegistryNode import snc.openchargingnetwork.node.models.ocpi.BasicRole import snc.openchargingnetwork.node.models.ocpi.InterfaceRole @@ -87,7 +87,7 @@ class RegistryServiceTest { } returns listOf("0x059a44557cF9Bd2b446d72fC772254F0E487BACf") every { - permissions.getApp("0x059a44557cF9Bd2b446d72fC772254F0E487BACf").sendAsync().get() + permissions.getService("0x059a44557cF9Bd2b446d72fC772254F0E487BACf").sendAsync().get() } returns Tuple5( provider.country.toByteArray(), provider.id.toByteArray(), @@ -100,7 +100,7 @@ class RegistryServiceTest { assertThat(actual.count()).isEqualTo(1) assertThat(actual.iterator().next()).isEqualTo( - OcnApp(provider = provider, permissions = listOf(OcnAppPermission.FORWARD_ALL_RECEIVER)) + OcnService(provider = provider, permissions = listOf(OcnServicePermission.FORWARD_ALL_RECEIVER)) ) } } \ No newline at end of file From 1b1299fde38098cd4a3ef874fd896a106c3467c0 Mon Sep 17 00:00:00 2001 From: Arzon Date: Fri, 19 Jun 2020 15:10:23 +0600 Subject: [PATCH 25/26] set service/permission option Signed-off-by: Arzon --- .../snc/openchargingnetwork/node/config/NodeProperties.kt | 2 ++ .../openchargingnetwork/node/services/AsyncTaskService.kt | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt index acf45e0..77f4c57 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt @@ -53,4 +53,6 @@ class NodeProperties { var plannedPartySearchRate: String = "3600000" // defaults to 1 hour var plannedPartySearchEnabled: Boolean = true + + var messageForwardEnabled: Boolean = true } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index d10ac30..d6daec5 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -20,10 +20,11 @@ import org.slf4j.LoggerFactory import org.springframework.scheduling.annotation.Async import org.springframework.stereotype.Service import snc.openchargingnetwork.node.components.OcpiRequestHandler +import snc.openchargingnetwork.node.config.NodeProperties import snc.openchargingnetwork.node.models.ocpi.ModuleID @Service -class AsyncTaskService(private val registryService: RegistryService) { +class AsyncTaskService(private val registryService: RegistryService, private val properties: NodeProperties) { companion object { private val logger = LoggerFactory.getLogger(AsyncTaskService::class.java) @@ -37,9 +38,11 @@ class AsyncTaskService(private val registryService: RegistryService) { fun forwardOcpiRequestToLinkedServices(requestHandler: OcpiRequestHandler<*>, fromLocalPlatform: Boolean = true) { // we only want to forward to services if the module is one of the default OCPI modules, // and only if the sender is a local platform (to avoid repeat forwarding on the recipient node) + // and also forward if message forward option is enabled val isDefaultModule = requestHandler.request.module != ModuleID.CUSTOM + val isEnabledForwarding = properties.messageForwardEnabled - if (isDefaultModule && fromLocalPlatform) { + if (isDefaultModule && fromLocalPlatform && isEnabledForwarding) { val request = requestHandler.request registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach { From 591141263b38f0d33cc8bfe4d955bd48108291b8 Mon Sep 17 00:00:00 2001 From: Arzon Date: Fri, 19 Jun 2020 22:19:53 +0600 Subject: [PATCH 26/26] changed variable name Signed-off-by: Arzon --- .../snc/openchargingnetwork/node/config/NodeProperties.kt | 2 +- .../openchargingnetwork/node/services/AsyncTaskService.kt | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt index 77f4c57..b286941 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/config/NodeProperties.kt @@ -54,5 +54,5 @@ class NodeProperties { var plannedPartySearchEnabled: Boolean = true - var messageForwardEnabled: Boolean = true + var serviceEnabled: Boolean = true } \ No newline at end of file diff --git a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt index d6daec5..35b7b2d 100644 --- a/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt +++ b/src/main/kotlin/snc/openchargingnetwork/node/services/AsyncTaskService.kt @@ -38,11 +38,10 @@ class AsyncTaskService(private val registryService: RegistryService, private val fun forwardOcpiRequestToLinkedServices(requestHandler: OcpiRequestHandler<*>, fromLocalPlatform: Boolean = true) { // we only want to forward to services if the module is one of the default OCPI modules, // and only if the sender is a local platform (to avoid repeat forwarding on the recipient node) - // and also forward if message forward option is enabled + // and also forward if service interface option is enabled val isDefaultModule = requestHandler.request.module != ModuleID.CUSTOM - val isEnabledForwarding = properties.messageForwardEnabled - if (isDefaultModule && fromLocalPlatform && isEnabledForwarding) { + if (isDefaultModule && fromLocalPlatform && properties.serviceEnabled) { val request = requestHandler.request registryService.getAgreementsByInterface(request.headers.sender, request.module, request.interfaceRole) .forEach {