Issue with BpmCallContext in REST API

What I am trying to do is to send Custom Context Header “ContextHeader” over Rest API call and then grab changed one.
When I am not sending “ContextHeader” everything is fine. However when “ContextHeader” is added I am receiving error from BPM processing.

Call with “ContextHeader”

curl --location --request GET ‘https://MyServer\MyInstance/api/v1/Ice.BO.UD01Svc/UD01s?$filter=Key1%20eq%20%2701.01.2017%2000:00:00%27&Company=%27MyCompany%27’
–header ‘ContextHeader: {“Context”:{“BpmData”:[{“Password”:“”,“ButtonValue”:0,“Character01”:“testing”,“Character02”:“”,“Character03”:“”,“Character04”:“”,“Character05”:“”,“Character06”:“”,“Character07”:“”,“Character08”:“”,“Character09”:“”,“Character10”:“”,“Character11”:“”,“Character12”:“”,“Character13”:“”,“Character14”:“”,“Character15”:“”,“Character16”:“”,“Character17”:“”,“Character18”:“”,“Character19”:“”,“Character20”:“”,“Number01”:0.0,“Number02”:0.0,“Number03”:0.0,“Number04”:0.0,“Number05”:0.0,“Number06”:0.0,“Number07”:0.0,“Number08”:0.0,“Number09”:0.0,“Number10”:0.0,“Number11”:0.0,“Number12”:0.0,“Number13”:0.0,“Number14”:0.0,“Number15”:0.0,“Number16”:0.0,“Number17”:0.0,“Number18”:0.0,“Number19”:0.0,“Number20”:0.0,“Date01”:null,“Date02”:null,“Date03”:null,“Date04”:null,“Date05”:null,“Date06”:null,“Date07”:null,“Date08”:null,“Date09”:null,“Date10”:null,“Checkbox01”:false,“Checkbox02”:false,“Checkbox03”:false,“Checkbox04”:false,“Checkbox05”:false,“Checkbox06”:false,“Checkbox07”:false,“Checkbox08”:false,“Checkbox09”:false,“Checkbox10”:false,“ShortChar01”:“”,“ShortChar02”:“”,“ShortChar03”:“”,“ShortChar04”:“”,“ShortChar05”:“”,“ShortChar06”:“”,“ShortChar07”:“”,“ShortChar08”:“”,“ShortChar09”:“”,“ShortChar10”:“”,“SysRowID”:“66e9ceee-7168-4fe8-8698-fa8d53a60456”,“RowMod”:“”,“SpecifiedProperties”:“”,“UserDefinedColumns”:{}}],“CallStack”:[],“CallState”:[],“Client”:[{“ClientType”:“”,“ProcessId”:“”,“AssemblyName”:“”,“CustomizationId”:“”,“CurrentUserId”:“MyUser”,“CurrentCompany”:“MyCompany”,“CurrentPlant”:“MfgSys”,“CGCCode”:“”,“SysRowID”:“da8e443b-2603-4b7c-b572-dc9655e10f2e”,“RowMod”:“”,“SpecifiedProperties”:“/wE=”,“UserDefinedColumns”:{}}],“ClientHandler”:[],“InfoMessage”:[],“ExtensionTables”:[]}}’
–header ‘Authorization: Basic cjemVwYW5nOkdlcm1hbiE3OQ==’

BPM Code to check BPMCallContext
variant 1

var rBpmData =
(
from a in this.callContextBpmData.Table.Cast()
where
a.Character01 == “testing”
select a
).FirstOrDefault();

variant 2

if (string.Equals(this.callContextBpmData.Character01, “testing”, StringComparison.OrdinalIgnoreCase))
{
this.callContextBpmData.Character01 = “processed”; //error is coming from here
}
return;

In both cases I have an error:
for variant 1

“Program”: “System.Core.dll”,
“Method”: “Cast”,
“LineNumber”: 0,
“ColumnNumber”: 0,
“Message”: “BPM runtime caught an unexpected exception of ‘ArgumentNullException’ type.\r\nSee more info in the Inner Exception section of Exception Details.”,
“TraceStack”: " at System.Linq.Enumerable.Cast[TResult](IEnumerable source)\r\n at Epicor.Customization.Bpm.BOB7DD542C70CF4F6B8C36D0CE94EA1A05.GetRowsPreProcessingDirective_UpdateFields_AF658F2527FF4DCEA1751C0F4A14B1C8.A001_CustomCodeAction() in GetRows.Pre.UpdateFields.cs:line 90\r\n at Epicor.Customization.Bpm.BOB7DD542C70CF4F6B8C36D0CE94EA1A05.GetRowsPreProcessingDirective_UpdateFields_AF658F2527FF4DCEA1751C0F4A14B1C8.ExecuteCore() in GetRows.Pre.UpdateFields.cs:line 54\r\n at Epicor.Customization.Bpm.DirectiveBase`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE3.2.300.14\Source\Server\Internal\Lib\Epicor.Customization.BPM\DirectiveBase.Generic.cs:line 147\r\n",

