Chaining functions that return Vavr Either - java

I have a series of functions that take in a Request object and return a Vavr Either.
The Either will contain a Result object if the task is complete or a modified Request object if the task needs to be completed by another function.
The thought was that I could chain them together by doing something like this:
// Note: The Request object parameter is modified by the function
// before being returned in the Either.
Function<Request, Either<Request,Result>> function1;
Function<Request, Either<Request,Result>> function2;
Function<Request, Either<Request,Result>> function3;
Function<Request, Result> terminalFunction;
Result result = function1.apply(request)
.flatMapLeft(function2)
.flatMapLeft(function3)
.fold(terminalFunction, r->r);
But apparently flatMapLeft is not a thing, so I just end up with a nested Eithers on the left side. Any ideas on how I can achieve this functionality? I'm open to alternative libraries.
Edit:
Result result = function1.apply(request)
.fold(function2, Either::right)
.fold(function3, Either::right)
.fold(terminalFunction, r->r);
Seems like this should work instead, but Intellij is giving this error on the second fold line:
no instance(s) of type variable(s) exist so that capture of ? extends Object conforms to Request

You need monadic composition on your Request side, which is left side in your type signatures, but you have monadic composition for Either on the right side. So you need to swap your eithers in your function definitions or you have to pass them through Either.swap() with
Function1.of(SomeType::function1).andThen(Either::swap)
Essentially, each of your function[1-3] would then become of type:
Function<Request, Either<Result, Request>>
Then your call chain becomes:
Result result = function1.apply(request)
.flatMap(function2)
.flatMap(function3)
.swap()
.getOrElseGet(terminalFunction);

Result result = function1.apply(request)
.fold(function2, Either::<Request, Result>right)
.fold(function3, Either::<Request, Result>right)
.fold(terminalFunction, r->r);
This appears to work, although it's a little clunky. Is this an abuse of the library? Would be interested in hearing some alternative approaches.

Related

Using Optional's ifPresentOrElse method but return promise instead of void

