I created a custom web service to accept xml from one of our lead aggregators. However, it is not deserializing the request body, and all I get in response is a Null Object Reference error. Am I missing something?

 

My custom web service:

namespace Terrasoft.Configuration.UsrMyLeadImportNamespace
{
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;
    using System;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Web;
    using System.Xml.Serialization;
    using Core;
    using Core.DB;
    using Terrasoft.Web.Common;
 
    [XmlSerializerFormat]
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
    public class UsrMyLeadImport: BaseService
    {
		private SystemUserConnection _systemUserConnection;
		private SystemUserConnection SystemUserConnection
		{
			get
			{
				return _systemUserConnection ?? (_systemUserConnection = (SystemUserConnection)AppConnection.SystemUserConnection);
			}
		}
		// Service operation.
		[OperationContract]
		[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "new")]
		public MyResponse New(LeadInformation leadInformation)
		{
			var response = new MyResponse();
			response.ResponseStatus = "Tracking Number is => " + leadInformation.TrackingNumber;
			return response;
		}
	}
 
 
	[XmlRoot(ElementName = "LeadInformation")]
	public class LeadInformation
	{
		[XmlElement(ElementName = "TrackingNumber")]
		public int TrackingNumber { get; set; }
 
	}
 
	[DataContract(Namespace = "")]
	[Serializable]
	public class MyResponse
	{
		[DataMember]
		public string ResponseStatus { get; set; }
	}
}

Request Body:

<LeadInformation>
    <TrackingNumber>12345</TrackingNumber>
</LeadInformation>

 

Like 0

Like

1 comments
Best reply

I've performed the same setup (additionally to the code you've shared, registered the service as anonymous) and sent the following request in Postman:

As you can see the result is correct so the issue is somewhere in the request you send or in the service settings. You need to double-check them.

I've performed the same setup (additionally to the code you've shared, registered the service as anonymous) and sent the following request in Postman:

As you can see the result is correct so the issue is somewhere in the request you send or in the service settings. You need to double-check them.

Show all comments

Hi community,



For an integration, I have the following JSON request parameter :



{

  "externalID": "57",

  "nomDuClient": "Simple text example",

  "nomDelOpportunite": "Simple text example",

  "calculateurs": [

    "57",

    "61"

  ]


}




Let's have a further look to the "calculateurs" field. When setting the WebService into Creatio with the automatic request paramters, I get something like this :



image.png

I have "Text" data which is an Array. Everything's fine. 



Now, if I use the "WebService" element in the business process, it automatically sets something like this :



image.png



This structure will give a parameter that will look at something like :



"calculateurs": [



{ "name of the element": "value of the first element" }, { "name of the element": "value of the second element" } 



]



Which will be an array of structure of strings and not just an array of string.



Furthermore, when trying to get a collection through looping into a subprocess, the collection of parameters given by the subprocess will look at something like this :



image.png



They will be an array of structure of strings. 



It has been hours that I am trying to populate my WebService parameter as just an array of strings.

How can I achieve that ?



Many thanks,

Jonathan

Like 0

Like

6 comments

Dear Jonathan,

 

Unfortunately, there is no OOB functionality for transferring data sets (IsArray type) to web service parameters by not the 'Read data" element.

 

 We have already registered the idea for our R&D team to implement this functionality in further releases. I will assign your case to this project in order to increase its priority.  

 

Best regards,

Bogdan

Bogdan,

 

I have this situation when setting up the Web Service :

 

When i call the WebService process element, the field automatically fills like this :

 

And with this element, it is impossible to send an array of strings like set in the webservice request parameter isn't it ?

 

If we have a JSON like :

 

"Assistant": [

{

"string": "hello world"

}

]

 

Where the "string" value is set in "Sélectionner la valeur" on the screenshot above. So there is really no way of sending an array of strings, really ?

 

When I test the request directly from the WebService section, I can just add lines to the array and manually setting values into it. Isn't there a way to do it automatically ?

 

