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

Allow to return a response from the state management function #745

Merged
merged 5 commits into from
Jan 16, 2025

Conversation

alba-s
Copy link
Contributor

@alba-s alba-s commented Jan 10, 2025

Hi @jbwheatley

This PR addresses the #416 issue by expanding the State Change function to have Map[String, String] response type. This allows Provider to inject custom values, like auto-generated IDs, into Generators. Currently, the only injected values are the ones passed in state parameters on Consumer side (through given)

Note: response type is Map[String, Any] in pact-jvm but so are state params, which in pact4s are of type Map[String, String]

The implementation preserves backwards compatibility by retaining the functions that expect Unit-returning state change functions

@jbwheatley
Copy link
Owner

Hey @alba-s thanks for the PR! Could you update the existing state change tests to demonstrate this new behaviour?

@jbwheatley
Copy link
Owner

especially helpful if the pact we use for testing could be updated to have generators like in the original issue

@alba-s
Copy link
Contributor Author

alba-s commented Jan 10, 2025

Added a new pact into the list of test pacts for each library and updated state change functions in tests accordingly

README.md Outdated
ProviderInfoBuilder().withStateChangeFunctionAndResponse {
case ProviderState("state", params) =>
val id: String = doSomething()
Map("someId" -> result)
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be helpful to show how/where someId can then be used. I.e. a complete example consumer/provider.

A link to a test in this repository could be enough.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've tried to expand the example

@@ -125,6 +125,11 @@ final class ProviderInfoBuilder private (
withStateChangeUrl(s"$protocol://$host:$port$endpointWithLeadingSlash")
}

def withStateChangeFunctionAndResponse(
Copy link
Owner

Choose a reason for hiding this comment

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

not trying to be too pedantic, but do we thinking this naming is self-explanatory enough? I don't use this so I don't know if folks using it will know that the Map[String, String] is to do with generated values. At the least a docstring explaining the function parameters would probably be helpful for discoverability (as well as maybe a different name? @gaeljw might defer to you for that point)

Copy link
Contributor Author

@alba-s alba-s Jan 12, 2025

Choose a reason for hiding this comment

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

I can't think of a better approach that would also preserve backwards compatibility, to be fair. Perhaps the function could be removed so that the only way would be to use withStateManagementFunction(stateManagementFunction: StateManagementFunction) and pass StateManagementFunction.withResponse(...) directly. The docstring could then be added onto the StateManagementFunction.withResponse instead. What do you think?

p.s. I've tried to look into pact-jvm for an example, but their version is even more complicated as they expect Any as a result type which is then internally type-matched to check if it is a Map in order to inject its values into generators...

val value = params.get("value").map(_.toInt)
Unsafe.unsafe { implicit unsafe: Unsafe =>
runtime.unsafe
.run((id, value).mapN(ProviderResource.apply).traverse_(store.create).as(Map.empty[String, String]))
Copy link
Owner

Choose a reason for hiding this comment

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

it seems a little clunky when you have a mix of state that uses the generated IDs and then some that do not, having to have the as(Map.empty[String, String])..

I wonder if on StateManagementFunction we should separate the two functions

      stateChange: PartialFunction[ProviderState, Unit],
      stateChangeWithGenerators: PartialFunction[ProviderState, Map[String, String]]

or something like that. Again, open to dissent on this point, just want to try and make this slick and easy to use/understand.

Copy link
Owner

Choose a reason for hiding this comment

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

but then maybe that would be more confusing :/

Copy link
Contributor Author

@alba-s alba-s Jan 12, 2025

Choose a reason for hiding this comment

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

I feel that the only way to make it easier to understand is to introduce some sort of an ADT for result type. I.e.

sealed trait StateChangeResult

object StateChangeResult {
  object Empty extends StateChangeResult
  final case class InjectValues(values: Map[String, String]) extends StateChangeResult
}

Copy link
Owner

Choose a reason for hiding this comment

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

yeah that might be a bit clunky too. I'm probably going to merge this once CI is happy (looks like a compilation issue)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The issue seems to be exclusive to Scala 2.12. Switched the project to 2.12 locally and was able to reproduce. Added an explicit type to satisfy the compiler

@jbwheatley
Copy link
Owner

sorry CI is broken due to #749. let me see if there is a quick fix

@jbwheatley
Copy link
Owner

hey @alba-s i've fixed the CI issue, you'll have to rebase off main if that's ok

@alba-s
Copy link
Contributor Author

alba-s commented Jan 15, 2025

I did a merge instead of rebase, so please squash all the commits into one upon merging

@jbwheatley jbwheatley merged commit b5276ca into jbwheatley:main Jan 16, 2025
6 checks passed
jbwheatley pushed a commit that referenced this pull request Jan 26, 2025
* Allow to return a response from state management function

* Add a test pact for provider state injection scenario

* Expand the README.md example

* Add explicit type in StateChanger's handling
jbwheatley pushed a commit that referenced this pull request Jan 26, 2025
* Allow to return a response from state management function

* Add a test pact for provider state injection scenario

* Expand the README.md example

* Add explicit type in StateChanger's handling
jbwheatley pushed a commit that referenced this pull request Jan 26, 2025
* Allow to return a response from state management function

* Add a test pact for provider state injection scenario

* Expand the README.md example

* Add explicit type in StateChanger's handling
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.

3 participants