Has anyone had any success batching REST calls together with the batch service methods?
We are currently on V11 in a RAMP environment and I'm having some issues with this. I am get the error "Object reference not set to an instance of an object." for any service URI I enter (local, relative, or even web.
I'm really stuck on this right now and I'm not sure if this is a V11, RAMP, or coding or other random issue.
Here is my current POST body to https://fairlb/testapi/constituentservice/constituents/batch from within my RAMP terminal (local to the gateway):
<BatchRequest>
<Requests>
<Request>
<Id>1</Id>
<Uri>https://fairlb/testapi/constituentservice/constituents/5</Uri>
<HttpMethod>GET</HttpMethod>
</Request>
</Requests>
</BatchRequest>
Has anyone had similar issues in a non-RAMP environment?
-Chris
Hi Chris,
It's a little tricky with the dataservice because it does not know about the objects you are trying to batch, so you have to use the content type of a:XElement like below:
<BatchRequest xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Content i:type="a:XElement" xmlns:a="http://schemas.datacontract.org/2004/07/System.Xml.Linq">
<CustomThing>
<CustomColumn>1</CustomColumn>
</CustomThing>
</Content>
<HttpMethod>POST</HttpMethod>
<Uri>https://website.com/testapi/tessituraservice/custom/customthings/</Uri>
<Id>2</Id>
<Uri>https://website.com/testapi/tessituraservice/custom/customthings/1</Uri>
Thanks Paul-- That will probably save me some extra headaches in the future. Seems my GET call is matching yours, though, so I'm still unsure of what might be wrong with my base calls.
Do you know if the API loops back through the network to make the batch calls? I'm still not entirely sure of the stack behavior when it comes to these Batch methods
Upgraded to HF48, but no change. Not surprised, frankly. Something else is clearly wrong.
In the meantime, trying to work around not being able try things out in Batch, I'm realizing that I might have a bigger problem. My assumption was that I was going to build some procedures to call through Custom/Execute, and I was going to used returned ids to fill in dependent procedures. However, Custom/Execute isn't going to return columns in a way that I think can be referenced by the DependsOnRequest...and if did, I wouldn't be able to apply it to the dependent call to Custom/Execute since the parameters are all bunged together in a single object ("Parameters").
I'm trying to figure out TR_DATASERVICE, but documentation is even more scanty for that, particularly for formatting POST and PUT commands.
Say Christopher/Nick,
When you were running this, were you doing it through the provided Rest Client (that's what I'm doing), or via cURL or the .Net libraries?
--Gawain
I was just using the REST client provided in the services web interface.
Hi Gawain - Postman is a useful REST client. It's one I use on a regular basis: https://www.getpostman.com/.
I also used Postman to test this.
The only thing I can think of if the client is the issue would be if you're not sending the response body explicitly and there is a serialization issue with the JSON object that is being sent for the batch call [sending a serialized .NET object somehow?].
Have you tried to connect to your gateway outside of .NET at all?
Hi Christopher,
I'm trying Postman to at least rule out whether or not the built-in client is my problem, but I'm not sure I'm doing it right: I changed to bulk edit for parameters and then copied and pasted in the request data, but I'm not sure that's right. Is there somewhere else to enter request data, or does it need to be formatted in a different way?
(duplicate)
Sorry, nevermind, found it. Now I get a different error at least!
(Update: wrong content-type)
Okay, so now it just fails silently. That's something, I guess?
Is your accept header set?
It wasn't, and now I get the same error I get through the built-in REST client. Oh well. Oddly, I didn't need it to run some other (not Batch) methods correctly.
Gawain, hoping this helps a bit:
in your batch request above, try moving Content to the first element. Using XML, the elements need to be alphabetically ordered in your request. It's not seeing Content first and deserializing it as NULL.
<BatchRequest> <Requests> <Request> <HttpMethod>POST</HttpMethod> <Id>1</Id> <Uri>http://calplb/TestAPI/TessituraService/CRM/Addresses/</Uri> <Content i:type="address" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Constituent><Id>20335666</Id></Constituent> <Addresstype><Id>14</Id></Addresstype> <Street1>111 Nob Hill Crescent</Street1> <City>San Francisco</City> <State><Id>11</Id></State> <PostalCode>94109</PostalCode> <Country><Id>1</Id></Country> <Label>True</Label> <PrimaryIndicator>False</PrimaryIndicator> <Months>YYYYNNNNYYYY</Months> </Content> </Request> </Requests> </BatchRequest>
I have one final note regarding custom and batching. At this time, the Custom resources are not supported in Batch requests. The Batch service relies heavily on known service entities (like 'Constituent', 'PriceType', e.g.) to build its requests and responses. Custom resources are by their nature ad hoc and dynamic and the capabiliity of building requests based on these does not exist in the service.
Hope to see you at TLCC!
-Ryan Creps
Tessitura Network
Hi Ryan,
Thanks! I realized I have read that before, but totally forgot when setting this up. That does have me moving forward a bit. But do you mean that even Custom resources defined in TR_DATASERVICE_TABLES cannot be used with Batch? Will that change in v14? If that's true, I'm really sunk.
This has moved me forward with my Batch->Addresses test, but now I'm getting an error "Field 'Address Months' cannot be null or empty." I'm specifying it, as you can see in the example, and that label is what is used in the REST documentation. Is there some other?
Actually, running a direct call to CRM/Addresses with XML nets more errors:
<ErrorMessages xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ErrorMessage> <Code>FIELD_CANNOT_BE_NULL</Code> <Description>Field 'Address Months' cannot be null or empty.</Description> <Details i:nil="true" /> <ErrorPath>Address Months</ErrorPath> </ErrorMessage> <ErrorMessage> <Code>FIELD_CANNOT_BE_NULL</Code> <Description>Field 'Address City' cannot be null or empty.</Description> <Details i:nil="true" /> <ErrorPath>Address City</ErrorPath> </ErrorMessage> <ErrorMessage> <Code>FIELD_CANNOT_BE_NULL</Code> <Description>Field 'Address State' cannot be null or empty.</Description> <Details i:nil="true" /> <ErrorPath>Address State</ErrorPath> </ErrorMessage> <ErrorMessage> <Code>FIELD_CANNOT_BE_NULL</Code> <Description>Field 'Address PostalCode' cannot be null or empty.</Description> <Details i:nil="true" /> <ErrorPath>Address PostalCode</ErrorPath> </ErrorMessage> </ErrorMessages>
Still, this does still raise the question of why other people can do it (Batch->CRM/Addresses->JSON) and I can't. Nick, Chris, one other variable (I guess), is that I'm on RAMP, and that means we are using SQL Server 2008. I'm being told this issue is somehow fixed in v14 (which we can't have because we're stuck on SS 2008 with RAMP. Is there a possibility that you guys are already on SS 2012?
Lastly, alas, I'm not going to be able to make the TLCC this year. I'll miss you guys!
Huh, if I just run CRM/Addresses, and I sort the elements alphabetically:
<Address xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Addresstype><Id>14</Id></Addresstype> <City>San Francisco</City> <Constituent><Id>20335666</Id></Constituent> <Country><Id>1</Id></Country> <Label>True</Label> <Months>YYYYNNNNYYYY</Months> <PostalCode>94109</PostalCode> <PrimaryIndicator>False</PrimaryIndicator> <State><Id>11</Id></State> <Street1>111 Nob Hill Crescent</Street1> </Address>
then I get a different error:
<ErrorMessages xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <ErrorMessage> <Code>FIELD_CANNOT_BE_NULL</Code> <Description>Field 'entity' cannot be null or empty.</Description> <Details> at Tessitura.Services.Common.Guard.ArgumentNotNull(Expression`1 expression, String errorPath) at Tessitura.Services.Common.Guard.EntityIdShouldNotBeGreaterThanZero(IEntity entity) at Tessitura.Service.Impl.SubEntityService.CreateSubEntity[T](T subEntity) at Tessitura.Service.Impl.AddressService.Create(AddressEntity subEntity) at Tessitura.Service.Web.Controllers.CRM.AddressesController.<>c__DisplayClass7.<Post>b__6() at Tessitura.Service.Web.Controllers.Base.BaseController.RunInTransaction[T](Func`1 func) at Tessitura.Service.Web.Controllers.CRM.AddressesController.Post(Address address) at lambda_method(Closure , Object , Object[] ) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.<GetExecutor>b__9(Object instance, Object[] methodParameters) at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()</Details> <ErrorPath>entity</ErrorPath> </ErrorMessage> </ErrorMessages>
You are missing a few properties. Try something along the lines of:
<Address xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <AddressType> <Id>1</Id> </AddressType> <AffiliatedConstituent/> <AltSalutationType> <Id>0</Id> </AltSalutationType> <City>San Francisco</City> <Constituent> <Id>1007</Id> </Constituent> <Country> <Id>1</Id> </Country> <GeoArea>1</GeoArea> <Id>-999</Id> <Inactive>false</Inactive> <Label>true</Label> <Months>YYYYYYYYYYYY</Months> <PostalCode>94109</PostalCode> <PrimaryIndicator>true</PrimaryIndicator> <State> <Id>11</Id> </State> <Street1>sample string 9</Street1> </Address>
Do I really need those other fields? I don't with JSON. And should I even be sending "Id" on a create? In any event, I tried this and got the same failure:
<Address xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Addresstype><Id>14</Id></Addresstype> <AffiliatedConstituent/> <AltSalutationType><Id>0</Id></AltSalutationType> <City>San Francisco</City> <Constituent><Id>20335666</Id></Constituent> <Country><Id>1</Id></Country> <GeoArea>1</GeoArea> <Id>-999</Id> <Inactive>False</Inactive> <Label>True</Label> <Months>YYYYNNNNYYYY</Months> <PostalCode>94109</PostalCode> <PrimaryIndicator>False</PrimaryIndicator> <State><Id>11</Id></State> <Street1>111 Nob Hill Crescent</Street1> </Address>