Javers Diff - Bug in JsonConverter - InitialValueChange - javers

I use JaVers 6.1.0 and want to create a diff of two objects with 'compare' and convert this to a JSON.
Diff diff = javers.compare(fooOldRevision, fooCurrent);
return javers.getJsonConverter().toJson(diff)
The output from this is:
{
"changes": [
{
"changeType": "NewObject",
"globalId": {
"entity": "cms.domain.Bar",
"cdoId": "8519527a-0686-4297-bbae-7e1680f2bed8"
}
},
{
"changeType": "ObjectRemoved",
"globalId": {
"entity": "cms.domain.Bar",
"cdoId": "ffe90351-8d47-4a0e-ac4c-fdbd79573495"
}
},
{
"changeType": "InitialValueChange",
"globalId": {
"entity": "cms.domain.Bar",
"cdoId": "8519527a-0686-4297-bbae-7e1680f2bed8"
},
"property": "id",
"propertyChangeType": "PROPERTY_VALUE_CHANGED"
}
]
}
After that, I want to transform this JSON into a Diff again at another method with:
Diff diff = javers.getJsonConverter().fromJson(changes, Diff.class);
Normally, this does work without a problem.
If the diff includes a new Object (with the changeType "InitialValueChange") the second code snippet will fail with the error:
org.javers.common.exception.JaversException: MALFORMED_CHANGE_TYPE_FIELD: no such Change type - 'InitialValueChange'
Is there a fix I could use?
Thank you for your help!

This should be fixed now in https://github.com/javers/javers/issues/1092.

Related

Trouble Deserializing Object Pulled from SQS using GSON

I have a lambda function the receives an S3Event object when a file is put into an S3 Bucket. When the lambda fails, it goes to a dead letter queue set up in Amazon SQS.
When I pull these messages, this this the body:
{
"Records": [
{
"eventVersion": "2.1",
"eventSource": "aws:s3",
"awsRegion": "us-east-1",
"eventTime": "d",
"eventName": "d:Put",
"userIdentity": {
"principalId": ""
},
"requestParameters": {
"sourceIPAddress": "2"
},
"responseElements": {
"x-amz-request-id": "",
"x-amz-id-2": "g"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "",
"bucket": {
"name": "",
"ownerIdentity": {
"principalId": ""
},
"arn": ""
},
"object": {
"key": "",
"size": 12502,
"eTag": "",
"sequencer": ""
}
}
}
]
}
That looks quite a bit like the S3Event object which contains a list of S3EventNotification records. I have tried to deserialize it to the S3 Event Object using the following:
S3Event event = new GsonBuilder().serializeNulls().create().fromJson(s3EventString, S3Event.class);
This results in a null object like so:
{"records":null}
I noticed in the json return from SQS, the "R" in Records is capitalized. I wasn't sure if that made a difference so I changed it to a lowercase "r" and it throws this error:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
I'm really not sure what type of object this actually is.
Any help would be greatly appreciated.
Strange. Using Jackson it works perfectly so I will use this for now..
import com.fasterxml.jackson.databind.ObjectMapper;
import com.amazonaws.services.sqs.model.Message;
private S3Event extractS3Event(Message message){
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(message.getBody(), S3Event.class)
}
//Then to get the S3 Details
S3Event event = extractS3Event(message);
S3Entity entity = event.getRecords().get(0).getS3();
String bucketName = entity.getBucket().getName();
String s3Key = entity.getObject().getKey();
Re:BEGIN_OBJECT but was STRING.
This is because AWS uses JodaTime for EventTime. You can avoid the issue by removing the field from the JSON text (assuming you do not need it)

Convert mongodb shell command to java code

I'm new in mongodb and spring, i need to convert the following query to java code but i didn't find how to do it.
db.collection.aggregate([
{
$project: {
members: {
$concatArrays: [
[
{
"userID": "$userID",
"userType": "$userType"
}
],
{
$reduce: {
input: "$clients",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[
{
userID: "$$this.userID",
userType: "$$this.userType"
}
],
"$$this.members"
]
}
}
}
]
}
}
},
{
$unwind: "$members"
},
{
$replaceRoot: {
newRoot: "$members"
}
}
])
I m stack in the $project part, i didn't find how to implement it in spring.
Can someone help me?
For use cases, where you find it hard to write a query in spring MongoDB java format, You can use the JSON/JavaScript code directly like this:
String jsonExpression = "{\"members\":{\"$concatArrays\":[[{\"userID\":\"$userID\",\"userType\":\"$userType\"}],{\"$reduce\":{\"input\":\"$clients\",\"initialValue\":[],\"in\":{\"$concatArrays\":[\"$$value\",[{\"userID\":\"$$this.userID\",\"userType\":\"$$this.userType\"}],\"$$this.members\"]}}}]}}";
AggregationOperation project = Aggregation.project().and(context -> context.getMappedObject(Document.parse(jsonExpression))).as("difference");
You can refer to my other answer here : Difference between "now" and a given date

How to have nested configurations in config json file and read it using bit?