Let me ask the question in a different way, how could I send an array of strings via the "Read data" element of the business process ?

 

Many thanks,

Jonathan

Jonathan Quendoz,

 

If the request parameter is a collection (the “Is array” checkbox is selected in the properties of the parameter at the web service record), the nested collection parameters will be displayed under the collection name in the process element parameters (Fig. 3). For example, Creatio “batch query” service can insert several records (e.g. contacts) to Creatio. To do this, the service would require information to write into the fields of each inserted record (e.g. contact names and types). In this case, you can pass the values as a request parameter of an array type, where “Name” and “Type” will be nested parameters and each instance of the collection would represent data for a separate contact record.

 

 

The values of collection parameters of one [ Call web service ] process element can be mapped to the nested parameters of another collection of a [ Read data ] or [ Call web service ] process element.

 

 

 

The parameters of the collection of process elements can be mapped to the process parameter of the “Collection of values” data type.

 

Please refer to the article here.

 

Best regards,

Bogdan

 

Jonathan Quendoz,

Hi! How are you? I'm running on the same problem. Did you find a solution? Appreciate it.

Regards

Uriel Nusenbaum,

 

Hi Uriel ! 

 

Unfortunately, I did not found a solution to this problem. 

I asked to the WebService supplier to adjust their webservice to have an array of structure of strings as json paramater instead of just an array of strings.

 

At the moment, I think that besides calling a WebService via C# in back-end, it is not possible to send an array of strings as parameter. It should really be reworked from Creatio.

 

You can only send an array of a structure of strings via a sequentiel sub-process.

 

Please, if you have any questions about this, do not hesitate.

 

Best regards,

Jonathan

Good day. Have there been any new solutions to this problem in recent versions?

Show all comments

Hi,

Please provide the procedure for call a web service in mobile application.

Thanks

Like 0

Like

0 comments
Show all comments

Hi community,

 

When implementing a web service, I have a GET method with a parameter :

 

public int GetMethod(string name)

 

In this method, I did a query to get the age of the person in parameter :

 

var select = new Select(UserConnection)

     .Column("Age")

     .From("Contact")

     .Where("Contact", "Name").IsEqual(Column.Parameter(name)) as Select;

 

Then I save the age into an int var :

 

var age = select.ExecuteScalar();

 

return age;

 

The problem is the following :

 

How can I check if the name of a contact in the method parameter exsists in the DB or not ?

 

Example :

 

if(name != exists) {

   return 0;

} else {

  return age;

}

 

Thanks a lot.

 

Best regards,

Jonathan

Like 0

Like

4 comments
Best reply

Hi Jonathan,

 

Usually you use dataReader to get the value from your select query. What you can do is:

bool hasRecord = false;
using (DBExecutor executor = UserConnection.EnsureDBConnection()) {
using (IDataReader dataReader = select.ExecuteReader(executor)) {
  while (dataReader.Read()) {
    hasRecord  = true;
  }
 }
}
if(hasrecord){
  return age
}else{
  return 0
}

Or you can just set the default value of the age =0. Therefore if the dataReader does not return any record, the default value age (0) will be returned.



regards,

Cheng Gong

Hi Jonathan

 

Please add the next using -     

     using Terrasoft.Common;   