for variant 2
“Program”: “Epicor.ServiceModel.dll”,
“Method”: “SetBit”,
“LineNumber”: 319,
“ColumnNumber”: 13,
“Message”: “BPM runtime caught an unexpected exception of ‘IndexOutOfRangeException’ type.\r\nSee more info in the Inner Exception section of Exception Details.”,
“TraceStack”: " at Ice.IceRow.SetBit(Byte[] bytes, Int32 index, Boolean value) in C:\_Releases\ICE\ICE3.2.300.14\Source\Shared\Framework\Epicor.ServiceModel\Ice\Tableset\IceRow.cs:line 319\r\n at Epicor.Customization.Bpm.BO30DC5C9612204F549EC7A39DC756A78F.GetRowsPreProcessingDirective_UpdateFields_AF658F2527FF4DCEA1751C0F4A14B1C8.A001_CustomCodeAction() in GetRows.Pre.UpdateFields.cs:line 88\r\n at Epicor.Customization.Bpm.BO30DC5C9612204F549EC7A39DC756A78F.GetRowsPreProcessingDirective_UpdateFields_AF658F2527FF4DCEA1751C0F4A14B1C8.ExecuteCore() in GetRows.Pre.UpdateFields.cs:line 54\r\n at Epicor.Customization.Bpm.DirectiveBase`3.Execute(TParam parameters) in C:\_Releases\ICE\ICE3.2.300.14\Source\Server\Internal\Lib\Epicor.Customization.BPM\DirectiveBase.Generic.cs:line 147\r\n",
“Properties”: {
“OriginalExceptionType”: “IndexOutOfRangeException”

Is this a bug in rest API? or I am doing something wrong?
I am using v1 in Epicor 10.2.300.14

It is a little hard to read your code and JSON because it is not properly formed in the paste, but try this:

if (callContextBpmData.Character01.ToLower() == "testing") callContextBpmData.Character01 = "Processed";

If this fails, try using the condition and set field widgets.

Both ways you proposed are returning error message exactly the same as I had in variant 2 described above.

BTW I fixed a bit formatting in my original message

Is anyone sending BPMCallContext over REST API?

Try to remove SpecifiedProperties completely, where did you take its value?
Also try to set RowMod to A

I took BpmCallContext structure from response from server. This was also including value for SpecifiedProperties.
I did few more tests and was able to successfully send BPMCallContext, modify it in BPM and received it back. What I did was to remove all columns from BPMCallContext dataset after first RowMod.
So in compare to my last example I removed:

,“SpecifiedProperties”:"",“UserDefinedColumns”:{}}],“CallStack”:[],“CallState”:[],“Client”:[{“ClientType”:"",“ProcessId”:"",“AssemblyName”:"",“CustomizationId”:"",“CurrentUserId”:“MyUser”,“CurrentCompany”:“MyCompany”,“CurrentPlant”:“MfgSys”,“CGCCode”:"",“SysRowID”:“da8e443b-2603-4b7c-b572-dc9655e10f2e”,“RowMod”:"",“SpecifiedProperties”:"/wE=",“UserDefinedColumns”:{}}],“ClientHandler”:[],“InfoMessage”:[],“ExtensionTables”:[]

What remains and what I sent in BPMCallContext is following:

