我们的组织使用许多由不同团队维护的类并交换字典集合。我们希望确保提供商团队在违反合同时会收到通知。使用契约可以吗?
一个示例提供商类:
import java.util.Dictionary;
import java.util.Hashtable;
public class MoneyTransfer {
public Dictionary<String, Object> toIBAN(Dictionary<String, Object> input)
{
double amount = (Double) input.get("amount");
String iban = input.get("iban").toString();
Double commission = doTransfer(iban, amount);
Dictionary<String, Object> output = new Hashtable<String, Object>();
output.put("commission", commission);
return output;
}
private double doTransfer(String iban, double amount) {
// TODO Auto-generated method stub
return 0;
}
}
因此,当开发人员进行此代码更改时,我们希望能够破坏合同测试,这将编译正常,但会破坏消费者:
String iban = input.get("account no").toString();
我们通过在接受字典的实际类前面编写适配器 REST 服务来做到这一点:
@RequestMapping(
value="/CALL_DICTIONARY_SERVICE",
method = { RequestMethod.POST })
public ResponseEntity<Object> callDictionaryService(@RequestBody Object request) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Dictionary<String, Object> input = DictionaryRequestFactory.createDictionary(request);
Object dictionaryService = locateDictionaryService(request);
Dictionary<String, Object> output = callDictionaryService(dictionaryService, input);
Object response = RestResponseFactory.createResponse(output);
return new ResponseEntity<Object>(response, HttpStatus.OK);
}
//Rest of the code omitted for simplicity..
消费者端使用 Pact 框架的简单扩展类构建合约:
@Pact(consumer="Money Transfer Consumer")
public RequestResponsePact pactTransferToIBAN(PactDslWithProvider dsl) {
Dictionary<String, Object> input = new Hashtable<>();
input.put("amount", "123");
input.put("iban", "TR000123456");
Dictionary<String, Object> output = new Hashtable<>();
output.put("commission", "12");
DictionaryDslWithProvider builder = new DictionaryDslWithProvider(dsl);
return builder
.given("Sender account has enough money")
.uponReceiving("TRANSFER_TO_IBAN")
.dictionary(input)
.willRespondWith()
.dictionary(output)
.toPact();
}
扩展类将字典参数转换为 REST 服务参数:
public class DictionaryDslWithProvider {
PactDslWithProvider baseDsl;
public DictionaryDslWithProvider(PactDslWithProvider baseDsl) {
this.baseDsl = baseDsl;
}
public DictionaryDslWithState given(String state) {
PactDslWithState dsl = baseDsl.given(state);
return new DictionaryDslWithState(dsl);
}
}
public class DictionaryDslWithState {
PactDslWithState baseDsl;
public DictionaryDslWithState(PactDslWithState dsl) {
baseDsl = dsl;
}
public DslRequestWithoutDictionary uponReceiving(String dictionaryServiceName) {
return new DslRequestWithoutDictionary(baseDsl.uponReceiving(dictionaryServiceName)
.path("/CALL_DICTIONARY_SERVICE"
,dictionaryServiceName
);
}
}
public class DslRequestWithoutDictionary {
PactDslRequestWithPath req;
private String dictionaryServiceName;
public DslRequestWithoutDictionary(PactDslRequestWithPath pactDslRequestWithPath, String dictionaryServiceName) {
req = pactDslRequestWithPath;
this.dictionaryServiceName = dictionaryServiceName;
}
public PactDslRequestWithDictionary dictionary(Dictionary<String, Object> dictionary) {
String jsonBody = dictionaryToJsonBody(dictionary, dictionaryServiceName);
return new PactDslRequestWithDictionary(
req
.method("POST")
.body(jsonBody)
);
}
//Rest of the code omitted for simplicity..
}
public class PactDslRequestWithDictionary {
PactDslRequestWithPath body;
public PactDslRequestWithDictionary(PactDslRequestWithPath body) {
this.body = body;
}
public DictionaryDslResponse willRespondWith() {
return new DictionaryDslResponse(body.willRespondWith());
}
}
当消费者创建合约时,他会得到以下内容:
Method:POST
Path:/CALL_DICTIONARY_SERVICE
Body:
{
"amount": "123",
"iban": "TR000123456",
"_DICTIONARY_SERVIS_NAME_": "TRANSFER_TO_IBAN"
}
Provider 使用合约中提到的字典服务名称来查找正确的方法,并使用从 JSON 转换为字典的输入来调用它。