I am trying to consume Braintree webhooks in a java microservice (Micronaut fwiw).
The issue that I'm having is that when I try to parse the webhook body, I get an error: " Error: payload contains illegal characters", which it does. So I'm wondering if maybe I'm casting the request body to something thats inserting the characters...? (the body is x-www-form-urlencoded)
package com.autonomy;
import com.braintreegateway.*;
import event_broker.BrokerServiceGrpc;
import event_broker.EventBroker;
import events.Events;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.micronaut.context.annotation.Value;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.HttpResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.Normalizer;
import java.time.Clock;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
#Controller("/webhooks")
public class WebhooksController {
private final Logger logger = LoggerFactory.getLogger("BraintreeServiceJava");
#Value("${env}") String env;
#Value("${braintree.merchant.id}") String braintreeMerchantId;
#Value("${braintree.public.key}") String braintreePublicKey;
#Value("${braintree.private.key}") String braintreePrivateKey;
#Post(consumes = MediaType.APPLICATION_FORM_URLENCODED)
public HttpResponse<?> consumeWebhook(#Body String body) {
BraintreeGateway gateway =
new BraintreeGateway(determineEnv(env),
braintreeMerchantId,
braintreePublicKey,
braintreePrivateKey
);
logger.info(body);
try {
String decodedBody = body; // was doing a decode here that didn't do anything
logger.info(decodedBody);
Map<String, String> map = new HashMap<>();
Arrays.stream(decodedBody.split("&")).toList().forEach(pair -> {
String[] param = pair.split("=");
map.put(param[0], param[1]);
});
WebhookNotification webhookNotification = gateway.webhookNotification().parse(
map.get("bt_signature"),
map.get("bt_payload")
);
..... Do stuff
} catch (Exception e) {
logger.error(String.format("Braintree webhook failed for %s. Error: %s", kind, e.getMessage()), e);
return HttpResponse.status(HttpStatus.BAD_REQUEST);
}
return HttpResponse.status(HttpStatus.OK);
}
private Environment determineEnv(String env) {
if (env.equals("beta") || env.equals("prod")) {
return Environment.PRODUCTION;
} else {
return Environment.SANDBOX;
}
}
}
Try:
#Post(consumes = MediaType.APPLICATION_FORM_URLENCODED)
public HttpResponse<?> consumeWebhook(String bt_signature, String bt_payload)
BTW: What 'illegal characters' were in the body that was logged?
Related
I have an application running on port 7070 on my local. It exposes and endpoint /metrics and shows all the tags that are available. Prometheus is not able to get these data and it says 'expected label name, got "BCLOSE"'.
I have been trying to figure this out but not sure why this code doesn't work:
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.exporter.common.TextFormat;
import metrics.PrometheusRegistry;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
#Path("/metrics")
public class MetricsController {
private final PrometheusMeterRegistry prometheusRegistry = PrometheusRegistry.INSTANCE();
#GET
public Response getMetrics(#Context HttpHeaders headers) {
Writer writer = new StringWriter();
try {
TextFormat.write004(writer, prometheusRegistry.getPrometheusRegistry().metricFamilySamples());
} catch (IOException e) {
e.printStackTrace();
}
return writer.toString();
}
}
Also, the application is neither a sprintboot nor a spring project.
Tried this:
#GET
public Response getMetrics(#Context HttpHeaders headers) {
String accept = headers.getRequestHeader("Accept").get(0);
System.out.println("Accept Header --------------------------> " + accept);
return Response.ok(prometheusRegistry.scrape(), "application/openmetrics-text").build();
}
Even then the same error as above SS.
This worked for me:
#GET
public Response getMetrics() {
return Response.ok(prometheusRegistry.scrape(), TextFormat.CONTENT_TYPE_004).build();
}
I was create a soap web service at java. When called from java working fine, but when called from c# its not working, would you please help me? How to call getHelloWorldAsString method from c# with header authentication. Thanks in advance
Here is the java code:
package com.mkyong.ws;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;
//Service Implementation Bean
#WebService(endpointInterface = "com.mkyong.ws.HelloWorld")
public class HelloWorldImpl implements HelloWorld{
#Resource
WebServiceContext wsctx;
#Override
public String getHelloWorldAsString() {
MessageContext mctx = wsctx.getMessageContext();
//get detail from request headers
Map<?,?> http_headers = (Map<?,?>) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);
List<?> userList = (List<?>) http_headers.get("Username");
List<?> passList = (List<?>) http_headers.get("Password");
String username = "";
String password = "";
if(userList!=null){
//get username
username = userList.get(0).toString();
}
if(passList!=null){
//get password
password = passList.get(0).toString();
}
//Should validate username and password
if (username.equals("abcd") && password.equals("abcd123")){
return "Hello World JAX-WS - Valid User!";
}else{
return "Unknown User!";
}
}
#Override
public String getSum() {
return "10";
}
}
Here is the java calling (this is working):
package com.mkyong.client;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.handler.MessageContext;
import com.mkyong.ws.HelloWorld;
public class HelloWorldClient{
private static final String WS_URL ="http://localhost:8080/ws/HelloWorld?wsdl";
public static void main(String[] args) throws Exception {
try {
URL url = new URL(WS_URL);
QName qname = new QName("http://ws.mkyong.com/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
/*******************UserName & Password ******************************/
Map<String, Object> req_ctx = ((BindingProvider)hello).getRequestContext();
req_ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WS_URL);
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put("Username", Collections.singletonList("abcd"));
headers.put("Password", Collections.singletonList("abcd123"));
req_ctx.put(MessageContext.HTTP_REQUEST_HEADERS, headers);
/**********************************************************************/
System.out.println(hello.getHelloWorldAsString());
System.out.println(hello.getSum());
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
C# Client Call:
After adding wsdl to the windows application web service,written following C# code,
webService.HelloWorldImplService ws = new webService.HelloWorldImplService(); Console.WriteLine(ws.getHelloWorldAsString());
Please help me diagnose the error message "Failed to connect to service endpoint:". That is the complete error message. Kind of looks like it can't find the endpoint, but as you can see below, I do supply the endpoint with the ".withEndpointConfiguration" method.
Here is my code:
package xyz.bombchu;
import java.util.HashMap;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.InstanceProfileCredentialsProvider;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
public class LambdaFunctionHandler implements RequestHandler<Object, String> {
DynamoDB ddb;
#Override
public String handleRequest(Object input, Context context) {
Regions REGION = Regions.AP_SOUTHEAST_2;
HashMap<String, AttributeValue> item_values =
new HashMap<String, AttributeValue>();
String relativeTime = "02000001";
item_values.put("dateTime", new AttributeValue().withN(relativeTime));
item_values.put("cID", new AttributeValue("TEST"));
AmazonDynamoDB ddb = AmazonDynamoDBClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("dynamodb.ap-southeast-2.amazonaws.com", "ap-southeast-2"))
.withCredentials(new InstanceProfileCredentialsProvider())
.withClientConfiguration(new ClientConfiguration())
.build();
try {
ddb.putItem("myTableTest", item_values);
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
}
I'm trying to confirm the value of an HTTP response header with Spring 5 WebClient, but only if the web call responds with an HTTP 200 status code. In this use case if authentication is not successful, the API call returns with an HTTP 401 without the response header present. I have the following code below which functionally works, but it is making the web call twice (because I'm blocking twice). Short of just blocking on the HTTP response header only, and putting a try/catch for an NPE when the header isn't present, is there any "cleaner" way to do this?
import java.net.URI;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientRequest;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import org.springframework.web.reactive.function.client.ExchangeFunctions;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
#SpringBootApplication
public class ContentCheckerApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(ContentCheckerApplication.class);
private ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ContentCheckerApplication.class);
// prevent SpringBoot from starting a web server
app.setWebApplicationType(WebApplicationType.NONE);
app.run(args);
}
#Bean
public CommandLineRunner myCommandLineRunner() {
return args -> {
// Our reactive code will be declared here
LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
formData.add("username", args[2]);
formData.add("password", args[3]);
ClientRequest request = ClientRequest.method(HttpMethod.POST, new URI(args[0]+"/api/token"))
.body(BodyInserters.fromFormData(formData)).build();
Mono<ClientResponse> mresponse = exchange.exchange(request);
Mono<String> mnewToken = mresponse.map(response -> response.headers().asHttpHeaders().getFirst("WSToken"));
LOGGER.info("Blocking for status code...");
HttpStatus statusCode = mresponse.block(Duration.ofMillis(1500)).statusCode();
LOGGER.info("Got status code!");
if (statusCode.value() == 200) {
String newToken = mnewToken.block(Duration.ofMillis(1500));
LOGGER.info("Auth token is: " + newToken);
} else {
LOGGER.info("Unable to authenticate successfully! Status code: "+statusCode.value());
}
};
}
}
Thanks to comments from #M. Deinum to guide me, I have the following code which is workable now.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ExchangeFunction;
import org.springframework.web.reactive.function.client.ExchangeFunctions;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
#SpringBootApplication
public class ContentCheckerApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(ContentCheckerApplication.class);
private ExchangeFunction exchange = ExchangeFunctions.create(new ReactorClientHttpConnector());
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ContentCheckerApplication.class);
// prevent SpringBoot from starting a web server
app.setWebApplicationType(WebApplicationType.NONE);
app.run(args);
}
#Bean
public CommandLineRunner myCommandLineRunner() {
return args -> {
// Change some Netty defaults
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(
options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 2000)
.compression(true)
.afterNettyContextInit(ctx -> {
ctx.addHandlerLast(new ReadTimeoutHandler(1500, TimeUnit.MILLISECONDS));
}));
LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
formData.add("username", args[2]);
formData.add("password", args[3]);
WebClient webClient = WebClient.builder().clientConnector(connector).build();
Mono<String> tokenResult = webClient.post()
.uri( args[0] + "/api/token" )
.body( BodyInserters.fromFormData(formData))
.exchange()
.onErrorMap(ContentCheckerApplication::handleAuthTokenError)
.map(response -> {
if (HttpStatus.OK.equals(response.statusCode())) {
return response.headers().asHttpHeaders().getFirst("WSToken");
} else {
return "";
}
});
LOGGER.info("Subscribing for the result and then going to sleep");
tokenResult.subscribe(ContentCheckerApplication::handleAuthTokenResponse);
Thread.sleep(3600000);
};
}
private static Throwable handleAuthTokenError(Throwable e) {
LOGGER.error("Exception caught trying to process authentication token. ",e);
ContentCheckerApplication.handleAuthTokenResponse("");
return null;
}
private static void handleAuthTokenResponse(String newToken) {
LOGGER.info("Got status code!");
if (!newToken.isEmpty()) {
LOGGER.info("Auth token is: " + newToken);
} else {
LOGGER.info("Unable to authenticate successfully!");
}
System.exit(0);
}
}
Hello I have a class for doing webdav related operations such as creating a directory, Implementatiion can be seen below (the createDir method). The question is how to test it nicely, perhaps using EasyMock or a similar lib. Any ideas? thanks!
package foobar;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.jackrabbit.webdav.client.methods.DavMethod;
import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import mypackage.httpdclient.util.URLHandler;
public class WebDavImpl{
private static final String SEPARATOR = " ----- ";
private HttpClient httpClient;
public StorageSpaceClientImpl() {
httpClient = new HttpClient();
}
public String createDir(String dirName) {
String response = null;
String url = URLHandler.getInstance().getDirectoryUrl(dirName);
DavMethod mkcol = new MkColMethod(url);
try {
httpClient.executeMethod(mkcol);
response = mkcol.getStatusCode() + SEPARATOR + mkcol.getStatusText();
} catch (IOException ex) {
} finally {
mkcol.releaseConnection();
}
return response;
}
}