‘ContextHeader: {“Context”:{“BpmData”:[{“Password”:"",“ButtonValue”:0,“Character01”:“testing”,“Character02”:"",“Character03”:"",“Character04”:"",“Character05”:"",“Character06”:"",“Character07”:"",“Character08”:"",“Character09”:"",“Character10”:"",“Character11”:"",“Character12”:"",“Character13”:"",“Character14”:"",“Character15”:"",“Character16”:"",“Character17”:"",“Character18”:"",“Character19”:"",“Character20”:"",“Number01”:0.0,“Number02”:0.0,“Number03”:0.0,“Number04”:0.0,“Number05”:0.0,“Number06”:0.0,“Number07”:0.0,“Number08”:0.0,“Number09”:0.0,“Number10”:0.0,“Number11”:0.0,“Number12”:0.0,“Number13”:0.0,“Number14”:0.0,“Number15”:0.0,“Number16”:0.0,“Number17”:0.0,“Number18”:0.0,“Number19”:0.0,“Number20”:0.0,“Date01”:null,“Date02”:null,“Date03”:null,“Date04”:null,“Date05”:null,“Date06”:null,“Date07”:null,“Date08”:null,“Date09”:null,“Date10”:null,“Checkbox01”:false,“Checkbox02”:false,“Checkbox03”:false,“Checkbox04”:false,“Checkbox05”:false,“Checkbox06”:false,“Checkbox07”:false,“Checkbox08”:false,“Checkbox09”:false,“Checkbox10”:false,“ShortChar01”:"",“ShortChar02”:"",“ShortChar03”:"",“ShortChar04”:"",“ShortChar05”:"",“ShortChar06”:"",“ShortChar07”:"",“ShortChar08”:"",“ShortChar09”:"",“ShortChar10”:"",“SysRowID”:“66e9ceee-7168-4fe8-8698-fa8d53a60456”,“RowMod”:""}}’

What is worrying me a bit is that in answer from server I received all columns in dataset which I previously removed. This possibly means that I will have to remove them again if I want to send BPMCallContext one more time to server.

I found one more thing during further testing.
I can also send whole structure, exactly as is returned by server.
However value for first SpecifiedProperties can’t be empty.
I set
“SpecifiedProperties”:“BAAAAAAAAAAAAA==”
and it’s working fine.

Does anyone know what is exactly the meaning (to server) of SpecifiedProperties column?

This is a bit mask, each bit corresponds to the column in the row. it contains 1 for the column which value was changed. It is used by smart client and in Rest, if you don’t sent it, it is set to null and is not used

I check and IMHO format of the “SpecifiedPropoerties” column is Base-64 char array or string.
I can programmatically decode it to byte array.

Based on my test I found that

  1. The safest is to do not send “SpecifiedProperties” column at all.
  2. If you are sending this column then it have to be in format of Base-64 char array or string
  3. In case if value of column is not in Base-64 char array or string you will receive error

IMHO there is also a bug in Epicor. Epicor is not checking if array you sent is big enough to store data and it just trying to write which sometimes result in IndexOutOfRangeException. That’s why I wrote that the best is to do not send this column because Epicor will create byte array by itself.

@Olga
I am not 100% sure if you are right. When I modified in BPM 3 columns Character01, Charater02 and ShortChar01 I received back in SpecifiedProperties “DAAAAAAAAEAAAA==” which means { 12, 0, 0, 0, 0, 0, 0, 64, 0, 0 }. Unless I did not make a mistake in decoding string back to byte array it shows that only 2 columns were modified.

I cannot check your calculations right now, but I was talking about bit array, not byte. So 1 field 1 bit. And you need to know exact number and order of fields.
This functionality was done long before REST for smart client where contracts give all this information for the client and server parts.

I found out another issue. If I am trying to send multiple BpmCallContext rows I am receiving IIS error saying “The size of the request headers is too long”. This is because Smart Client is sending BpmCallContext in Request Headers and IIS request headers size is limited. It can’t be changed to desired value i.e. 1MB. Any idea how this can be bypassed?

It can’t.

IIS Header limit 16384 Bytes (for each header field)

You’ll need to restructure how you are sending your data. I’m not familiar with what you are doing, but maybe use a function or send with the dataset if possible.

1 Like

Is this going to a BAQ, or something else. If it’s a BAQ or you can shove a BAQ in between I have an idea…

What business problem are you trying to solve by pushing all that data through the BPM Context?

To be fair. It’s questionable that they decided to send that information in the header.

This means you can do things in the client and BPMs that you can’t do with rest without rework.

I would have never assumed it would be in the header if I hadn’t been poking around in swagger before I started to get going.

1 Like

I have a custom screen based on UD table. I have multiple grids and multiple lines in grid. I need to send all this data to server in order to do some calculations which are based on other DB tables.
I already have a workaround in my head but this will require some code rewrite.

I believe that this is not a fully right. I was searching here and there and found out that by adding some registry settings you can change that limit to max 64KB. This however might be still not enough for me.

It was always header in ERP.
But WCF did not use IIS headers to send its headers, it created complex message that included its headers too inside request body.

Now without WCF header is really IIS header and this is a result of it.

1 Like

So now it shouldn’t be a header. This should be in body - same as i.e. whereClause.
By having BPMCallContext in header we are basically loosing this functionality if multiple rows are required.

whereClause is a parameter of specific method. BPMCallContext is irrelevant to method and can be added to any of them.

1 Like