Skip to content
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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

yanncourtel
Copy link
Contributor

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:

  • V3 support with parameters mapping
  • Adding an action to execute provider states at set up or teardown.

@yanncourtel
Copy link
Contributor Author

yanncourtel commented Oct 8, 2021

@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.

yanncourtel and others added 5 commits October 8, 2021 23:06
…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
@yanncourtel yanncourtel changed the title WIP feat: provider states middleware V3 support feat: provider states middleware V3 support Oct 18, 2021
@yanncourtel yanncourtel marked this pull request as ready for review October 18, 2021 12:58
@yanncourtel yanncourtel requested review from adamrodger, mefellows and a user October 18, 2021 12:58
@mefellows
Copy link
Member

Sorry, this got lost in my inbox noise Yann.

@mefellows
Copy link
Member

I'll comment on the public facing bits of the interface. This reads well enough to me:
https://github.com/pact-foundation/pact-net/pull/335/files#diff-2737526990aa399bcbffa370bc0098962650bae1387d4fba5dcc11452d85834aR58-R65

If I understand correctly, there are a few different types that can be put into the Add function depending on context, which is good. I'm assuming asynchronous things can go in there too (I can't quite tell from the Action and other types)?

@yanncourtel
Copy link
Contributor Author

I'll comment on the public facing bits of the interface. This reads well enough to me: https://github.com/pact-foundation/pact-net/pull/335/files#diff-2737526990aa399bcbffa370bc0098962650bae1387d4fba5dcc11452d85834aR58-R65

If I understand correctly, there are a few different types that can be put into the Add function depending on context, which is good. I'm assuming asynchronous things can go in there too (I can't quite tell from the Action and other types)?

Yes async add has to be included, in the two middlewares.
I'm going to add the unit tests left on the middleware and the async support.

@mefellows
Copy link
Member

Great, thanks Yann!

Copy link
Contributor

@adamrodger adamrodger left a 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)
Copy link
Contributor

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());

Copy link
Contributor Author

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);
Copy link
Contributor

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

Copy link
Contributor Author

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())
Copy link
Contributor

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).

Copy link
Contributor Author

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;
Copy link
Contributor

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?

Copy link
Contributor Author

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; }
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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)
Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented code

Copy link
Contributor Author

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 :/

@yanncourtel
Copy link
Contributor Author

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.

Thanks for the review! Sorry for the big PR. Outside of the StateHandler with params, I don't see much issue.

Base automatically changed from feature/4.0.0 to master February 9, 2022 13:48
@fabio-andre-rodrigues
Copy link

is this PR dead?

@mefellows
Copy link
Member

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants