Sana Assistant (online)
Table of Contents

Example of ConnectionExtension Implementation

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Sana.Extensions.Connection;
using System.Xml.XPath;

namespace Sana.ProductClassifier
{
    public class ProductClassifierExtension : ConnectionExtension
    {
        const string ClassFieldName = "Custom_Class";
        const string ClassFieldXPath = "/field[@name=\"" + ClassFieldName + "\"]";
        const string EntityField = "<Field>" +
            "<field name=\"Name\" value=\"" + ClassFieldName + "\"/>" +
            "<field name=\"Caption\" value=\"Discovered class\"/>" +
            "<field name=\"Type\" value=\"String\"/>" +
        "</Field>";

        public override XDocument ExecuteRequest(string operation, IList<XElement> parameters, ExecuteRequestMethod execute)
        {
            if (operation.Equals("GetEntityFields", StringComparison.Ordinal) && parameters.Any(param => param.Name.LocalName == "Table" && param.Value == "Product"))
                return InterceptGetEntityFields(parameters, execute);

            if (operation.Equals("GetProducts", StringComparison.Ordinal))
                return InterceptGetProducts(parameters, execute);

            return execute(operation, parameters);
        }

        protected virtual XDocument InterceptGetEntityFields(IList<XElement> parameters, ExecuteRequestMethod execute)
        {
            var document = execute("GetEntityFields", parameters);
            XElement resultNode = GetResultNode(document);
            if (resultNode != null && resultNode.HasElements)
                resultNode.Add(XElement.Parse(EntityField));

            return document;
        }

        protected virtual XDocument InterceptGetProducts(IList<XElement> parameters, ExecuteRequestMethod execute)
        {
            var fieldsNode = parameters.FirstOrDefault(e => e.Name.LocalName == "ExtraFields");
            var classField = fieldsNode?.XPathSelectElement(ClassFieldXPath);
            if (classField == null)
                return execute("GetProducts", parameters);

            // Prevent ERP error on not found field.
            classField.Remove();

            var document = execute("GetProducts", parameters);

            XElement resultNode = GetResultNode(document);
            if (resultNode != null && resultNode.HasElements)
            {
                foreach (var product in resultNode.Elements("Product"))
                {
                    // Assume GetClass method is some ML-based algorithm or advanced text search function.
                    var txt = "<field name=\"" + ClassFieldName + "\" value=\"" + GetClass(product) + "\"/>";
                    product.Add(XElement.Parse(txt));
                }
            }

            // Restore original parameters state.
            fieldsNode.Add(classField);
            return document;
        }

        XElement GetResultNode(XDocument document) => document.Root.Element("Result");

        protected virtual string GetClass(XElement product)
        {
            var title = (string)product.XPathEvaluate("string(field[@name=\"Title\"]/@value)");
            return GetClass(title);
        }

        string GetClass(string title)
        {
            bool contains(params string[] strings) => strings.Any(sub => title.IndexOf(sub, StringComparison.CurrentCultureIgnoreCase) != -1);

            return contains("Bicycle", "cycle", "bike")
                ? "bike"
                : "other";
        }
    }
}