I'm trying to get my around a current issue I'm facing.
I have a function that returns an Optional type (an object with a few properties)
One of the properties is an url that might be present or not. I extract that url in order to make an HTTP request
injectedClass.method(tenant.clientKey()).flatMap(optionalProperty ->
optionalProperty.ifPresentOrElse(fi -> {
Blocking.get(() -> httpClientProvider.withHttpClient((HttpClient httpClient) ->
httpClient.request(URI.create(optionalProperty.webTriggerUrl()), (RequestSpec spec) -> {
LogstashMarker markers = append("webTriggerUrl", fi.webTriggerUrl()).and(append("method", "Post").and(append("behaviour", objectMapper.writeValueAsString(baseDTO))));
logger.debug(markers, "Executed a Post request to something webTriggerUrl");
spec.method(HttpMethod.POST);
spec.getBody().type(HttpHeaderValues.APPLICATION_JSON).text(objectMapper.writeValueAsString(baseDTO), CharsetUtil.UTF_8);
final MutableHeaders headers = spec.getHeaders();
headers
.set(HttpHeaderNames.USER_AGENT, userAgent);
headers.set(CorrelationId.HEADER_NAME, correlationId.id());
})
)).then(resp -> logger.info("ok"));
}, () -> logger.error("something"))
Blocking.get brings back a Promise and I get an error in my code basically saying that the expected return type of ifPresentOrElse should be void and not Promise
Is there a functional and better way to achieve this?
Yes there are ways, but you also have to decide what to do if the Optional is empty. Currently you want to return a Promise if the optional is present, and return nothing ("void") if it is empty. This doesn't work, the types for both branches need to be the same.
You can just use optionalProperty.map() to map your original Optional to a Optional<Promise>, and then use ifPresentOrElse, to do something with either the Promise or with the empty Optional, e.g. logging as you seem to be doing in your case.
But you also have a higher-level flatMap which I'm unclear from which type it is. Does this flatmap a Promise? Then you must return a Promise also from the other branch of the optional, and you could use optionalProperty.map(...).orElse( <create empty Promise here> ).
Also check out orElseGet() instead of orElse(), if you want to create the empty branch lazily (via Supplier).
ifPresentOrElse returns void. What you probably want is a combination of map and orElseGet:
optionalProperty.map(/* code to return a Promise */)
.orElseGet(() -> /* code to return a Promise that is immediately resolved */);
Inside the supplier to orElseGet() you can put your logger.error statement.

How to verify PACT when data returns items with or without children (filled in or no existing array)

I'm using PACT and Java for contract tests and my issue is that I have an api where the items may come up like this:
[
{
"programCode": "ELA_NGL_G7_TX",
"contentResources": [
{
"tocPosition": 1827,
"contentIdentifier": "l_6bf0783e-8499-4f6c-9f9b-c8fbdc8dcf6b_e5f25016-e2fa-4223-8969-2004c644917d"
},
{
"tocPosition": 1828,
"contentIdentifier": "l_192af774-54b9-4280-87e9-71f2b86a7d4d_e5f25016-e2fa-4223-8969-2004c644917d",
"skills": [
{
"skillId": "ae836bd9-4758-4665-b3f8-8339313363e3",
"spineId": "63c2b7d0-cd69-4e8a-9761-c90623104b8c"
}
]
}
]
So as you can see, sometimes the inner skills array appears others it won't and not sure how to go about incorporating this scenario on my consumer tests. I mean, if the response had or no skills array depending on specific params, I could have two different tests and it might be fine, but here they come from the same call. So I guess what I need is something like an if else, that if the skills array is present then I would assert its inner children, otherwise just ignore it instead.
This is my consumer:
#ExtendWith(PactConsumerTestExt.class)
public class PublishContractWithTocGetSummaryTest {
Map<String, String> headers = new HashMap<>();
String getRecommendations = "/toc/getsummary/ELA_NGL_G7_TX";
#Pact(provider = "CRS-METADATA-FILTERING-SERVICE", consumer = "CRS-TOC-RECOMMENDER")
public RequestResponsePact createPact(PactDslWithProvider builder) throws IOException {
headers.put("Content-Type", "application/json");
DslPart body = new PactDslJsonBody()
.stringValue("programCode", "ELA_NGL_G7_TX")
.eachLike("contentResources")
.integerType("tocPosition", 0)
.stringType("contentIdentifier", "l_9d23cb4f-69dc-4032-bb53-73501234dc14_e5f25016-e2fa-4223-8969-2004c644917d")
.closeArray();
return builder
.given("get TOC Summary")
.uponReceiving("get TOC Summary")
.path(getRecommendations)
.method("GET")
.headers(headers)
.willRespondWith()
.status(200)
.body(body)
.toPact();
}
Many thanks.
The short answer to your question is that there isn't a way to do exactly what you want.
The longer answer about why that is not available is in the FAQs:
Why is there no support for specifying optional attributes?
Firstly, it is assumed that you have control over the provider's data (and consumer's data) when doing the verification tests. If you don't, then maybe Pact is not the best tool for your situation.
Secondly, if Pact supports making an assertion that element $.body.name may be present in a response, then you write consumer code that can handle an optional $.body.name, but in fact, the provider gives $.body.firstname, no test will ever fail to tell you that you've made an incorrect assumption. Remember that a provider may return extra data without failing the contract, but it must provide at minimum the data you expect.
The same goes for specifying "SOME_VALUE or null". If all your provider verification test data returned nulls for this key, you might think that you had validated the "SOME_VALUE", but in fact, you never had. You could get a completely different "SOME_VALUE" for this key in production, which may then cause issues.
The same goes for specifying an array with length 0 or more. If all your provider verification data returned 0 length arrays, all your verification tests would pass without you ever having validated the contents of the array. This is why you can only specify an array with minimum length 1 OR a zero length array.
Remember that unlike a schema, which describes all possible states of a document, Pact is "contract by examples". If you need to assert that multiple variations are possible, then you need to provide an example for each of those variations. Consider if it's really important to you before you do add a Pact test for each and every variation however. Remember that each interaction comes with a "cost" of maintenance and execution time, and you need to consider if it is worth the cost in your particular situation. You may be better off handling the common scenarios in the pact, and then writing your consumer to code to gracefully handle unexpected variations (eg. by ignoring that data and raising an alert).
https://docs.pact.io/faq#why-is-there-no-support-for-specifying-optional-attributes
So the TL;DR of Beth's answer:
Decide on what is valuable to test - empty arrays, non-empty or both
Use provider states to specify any variations on the response (consumer test)
Implement the state for the provider test to be able to control the response

How do I rewrite this java function in scala keeping the same Optional input parameter?

I have the following method in java
protected <T> T getObjectFromNullableOptional(final Optional<T> opt) {
return Optional.ofNullable(opt).flatMap(innerOptional -> innerOptional).orElse(null);
}
It takes a java Optional that can itself be null (I know, this is really bad and we're going to fix this eventually). And it wraps it with another Optional so it becomes either Some(Optional<T>) or None. Then it's flatMapped, so we get back Optional<T> or None, and finally apply orElse() to get T or null.
How do I write the same method with the same java.util.Optional in scala?
protected def getObjectFromNullableOptional[T >: Null](opt : Optional[T]): T =
???
I tried
protected def getObjectFromNullableOptional[T >: Null](opt : Optional[T]): T =
Optional.ofNullable(opt).flatMap(o => o).orElse(null)
But this gives me a Type mismatch error
Required: Function[_ >: Optional[T], Optional[NotInferedU]]
Found: Nothing => Nothing
I tried
protected def getObjectFromNullableOptional[T >: Null](opt : Optional[T]): T =
Option(opt).flatMap(o => o).getOrElse(null)
But this gives me
Cannot resolve overloaded method 'flatMap'
Edit I neglected to mention I'm using scala 2.11. I believe #tefanobaghino's solution is for scala 2.13 but it guided me towards the right path. I put my final solution in comments under this solution
The last error raises a few suspicions: it looks like you're wrapping a Java Optional in a Scala Option. I would have instead have expected this to have failed because you're trying to flatMap to a different type, something like
error: type mismatch;
found : java.util.Optional[T] => java.util.Optional[T]
required: java.util.Optional[T] => Option[?]
This seems to fulfill your requirement:
import java.util.Optional
def getObjectFromNullableOptional[T](opt: Optional[T]): T =
Optional.ofNullable(opt).orElse(Optional.empty).orElse(null.asInstanceOf[T])
assert(getObjectFromNullableOptional(null) == null)
assert(getObjectFromNullableOptional(Optional.empty) == null)
assert(getObjectFromNullableOptional(Optional.of(1)) == 1)
You can play around with this here on Scastie.
Note that asInstanceOf is compiled to a cast, not to an actual method call, so this code will not throw a NullPointerException.
You can also go into something closer to your original solution by helping Scala's type inference a bit:
def getObjectFromNullableOptional[T](opt: Optional[T]): T =
Optional.ofNullable(opt).flatMap((o: Optional[T]) => o).orElse(null.asInstanceOf[T])
Or alternatively using Scala's identity:
def getObjectFromNullableOptional[T](opt: Optional[T]): T =
Optional.ofNullable(opt).flatMap(identity[Optional[T]]).orElse(null.asInstanceOf[T])
For a solution using Scala's Option you can do something very close:
def getObjectFromNullableOption[T](opt: Option[T]): T =
Option(opt).getOrElse(None).getOrElse(null.asInstanceOf[T])
Note that going to your flatMap solution with Scala's Option allows you to avoid having to be explicit about the function type:
def getObjectFromNullableOption[T](opt: Option[T]): T =
Option(opt).flatMap(identity).getOrElse(null.asInstanceOf[T])
I'm not fully sure about the specifics, but I believe the issue is that, when using java.util.Optional you are passing a Scala Function to Optional.flatMap, which takes a Java Function. The Scala compiler can convert this automatically for you but apparently you have to be specific and explicit about type for this to work, at least in this case.
A note about your original code: you required T to be a supertype of Null but this is not necessary.
You have a better context regarding what you are doing, but as a general advice it's usually better to avoid having nulls leak in Scala code as much as possible.

adding function to jsonjava object and calling it from xpages control

I am trying to add a function to a JSONJavaObject and calling it from a control on an xpage.
so far I have:
json = (JsonJavaObject) JsonParser.fromJson(factory, colJson);
String func = "function () { alert('you clicked?'); }";
json.put("onClick", new JsonReference(func) );
In the first line I add key-value pairs from a column in a Notes view.
In the second line I define the function as a string.
In the last line I place the converted string as function in the jsonjava object.
I read about this in the following blog post:
http://camerongregor.com/2016/01/19/doublequoteavoidance/
In the next step I bind the function to e.g. a button control as followed:
<xp:button value="Label" id="button1">
<xp:eventHandler event="onclick" submit="false">
<xp:this.script><![CDATA[obj.onClick]]></xp:this.script>
</xp:eventHandler>
</xp:button>
obj is the respresentation of the JSONJava object in SSJS.
But without success. Anyone know how I can call the function in the object?
I hope I will make sense here, let me know if anything to clarify.
If you are simply trying to dynamically output the client side script of a button event, then you don't need to use JsonReference at all. You can just use a String.
In my blog article I might not have make it clear why I needed to use JsonReference. I was using it in the process of rendering a custom UIComponent, part of this process required generating a Json object client side. To do this I created the JsonJavaObject as you did and then asked it to be turned into a string with the 'toJson' method. My problem was that when I asked the whole object to become a string, every property of that object that was a String, would begin and end with a double quote. I needed to ensure that the properties which were intended to be functions did not begin and end with "". By using the JsonReference the JsonGenerator became aware of my intention not to include these double quotes.
In your case, it looks as though you are just trying to dynamically determine what happens with onClick. To do this you could simply use a String instead of the JsonReference. The inclusion of the 'function() {}' is unnecessary as this will be generated when the event handler is rendered at the end of the page.
For Example here would be the Json Java Object
JsonJavaObject obj = new JsonJavaObject();
String func = " alert('you clicked?'); ";
obj.put("onClick", func);
return obj;
And here would be the button:
<xp:button id="button1" value="Alert Me">
<xp:eventHandler event="onclick" submit="false"
script="#{javascript: myBean.myObject.get('onClick')}">
</xp:eventHandler>
</xp:button>
This should give you the end result of seeing 'you clicked?' alert.
You can also inspect how this has all been generated in the script block near the end of the page using 'view Source' or your favourite web browser developer tools:
function view__id1__id2_clientSide_onclick(thisEvent) {
alert('you clicked?');
}
XSP.addOnLoad(function() {
XSP.attachEvent("view:_id1:_id2", "view:_id1:button1", "onclick",
view__id1__id2_clientSide_onclick, false, 2);
});
Let me know if anything isn't clear, hope it helps!
Does obj.onClick already give you a handle to the function returned by the Java class? If it does then you should be able to call it using the call or apply methods that are available in JavaScript:
obj.onClick.call();
obj.onClick.apply();
More details about those two methods can be found here: What is the difference between call and apply?

Redirect with anchor in play 2

I'm looking for possibility to add anchor to url returned in controller:
public static Result open(String id) {
// here I want to add acnhor like #foo to the produced url
return redirect(routes.MyPage.show(id));
}
I found that it was possible in play 1 using addRef method, but I couldn't find any replacement of the method in play 2.
Of course I can use concatenation like:
public static Result open(String id) {
// here I want to add acnhor like #foo to the produced url
return redirect(routes.MyPage.show(id).url + "#foo");
}
But it seems ugly.
Thank you for any help! Have a good day!
Before trying to answer that question.
I should recommend you change whatever behavior you're currently setting.
Because, an URL fragment's purpose is client side only. Such fragment is never sent to the server, so that it's cumbersome to do the opposite.
However, here is the embryo of a (quite?) elegant solution that you could follow.
What I'll try to do is to leave the browser deal with the fragment, in order to keep potential behaviors (f.i. go to ID or even deal with history...).
To do so, you could add an implicit parameter to your main template which will define the fragment that the URL should have:
#(title: String)(content: Html)(urlFragment:Option[UrlFragment] = None)
As you can see I wrapped the parameter in an Option and default'ed it to None (in order to avoid AMAP pollution).
Also, it simply wraps a String but you could use String alone -- using a dedicated type will enforce the semantic. Here is the definition:
case class UrlFragment(hash:String)
Very simple.
Now here is how to tell the browser to deal with it. Right before the end of the head element, and the start of body, just add the following:
#urlFragment.map { f =>
<script>
$(function() {
//after everything is ready, so that other mechanism will be able to use the change hash event...
document.location.hash = "#Html(#f.hash)";
});
</script>
}
As you can see, using map (that is when the urlFragment is not None) we add a script block that will set the hash available in urlFragment.
This might be a start, however... Think about another solution for the whole scenario.
As of Play 2.4, it's possible to use Call.withFragment().
routes.Application.index.withFragment("some-id").absoluteURL(request)
This was added by PR #4152.

Categories