update detail object from parent request spring - spring

i have two classes (entity) that reflect a master/detail dependency
public class Invoice {
String id
String total
String number
...
#OneToMany
List<InvoiceDetail> invoiceDetail
}
public class InvoiceDetail {
String id
String code
String price
....
}
for each resource service i have a method (reflection implementation) that does a partial update individually
// detail patch example
request.forEach((key, value) -> {
Field field = ReflectionUtils.findRequiredField(invoiceDetail.class, key.toString());
field.setAccessible(true);
ReflectionUtils.setField(field, invoiceDetail, value);
});
return invoiceDetailRepository.save(invoiceDetail);
but now i want to implement one time shot partial update with master and detail objects as json and use the invoice and invoiceDetail patch methods. My target is only change and update fields that comes from body json.
{
"number": "string", //only change number in invoice
"invoiceDetails": [
{
"id": 1,
"code": "string", //only change code that invoice detail
},
{
"id": 3,
"price": "number" //only change price that invoice detail
......
}
]
}
It's a good idea to receive the json body and send the respective objects (master/detail) to respective patch methods ?

Related

Spring Data MongoRepository Saving Objects with Differing Numbers of Fields

I am storing game states in a MongoDB database and am using Spring Data to manage my database interactions. I am new to Spring Data and am unsure how to deal with the following scenario.
I have a document of type "Game" with a bunch of properties such as id, timestamp, etc... One of these properties is a list of actions taken by users. These actions are of the form:
{ type: 2 }, {type: 3, value: 4}, {type: 5, id: 1234}, {type 6}, {type: 5, value: 6, id: 56}
In other words, an action can have three properties: type, value, and id. However, not every action requires all three values to be stored. I want to avoid having a bunch of null values in my database, and would like my database to just not include and id or a value if they are not specified.
Using Spring Data's MongoRepository model, I am not sure how to achieve this. I can create a CRUD Game class and have one of its properties be a list of Action (where Action itself is a CRUD class with properties type, value, and id), but won't that end up storing null values in the database if I do not specify the value or id?
In short, how can I use Spring Data's MongoRepository but still maintain the flexibility of being able to store lists of objects with varying parameters or object types in general.
I will explain how varying fields are handled with an example. The following Game.java POJO class represents the object mapping to the game collection document.
public class Game {
String name;
List<Actions> actions;
public Game(String name, List<Actions> actions) {
this.name = name;
this.actions = actions;
}
public String getName() {
return name;
}
public List<Actions> getActions() {
return actions;
}
// other get/set methods, override, etc..
public static class Actions {
Integer id;
String type;
public Actions() {
}
public Actions(Integer id) {
this.id = id;
}
public Actions(Integer id, String type) {
this.id = id;
this.type = type;
}
public Integer getId() {
return id;
}
public String getType() {
return type;
}
// other methods
}
}
For the Actions class you need to provide constructors with the possible combinations. Use the appropriate constructor with id, type, etc. For example, create a Game object and save to the database:
Game.Actions actions= new Game.Actions(new Integer(1000));
Game g1 = new Game("G-1", Arrays.asList(actions));
repo.save(g1);
This is stored in the database collection game as follows (queried from mongo shell):
{
"_id" : ObjectId("5eeafe2043f875621d1e447b"),
"name" : "G-1",
"actions" : [
{
"_id" : 1000
}
],
"_class" : "com.example.demo.Game"
}
Note the actions array. As we had stored only the id field in the Game.Actions object, only that field is stored. Even though you specify all the fields in the class, only those provided with values are persisted.
These are two more documents with Game.Actions created with type only and id + type using the appropriate constructors:
{
"_id" : ObjectId("5eeb02fe5b86147de7dd7484"),
"name" : "G-9",
"actions" : [
{
"type" : "type-x"
}
],
"_class" : "com.example.demo.Game"
}
{
"_id" : ObjectId("5eeb034d70a4b6360d5398cc"),
"name" : "G-11",
"actions" : [
{
"_id" : 2,
"type" : "type-y"
}
],
"_class" : "com.example.demo.Game"
}

Custom ASP.NET Core MVC validation response

ASP.NET Core MVC has a great model binding & model validation subsystem which supports almost any scenario. But when developing APIs things can go a little more complicated.
Suppose we have a model class with a property XYZ which is annotated with [MinLength(5)].
public class ViewModel
{
[MinLength(5)]
public string XYZ { get; set; }
}
If anything goes wrong with this property what MVC will give you is something like this:
{ "XYZ": [ "The field XYZ must be a string or array type with minimum length of '5'" ] }
But this is not what the client needs! The client needs an object with specific details. She will create her own message however she wants to:
{ "error": "minLength", "property": "XYZ", "minimum": 5 }
Possible Solutions:
You can use InvalidModelStateResponseFactory to generate customized responses. It gives you the ActionContext which contains the ModelState property. But all you can do is to process error messages which are pure strings! That could lead to some problems.
Another option is to completely disable the MVC Validation and implement one for yourself.
I appreciate any other solutions.
For general validation message, it is pure string. And for minLength and minimum are different for different validation attribute. I am wondering how the client will check the different node.
For server side, InvalidModelStateResponseFactory would be better to return json object. and you need to check the ValidationAttribute for return different object like
services.Configure<ApiBehaviorOptions>(o =>
{
o.InvalidModelStateResponseFactory = actionContext =>
{
var error = new Dictionary<string, string>();
foreach (var key in actionContext.ModelState.Keys)
{
foreach (var parameter in actionContext.ActionDescriptor.Parameters)
{
var prop = parameter.ParameterType.GetProperty(key);
if (prop != null)
{
var attr = prop.GetCustomAttributes(typeof(ValidationAttribute), false).FirstOrDefault() as ValidationAttribute;
if (attr is MinLengthAttribute minLengthAttribute)
{
error.Add("Error", "minLength");
error.Add("Property", key);
error.Add("minimum", minLengthAttribute.Length.ToString());
}
}
}
}
return new BadRequestObjectResult(error);
};
});

web api : checking odata query

I have a web api exposing a list of sessions. This is my code :
[RoutePrefix("api/data")]
public class SessionController : ApiController
{
[HttpGet]
[Route("sessions")]
[Queryable]
public IQueryable<Session> Get()
{
List<Session> list = new List<Session>();
list.Add(new Session { Id = 1, Name = "name 1", Place = "place 1", SessionOn = Convert.ToDateTime("1/1/2014") });
list.Add(new Session { Id = 2, Name = "name 2", Place = "place 2", SessionOn = Convert.ToDateTime("2/1/2014") });
list.Add(new Session { Id = 3, Name = "name 3", Place = "place 3", SessionOn = Convert.ToDateTime("3/1/2014") });
return list.AsQueryable();
}
}
public class Session
{
public int Id { get; set; }
public string Name { get; set; }
public string Place { get; set; }
public DateTime SessionOn { get; set; }
}
An user can request this api to see all sessions like this :
mydomain/api/data/sessions
I have added the oData to allow user querying and filtering those data like this :
mydomain/api/data/sessions?$filter=Name eq 'name1'
mydomain/api/data/sessions?$filter=Place eq 'place 1'
Everything is working well, the only problem remaining is that I would like to check the query given by the user to tell him that a value is not valid for example :
In my list of sessions, the possible values for the field 'Place' are :
place 1
place 2
place 3
place 4
If the user do the following request :
mydomain/api/data/sessions?$filter=Place eq 'placezzzzz 1'
He will just get an empty sets of Session. What I would like to do, is check in my backend code api the value given (that is to say 'placezzzzz 1') and returns a response to the user telling that this value is invalid.
You could change your method signature to include a ODataQueryOptions<T> parameter:
[EnableQuery]
public IQueryable<Session> Get(ODataQueryOptions<Session> options) {
//Do something fun with the filter:
var filter = options.Filter;
}
The options argument can be added to your Get method without changing the routing. It contains all kinds of info about the request, including the filter that was used. You can still return your IQueryable, just like you are used to.
I use the [System.Web.OData.EnableQuery] attribute instead of [Queryable] because QueryableAttribute is obsolete. (source msdn) I'm not sure this solution will work with the older version.

Kendo UI Grid - How to Bind to Child Properties

How to bind a column/field to a child property of the json result in the model settings of the Kendo grid (in javascript)? For example, I want the grid to contain columns: FName, LName, Street and Address. Basically I want to flatten the hierarchical structure returned by the web service.
Kendo Settings
fields: {
FName: { type: "string" },
LName: { type: "string" },
// How to map to child properties below?
Street: { field: "Address.Street" }, // this is wrong
City: { field: "Address.City" } // this is wrong
}
JSON
{
"FName": "William",
"LName ": "Shakespeare",
"Address":
{
"Address": "123 Street Ln",
"City": "Philadelphia"
}
}
You don't do it like that. You need to create a class 'Model' that flattens the data graph. You will be able to use lazy loading during the construction of the Model. Either send this Model to the View via the controller or attach it to a larger ViewModel (just a Model of Models not MVVM) that is sent to the View. Then bind this to the Grid.
But, you will be happier to use Ajax loading of the same Model as JSON, which is what I think you are trying to do.
Model
public class ContactModel
{
public string FName { get; set; }
public string LName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public ContactModel()
{}
public ContactModel(Contact contact) // IContact is better if you have Interfaces
{
FName = contact.FName;
LName = contact.LName;
Address = contact.Address.Address;
City = contact.Address.City;
}
// Neat Linq trick to convert database query results directly to Model
public static IList<ContactModel> FlattenToThis(IList<Contact> contacts)
{
return contacts.Select(contact => new ContactModel(contact)).ToList();
}
}
Controller
public JsonResult ReadContacts([DataSourceRequest]DataSourceRequest request)
{
var contacts = _contactsDataProvider.Read(); // Your database call, etc.
DataSourceResult result = ContactModel.FlattenToThis(contacts).ToDataSourceResult(request);
return Json(result, JsonRequestBehavior.AllowGet);
}
But I don't think Will ever made it to Philly. ;)

How to Parse Json webService response in WP7

I want to display data in Listbox without using any DLL.And my webservice responding in json format.
My Web service Response is as below.it has more than 800 records
[
{
"st_id":"1",
"st_name":"name xyz"
},
{
"st_id":"2",
"st_name":"name ABC"
},
{
"st_id":"3",
"st_name":"name HIJK"
},
{
"st_id":"4",
"st_name":"name OPQ"
},
]
my Class for the data is as below
[DataContract]
public class Student
{
[DataMember=("st_id")]
public bool st_id { get; set; }
[DataMember=("st_name")]
public string st_name { get; set; }
}
i m trying serialize object using DataContractJsonSerializer & m getting WS response in Stream.But i am not able to serialize.Suggest links or basic tutorial for Serilize and Deserilize of the json
DataContractJsonSerializer stdserialize =
new DataContractJsonSerializer(typeof(Student));
Student stuser = (Student)stdserialize.ReadObject(responseStream);
so please help for the json response parsing & suggest link for datacontract and all which gives knowledge from basics.
Thanks,
You declared st_id as a bool, but the type of data you're trying to deserialize is string (which can be converted to numbers - not to booleans). Try declaring it as string and it should work.
Also, the response is an array of objects, so the type you should use is Student[]:
DataContractJsonSerializer stdserialize =
new DataContractJsonSerializer(typeof(Student[]));
Student stuser = (Student[])stdserialize.ReadObject(responseStream);

Resources