How to check jsonPath for value to expect on different property - spring

I have error messages that comes back in the following structure:
[{
"detail": "must be unique",
"source": {
"pointer": "name"
}
}]
I've omitted unnecessary bits for the sake of this example. In my test, I can do the following:
mockMvc.perform(post(URL)
.contentType(contentType)
.content(this.json(brand)))
.andExpect(status().isUnprocessableEntity())
.andExpect(jsonPath("$.[0].detail", is("must be unique")));
However, if I am testing that I get multiple errors back, $.[0].detail isn't always Name. So I need to first check what the $.[0].source.pointer is and then validate the detail message. I'm just unsure on how to go about doing that.

Related

How to return both errors and data with Apollo server?

I know Apollo can return a combination of errors and data, the client documentation even covers this: and I believe this can happen automatically when field resolvers run into issues, but how can I manually return mixed payloads? I have use cases in my project where users will send large amounts of data to mutate in the form of an input array. Some members of the array might be malformed and I want to throw validation errors for those but I still want the other changes to go through. An example pseudo request and response pair would be:
// input
{
mutateEntities: [
entityAMutationInput,
entityBMutationInput,
entityCMutationInput,
entityDMutationInput,
]
}
// response
{
data: {
mutateEntities: [
entityAMutationPayload,
entityBMutationPayload,
]
},
// members of the error array seem to refer to individual GQL operations in Apollo so
// there would only be one here
errors: [{
...errorMetadata
extensions: {
validationErrors: [
entityCValidationError,
entityDValidationError
]
}
}]
}
I realise that I have the option of defining errors as a GQL type and including them as part of the payload data but our FE team already has error handling that expects errors to appear in payload.errors and not, for example, payload.data.validationErrors or some other custom field. I want my errors to appear in payload.errors.extensions.validationErrors to minimise the impact on the front end. I know how to do this if I throw a custom error but, if I throw errors, then I won't have any data under payload.data.mutateEntities.

GraphQL java: return a partial response and inform a user about it

