micrometer statistics for RestTemplate - java

I am trying to get metric for rest uri using micrometer. I read this, this, and also bunch of SO posts. After reading all these, I have a some questions about it.
For micrometer to work correctly uri should be parameterized(Reference). I assume this is only path variable not request params. Am i correct?
should we not use UriComponentsBuilder at all because we don't want to expand uri outside resttemplate and let resttemplate do it for us.
lets say if i use this restClient.getForObject( fooSvcUrl, FooBar.class, uriVariables );, does micrometer looks at fooSvcUrl and it should be parametertized at this point ?
I am asking this because if i do following, it will not work. AM I correct ? restClient.getForObject(UriComponentsBuilder.fromUriString(fooSvcUrl).buildAndExpand(uriVariables ).toUri() , FooBar.class);

I recently had to deal with all that stuff, so I can give you the following answers:
1. "Parametrized" means here that you apply placeholders into the URI, regardless of their position. Basically, you can parametrize everything in the URI - the scheme, the host, path variables and even query parameters, as long as you provide values to replace the placeholders.
As such, the following URIs will work when passed to RestTemplate:
https://www.foo.bar/baz/{id} with {"id":1}
https://www.foo.bar/baz?id={id} with {"id":1}
https://{host}/foo/{id}?target={target} with {"host":"www.foo.bar", "id":1, "target":"baz"}
2. You should not, especially if you would expect reserved symbols that would be put into your URI, as UriComponentsBuilder will try to encode them immediately which results in RestTemplate doing a double tap on the encoding, turning a non-fragment # first into a %23 and then into a %2523.
3. You are right, the URI should be parametrized when used as an argument for RestTemplate.getForObject, as the overloaded methods taking parameter value arguments will expand it before actually performing the request.

Related

Android Retrofit - send dynamic number of POST parameters

I need to send a dynamic number of POST parameter values to an endpoint (there could be 1 or there could be 50). All of them will have the same key value.
Is this possible? I can't seem to figure out how to create a RequestBody that encompasses something like this, even when I try to construct it in plain text.
I have the list of strings prepared for it, but I just don't know how to create this kind of thing. The endpoint works in PostMan when I input a lot of post form parameters with the same key value, so the endpoint is setup properly for it. I'm just not sure if Retrofit supports this kind of thing, and I cannot seem to find any info around it.
I'm currently working with Java instead of Kotlin. Thoughts?
You can also pass multiple field parameter to your request like this:
#FormUrlEncoded
#POST("/oauth/access_token")
Call<YourResponseObject> sendData(
#FieldMap Map<String, String> params
);
The map can take variable number of args.
So you can pass data like:
/*
map = { "field1_key"="value1", "field2_key"="value2", "field3_key"="value3", ...}
*/
retrofit().create(YourInterface.class).sendData(mapOfFields)
p.s: retrofit() is a method that returns a Retrofit instance to work with.

Filtering by machine type in GCP

I'm trying to filter the list of instance based on the machine type. However this doesn't seem to work.
Compute.Instances.List request = computeService.instances().list("project-name","us-central1-a" );
request.setFilter("(machinetype = zones/us-central1-a/machineTypes/n1-standard-1)");
InstanceList instanceList = request.execute();
List<Instance> instances = instanceList.getItems();
The response is empty even though, I have an instance that match the filter! (when I remove the filter it gets the instance.)
[chaker#cbenhamed:~]$ gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
foo-bar-worker-n1-standard-1-65304152130-zfq us-central1-a n1-standard-1 true 10.240.0.2 00.000.00.255 RUNNING
According to the documentation, the filter parameter should work in this case. Because, first the machineType is in the root of the Instance object. And second that's the right form of the machineType argument
Full or partial URL of the machine type resource to use for this instance, in the format: zones/zone/machineTypes/machine-type. This is provided by the client when the instance is created.
I tried to inspect HTTP requests made by gcloud
gcloud compute instances list --filter="machineType:n1-standard-1" --log-http
But it turned out that it gets the whole list (across all zones!) and filter them locally!
It seems to be a misunderstanding, the documentation describes the machineType as an argument of the Response body no as a filter. So in this case you can't use a partial URL also you can only use the following comparison operators =, !=, >, or < which none of them works as a like.
I think the only way to use this filter is using the full URL just as Oleksandr Bushkovskyi commented:
machineType="https://www.googleapis.com/compute/v1/projects/[PROJECT]/zones/[ZONE]/machineTypes/[MACHINE_TYPE]"

How to verify optional parameters using Wiremock Request Matching Stubs(JAVA Usage)?

How to match a wiremock POST request with some optional JSON parameters & any values?
Being new to Stack community, I have raised the below query(afraid that it would be marked as duplicate) in the post (link mentioned above) but it has been deleted stating its the different question and need to raise new question.
Below is my query on top of it:
If the optional parameter is not present in the request than the solution provided in the above post suffice the purpose.
But If the optional parameter is present and we need to check that it contains specific values only (say not null) then how to do that using request matching in wiremock.
Kindly provide your inputs as I am new to wiremock
For instance:
{
"optional1"="ValueAlwaysYESIfPresent",
"optional2"="ValueAlwaysNOIfPresent"
}
Thanks in advance.
Have a look at http://wiremock.org/docs/request-matching/
instead of going to other places
basically you can do matching like
withQueryParam("optional2", equalTo("ValueAlwaysNOIfPresent"))
Or
withQueryParam("optional2", matching(".*12345.*"))
and many other things.

Adding custom step definitions to Karate framework

I have a need to extract a field parsed from a "complex" response header and use that value later in the test.
It seems that the "header" keyword in Karate is set up for setting request headers, not parsing response headers.
Is there a way to add a custom step definition maintaining access to the scenario variable stores? It appears the variable stores are private in the StepDefs class, and there doesn't seem to be a way to extend it easily.
You can get access to the response headers. Please look at the documentation for responseHeaders.
That said, the match header short-cut is most likely what you are looking for.
Karate's philosophy is that you never need to write custom step-definitions.
edit: some examples, sounds like you just need to do some string manipulation of the Location header ? You can freely mix JS code into Karate expressions.
* def location = responseHeaders['Location'][0]
# assume location = 'foo?bar=baz'
* def bar = location.substring(location.indexOf('bar=') + 4)

Handling multiple parameters in a URI (RESTfully) in Java

I've been working on a small scale web service in Java/Jersey which reads lists of user information from clients contained in XML files. I currently have this functioning in all but one aspect: using multiple parameters in the URI to denote pulling multiple sets of user information or multiple sets of client information. I have a version which currently works, but is not the best way nor what the project description calls for.
Currently, my code looks like this:
#Path("Client/{client}/users")
public class UserPage
{
#GET
#Produces(MediaType.TEXT_HTML)
public String userChoice(#PathParam(value = "client") final String client)
{****Method here which handles a list of 'users'****}
#GET
#Path("{name}")
#Produces(MediaType.TEXT_HTML)
public String userPage(#PathParam(value = "client") final String client, #PathParam(value = "name") final String name)
{****Method here which handles 'user' information****}
The first method handles a list of users from a 'client' denoted by "{client}" in the URI. The second method delivers 'user' information denoted by "{name}" in the URI. Both will function with a single argument. Currently, in order to handle multiple 'users' I have "{name}" comma separated like "Client/Chick-Fil-A/users/Phil,Bradley". I can parse this after using #PathParam and create an array of these 'users', but again, I feel this is not the best way to handle this, and the project description calls for something different.
Is there a way to accomplish this same task with a URI formatted as "Client/Chick-Fil-A;cd=Phil,Bradley"? (The ;cd= is what's giving me the most trouble.)
I also need to be able to use this format for multiple clients, i.e. "Client;cd=Chick-Fil-A,Subway/users;cd=Phil,Bradley".
Edit: To clarify the project:
The client information is contained in 6 separate files. Each of these files has the same 3 users (this is a proof of concept, effectively). I need to be able to pull different subsets of information, for instance, user Phil from McDonalds and Chick-Fil-A, or users Phil and Peter from McDonalds, or users named Peter from all clients, etc.
You cannot use '=' in the URL path since it's a reserved character. However there are many other character you can use as delimiters such as '-' and ','. So instead of '=' you can use '-'. If you really really want to use '=' then you will have to URL-encode it; however, I would strongly recommend against this because it may make things more complicated then it should be.
You can see the grammar of the URL string here:
http://www.w3.org/Addressing/URL/url-spec.txt
Copy and search the following string to skip to the path grammar:
path void | segment [ / path ]
segment xpalphas
That said, I believe HTTP request is usually used for request single resource only. So my personal opinion is to not implement the service the way you implemented. For getting multiple clients I would use query parameters as filters like this:
Client/{cName}/users?filters=<value1>,<value2> ...
Edit: From the business case you got there, it seems like you probably need service like
/users?<filters>
/clients?<filters>
So say you want to get Peter from all clients then can have a request of this form:
/users?name=Peter
Similarly, if you want to get Jack and Peter from Starbucks then you can do:
/users?name=Peter,Jack&client=Starbucks
Hopefully this helps.
Query strings have the following syntax and you can have multiple parameters with the same name:
http://server/path/program?<query_string>
where query_string has the following syntax:
field1=value1&field1=value2&field1=value3…
For more details check out this entry in Wikipedia: http://en.wikipedia.org/wiki/Query_string

Categories

Resources