on the top of the page(if you didn't have this one). 

 

Then in the part of the code when you are trying to retrieve the age:

 

if (name.IsNullOrEmpty())

{

return 0;

}

else

{

return age;

}

 

Best Regards, 

 

Bogdan L.

Hi Jonathan,

 

Usually you use dataReader to get the value from your select query. What you can do is:

bool hasRecord = false;
using (DBExecutor executor = UserConnection.EnsureDBConnection()) {
using (IDataReader dataReader = select.ExecuteReader(executor)) {
  while (dataReader.Read()) {
    hasRecord  = true;
  }
 }
}
if(hasrecord){
  return age
}else{
  return 0
}

Or you can just set the default value of the age =0. Therefore if the dataReader does not return any record, the default value age (0) will be returned.



regards,

Cheng Gong

Bogdan Lesyk,

Thanks for your answer. It helped a lot !

Cheng Gong,

 

Your answer is really perfect ! Thanks a lot, i'll definitely save this snippet of code for my future implementations.

 

Regards,

Jonathan

Show all comments

Hi community,

 

Let's take an example :

 

I created multiple records in a section. In this section I have a detail that contains some fields like the "name", the "date", the "quantity". I want to return, through some code in a webservice, all the names of the detail that belongs to a certain record (the record passed as a parameter).

 

How can I do this link between the detail and its record ?

 

If we take the standard example of the "contact" section, there is something strange. In the "Noteworthy events", when we create a birthday for instance, we directly can access to the "BirthDate" field from the "Contact" object. But when I create a custom section I can't access like this to the fields of the detail of a specific record.

 

Do you have any solutions for this ?

 

Thanks a lot.

 

Best regards,

Jonathan

Like 0

Like

4 comments

Hi Jonathan,

 

When you create a custom detail for a custom section there is always some column that is used for connection. Let's take your example with noteworthy events of contacts. The table that represents noteworthy events of contacts in the database is called ContactAnniversary and if you perform a SELECT query to it:

SELECT TOP 1 * FROM ContactAnniversary --MS SQL
SELECT * FROM "ContactAnniversary" LIMIT 1 --PostgreSQL

You will see that the column that is used for connecting the "Contact" table and  the "ContactAnniversary" table is "ContactId":

So you need to use this column as a reference.

 

As for the custom section - when you add some detail to the custom section you also specify some column to connect detail and the section. This is also described here (see "Add an existing detail to a record page" paragraph and step 6.(c-d)). And the "Where detail column" field contains the column that connects your detail with your section and it should be used as a connection in your webservice.

 

Best regards,

Oscar

Oscar Dylan,

 

Hey, thanks a lot for your useful answer. By the way, how can you query this foreign key column ? 

 

For example :

 

var esq = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "ContactAnniversary");

 

var contactColForeignId = esq.AddColumn("ContactId");

 

Doesn't work because, I think, that is doesn't recognize it as a real "column" in the "ContactAnniversary" object. How can I fetch this foreign ID in a C# code and then saying that the Foreign Id of the "ContactAnniversary" object must correspond to the Id of the "Contact" ?

 

Thanks a lot for your help Oscar ! :)

Jonathan Quendoz,

 

You shouldn't use an Id suffix for the column name. Esq uses an actual column name from the object (which is "Contact"). So you need to use:

 

var contactColForeignId = esq.AddColumn("Contact");

And then it will find the column correctly.

 

Best regards,

Oscar

Oscar Dylan,

 

Thanks for your answer, now I encounter another problem :

 

Let me take another example :

 

var result = " ";

var esqAnniversary = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "ContactAnniversary");

var esqContact = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "Contact");

 

var esqColId = esqAnniversary .AddColumn("Id");

var esqColForeignId = esqAnniversary .AddColumn("Contact.Id");

 

 

var esqContactColId = esqContact.AddColumn("Id");

var esqContactColName = esqContact.AddColumn("Name");

 

Then I initialize an entity collection :

 

var entites = esq.GetEntityCollection(UserConnection);

 

How can I query, through a filter, both the esq ?

 

Like :

 

First I filter through the Contact I want :

 

var esqFilterContact = esqContact.CreateFilterWithParameters(FilterComparisonType.Equal, esqContactColName.Name, parameter);

 

var esqFilterId = esqAnniversary.CreateFilterWithParameters(FilterComparisonType.Equal, esqColForeignId.Name, esqContactColId.Name);

 

So I take only the detail corresponding to the name of the contact in parameter ?

How can I do a query like this ? My example doesn't work, can you help me troubleshooting this ?

 

Hopefully you can understand my problem and help me through this !

 

 

 

 

 

Best regards,

Jonathan

Show all comments

Hi community!

 

I'm trying to set up a call to a web service (the built-in no-code approach), which uses a custom HTTP header for authentication called 'X-API-KEY'.

 

I haven't found a way to add a header to the web service via the UI. Is there some way to achieve this?

 

Thanks,

Robert

Like 0

Like

3 comments

Hi Robert, 

 

Usually such operations is performed by using OData: 

 

https://academy.creatio.com/docs/developer/integrations_and_api/data_se…



https://documenter.getpostman.com/view/10204500/SztHX5Qb?version=latest

 

Please also check out this articles to find some useful tips: 

 

[Call web service] process element -

 

https://academy.creatio.com/docs/user/bpm_tools/process_elements_refere…

 

API Keys - 

 

https://swagger.io/docs/specification/authentication/api-keys/

 

 

Regards, 

 

Bogdan L.

 

 

 

Hi Bogdan,

thanks for the answer!

However, that's not quite what I was looking for ;)

 

I know of OData and how to use it. Unfortunately, the 3rd party service doesn't support it.

What I'm looking for is actually a way to add custom headers to HTTP requests when calling external web services via business processes.

 

I know I could use a script task and program it myself, but I was wondering if it also worked when using the standard web service integration.

 

Thanks,

Robert

Robert Pordes,

 

Actually we don't have practical examples of such implementation. 

 

So in this case as you said you may use a script task to achieve required result. 

 

Probably the link of how to Run business process via web-service will be also useful for you:

 

https://academy.creatio.com/docs/developer/front-end_development/creati…

 

Thank you! 

 

Regards, 

 

Bogdan L.

Show all comments

Hi,

 

Does the ODATA Web services mechanism supports collections?

meaning, using a post request with a json that includes arrays?

 

Thanks,

Raz

Like 0

Like

4 comments
Best reply

Hi Raz, 

 

Unfortunately we don't have practical examples of such implementation.

 

{

 "id": 1234,

"status": "ABC",

"Line_Items": [ {"prod_id":1111,"quantity":10},{"prod_id":2222,"quantity":20}],

"Order_Lines": [],

"Coupon_LInes": []

}

 

Most likely this code will not work, because Line_Items, Order_Lines and etc. will be perceived by the system like "field". 

 

You may create one order, in response you will get it's "Id" and then with using batch you may add  couple records in your details.

 

Best Regards, 

 

Bogdan L.

Hi Raz,

 

For this kind of requests OData has Butch. 

 

Please follow the link to check the details of how to work with it: 

 

https://academy.creatio.com/docs/developer/integrations_and_api/data_se…

 

Best Regards, 

 

Bogdan L.

Bogdan Lesyk,

Hi Bogdan,

 

Thank you for your response and article.

I didn't understand how the batch requests solves my scenaro.

I'll elaborate:

 

I have a woocommerce website that needs to update creatio every time there is a new order.

 

The woocommerce json looks like this:

{

 "id": 1234,

"status": "ABC",

"Line_Items": [ {"prod_id":1111,"quantity":10},{"prod_id":2222,"quantity":20}],

"Order_Lines": [],

"Coupon_LInes": []

}

 

As you can see one Json includes all the order data.

 

In Creatio for each array, like Line_Items, Order_Lines and etc, we have a detail connected to the order section.

 

Is there a way to use the ODATA Web services mechanism in such a way that the woocommerce website can update all the relevant sections and details?

 

Thanks,

Raz

 

Hi Raz, 

 

Unfortunately we don't have practical examples of such implementation.

 

{

 "id": 1234,

"status": "ABC",

"Line_Items": [ {"prod_id":1111,"quantity":10},{"prod_id":2222,"quantity":20}],

"Order_Lines": [],

"Coupon_LInes": []

}

 

Most likely this code will not work, because Line_Items, Order_Lines and etc. will be perceived by the system like "field". 

 

You may create one order, in response you will get it's "Id" and then with using batch you may add  couple records in your details.

 

Best Regards, 

 

Bogdan L.

Bogdan Lesyk,

Thank you for your response

Show all comments

Hi,



I'm working on Activities right now and I created my own activity.

I wanted to insert a New Activity with a press of a button which is located in the MainHeader. Question, what's the best way to insert my own activity?



I am currently trying to insert it via custom web service through

Insert insert = new Insert(UserConnection)
                .Set("","")
                .Into("Activity")

I'm afraid there are events, computations that won't be triggered.



Any comments?

Like 0

Like

6 comments
Best reply

Solem Khan Abdusalam,

After you call activity.Save() you can get the created record Id using 

// get primary Id value
var id = activity.PrimaryColumnValue;
// or get any of the column values using 
var title = activity.GetTypedColumnValue&lt;string&gt;("Title");
var start = activity.GetTypedColumnValue&lt;DateTime&gt;("StartDate");
// etc

Ryan

Using the Insert class does not trigger process events, etc. Instead, you can use the Entity class. Something like this: 

var schema = UserConnection.EntitySchemaManager.GetInstanceByName("Activity");
// Create a new Activity object
var activity = schema.CreateEntity(UserConnection);
// Set any any default column values 
activity.SetDefColumnValues();
// Now set the column values as needed
activity.SetColumnValue("Title", "This is a new activity");
activity.SetColumnValue("StartDate", DateTime.Now);
// etc
// Save when done
activity.Save();

Hope this helps.

Ryan

Ryan Farley,



Is there a way to fetch the Id of the created Activity?

Solem Khan Abdusalam,

 

One of possible ways is to create a SELECT query to the database using or the script task process element (if you are going to use a business process) or in the custom web service that is triggered upon clicking the button and select the last created activity (top 1 and ORDER BY CreatedOn conditions). Also the created activity can have some marker in its subject (like "created via web service" or so) that could be used in the filtering condition as well.

 

Insert query you've proposed won't return an Id of the record, but you can select it once the record is created.

 

Best regards,

Oscar

Solem Khan Abdusalam,

After you call activity.Save() you can get the created record Id using 

// get primary Id value
var id = activity.PrimaryColumnValue;
// or get any of the column values using 
var title = activity.GetTypedColumnValue&lt;string&gt;("Title");
var start = activity.GetTypedColumnValue&lt;DateTime&gt;("StartDate");
// etc

Ryan

Is there any way to trigger the Creatio processes that happen when using the Entity class to insert data when using something like the Insert class/other operations that happen on the database more directly?

 