I have a SpringBoot application that uses GraphQL to return data to a request.
What I have
One of my queries returns a list of responses based on a list of ids supplied. So my .graphqls file is a follows:
type Query {
texts(ids: [String]): [Response]
}
type Response {
id: String
text: String
}
and the following are request & response:
Request
texts(ids:["id 1","id 2"]){
id
text
}
Response
{
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
}
]
}
At the moment, if id(s) is/are not in aws, then exception is thrown and the response is an error block saying that certain id(s) was/were not found. Unfortunately, the response for other ids that were found is not displayed - instead the data block returns a null. If I check wether data is present in the code via ssay if/else statment, then partial response can be returned but I will not know that it is a partial response.
What I want to happen
My application fetches the data from aws and occasionally some of it may not be present, meaning that for one of the supplied ids, there will be no data. Not a problem, I can do checks and simply never process this id. But I would like to inform a user if the response I returned is partial (and some info is missing due to absence of data).
See example of the output I want at the end.
What I tried
While learning about GraphQL, I have encountered an instrumentation - a great tool for logging. Since it goes through all stages of execution, I thought that I can try and change the response midway - the Instrumentation class has a lot of methods, so I tried to find the one that works. I tried to make beginExecution(InstrumentationExecutionParameters parameters) and instrumentExecutionResult(ExecutionResult executionResult, InstrumentationExecutionParameters parameters) to work but neither worked for me.
I think the below may work, but as comments suggests there are parts that I failed to figure out
#Override
public GraphQLSchema instrumentSchema(GraphQLSchema schema, InstrumentationExecutionParameters parameters) {
String id = ""; // how to extract an id from the passed query (without needing to disect parameters.getQuery();
log.info("The id is " + id);
if(s3Service.doesExist(id)) {
return super.instrumentSchema(schema, parameters);
}
schema.transform(); // How would I add extra field
return schema;
}
I also found this post that seem to offer more simpler solution. Unfortunately, the link provided by host does not exist and link provided by the person who answered a question is very brief. I wonder if anyone know how to use this annotation and maybe have an example I can look at?
Finally, I know there is DataFetcherResult which can construct partial response. The problem here is that some of my other apps use reactive programming, so while it will be great for Spring mvc apps, it will not be so great for spring flux apps (because as I understand it, DataFetcherResult waits for all the outputs and as such is a blocker). Happy to be corrected on this one.
Desired output
I would like my response to look like so, when some data that was requested is not found.
Either
{
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
},
{
"id": "Non existant id",
"msg": "This id was not found"
}
]
}
or
{
"error": [
"errors": [
{
"message": "There was a problem getting data for this id(s): Bad id 1"
}
]
],
"data": [
{
"id": "id 1",
"text": "Text 1"
},
{
"id": "id 2",
"text": "Text 2"
}
]
}
So I figured out one way of achieving this, using instrumentation and extension block (as oppose to error block which is what I wanted to use initially). The big thanks goes to fellow Joe, who answered this question. Combine it with DataFetchingEnviroment (great video here) variable and I got the working solution.
My instrumentation class is as follows
public class CustomInstrum extends SimpleInstrumentation {
#Override
public CompletableFuture<ExecutionResult> instrumentExecutionResult(
ExecutionResult executionResult,
InstrumentationExecutionParameters parameters) {
if(parameters.getGraphQLContext().hasKey("Faulty ids")) {
Map<Object, Object> currentExt = executionResult.getExtensions();
Map<Object, Object> newExtensionMap = new LinkedHashMap<>();
newExtensionMap.putAll(currentExt == null ? Collections.emptyMap() : currentExt);
newExtensionMap.put("Warning:", "No data was found for the following ids: " + parameters.getGraphQLContext().get("Faulty ids").toString());
return CompletableFuture.completedFuture(
new ExecutionResultImpl(
executionResult.getData(),
executionResult.getErrors(),
newExtensionMap));
}
return CompletableFuture.completedFuture(
new ExecutionResultImpl(
executionResult.getData(),
executionResult.getErrors(),
executionResult.getExtensions()));
}
}
and my DataFetchingEnviroment is in my resolver:
public CompletableFuture<List<Article>> articles(List<String> ids, DataFetchingEnvironment env) {
List<CompletableFuture<Article>> res = new ArrayList<>();
// Below's list would contain the bad ids
List<String> faultyIds = new ArrayList<>();
for(String id : ids) {
log.info("Getting article for id {}",id);
if(s3Service.doesExist(id)) {
res.add(filterService.gettingArticle(id));
} else {
faultyIds.add(id);// if data doesn't exist then id will not be processed
}
}
// if we have any bad ids, then we add the list to the context for instrumentations to pick it up, right before returning a response
if(!faultyIds.isEmpty()) {
env.getGraphQlContext().put("Faulty ids", faultyIds);
}
return CompletableFuture.allOf(res.toArray(new CompletableFuture[0])).thenApply(item -> res.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
You can obviously separate error related ids to different contexts but for my simple case, one will suffice. I however still interested in how can the same results be achieved via error block, so i will leave this question hanging for a bit before accepting this as a final answer.
My response looks as follows now:
{
"extensions": {
"Warning:": "No data was found for the following ids: [234]"
},
"data": { ... }
My only concern with this approach is security and "doing the right thing" - is this correct thing to do, adding something to the context and then using instrumentation to influence the response? Are there any potential security issues? If someone know anything about it and could share, it will help me greatly!
Update
After further testing it appears if exception is thrown it will still not work, so it only works if you know beforehand that something goes wrong and add appropriate exception handling. Cannot be used with try/catch block. So I am a half step back again.

transfer a sibling element in a reponse to a parameter in Ready API / SOAP UI

I have one REST endpoint which returns a response like this
[
{
"id": "dbfff519-e8f6-4db3-9e26-a4e9014dc360",
"code": "123456789012345678901234567890123456789012345678901234567890",
"name": "client-code-with-character-length-sixty",
},
{
"id": "a673fb54-3503-4996-ba9b-a4e9014dc3ea",
"code": "18MTH",
"name": "18 Month",
},
{
"id": "60b781e3-4515-40f5-81ee-a4e9014dc400",
"code": "2periods",
"name": "I Have 2 Periods",
}...
etc.
I would like to be able to retrieve the ID where, for example, the code="2periods" UI can retrieve using either ResponseAsxml (with xpath) or Response with JSONPath. If I use the former I have managed to get the following to nearly retrieve my ID
//Response[1]/e[code='2periods']/id
BUT this looks like this
<id>0bc4aa5f-f8ab-4efe-b788-a4e9014dc45f</id>
And I don't know how to remove the start and end tags, the id has to be just the GUID.
I can't work out how to do something similar in the JSONPath - I've only managed to get something like
$[3].id
to work, but the order of the entities is not guaranteed in the test environment.
For ResponseAsXml using the XPath to get only the id value without <id> tag just add /text() to your actual expression:
//Response[1]/e[code='2periods']/id/text()
If you want to do the same using Response property with JSONPath try with:
$..[?(#.['code']=='2periods')].id[0]
Hope it helps,

Mailchimp API 2.0 lists/subscribe responding with error 250 List_MergeFieldRequired MMERGE4 is required

I'm attempting to use the lists/subscribe Mailchimp API 2.0 endpoint to subscribe an email to a list, but I keep getting a puzzling error. My request looks like:
{
"apikey":"myapikey-us5",
"id":"listid",
"email":{"email":"my#email.com"},
"double_optin":false,
"send_welcome":true
}
I'm sending this to https://us5.api.mailchimp.com/2.0/lists/subscribe.json and getting this response:
{
"status": "error",
"code": 250,
"name": "List_MergeFieldRequired",
"error": "MMERGE4 must be provided - Please enter a value"
}
And if I specify "merge_vars": {} I still get the same error. What am I missing here?
It means you need to provide a value for MMERGE4 merge field. How you do this depends on what type of merge field MMERGE4 is, but It would be like this:
{
"apikey":"myapikey-us5",
"id":"listid",
"email":{"email":"my#email.com"},
"double_optin":false,
"send_welcome":true,
"merge_vars": {
"MMERGE4": "something"
}
}
But you should look and see what type of data that is, otherwise you might cause issues for whomever set that up as a required field.

How to handle (failed) validation when creating multiple entities with a REST API

Let's say I have an endpoint user/1/results and I want to upload multiple results at a time.
So I send it JSON like:
{
"data": [
{
"date": "2014-02-14 03:15:41",
"score": 18649,
"time": 42892
},
{
"date": "2013-11-18 09:21:46",
"score": 7856,
"time": 23568.8
}]
}
Let's say time needs to be an integer, so the second entity fails validation.
What's the best thing to do:
Fail both, nothing saves, respond with error message..
Save first entity, respond with error message.
In either case, what would an error message look like? i.e. how/does it specify that it's the second entity that fails validation.
I think you should fail both and respond with an error message because it might be cumbersome again to track the remaining results.
Error message should give the details of failing location. for example if it fails at the second one then specify it in json response.

Resources