How to integrate Kentico 12 XP with a PIM system

Massimo Della Calce asked on October 8, 2020 09:40

The need is to integrate Kentico 12 XP with an external PIM system. I would like to be able to process the information received by the PIM in a scheduled job, in order to create or update elements in Kentico. I tried to implement a POC, using the native Integration Bus and creating a custom scheduled task in Kentico. From what I understand from the documentation, you can get into the bus integration pipeline by extending the BaseIntegrationConnector base class and overwriting the PrepareInternalObject method:

public override ICMSObject PrepareInternalObject(object obj
    ,TaskTypeEnum taskType
    ,TaskDataTypeEnum dataType
    ,string siteName)
{
    var pimObj = (dynamic)obj;
    var node = new Product
    {
        DocumentName = pimObj.Name,
        DocumentCulture = "en-us",
        Name = pimObj.Name,
        Family = pimObj.Family,
        NodeParentID = 2
    };

    return node;
}

As a first step, I implemented a connector and overridden the above method, so that it can create a new Product instance after reading data from the PIM through its APIs.

As a last step I created a custom task as follows:

public class CustomSyncTask : CMS.Scheduler.ITask
{
    public string Execute(TaskInfo task)
    {
        // Call PIM and build pimObject ...

        try
        {
            IntegrationHelper.ProcessExternalTask("CustomIntegrationConnector"
            ,pimObject
            ,IntegrationProcessTypeEnum.SkipOnError
            ,TaskTypeEnum.CreateDocument
            ,TaskDataTypeEnum.Simple
            ,SiteContext.CurrentSite.SiteName);
        }
        catch (Exception ex) {
            return ex.Message;
        }
    }

    return null;
}

I did not expect any particular errors, since the implementation is really simple, unfortunately, running the task I always get the same error:

Message: Object reference not set to an instance of an object.

Exception type: System.NullReferenceException
Stack trace:
at CMS.DataEngine.TranslationHelper.RegisterRecord(BaseInfo infoObject)
at CMS.DocumentEngine.DocumentSynchronizationHelper.GetDocumentDataSet(TreeNode node, TranslationHelper th, TaskTypeEnum taskType, TaskDataTypeEnum dataType, TaskParameters taskParams, String siteName)
at CMS.DocumentEngine.DocumentSynchronizationHelper.GetDocumentXML(TreeNode node, TranslationHelper th, TreeProvider tree, TaskTypeEnum taskType, TaskDataTypeEnum dataType, TaskParameters taskParams, String siteName)
at CMS.DocumentEngine.DocumentSynchronizationHelper.LogExternalIntegration(TreeNode node, TreeProvider tree, TaskTypeEnum taskType, TaskDataTypeEnum dataType, IntegrationProcessTypeEnum result, String connectorName, TranslationHelper th, String siteName)

I deeply investigated in the Kentico source code and found that, when trying to call the RegisterRecord, the Site property ( null ) is passed as parameter. The Site property is set when the Product item is created in Kentico. Hence the contradiction: how is it possible that the object should already be created if the intention is to create it? Am I doing something wrong? Did I misinterpre the entire process?

Recent Answers


Dmitry Bastron answered on October 8, 2020 16:07 (last edited on October 8, 2020 16:08)

Hi Massimo,

It looks like the CMS can't find a connector for your website or integration bus is not enabled. Please check the following:

  • you have actually enabled Integration Bus in settings application (note it should be done globally)
  • you have registered your connector
  • and if your connector's code is in separate assembly - make sure you have added [assembly: RegisterCustomClass("CMSIntegrationConnector", typeof(CMSIntegrationConnector))]
0 votesVote for this answer Mark as a Correct answer

Massimo Della Calce answered on October 8, 2020 17:11

Hi Dimitry, first of all thanks for your answer! The bus is enabled, in fact the process fails at the step "Prepare TranslationHelper", in relation to the image shown in the document: https://docs.xperience.io/k12sp/integrating-3rd-party-systems/using-the-integration-bus/creating-integration-connectors/implementing-incoming-synchronization