We have a need to do some atomic data operations in the table (create a record where it doesn't already exist, and with a high chance of 2 processes attempting to do so at the same time) which would be much simpler/actually possible to implement in SQL, but we still want the Creatio processes to occur after the record has been created.

 

The problem with the Creatio data operations, definitely in BPs but also in C# as well, is that the data checks happen and then a record is created, but in the meantime another process may have already created the record, so you get 2+ processes trying to create the same record. We can make it fail with a unique index, but this will just break a BP instance (cause it to error out and not be recoverable) and isn't the most ideal way of doing things in general.

Harvey Adcock,
 

In this case, you can call the business process through C# code using the IProcessEngine Interface from the Terrasoft.Core.dll namespace

Example of starting a process without transferring parameter values:

UserConnection userConnection = Get<UserConnection>("UserConnection");
IProcessEngine processEngine = userConnection.ProcessEngine;
IProcessExecutor processExecutor = processEngine.ProcessExecutor;
processExecutor.Execute("UsrProcess2Custom1");
return true;


The relevant documentation can be found at this link - https://academy.creatio.com/api/netcoreapi/7.18.0/api/Terrasoft.Core.IProcessEngine.html


Or you can also make a GET call to the endpoint 
0/ServiceModel/ProcessEngineService.svc

The corresponding documentation can be found at this link - 
https://academy.creatio.com/docs/8.x/dev/development-on-creatio-platfor…
 

 

 

 

 

Show all comments

In the webservices section - is it possible to have dynamic URL? the URL to be used is subdomain based so the URL changes for 2 or more instances.. 

 

so is it possible to make the webservice URL dynamic?

Like 0

Like

2 comments
Best reply

Hello Ganesh,

 

The only thing that can be modified in terms of one web service integration setup is the link to the method that is called. The problem here is that the main domain remains the same and only the link to the method is modified.

 

In your case you can create two separate web services and call each of them based on some condition (can be specified in the process itself using conditional flows).

 

Best regards,

Oscar

Hello Ganesh,

 

The only thing that can be modified in terms of one web service integration setup is the link to the method that is called. The problem here is that the main domain remains the same and only the link to the method is modified.

 

In your case you can create two separate web services and call each of them based on some condition (can be specified in the process itself using conditional flows).

 

Best regards,

Oscar

ok thanks Oscar

Show all comments

Hi,

For a few weeks I struggled with sending a HTTP POST Request to a third party application via script task.

I have a collection object that needs to be sent via the web service and eventually catch the response and use the data inside process parameters.

 

Here you can find the code for doing that using a JSON object.

 

string x_param = Get("x_process_param");

string y_param = Get("y_process_param");

string z_param = Get("z_process_param");



var ProductLines = Get>("ReadDataUserTask6.ResultCompositeObjectList");

var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://url/api/v1/Documents/?token="+x_param);

httpWebRequest.ContentType = "application/json";

httpWebRequest.Method = "POST";

int ProductLineIndex = 0;

using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))

using (JsonTextWriter writer = new JsonTextWriter(streamWriter))

{

    JObject InvoiceJSon = new JObject (

                            new JProperty("y",y_param),

                            new JProperty("z",z_param));

                            

    JArray JProductLinesArray = new JArray();

    foreach(var ProductLine in ProductLines) {

        

        var ProductName = "";

        decimal Qty;

        decimal UnitPrice;

        

        ProductLine.TryGetValue("Name", out ProductName);

        ProductLine.TryGetValue("Quantity", out Qty);

        ProductLine.TryGetValue("Price", out UnitPrice);

        

        JProductLinesArray.Add(new JObject(

            new JProperty("ChargeVAT","true"),

            new JProperty("Number",++ProductLineIndex),

            new JProperty("ProductName",ProductName),

            new JProperty("Qty",Qty),

            new JProperty("Rate",1),

            new JProperty("UnitPrice",UnitPrice),

            new JProperty("VATRate",17)));

    }                        

    

    InvoiceJSon.Add(new JProperty("DocumentLines",JProductLinesArray));

    

    InvoiceJSon.WriteTo(writer);

    

}



var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();

using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))

{

    var result = streamReader.ReadToEnd();

    var LinkToPdf = JObject.Parse(result)["LinkToPdf"].ToString();

    var CaspitDocNum = JObject.Parse(result)["Number"].ToString();

    

    Set("DocLinkToPdf",LinkToPdf);

    Set("FinanceDocId",DocNum);

}

return true;

 

Furthermore, I attached a screenshot with the methods that I'm using in the process

 

Hope it helps.

 

Have fun :)

Good luck

 

Like 1

Like

Share

2 comments

Hi!

 

Thank you for contacting us! 

 

Could you please provide us with additional screenshots of the issue appeared to understand the case better?

 

Regards, 

Anastasiia

Anastasiia Markina,

Hi Anastasiia,



There is no issue. I struggled with this issue and I solved it.



The above code is my collaboration to the community :)

 

Hope it helps devs with the same issue.

 

Best Regards,

Raz

Show all comments