Error while substituting the templates after upgrade to 2.3 for custom installation page

After updating my app to version 2.3 of freshservice platform, I seem to be getting error even after following the migration guide.
Below is my code:
iparams.html is as below

<html>

<head>
    <title>deo</title>
    <link rel="stylesheet" type="text/css" href="https://static.freshdev.io/fdk/2.0/assets/freshservice.css" />
    <link rel="stylesheet" type="text/css" href="./assets/iparams.css" />
    <style>
        /* Put your custom style here */
    </style>
</head>

<body>
    <div class="form">
        <section id="step1" class="">
            <fw-label value="" class="hide error" color="red" id="errorMessage"></fw-label>
            <fw-input id="domainName" class="domaindetails" label="Domain" placeholder="Please enter the Freshservice Domain name" state="normal" required="true" clear-input>
            </fw-input>
            <fw-input id="apiKey" class="domaindetails" label="API Key" type="password" placeholder="Please enter the Freshservice API Key" state="normal" required="true" clear-input>
            </fw-input>
            <fw-button id="submit" color="primary"> Submit</fw-button>
            <fw-spinner id="spinner" class="hide" color="blue"></fw-spinner>
            <br />
            <section id="postRequest" class="hide"></section>
        </section>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
    <script src="{{{appclient}}}"></script>
    <script type="module" src="https://cdn.jsdelivr.net/npm/@freshworks/crayons/dist/crayons/crayons.esm.js"></script>
    <script nomodule src="https://cdn.jsdelivr.net/npm/@freshworks/crayons/dist/crayons/crayons.js"></script>
    <script src="./assets/iparams.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {
        window.validation = 1;
        app
            .initialized()
            .then(function(client) {
                window.client = client;
            })
            .catch(function(er) {
                console.log(er);
            });
    });

    function getConfigs(configs) {
        $("#domainName").val(configs.domainName);
        $("#apiKey").val(configs.apiKey);

        console.log("domain name: " + configs.domainName);
        console.log("aspi key: " + configs.apiKey);
        
        window.customObjectId = configs.customObjectId;
        window.storeFieldName = configs.storeFieldName;

        window.storeNumber = configs.storeNumber;
        window.storeNrName = configs.storeNrName;
        window.language = configs.language;
        window.tvaFormatted = configs.tvaFormatted;
        window.companyName = configs.companyName;
        window.street = configs.street;
        window.city = configs.city;
        window.franchiseDirector = configs.franchiseDirector;
        window.gsmFranchise = configs.gsmFranchise;
        window.emailFranchise = configs.emailFranchise;
        // makeRequest();
        // window.valid = true;
    }

    function validate() {
        checkmainfields("domaindetails,.icddetails,.dropdowns");
        //write your code here
        return window.valid;
    }

    function postConfigs() {
        //write your code here
        var obj = {
            domainName: $("#domainName").val(),
            apiKey: $("#apiKey").val(),
            customObjectId: $("#customObjectId").val(),
            storeFieldName: $("#storeFieldName").val(),
            storeNumber: $("#storeNumber").val(),
            storeNrName: $("#storeNrName").val(),
            language: $("#language").val(),
            tvaFormatted: $("#tvaFormatted").val(),
            companyName: $("#companyName").val(),
            street: $("#street").val(),
            city: $("#city").val(),
            franchiseDirector: $("#franchiseDirector").val(),
            gsmFranchise: $("#gsmFranchise").val(),
            emailFranchise: $("#emailFranchise").val()
        };
        console.log("object data: " + JSON.stringify(obj));
        return obj;
    }

    function checkmainfields(field) {
        $(`.${field}`).each(function(i, elem) {
            if ($(elem).val().length === 0) {
                setState(elem, "error", "Please fill this field");
                // eslint-disable-next-line no-undef
                valid = false;
            } else {
                setState(elem, "normal", "");
            }
        });
    }

    $("#submit").on("fwClick", function(data) {
        window.valid = true;
        checkmainfields("domaindetails");
        if (
            $("#domainName")
            .val()
            .match(/^[a-zA-Z0-9-]+\.freshservice\.com$/)
        ) {
            if (window.valid) {
                $("#spinner").removeClass("hide");
                makeRequest();
            }
        } else {
            setState($("#domainName"), "error", "Please enter a valid Url");
        }
    });

    function apiCall(templateName, iparamData) {
        if (templateName === "getObjectById") {
            let customId = iparamData;
            iparamData = { context: { customObjectId: customId } };
        }
        return client.request.invokeTemplate(templateName,  iparamData);
    }

    /*function apiCall(url) {
        var options = {
            headers: getHeaders(),
        };
        return client.request.invoke('get', url, options);
    }

    function getHeaders() {
        return {
            Authorization: "Basic " + btoa($("#apiKey").val() + ":*"),
        };
    }*/


    function makeRequest() {
        $(".error").addClass("hide");

        let iparamData = { context: { subdomain: $("#domainName").val(), api_key: $("#apiKey").val() } };
       
        let apiCalls = [
            apiCall("getTicketFormFields", iparamData),
            apiCall("getObjects", iparamData)
        ];

        if (window.customObjectId) {
            apiCalls.push(apiCall("getObjectById", window.customObjectId ));
        }
        Promise.all(apiCalls)
            .then(function(results) {
                console.log("result: " + results);
                let ticket_fields = JSON.parse(results[0].response);
                let object_list = JSON.parse(results[1].response);
                let custom_object_fields = results[2] ? JSON.parse(results[2].response) : null;
                let fieldList = [];
                let customObjectList = [];

                $.each(ticket_fields.ticket_fields, function(i, item) {
                    let obj = {};
                    obj.value = item.name;
                    obj.text = item.label;
                    fieldList.push(obj);
                });

                $.each(object_list.custom_objects, function(i, item) {
                    let obj = {};
                    obj.value = item.id;
                    obj.text = item.title;
                    customObjectList.push(obj);
                });

                $("#postRequest").html("");
                $("#postRequest")
                    .append(formSelect(fieldList, "storeFieldName"))
                    .append(formSelect(customObjectList, "customObjectId", "Select a custom object where the franchise information is stored"))

                $("#postRequest").removeClass("hide");
                $("#spinner").addClass("hide");
                setTimeout(function() {
                    $("#storeFieldName").val(window.storeFieldName);
                    $("#customObjectId").val(window.customObjectId);
                    $("#postRequest").append("<div id='franchiseConfig'></div>");
                    if (custom_object_fields) {
                        retrieveFranchiseConfig(custom_object_fields)
                    }
                    initSelection();
                }, 500);
            })
            .catch(function(error) {
                console.log("Log: makeRequest -> error", error);
                setState($("#domainName"), "error", "Please enter a valid url");
                setState($("#apiKey"), "error", "Please enter valid API key");
                $("#errorMessage").val(error.response).removeClass("hide");
                $("#spinner").addClass("hide");
            });
    }

    function retrieveFranchiseConfig(data, franchiseObjectChange) {
        let customObjectFieldList = [];
        $.each(data.custom_object.fields, function(i, item) {
            let obj = {};
            obj.value = item.name;
            obj.text = item.label;
            customObjectFieldList.push(obj);
        });
        $("#franchiseConfig")
            .append(formSelect(customObjectFieldList, "storeNumber", "Select a field to map to Store's Number", "store"))
            .append(formSelect(customObjectFieldList, "storeNrName", "Select a field to map to Store's Nr-Name", "store"))
            .append(formSelect(customObjectFieldList, "language", "Select a field to map to Store's Language", "store"))
            .append(formSelect(customObjectFieldList, "tvaFormatted", "Select a field to map to Store's TVA Formatted", "store"))
            .append(formSelect(customObjectFieldList, "companyName", "Select a field to map to Store's Company Name", "store"))
            .append(formSelect(customObjectFieldList, "street", "Select a field to map to Store's Street", "store"))
            .append(formSelect(customObjectFieldList, "city", "Select a field to map to Store's City", "store"))
            .append(formSelect(customObjectFieldList, "franchiseDirector", "Select a field to map to Store's Franchise Director", "store"))
            .append(formSelect(customObjectFieldList, "gsmFranchise", "Select a field to map to Store's GSM Franchise", "store"))
            .append(formSelect(customObjectFieldList, "emailFranchise", "Select a field to map to Store's Email Franchise", "store"));
        if (!franchiseObjectChange) setTimeout(function(data) {
            $("#storeNumber").val(window.storeNumber);
            $("#storeNrName").val(window.storeNrName);
            $("#language").val(window.language);
            $("#tvaFormatted").val(window.tvaFormatted);
            $("#companyName").val(window.companyName);
            $("#street").val(window.street);
            $("#city").val(window.city);
            $("#franchiseDirector").val(window.franchiseDirector);
            $("#gsmFranchise").val(window.gsmFranchise);
            $("#emailFranchise").val(window.emailFranchise);
        }, 500);
    }

    function formSelect(data, id, label, cssClass) {
        let fieldTemplate = `<fw-select
            id="${id}"
            label="${
              label ||
              "Select a custom field where the store name is stored"
            }"
            required="true"
            placeholder=""
            force-select
            class="dropdowns ${cssClass}">
          ${formOption(data)}
          </fw-select>`;
        return fieldTemplate;
    }

    function formOption(data) {
        let options = ``;
        $.each(data, function(i, item) {
            options += `<fw-select-option value="${item.value}">${item.text}</fw-select-option>`;
        });
        return options;
    }

    function setState(elem, state, stateText) {
        $(elem).prop("state", state);
        $(elem).prop("stateText", stateText);
    }

    function initSelection() {
        $("#customObjectId").on("fwChange", function(data) {
            $("#franchiseConfig").html("");
            client.request.invokeTemplate('getObjectById', { customObjectId: $("#customObjectId").val() }).then(function(data) {
                retrieveFranchiseConfig(JSON.parse(data.response), true)
            });
            /*apiCall("https://" + $("#domainName").val() + "/api/v2/objects/" + $("#customObjectId").val()).then(function(data) {
                retrieveFranchiseConfig(JSON.parse(data.response), true)
            });*/
        });
    }
    </script>