I don't know if I am using the bus correctly. I mean that my need is to create a content item by intervening within the Incoming synchronization pipeline. From what I understand the flow stops because it expects that the item has already been created: but my intention is to create it! Does what I wrote make sense?

0 votesVote for this answer Mark as a Correct answer

Dmitry Bastron answered on October 8, 2020 18:07

From the code you displayed you got it all right and it appears to be correct. Something happens when the system actually tries to save your Product object. It could be that some of the fields are missing here:

var node = new Product
{
    DocumentName = pimObj.Name,
    DocumentCulture = "en-us",
    Name = pimObj.Name,
    Family = pimObj.Family,
    NodeParentID = 2
};

Could you give more info about Product type? Is it your custom page type? Does it have SKU enabled?

Also, do you have GetInternalDocumentParams method implemented in connector?

0 votesVote for this answer Mark as a Correct answer

Massimo Della Calce answered on October 8, 2020 18:32

Product type is a simple custom page type with text fields. The SKU is not enabled. GetInternalDocumentParams is not implemented.

If it can be useful, following is the code snippet of the GetDocumentDataSet method that fails. The property passed to RegisterRecord is Site and it is null, because the node (Product) object has not yet been inserted in Kentico.

 private static DataSet GetDocumentDataSet(
      TreeNode node,
      TranslationHelper th,
      TaskTypeEnum taskType,
      TaskDataTypeEnum dataType,
      TaskParameters taskParams,
      string siteName)
    {
        ...

        bool translationsNotPresent = th == null;
        if (translationsNotPresent)
            th = new TranslationHelper();

        ...

        if (translationsNotPresent)
        {
            ...

            th.RegisterRecord((BaseInfo) node.Site);

            ...
        }
        ...
    }
0 votesVote for this answer Mark as a Correct answer

Dmitry Bastron answered on October 8, 2020 18:50

Ok, got you now, sorry. In your scheduled task when you call ProcessExternalTask is SiteContext.CurrentSite.SiteName null? In theory, it should be used automatically when adding a new page to the site. This could be null if your task is run in external Windows service but not in Kentico scheduler.

Alternatively, you can simply add NodeSiteID into your constructor and it should work:

var node = new Product
{
    DocumentName = pimObj.Name,
    DocumentCulture = "en-us",
    Name = pimObj.Name,
    Family = pimObj.Family,
    NodeParentID = 2,
    NodeSiteID = yourSiteID
};
0 votesVote for this answer Mark as a Correct answer

Massimo Della Calce answered on October 8, 2020 18:57 (last edited on October 8, 2020 19:07)

SiteContext.CurrentSite.SiteName has the correct site name value: as you can see ProcessExternalTask is called within a Kentico scheduled task. NodeSiteID is marked as readonly.

0 votesVote for this answer Mark as a Correct answer

Massimo Della Calce answered on October 9, 2020 15:14 (last edited on October 9, 2020 15:47)

After having change the call to the ProcessExternalTask method as follows:

IntegrationHelper.ProcessExternalTask("CustomIntegrationConnector", pimObject, IntegrationProcessTypeEnum.SkipOnError, TaskTypeEnum.CreateDocument, CMS. Synchronization.TaskDataTypeEnum.SimpleSnapshot, SiteContext.CurrentSite.SiteName);

and having implemented the GetInternalObjectParams method:

        public override void GetInternalObjectParams(int id
            ,string objectType
            ,out string codeName
            ,out string siteName
            ,ref int parentId,
             ref int groupId)
            {

                codeName = Product.CLASS_NAME;
                siteName = SiteContext.CurrentSite.SiteName;
                parentId = 2;
            }

This generates the task. The problem now is that the page is created under the site root and not under the node that I specified... why?

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.