auth0/auth0-java

Support Java 8 Datetime in Requests

larsf96 opened this issue · 10 comments

Describe the problem you'd like to have solved

At the moment, when we try to set a custom List to a user's user_metadata that contains Objects with a Java 8 Date (an Instant in this case), we get the following error message:

com.auth0.exception.Auth0Exception: Couldn't create the request body.
	at com.auth0.net.ExtendedBaseRequest.createRequest(ExtendedBaseRequest.java:51)
	at com.auth0.net.BaseRequest.execute(BaseRequest.java:33)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.Instant` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.auth0.json.mgmt.users.User["user_metadata"]->java.util.LinkedHashMap["consents"]->java.util.LinkedList[0]->com.claas.id.backend.backendservice.model.user.Consent["created_at"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
	at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1300)

We use Spring Boot and have a @bean declared in our Project with an ObjectMapper that has the JavaTimeModule registered. When using this Bean, the conversion works without problems. But it looks like this library internally instantiates a separate ObjectMapper without module registration which causes this error.
If I'm doing something wrong, please correct me

Describe the ideal solution

The ideal solution would be that the Auth0 ObjectMapper searches for Modules:
new ObjectMapper()..findAndRegisterModules();

Alternatives and current work-arounds

Currently, we don't have a work-around other than not using Java 8 Time formats

Additional information, if any

Hi @larsf96, thanks a lot for your contribution with the PR. We really appreciate it.

We will take a look into this PR and get back to you in the next week. Hope that works!

@poovamraj Is there any update?

Hi @larsf96 Sorry for the late response. I just checked out the PR and looks like this exposes our API to have dependency with the Jackson library. We have to find a different way to provide support for this. Also, can you share the parameter and the request this fails for so that we can try to provide a better API for that particular request?

Hi @poovamraj
I added the jackson library as testImplementation to ensure the unit tests, otherwise the tests are failing. But there is already a jackson dependency within this library.
Do you mean the request, that is currently failing?
Basically, whenever you try to save either user_metadata or app_metadata of a user that includes a property that uses an Instant date type, this is failing.

So for example

        User newUser = new User();
        Map<String,Object> userMetadata = new LinkedHashMap<>();
        userMetadata.put("anyUser", Instant.now());
        newUser.setUserMetadata(userMetadata);
        mgmt.users().update("newUser", newUser);

It is not adding the test dependency @larsf96, I was looking into providing the API as registerModule(Module module) because doing findAndRegisterModules will register all the modules and could interfere with API properly working.

Providing both these API would mean we are exposing Jackson features through our library and that is not great (In case we move to GSON internally this API will be affected)

Regarding conversion of Instants to time. I will check whether and how we can support this feature

Hi @poovamraj
Thanks for clarifying that, you are absolutely right with that point.
I won't do any changes for now and wait for your reply for the Instant topic

Hi @larsf96, I was checking out this issue and unfortunately the SDK doesn't have the ability to do the customization you require.

Ideally, the CustomRequest being extensible should provide an instance of the body which should be customized in the createRequestBody. But currently, we are not exposing an instance of the body where you can do this.

A better way is to provide a lambda parameter with an instance of the body where you can customize it. This way you wouldn't need to extend a class unnecessarily.

I am thinking of a syntax like this

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();

ManagementAPI mgmt = new ManagementAPI("", "");

User newUser = new User();
Map<String,Object> userMetadata = new LinkedHashMap<>();
userMetadata.put("anyUser", Instant.now());
newUser.setUserMetadata(userMetadata);
CustomRequest<User> customRequest = (CustomRequest<User>) mgmt.users().update("newUser", newUser);
customRequest.customizeRequestBody((body, parameter) -> {
    try {
        return objectMapper.writeValueAsString(body);
    } catch (JsonProcessingException e) {
        e.printStackTrace();
        return null;
    }
}).execute();

I am going to leave this out here to gather some feedback from others and also from my team. If all good, we can integrate this as a solution.

I think enabling richer customization of requests is something we should look into adding, most likely in v2 so we can consider simplifying the request building. Support for java 8 datetime formats is also something we should add in v2.

stale commented

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️

We didn't add direct support for Java 8+ datetime objects in v2; it will be something we consider for v3. In the meantime, it's recommended to convert Instants to Dates prior to serializtion.