so far I've seen some sample code in which some configuration is inserted in a file named environment.json like this:
[
{
"Name": "Default",
"AppInfo": {
"Name": "blahblah",
"Version": "1"
},
"Configs": [
{
"Key": "someconfiguration",
"Value": "some value"
},
{
"Key": "another configuration ",
"Value": "blah blah"
},
]
}
]
and then when needed, data can be read from configuration file like this:
var value = DefaultAppEnvironmentsProvider.Current
.GetActiveAppEnvironment()
.GetConfig<string>("SomeConfiguration");
The Question is:
what if I want to have some configuration whose value is a nested json list or json object. I want some thing like this:
"key": "Address",
"value": {
"street": "some street name",
"postal code": "blah blah",
...
}
how can I read such configurations using bit?
thanks for your time in advance.
First of all, create a class which defines your configuration contract:
public class MailServerConfig
{
public Uri ServerUrl { get; set; }
public string AnotherConfig { get; set; }
}
Then add followings to your environments.json file:
,
{
"Key": "MailServerConfig",
"Value": {
"$type": "SampleApp.Configurations.MailServerConfig, SampleApp",
"ServerUrl": "https://google.com/",
"AnotherConfig": "!"
}
}
In your controllers (Or wherever you want to read your configs), inject AppEnv
public AppEnvironment AppEnv { get; set; }
Then read your config as below:
MailServerConfig mailServerConfig = AppEnv.GetConfig<MailServerConfig>("MailServerConfig");
Note that in environments.json, $type is mandatory, and it should be the first line of your nested json object.

Spring Data ElasticSearch Build In IN query returning partial match

I am new to elastic search spring data, Today I was trying to get In query working with Spring data ES repository.
I have to do a lookup for list of user names, and if its exactly match in the index, need to get those users back as result.
I tried to use the built in repository 'In' method to do so, but it returns partial matches, please help me to make this working like SQL IN query.
Here is my repository code:
public interface UserRepository extends ElasticsearchRepository<EsUser, String>
{
public List<EsUser> findByUserAccountUserNameIn(Collection<String> terms);
}
REQUEST:
{"terms":["vijay", "arun"], "type":"NAME"}
RESPONSE:
[
{
"userId": "236000",
"fbId": "",
"userAccount": {
"userName": "arun",
"urlFriendlyName": "arun",
},
"userProfile": {
},
"userStats": {
}
},
{
"userId": "6228",
"userAccount": {
"userName": "vijay",
"urlFriendlyName": "vijay",
},
"userProfile": {
},
"userStats": {
}
},
{
"userId": "236000",
"fbId": "",
"userAccount": {
"userName": "arun singh",
"urlFriendlyName": "arun-singh",
},
"userProfile": {
},
"userStats": {
}
}
{
"userId": "236000",
"fbId": "",
"userAccount": {
"userName": "vijay mohan",
"urlFriendlyName": "vijay-mohan",
},
"userProfile": {
},
"userStats": {
}
}
]
This is because your userAccount.userName field is an analyzed string, and thus, the two tokens arun and singh have been indexed. Your query then matches the first token, which is normal.
In order to prevent this and guarantee an exact match you need to declare your field as not_analyzed, like this:
#Field(index = FieldIndex.not_analyzed)
private String userName;
Then you'll need to delete your index and the associated template in /_template, restart your application so a new template and index are created with the proper field mapping.
Then your query will work.

Which is the better design for this API response

I'm trying to decide upon the best format of response for my API. I need to return a reports response which provides information on the report itself and the fields contained on it. Fields can be of differing types, so there can be: SelectList; TextArea; Location etc..
They each use different properties, so "SelectList" might use "Value" to store its string value and "Location" might use "ChildItems" to hold "Longitude" "Latitude" etc.
Here's what I mean:
"ReportList": [
{
"Fields": [
{
"Id": {},
"Label": "",
"Value": "",
"FieldType": "",
"FieldBankFieldId": {},
"ChildItems": [
{
"Item": "",
"Value": ""
}
]
}
]
}
The problem with this is I'm expecting the users to know when a value is supposed to be null. So I'm expecting a person looking to extract the value from "Location" to extract it from "ChildItems" and not "Value". The benefit to this however, is it's much easier to query for things than the alternative which is the following:
"ReportList": [
{
"Fields": [
{
"SelectList": [
{
"Id": {},
"Label": "",
"Value": "",
}
]
"Location": [
{
"Id": {},
"Label": "",
"Latitude": "",
"Longitude": "",
"etc": "",
}
]
}
]
}
So this one is a reports list that contains a list of fields which on it contains a list of fieldtype for every fieldtype I have (15 or something like that). This is opposed to just having a list of reports which has a list of fields with a "fieldtype" enum which I think is fairly easy to manipulate.
So the Question: Which format is best for a response? Any alternatives and comments appreciated.
EDIT:
To query all fields by fieldtype in a report and get values with the first way it would go something like this:
foreach(field in fields)
{
switch(field.fieldType){
case FieldType.Location :
var locationValue = field.childitems;
break;
case FieldType.SelectList:
var valueselectlist = field.Value;
break;
}
The second one would be like:
foreach(field in fields)
{
foreach(location in field.Locations)
{
var latitude = location.Latitude;
}
foreach(selectList in field.SelectLists)
{
var value= selectList.Value;
}
}
I think the right answer is the first one. With the switch statement. It makes it easier to query on for things like: Get me the value of the field with the id of this guid. It just means putting it through a big switch statement.
I went with the first one because It's easier to query for the most common use case. I'll expect the client code to put it into their own schema if they want to change it.

Resources