</body>

</html>

Below is my code for requests.json

{
    "getTicketFormFields": {
      "schema": {
        "protocol": "https",
        "method": "GET",
        "host": "<%= iparam.domainName %>",
        "path": "/api/v2/ticket_form_fields",
        "headers": {
          "Authorization": "Basic <%= encode(iparam.apiKey + ':X') %>",
          "Content-Type": "application/json"
        }
      }
    },
    "getObjects": {
      "schema": {
        "protocol": "https",
        "method": "GET",
        "host": "<%= iparam.domainName %>",
        "path": "/api/v2/objects",
        "headers": {
          "Authorization": "Basic <%= encode(iparam.apiKey + ':X') %>",
          "Content-Type": "application/json"
        }
      }
    },
    "getObjectById": {
      "schema": {
        "protocol": "https",
        "method": "GET",
        "host": "<%= iparam.domainName %>",
        "path": "/api/v2/objects/<%= iparam.customObjectId %>",
        "headers": {
          "Authorization": "Basic <%= encode(iparam.apiKey + ':X') %>",
          "Content-Type": "application/json"
        }
      }
    }
  }

I have been trying but unable to find the issue. Please advise.

Hi @ivan1

Which part of your code throws an “Error while substituting the templates” error?

@Kithiyon I seem to be getting the error on iparams.html the moment i click submit on settings which runs makeRequest function.

Hi @ivan1

Change your host and authorization value

from:

To:
"host": "<%= context.domainName %>",

"Authorization": "Basic <%= encode(context.apiKey + ':X') %>",

I hope this helps :innocent:

1 Like

perfect! exactly what I was looking for. thank you so much for your help

1 Like

This topic was automatically closed 6 days after the last reply. New replies are no longer allowed.