How to check i18n message resource arguments is empty or not? - java

I mean for example this is my messages.properties file:
BFF.ERROR.PRODUCT_NOT_FOUND = Product with {0} not found
I want to do that if the arguments array is empty, the client shouldn't see the message like this
Product with {0} not found
I want to the user see this one
Product not found.
Can I do something like that?
BFF.ERROR.PRODUCT_NOT_FOUND = Product with {0} not found | Product not found

I think u can define two keys :
BFF.ERROR.PRODUCT_NOT_FOUND = Product with {0} not found
BFF.NULL.PRODUCT_NOT_FOUND = Product not found
then deal with it in code

Why would the arguments array be empty? Can you not use two different keys and check ahead of time to use the correct message based on the arguments? They're two different messages and should have their own keys.
messages.properties
key1=message with argument {0}
key2=message without argument
Somewhere in your code
final String msg;
if (condition) {
msg = ... // if you have the argument for placeholder {0}, get message for key1
} else {
msg = ... // fallback, get message for key2
}
If you really wanted to you could look into creating a custom message source and/or supporting beans and handle this kind of thing there but it seems like more trouble than it's worth. In that case, have a look at the MessageSourceSupport class Spring provides, in particular methods formatMessage and renderDefaultMessage.

Related

KSQL: UDF does not accept parameters (STRING, STRING)

I'm running in to a problem with KSQL while trying to set up an ETL pipeline using a UDF. At some point in the ETL process I need to isolate specific info from a description field (VARCHAR) in my data. A made-up example for context:
description = "species=dog.sex=male.color=blonde.age=10." (the real data is formatted in the same way)
I've written a simple UDF to isolate any information on demand. It looks like this:
package com.my.package;
/** IMPORTS **/
import io.confluent.ksql.function.udf.Udf;
import io.confluent.ksql.function.udf.UdfDescription;
/** ClASS DEFINITION **/
#UdfDescription(name = "extract_from_description",
author = "Me",
version = "0.0.1",
description = "Given a description and a request for information, isolates and returns the requested information. Pass requested tag as 'tag='".)
public class Extract_From_Description {
#Udf(description = "Given a description and a request for information, isolates and returns the requested information. Pass requested tag as 'tag='.)
public String extract_from_description(final String description, final String request) {
return description.split(request)[1].split("\\.")[0];
}
}
I can upload and register the function just fine, it's listed and described properly when I run:
ksql> list functions;
ksql> describe function EXTRACT_FROM_DESCRIPTION;
I call the function like this to create a new stream:
CREATE STREAM result AS
SELECT recordId,
OtherVariables,
EXTRACT_FROM_DESCRIPTION(description, 'species=') AS species
FROM parent_stream
EMIT CHANGES;
There I get an error I can't make sense of:
Function 'extract_from_description' does not accept parameters (STRING, STRING).
Valid alternatives are:
Apparently KSQL can't properly interpret what the input for the function is supposed to be (looks like it expects no input?) and I can't figure out why. I've read through documentation to see if I define my function in a weird way but can't find any differences between the examples and my function. I did notice there are supposed to be several ways to define the input a function takes and tried them all, but the result is always the same.
I use Maven to create the jar file for this function (JDK1.8.0_201). Can anyone help me figure out what's going on?
TL;DR: My KSQL UDF doesn't accept input of type (String, String) even though the function specifies the input should be of type (String, String)
Found the problem, answering here for anyone that might run in to the same problem.
You need to specify the parameters using #UdfParameter, like this:
import io.confluent.ksql.function.udf.UdfParameter; // add this to the list of imports
// add #UdfParameter(name) to each input variable
public String extract_from_description(#UdfParameter(value = "description") final String description, #UdfParameter(value = "request") final String request){
function body
}

Structured logging where logger argument not wanted in message

I'm using structured logging in a Spring Boot app using logstash and sometimes I want to include key values in the log that I don't want to be used in the message of the log. Is there a StructuredArgument or similar that allows for this?
An example of what I am doing currently is something like this:
// import lombok.extern.slf4j.Slf4j;
// import static net.logstash.logback.argument.StructuredArguments.kv;
// import static net.logstash.logback.argument.StructuredArguments.v;
log.info(
"My message with one arg {}",
v("key1":"arg I want to include value in place of placeholder in message and in json as key/value"),
kv("key2", "arg I only want in the json and not in the message"))
Everything works as I intended, by which I mean the log includes both key value pairs and the message only includes the first value in place of the placeholder. The issue is that I get a warning from the compiler which is flagged by intellij (PlaceholderCountMatchesArgumentCount) about the second structured argument and I would like to avoid this without resorting to suppressing/ignoring it
You can use Markers and pass it before your logging message - more details on github.
logger.info(append("key2", "only json"),
"My message with one arg {}",
v("key1":"arg in msg and json"));
I personally don't like this because markers have different purpose, so if structured argument works for you, just ignore warning in IDE.
Anyway, all this json/structured implementations are workarounds for SLF4J 1.*, which has not built for that. There was SLF4J 2.0.0-alpha1 release almost a yeah ago, but it is still in alpha and I haven't used it. But it's API should be ready for key-values that are crusial in nowadays distributed log management systems.
You can make the log message as a constant String, then the code quality checks will not warn this
You can make the structured argument print nothing into the formatted message:
(1) include the second placeholder {} inside the message
(2) use keyValue() instead of kv()
(3) provide the optional messageFormatPattern parameter (JavaDoc) equal to ""
Adjusting your example:
log.info(
"My message with one arg {}{}", //note (1)
v("key1":"arg I want to include value in place of placeholder in message and in json as key/value"),
keyValue("key2", "arg I only want in the json and not in the message", "")) //note (2) + (3)
This will effectively replace the second placeholder with an empty string.

