Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Post method write data to CRM but give Exception when building response #450

Open
vaughnmb opened this issue May 16, 2024 · 10 comments · Fixed by #451
Open

Post method write data to CRM but give Exception when building response #450

vaughnmb opened this issue May 16, 2024 · 10 comments · Fixed by #451
Labels
enhancement New feature or request question Further information is requested

Comments

@vaughnmb
Copy link

I just found this library today. Sorry, if I there is a better place to post this question

I run this code

Account account = Account.builderAccount()
				.accountnumber("987654329")
				.name("test company")
				.address1_line1("test add1")
				.address1_line2("test add2")
				.address1_line3("test add3")
				.address1_city("test ciyt")
				.build();
Account account2 = client.accounts().post(account);

and it successfully writes data to Dynamics CRM, however, it returns the below message.

java.lang.IllegalArgumentException: argument "content" is null
at com.fasterxml.jackson.databind.ObjectMapper._assertNotNull(ObjectMapper.java:5054)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3813)
at com.github.davidmoten.odata.client.Serializer.deserialize(Serializer.java:96)
at com.github.davidmoten.odata.client.internal.RequestHelper.submitAny(RequestHelper.java:194)
at com.github.davidmoten.odata.client.internal.RequestHelper.postAny(RequestHelper.java:159)
at com.github.davidmoten.odata.client.internal.RequestHelper.post(RequestHelper.java:131)
at com.github.davidmoten.odata.client.CollectionPageEntityRequest.post(CollectionPageEntityRequest.java:60)
at com.github.davidmoten.odata.client.CollectionEntityRequestOptionsBuilder.post(CollectionEntityRequestOptionsBuilder.java:199)
at com.github.davidmoten.odata.client.CollectionPageEntityRequest.post(CollectionPageEntityRequest.java:97)

The issue is that in this code the "String text" variable is null.

        // get the response
        HttpResponse response = cp.context().service().submit(method, cp.toUrl(), h, json, options);

        // TODO could be tightened to 201 for POST create but POST Action calls need to
        // accept any successful code
        checkResponseCodeOk(cp, response);

        String text = response.getText();
        // deserialize
        Class<? extends T> c = getSubClass(cp, contextPath.context().schemas(), responseClass,
                text);
        // check if we need to deserialize into a subclass of T (e.g. return a
        // FileAttachment which is a subclass of Attachment)
        return cp.context().serializer().deserialize(text, c, contextPath, false);

How do I make it return data in the Response? Am I missing something in my method calls?

@davidmoten
Copy link
Owner

Righto, can you tell me what the returned json is so I can figure out the deserialisation problem?

@davidmoten
Copy link
Owner

Is Account your extension or part of base CRM? If an extension can you supply your odata metadata?

@vaughnmb
Copy link
Author

vaughnmb commented May 16, 2024 via email

@davidmoten
Copy link
Owner

davidmoten commented May 16, 2024

Thanks. I've just read the specification which says that a post "MUST contain the resource created" (and thus can't return nothing which would give the null problem you saw) if response code is 201. What status code was returned?

9.1.2 Response Code 201 Created
A Create Entity, Create Media Entity, Create Link or Invoke Action request that successfully creates a resource returns 201 Created. In this case, the response body MUST contain the resource created.

9.1.4 Response Code 204 No Content
A request returns 204 No Content if the requested resource has the null value, or if the service applies a return=minimal preference. In this case, the response body MUST be empty.

@vaughnmb
Copy link
Author

The Response Code is a 204

@vaughnmb
Copy link
Author

vaughnmb commented May 16, 2024

ok. I got it to return back a 201 and the Account data from the POST. Just needed to add "requestHeader" to the line of code below.

client.accounts().requestHeader("Prefer", "return=representation").post(account);

Thanks for helping with this

@davidmoten
Copy link
Owner

ok. I got it to return back a 201 and the Account data from the POST. Just needed to add "requestHeader" to the line of code below.

client.accounts().requestHeader("Prefer", "return=representation").post(account);

Thanks for helping with this

Nice, glad you have a workaround. I will address this though. The fact that this is a possibility means that I should return Optional<Account> from the post() method (a breaking change). I'll ponder it a bit.

@davidmoten
Copy link
Owner

davidmoten commented May 16, 2024

Is it possible to do these two things with this project?

https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/create-entity-web-api#create-related-table-rows-in-one-operation

https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/create-entity-web-api#associate-table-rows-on-create

In short, you can do anything with this project using custom requests.

NavigationProperties that have ContainsTarget="True" will be listed as settable properties in an Account builder so that single operation efficiency can happen. I don't support the Partner attribute currently which is what the create table row call relates to and frankly the ability to effectively contain those properties, enabling a single post call, is not apparent in the odata 4 specification so I assume that this is an out-of-spec offering by Microsoft and might be in their custom metadata annotations (no idea where to find them, have seen such a thing for Graph service only).

So you can use a CustomRequest to do anything but you can get a mix by setting unmapped fields. Would be nice to be able to set UnmappedFields in the builder (I'll look at that sometime) but it is doable using the with* mutators. Here's an example based on your first link:

    @Test
    public void test() {
        Account a = Account.builderAccount() //
                .name("Sample Account") //
                .build() //
                .withUnmappedField( //
                        "primaryContactId", //
                        Contact.builderContact() //
                                .firstname("John") //
                                .lastname("Smith") //
                                .build()) //
                .withUnmappedField("opportunity_customer_accounts", //
                        Lists.newArrayList( //
                                Maps //
                                        .put("name", (Object) "Opportunity associated to Sample Account")
                                        .put("Opportunity_Tasks", //
                                                Lists.newArrayList( //
                                                        Maps.put("subject", "Task associated to opportunity").build()))
                                        .build()));

        System.out.println(Serializer.INSTANCE.serializePrettyPrint(a));
        if (false) {
            microsoft.dynamics.crm.container.System client = ...
            client.accounts().post(a);
        }
    }

Output is

{
  "@odata.type" : "Microsoft.Dynamics.CRM.account",
  "name" : "Sample Account",
  "opportunity_customer_accounts" : [ {
    "Opportunity_Tasks" : [ {
      "subject" : "Task associated to opportunity"
    } ],
    "name" : "Opportunity associated to Sample Account"
  } ],
  "primaryContactId" : {
    "@odata.type" : "Microsoft.Dynamics.CRM.contact",
    "firstname" : "John",
    "lastname" : "Smith"
  }
}

The catch is that the create table row docs indicate a 204 response again. Might be able to give it that header and have it work, you could try. I'll look very shortly at returning Optional<> from post operations (and perhaps others too, I'll poke around) so that we support the 204 No Content case properly.

@davidmoten
Copy link
Owner

davidmoten commented May 19, 2024

post and patch methods now return Optional<T> so that we have the No-Content response case covered. Available in 0.2.0 on Maven Central now. Let me know how you go.

@davidmoten davidmoten added enhancement New feature or request question Further information is requested labels May 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
2 participants