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

Requesting enhancement to update query param and body param in post call in provider state before pact verification #385

Open
tl-madhulika-mitra opened this issue Apr 11, 2022 · 15 comments
Labels
feature request Indicates new feature requests

Comments

@tl-madhulika-mitra
Copy link

Hi , I found a post for JVM https://pactflow.io/blog/injecting-values-from-provider-states/ where we can modify the request before the validation, but is there a mechanism in C#?

@mefellows
Copy link
Member

I don't believe so.

You could achieve this by adding some custom middleware to your test application that can transform the incoming request.

@tl-madhulika-mitra
Copy link
Author

Thank you @mefellows , @adamrodger- Could you let me know if we plan to add this for the upcoming versions, as currently we are adding some heavy middleware to support our chained requests.

@adamrodger
Copy link
Contributor

I don't fully understand the feature request if I'm honest.

It would be really helpful if you could create a proposal for the API changes with an explanation of what the changes do, and then we can discuss about feasibility/design.

For example, see issue #392 for a good format for proposing changes.

@mefellows
Copy link
Member

to support our chained requests.

I'd be keen to understand this use case also. "chained requests" is an anti-pattern as far as contract testing is concerned, each request/response should be able to be tested in isolation.

Whilst the feature of essentially proxying incoming/outgoing bodies is doable, I'd be wary of adding features that readily enables improper use of the tool that will lead to future challenges for all parties.

@mefellows mefellows added feature request Indicates new feature requests question labels Aug 18, 2023
@adamrodger
Copy link
Contributor

Closed due to inactivity

@Dreamescaper
Copy link

Please consider reopening this issue.
The problem is described in the blog post: https://pactflow.io/blog/injecting-values-from-provider-states/ .

Assume I have the following setup:

        _pactBuilder
            .UponReceiving("A POST request to process the entity")
                .Given("An entity {id} is created", new Dictionary<string, string> { ["id"] = entityId })
                .WithRequest(HttpMethod.Post, $"/entities/{entityId}/process")
                    .WithHeader("X-Api-Key", ApiKey)
            .WillRespond()
                .WithStatus(HttpStatusCode.OK)
                .WithJsonBody(new
                {
                    Result = Match.Regex("123456789012", "\\w{13}"),
                });

The problem is that the entityId is auto-generated, I cannot easily force it via Given step.

Therefore, it would be nice to be able to return that auto-generated value from Given state, and make it accessible in VerifyAsync, e.g.

        await _pactBuilder.VerifyAsync(async ctx =>
        {
              // Injected from provider state
              var entityId = ctx.FromProviderState("entityId");
              await ProcessEntity(entityId);
        });

@mefellows
Copy link
Member

Do you only need the dynamic variable on the consumer side? If so, you could simply use a matcher (in this case, on the path).

The blog above describes a scenario, where the variable in the path (in this case, the entity ID) can't be known in advance because the IDs are generated. So they can't be stored in the pact file. The "from provider state" feature asks the provider "what entity ID should I use to send this request?", the provider state handler returns the entity ID to the framework, and when the request is sent replaces the one in the pact file, with the one the provider can handle.

It would be really helpful if you could create a proposal for the API changes with an explanation of what the changes do, and then we can discuss about feasibility/design.

I think this point still stands, let's define the interface and go from there.

@Dreamescaper
Copy link

Dreamescaper commented Jul 12, 2024

The blog above describes a scenario, where the variable in the path (in this case, the entity ID) can't be known in advance because the IDs are generated. So they can't be stored in the pact file. The "from provider state" feature asks the provider "what entity ID should I use to send this request?", the provider state handler returns the entity ID to the framework, and when the request is sent replaces the one in the pact file, with the one the provider can handle.

That's exactly what I have now.

I think this point still stands, let's define the interface and go from there.

I am not that comfortable with Pact yet to suggest an interface.
But, considering that ProviderState generator is a part of the spec, it should be supported by Pact for some other languages?
I'm not sure if any other generators are supported in pact.net though...
https://github.com/pact-foundation/pact-specification/tree/version-4?tab=readme-ov-file#supported-generators

@mefellows
Copy link
Member

    await _pactBuilder.VerifyAsync(async ctx =>
    {
          // Injected from provider state
          var entityId = ctx.FromProviderState("entityId");
          await ProcessEntity(entityId);
    });

Correct me if I'm wrong, but this verification is the consumer side, yes?

@Dreamescaper
Copy link

Correct me if I'm wrong, but this verification is the consumer side, yes?

Yes.
Entity is created on provider side with auto-generated Id.
Consumer needs to acess this entity (in ProcessEntity method), therefore needs to get this auto-generated Id.

@mefellows
Copy link
Member

That's not how this works. The consumer uses whatever it wants in its consumer test and that gets serialised to the pact file.

During verification on the provider side, the ID in the pact file is replaced by an ID known to the provider using data from the callback from the provider states endpoint.

i.e.

        await _pactBuilder.VerifyAsync(async ctx =>
        {
              // Injected from provider state
              var entityId = ctx.FromProviderState("entityId"); <--- this can't come from the provider during a consumer test
              await ProcessEntity(entityId);
        });

I think that would still satisfy your need?

@Dreamescaper
Copy link

Yes, I think that makes sense!

By this line

var entityId = ctx.FromProviderState("entityId");

I meant that Pact would somehow mention in the contract file that it should come from the provider state. Not actually getting it from the provider :)

@adamrodger
Copy link
Contributor

In this example the entity ID is generated on the consumer side though, and then written into the pact file, both in the provider state and also in the URL.

The provider is then expected to respond to the provider state ("given an entity with ID foo exists") by creating or stubbing an entity with that ID existing. Then when the provider test runs and requests the entity it will get the response it expects.

I'm not clear how that doesn't cover the request here, so I'd need to see a more worked example. In any case, that sounds more like a new ticket rather than reopening this one.

@Dreamescaper
Copy link

@adamrodger

The example that I've shared does not work - because the IDs are auto-generated it is not possible to hardcode them.

Maybe I'm bad at explaining things, especially considering that I'm new to Pact. But the issue I'm having is exactly the one that is described in OP's blog post:

https://pactflow.io/blog/injecting-values-from-provider-states/

@mefellows
Copy link
Member

The provider is then expected to respond to the provider state ("given an entity with ID foo exists") by creating or stubbing an entity with that ID existing. Then when the provider test runs and requests the entity it will get the response it expects.

This is where the challenge is. In some systems, reliably creating an entity with the same dynamic ID (e.g. a UUID) is not possible, because of a technical constraint.

This is where the "value from provider state" generator kicks in. The provider state handler on the provider side receives the state request e.g. "An entity with ID 1234 is created", creates an entity and gets the dynamic ID. It then returns back a JSON object with that id to use in the actual test request: {"ID": "abcd"}.

When the verifier sends the request to the provider for that scenario, instead of it sending id 1234 (the value in the pact file) it sends abcd which is the entity id that was just created, and the test will pass.

I believe the change would require:

  1. Adding the new provider state matcher/generator on the consumer side to allow expressions. See the spec https://github.com/pact-foundation/pact-specification/tree/version-4?tab=readme-ov-file#supported-generators, compatibility suite scenario and JS fromProviderState matcher as an example.
  2. Possibly making a change on the provider side (I don't think so because users create their own provider state handlers, and I believe simply by returning JSON in the state response would address this. So it probably just needs an update of the documentation to describe how it should work would be sufficient)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Indicates new feature requests
Projects
Status: New Issue
Development

No branches or pull requests

4 participants