Parsing of two different message types coming in same message channel

We are going to receive the two types of messages from same MQ queue. The structure of these two messages is completely different, there is no common field. I have corresponding POJO's for both the classes. How to I identify smartly which message corresponds to which POJO?
What I am currently doing is as follows:
receivedMessageClassA = objectMapper.readValue(payload, ClassA.class);
Check if the parsing above succeeds OR check if one of mandatory field is present in receivedMessageClassA.
If the above check fails, do the parsing for second class
receivedMessageClassB = objectMapper.readValue(payload, ClassB.class);
However this approach is error prone and I am not completely satisfied with it. Can someone help here please?
Well, I believe the best method is to use instanceof:
if (payload instanceof ClassA)
receivedMessageClassA = objectMapper.readValue(payload, ClassA.class);
else
receivedMessageClassB = objectMapper.readValue(payload, ClassB.class);

How to customize / replace exception messages in Freemarker to make them more meaningful?

I would like to "improve" some exception messages thrown by Freemarker template messages to make the exceptions more meaningful for the users. Although Freemarker has become a lot better in terms of meaningful error messages, there are still cases, where I would like to be more specific.
Example
Freemarker is throwing this exception for a template like this:
<#if (""?number > 1)>foo</#if>
(just an example... imagine the empty string could also be a variable containing an empty string)
value of templateException.getMessage():
(java.lang.String) Can't convert this string to number: ""
The blamed expression:
==> ""?number [in nameless template at line 1, column 7]
----
FTL stack trace ("~" means nesting-related):
- Failed at: #if (""?number > 1) [in nameless template at line 1, column 1]
----
I would like to rephrase this specific case to:
You tried to convert an EMPTY string variable to a number.
I could try my own Exception handler, to contains checks, replace the message and rethrow an Exception like this:
configuration.setTemplateExceptionHandler(new TemplateExceptionHandler() {
public void handleTemplateException(TemplateException te, Environment env, java.io.Writer out)
throws TemplateException {
String message = te.getMessage();
if(StringUtils.contains(message, "Can't convert this string to number: \"\"")){
message = StringUtils.replace(message, "Can't convert this string to number: \"\"", "You tried to convert an EMPTY string variable to a number. Solution: Try checking if the variable is empty to avoid this error.");
}
throw new TemplateException(message, env);
}
});
But this feels very hacky.
My questions:
Is there a way how I can customize the Exception messages Freemarker is throwing? I have the feeling in my TemplateExceptionHandler it is too late, as the message gets constructed much earlier inside Freemarker.
What are common ways to improve / rewrite exception messages from 3rd party libs?
Search and replace may won't work after version updates, as there's no backward compatibility promise regarding the message content.
If the changes you want are generally useful (not only for your project), then you could improve the existing error messages by contributing to FreeMarker (sign Apache CLA, fork on GitHub, make pull request).
The only really correct and flexible way I see is adding l10n support to the error message mechanism, where the message strings aren't hard-wired in to the code (except their defaults), but are retrieved based on message keys from external source. It can be a big work of course, especially as FreeMarker messages are assembled from many smaller pieces.

Authorize.net create ARB and get Id

When I create a new ARB subscription the response comes back and I save the id it gives us. I tried it out and it gives us back "33".
Then when the silent post callback hits our method, the response has a different id, 15631016.
15631016 is correct in matching up with the one we see in the authorize.net online portal.
So, what is 33 and why doesn't it return the real ARB ID?
Here is the code that creates the new ARB and then gets the arbId:
net.authorize.arb.Transaction arbTransaction = createARBTransaction(startDate.getTime(), creditCard, member, splitOccurrences.intValue() - 1, splitUnit, useBillingAddress, billingAddress, recurringOrder.getTotalAmount().doubleValue(), recurringOrder);
net.authorize.arb.Result<?> arbResult = (net.authorize.arb.Result<?>) merchant.postTransaction(arbTransaction);
String arbId;
if (arbResult.isOk()) {
arbId = arbResult.getResultSubscriptionId();
}
If getResultSubscriptionId() is not the correct way to get the new ARB subscription ID, what is the correct method to use?
I went through the sample code and also their community and there isn't much to go on. The only thing I can think of trying is changing:
arbResult.getResultSubscriptionId();
to:
arbTransaction.getResultSubscriptionId();
I know that doesn't sound logical but it's the best I can some up with.
According to the source code, you are using the correct method.
If you trace the calls back into the code you'll see that the subscription id gets set by the following call in importResponseMessages() of net.authorize.arb.Result
getElementText(txn.getCurrentResponse().getDocumentElement(),AuthNetField.ELEMENT_SUBSCRIPTION_ID.getFieldName());
so if you call this on your arbResult variable, you might get closer. Note that txn should be replaced by your variable arbTransaction.
Alternatively, you can dig into the response itself to see why the Authorize.net APK isn't returning the correct subscription id.
xml = arbTransaction.getCurrentResponse().dump(true);
The true determines whether the XML tree is collapsed. xml should be a string containing your XML response from authorize.net

Categories