-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement a Matcher
interface
#746
Comments
@valkolovos has expressed interest in implementing this. If possible, I would like to first get some eyes across the proposed implementation to hopefully "get it right" the first time around. Tagging @YOU54F, @mefellows |
Following you community meetup, the use of As a result, I suggest we support |
Great write up @JP-Ellis I would try to maintain as close to the public interface for matchers already existing in pact-python which are aliases to the new terms, as eluded due to Josh's comments. I think there are still ambiguity over matchers/generators and spec compat. v3 had more matchers and introduced generators should rust or client libraries enforce matchers/generators only used against the appropriate spec? example Do we need a v2/v3/v4 matchers interface, or can the client lib try and be smart about it, so the end users sets the spec, and then they get the appropriate matchers that can be used with that spec or errors if they try to step outside the bounds. What were your thoughts on this as the probably doesn't suggest the matchers are split by spec version (unlike Pact JS) I assume pact ref in rust it accepts a certain standard via the integration json format, regardless of spec and then with coerce into whatever shape is required for v2/v3/v4. I'd want to check in rust, can we store a v4 matcher/generator in a v2 spec file. what happens on the consumer/provider matching sides, just to get an idea of the emergent behaviour and whether it is preferable or not. All those concerns aren't worth blocking efforts on movement on this ticket. |
I think that having a split in matcher interfaces is an unnecessary complication for the end user, especially when under the hood they mostly work the same. From what I understand, the main differences is not how matchers fundamentally work, but rather which sets of matchers are generally supported. Furthermore, splitting matcher interfaces I think will result in end users being unnecessarily locked in to older Pact versions, which ultimately will only result in a more and more fractured ecosystem. If/when Pact V5 is released, I would want to ensure that the transition to the new Pact is essentially seamless, instead of the end user having to rewrite every single Pact. As a result, I think Pact Python should be developed with a "v4-by-default" mindset, with explicit options to downgrade from V4 to older versions of Pact. Taking this further, I think we should expose all the options that V4 has to offer all the time, and should the end user explicitly decide to downgrade to an older version, then whichever functions are incompatible can error. Lastly, as to the error logic, I think this should be handled by the FFI, though I understand that this may need to be tested. |
Summary
Provide a Pythonic interface for creating matching rules.
Motivation
The Pact specification supports matching rules for most aspects of an interaction. These matching rules ensure that the content fit some more customisable constraints, as opposed to a literal value.
Some example of matching rules including matching a particular regex pattern, or ensuring a number is constrained to a range, or simple asserting that an entry is a floating point.
At present, functions such as
with_body
orwith_matching_rules
only take strings or byte arrays which are passed as-is to the underlying FFI. As a result, while matchers are technically supported, the end user is responsible for writing out the matching rules in a JSON string. This is clearly undesirable.Proposed Implementation
General Considerations
The proposed changes would apply to all functions that can support matching rules, whether it be the body, headers, metadata, etc. The specific might change slightly based on context, but the general idea should remain the same.
There should also be consistency between using
with_body(...)
with a matching rule, andwith_matching_rules(...)
. That is, a user should be able to straightforward refactor from one to the other. Similarly, adapting a rule fromwith_body
towith_header
should be equally straightforward.Literal Conversion
At present,
with_body
consumes a string or a byte array. A dictionary for example is not supported and the end-user must wrap is with ajson.dumps
to serialise the data into a JSON string. If someone wants to match a value literally, they should be able to pass the value directly towith_body
and the serialisation should be handled automatically.There should be support for:
Pydantic Support
Values which are subclasses of Pydantic's
BaseModel
should be serialised to JSON objects. Support for Pydantic should be opt-in so as to not introduce a new dependency.Custom Serialisation
Lastly, we should consider whether to support arbitrary classes. For example, it might be useful to inspect a value for any of the following methods:
to_json
/as_json
/json
to_dict
/as_dict
/dict
It might also be worth standardising a
__pact__
method which can be used to provide a custom serialisation.Matching Rules Constructor
When a matching rule is required, we should expose a set of functions which can be used to create and compose rules.
I would suggest the following, but I am open to suggestions:
regex
,type
,min
,max
,timestamp
,time
,date
, etc.like
andeach_like
from the Pact JS. The term 'like' might be a bit ambiguous I wonder if there is a better name.MatchingRule
object.with_body
function and friends.&
and|
operators to allow for logical and and or. This would allow for the creation of more complex rules such astype(42) & min(0) & max(100)
.To avoid polluting the namespace, it might be best to introduce a best practice of importing the module with an alias:
References
Below are some references as to how Pact JS handles matching rules, and how the Rust library handles them internally.
Rust Library
The main logic for parsing an arbitrary JSON value into a
MatchingRule
is in thepact_models
library (rules_from_json
andMatchingRule::from_json
)Pact JS
Pact JS has quite a nice API for creating matching rules. The
like
andeachLike
functions are particularly useful.The text was updated successfully, but these errors were encountered: