Our organization uses many classes which are maintained by different teams and exchange dictionary collections. We want to make sure that the provider team will be notified when they break a contract. Is this possible using pact?
One sample provider class:
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;
}
}
So we want to be able to break a contract test when a developer makes this code change which will compile OK but break the consumers:
String iban = input.get("account no").toString();
We did this by writing an adapter REST service in front of the actual class accepting a dictionary:
@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..
Consumer side constructs the contract using simple extension classes to Pact framework:
@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();
}
Extension classes convert the dictionary parameter to REST service parameters:
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());
}
}
When the consumer creates the contract, here is what he gets:
Method:POST
Path:/CALL_DICTIONARY_SERVICE
Body:
{
"amount": "123",
"iban": "TR000123456",
"_DICTIONARY_SERVIS_NAME_": "TRANSFER_TO_IBAN"
}
Provider uses the dictonary service name mentioned in the contract to find the correct method and calls it with the input converted from JSON to Dictionary.