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
feat: provider states middleware V3 support #335
base: master
Are you sure you want to change the base?
Conversation
@adamrodger and @mefellows this is a similar approach as the messaging middleware. It does so far execute simple methods but soon will be able to do it with parameters and also defines if the provider state (or we should name it the state handler) executes at setup or teardown. I also added a class I called Accessor (ProviderStateAccessor) which allow the middleware to talk to the static class containing the state handlers. That way the static class can be internal (god yes). I added it also for the messaging middleware. |
…V3 and setup, teardown.
…3 provider state middleware.
…ct-net into feature/provider-states-middleware # Conflicts: # samples/EventApi/Provider.Tests/EventAPITests.cs # samples/PactV2/EventApi/Consumer.Tests/Consumer.Tests.csproj # samples/PactV2/EventApi/Provider.Tests/Provider.Tests.csproj # samples/PactV3/EventImporter/Consumer.Tests/Consumer.Tests.csproj # samples/PactV3/EventImporter/Provider.Tests/Provider.Tests.csproj # src/PactNet.Abstractions/Verifier/Messaging/IMessageScenarios.cs # src/PactNet.Abstractions/Verifier/Messaging/MessageScenarios.cs # src/PactNet/Verifier/Messaging/IMessageScenarios.cs # src/PactNet/Verifier/Messaging/IMessageScenariosFactory.cs # src/PactNet/Verifier/Messaging/MessageScenarios.cs # src/PactNet/Verifier/Messaging/MessageScenariosFactory.cs # src/PactNet/Verifier/PactVerifierPair.cs
Sorry, this got lost in my inbox noise Yann. |
I'll comment on the public facing bits of the interface. This reads well enough to me: If I understand correctly, there are a few different types that can be put into the |
Yes async add has to be included, in the two middlewares. |
Great, thanks Yann! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've reviewed the design generally, but not reviewed the tests as you've indicated there are more to come. I'd say we're along the right track, just perhaps a few API changes to be made.
It may have been a better idea to split the messaging middleware and provider state middleware into separate PRs though, and perhaps also the bit that moves all the sample projects around. It's made the PR really big, so I've had to wait until I could find a solid 1.5hrs to sit down and review it.
providerStates | ||
.Add( | ||
"there are events with ids '45D80D13-D5A2-48D7-8353-CBB4C0EAABF5', '83F9262F-28F1-4703-AB1A-8CFD9E8249C9' and '3E83A96B-2A0C-49B1-9959-26DF23F83AEB'", | ||
this.InsertEventsIntoDatabase, StateAction.Setup) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan of the enum on the end here, especially because it's optional (and defaults to Setup
?). I'd prefer the methods themselves to indicate what they were doing, like:
states.AddSetup("my provider state", () => db.InsertData())
.AddTeardown("my provider state", () => db.ClearData());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point
|
||
if (context.Request.Method != HttpMethod.Post.ToString()) | ||
{ | ||
await WriteErrorInResponseAsync(context, "The provider state invocation needs to be a post method. Check on your verifier side if the provider state url is nicely configured.", HttpStatusCode.BadRequest); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should have status code 405 - Method Not Allowed: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noted
|
||
private static void ExecuteState(ProviderStateInteraction providerStateBody, IStateHandler stateHandler) | ||
{ | ||
if (providerStateBody.Params == null || !providerStateBody.Params.Any()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The design here seems a little unsafe to me - if you define a setup which takes arguments but the invocation doesn't provide any then it's ambiguous what should happen:
- Should it invoke the action anyway, even with no arguments? But then what happens to the templated parts?
- Should it error instead? I'd vote yes, given the invocation is incompatible with the defined provider state
To secure that, I'd have two different interfaces for IStateHandler
- one which takes args and one which doesn't. That way you can't even attempt to invoke a handler which requires args from an invocation that didn't provide any (or vice versa).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Adam, I'm finally having some bandwidth to work on finishing up this PR
I'm having now 2 interfaces implementing IStateHandler with different execute method.
On the middleware, I'm having something like this :
/// <summary>
/// Execute a provider state
/// </summary>
/// <param name="providerStateBody">The provider state from the consumer contract</param>
/// <param name="stateHandler">The state handler from the provider</param>
private static void ExecuteState(ProviderStateInteraction providerStateBody, IStateHandler stateHandler)
{
switch (stateHandler)
{
case IStateHandlerWithArgs handler when providerStateBody.HasParameters():
handler.Execute(providerStateBody.Params);
break;
case IStateHandlerWithoutArgs handlerWithoutArgs:
handlerWithoutArgs.Execute();
break;
default:
throw new InvalidOperationException(
"The provider state in the contract is not compatible with the state definition. Please re-configure your state handler correctly.");
}
}
Does that sound like what you were mentioning when talking interface segregation here?
///// <summary> | ||
///// the after interaction callback | ||
///// </summary> | ||
//private Dictionary<string, Action> afterInteractionCallbacks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what this commented out code is for - does it need removing or is it unfinished?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
haha, leftover code
/// <summary> | ||
/// Route of provider state endpoint | ||
/// </summary> | ||
public string RouteProviderState { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should have a default value and we should have an overload in ProviderStateMiddlewareExtensions
which just uses the defaults. That makes the simple API possible with sensible defaults:
services.AddPactProviderState();
@@ -5,7 +5,7 @@ namespace PactNet.Verifier.Messaging | |||
/// <summary> | |||
/// Defines the scenario model | |||
/// </summary> | |||
public class Scenario | |||
public class Scenario : IScenario |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also internal
|
||
namespace PactNet.Verifier.ProviderState | ||
{ | ||
public class ProviderStateAccessor : IProviderStateAccessor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be internal and documented
/// <summary> | ||
/// Defines the provider description model | ||
/// </summary> | ||
public class StateHandler : IStateHandler |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be internal
/// </summary> | ||
public StateAction Action { get; } | ||
|
||
//public IStateHandler SetParams(IDictionary<string, string> args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove commented code
///// <summary> | ||
///// Arguments | ||
///// </summary> | ||
//private IDictionary<string, string> args; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove commented code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Damn, a lot of leftover code :/
Thanks for the review! Sorry for the big PR. Outside of the StateHandler with params, I don't see much issue. |
is this PR dead? |
Probably - but hopefully it gives somebody an idea of how to proceed. I'd still like to see it in Pact .NET, because making users create middleware is a pain in the butt! |
A PR to support provider states handling directly from a middleware in .NET core. (using a similar approach as the messaging middleware)
So far the middleware, once registered in the test dependency injection, is able to read from a set of provider states (we should maybe call them state handlers) configured by the verifier beforehand and execute a simple, parameterless, method.
Next steps: