Custom serialization of specific return type for DataFetcher - spring-boot

I am using Spring for GraphQL (version 2.7.0-M1).
In my domain model, a lot of properties return an object Foo. This object must be serialized to a String based on data available from GraphQlContext. So the schema looks like:
type Parent {
code: String!
foo: String
...
}
It is easy to do this with #SchemaMapping for a specific parent type.
#SchemaMapping(typeName = "Parent", field = "foo")
public String foo(Parent parent, DataFetchingEnvironment env) {
var context = env.getGraphQlContext();
return ...
However, this is not very DRY. I am looking for a way to have this code at one place, like a custom scalar.
Is there a way to do this with spring-graphql / graphql-java?
Example
An example is a Localized<T> object we use. For instance a Product instance has Localized<String> properties for title and description (and more).
For the GraphQL query we can set the context, part of the context is the Locale. For all Localized property values the value can be converted to the string value for the locale. We are looking for a way to do this automagically. Otherwise it creates a lot of boiler plate code

Would #ContextValue help here? This would remove a bit of boilerplate from your controller handlers.
#SchemaMapping(typeName = "Parent", field = "foo")
public String foo(Parent parent, #ContextValue Foo foo) {
If you'd like something more involved, I think you should elaborate on the exact relationship between an attribute of one or multiple types in your schema, and some random value in the context.
Maybe you could come up with some concrete example here?

Related

Deserialize nested Kotlin object in springMVC

I'm trying to deserialize a complex GET request into a structure of nested objects.
The GET requests looks like:
curl 'localhost:8080/?id=1&inner.id=1'
and the code should look like this:
class RootObj(val id: Int, inner: InnerObject)
class InnerObject(val id: Int)
#RestController
class SearchController {
#GetMapping(path = ["/"])
fun getRoot(rootObj: RootObj): String {
return "ok"
}
}
This doesn't work out of the box I guess because spring doesn't know how properly create this nested structure.
Parameter specified as non-null is null: [...] parameter inner","path":"/"}%
Is there a way to overcome this problem? Maybe providing a totally custom deserializer code?
As alternative solution, I guess I could flatten the object hierarchy but for doing so I must be able to map a query parameter like inner.id to a field named innerId
Spring can actually map the query params to the custom object directly, but you need to provide defaults to the params of the custom object constructor.
So you need to define your classes as below for it to work
class RootObj(val id: Int = 0, val inner: InnerObject = InnerObject(0))
class InnerObject(var id: Int = 0)
Do note that the id field of InnerObject would have to be declared as var for Spring to be able to map it. Then curl 'localhost:8080/?id=1&inner.id=1' would work fine.

Apollo Graphql modify input data

In Apollo Server, one could use a schema directive to implement a resolver middleware like such:
adminGetUsers(getUsersPL: GetUsersPL!): [User] #hasRole(role: "ADMIN")
#hasRole(role: "ADMIN") serves as a middleware to prevent any non-admin user from using this mutation.
So how would one sanitize/transform input data? For example,
getUser(userId: String! #transform): [User]
#transform will take in userId as a hashed id (ie: xyfd), and transform it to a numbered ID (ie: 12). This syntax is not allowed of course. Basically I want a way to modify input data before it goes into resolver.
That actually is valid syntax. You can define a directive that's applied to argument definitions like this:
directive #test on ARGUMENT_DEFINITION
type Query {
foo(input: String #test): String
}
Schema directives are not middleware. They are just ways of altering individual definitions inside your schema. Most commonly they are used to alter field definitions, but you can alter other definitions like object types, input object types, enums, unions, etc. When using a directive with a field, you can wrap the existing resolve function inside another one (or replace it altogether) -- in doing so, we can create "middleware" for resolvers. However, that's not the purpose of schema directives.
That aside, you can't use an argument directive to alter the value the argument is passed. At best, you can change the type of the argument to something else (like a custom scalar). However, you can just use a field directive to do what you're trying to accomplish:
class ExampleDirective extends SchemaDirectiveVisitor {
public visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
field.resolve = async function (
source,
args,
context,
info,
) {
args.someArg = doSomething(args.someArg)
return resolve.call(this, source, args, context, info);
}
}
}

Custom serializer with django 2

I would like to create a serializer that given an input JSON object select certain attributes.
There is an legacy system that sends an object, for example:
{
"a": {
"b": "test"
}
}
I need to keep certain properties of the object. For example a.b. I have created the following serializer, but it is not working:
class CustomSerializer(serializers.Serializer):
b = serializers.CharField(source='a.b', required=True)
Does anyone know what is failing?
Thanks!
You need to implement nested serializer. For your example, you can try something like this :
class BSerializer(serializers.Serializer):
b = serializers.CharField()
class ASerializer(serializers.Serializer):
a = BSerializer()
Note : this is just an example of nested serializer. You might need to add few things to make it work properly for you (adding Meta, other fields if needed, use it in view etc. )

AutoMapper Project().To() not working for interface destination

I have a situation in AutoMapper where I need to create a mapping with an interface destination. This is not a problem, and when using the normal Mapper.Map, it returns a proxy class as expected. However, when I try to do something similar with .Project().To(), which I need to use because an ORM is involved, I have issues. Here is the exact code that is failing which I replicated in a unit test:
AutoMapper.Mapper.CreateMap<RoleDto, IRole>(); //both just have Id/Name
IQueryable<RoleDto> roleDtos = new List<RoleDto>
{
new RoleDto { Id = 1, Name = "Role1" },
new RoleDto { Id = 2, Name = "Role2" }
}.AsQueryable();
//this works:
List<IRole> roles = roleDtos.Select(
roleDto => AutoMapper.Mapper.Map<IRole>(roleDto)
).ToList();
//this does not work:
List<IRole> roles2 = roleDtos.Project().To<IRole>().ToList();
I'm getting ArgumentException:
ArgumentException: Type 'UnitTestProject5.IRole' does not have a default constructor
In my real implementation the .Select is being performed on an Entity Framework collection, which is why I need to stick with .Project().To().
I have no issues with .Project().To() if the destination is not an interface. Additionally, I have no issues with the interface destination if I use .Map().
How can I get the interface destination and .Project.To() to work at the same time? Why is .Project.To() not giving me proxy classes like .Map() is? Any ideas?
Thanks!
Mapper.Map() takes the linq-to-objects route to materialize objects. As you said, AutoMapper is capable of creating types on the fly if the mapped target is an interface.
Project().To() is a way to translate the whole query, including the mapping, into SQL. Which is great, because only the properties that are required for the target object are included in the SQL query. However, the things AutoMapper does for creating types on the fly (undoubtedly some Refection voodoo) can never be part of an expression tree that can be converted into SQL. That's why Project.To simply tries to new up an object, even if it's an interface.
You'll have to use a concrete type as a mapping target. Of course, this type can implement an interface, so you can keep the independence you want.

In Spring MVC 3, how do I bind an object to a query string when the query string parameters don't match up with the object fields?

A 3rd party is sending me part of the data to fill in my domain object via a query string. I need to partially fill in my domain object, and then have the user fill in the rest via a form. I don't have any control over the query string parameters coming in, so I can't change those, but I'd really like to be able to use Spring MVC's data binding abilities, rather than doing it by hand.
How can I do this?
To add some complication to this, some of the parameters will require extensive processing because they map to other objects (such as mapping to a user from just a name) that may not even exist yet and will need to be created. This aspect, I assume, can be handled using property editors. If I run into trouble with this, I will ask another question.
Once I have a partially filled domain object, passing it on to the edit view, etc. is no problem, but I don't know how to properly deal with the initial domain object population.
The only thing I have been able to come up with so far is to have an extra class that has it's properties named to match the inbound query parameters and a function to convert from this intermediary class to my domain class.
This seems like a lot of overhead though just to map between variable names.
Can you not just have the getter named differently from the setter, or have 2 getters and 2 setters if necessary?
private int spn;
// Standard getter/setter
public int getSpn() {
return spn;
}
public void setSpn(int spn) {
this.spn = spn;
}
// More descriptively named getter/setter
public int getShortParameterName() {
return spn;
}
public void setShortParameterName(int spn) {
this.spn = spn;
}
Maybe that is not standard bean convention, but surely would work?

Resources