Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    null checks on record param for embedded components function

    Hi there, we are seeing errors generated in our production application using Smartclient 12.1 because there are a series of functions that are not checking to ensure that record is not null in ISC_Grids.js for the methods below related to embedded components. There is one method below (getRecordComponents) that does check for a null record. But, the others below do not. We can add patches for all of these on our own. But, I'm just trying to be a good neighbor and recommending you add these simple checks to your code. Sorry, I do not have time to provide test cases for all of these. So, if you choose to ignore my help, that's your prerogative

    Code:
    _setExpanded : function (record, value) {
            record[this._$expandedPrefix + this.ID] = value;
        },
        _getExpandedRows : function () {
            return this.data.findAll(this._$expandedPrefix + this.ID, true);
        },
    
        _$hasExpansionComponentPrefix:"_hasExpansionComponent_",
        _hasExpansionComponent : function (record) {
            return record[this._$hasExpansionComponentPrefix + this.ID];
        },
        _setExpansionComponent : function (record, value) {
            record[this._$hasExpansionComponentPrefix + this.ID] = value;
        },
    
    
        _$embeddedComponentsPrefix:"_embeddedComponents_",
    
        _hasEmbeddedComponents : function (record) {
            var ids = record[this._$embeddedComponentsPrefix + this.ID];
            return (ids != null && ids.length > 0);
        },
        _getEmbeddedComponents : function (record) {
            // Convert array of IDs into an array of component references
            var ids = record[this._$embeddedComponentsPrefix + this.ID],
                components = []
            ;
            if (!ids) return null;
            for (var i = 0; i < ids.length; i++) {
                components[i] = isc.Canvas.getById(ids[i]);
            }
            return components;
        },
        _setEmbeddedComponents : function (record, value) {
            record[this._$embeddedComponentsPrefix + this.ID] = value;
        },
    
        _addEmbeddedComponent : function (record, component) {
            if(!record[this._$embeddedComponentsPrefix + this.ID]) {
                record[this._$embeddedComponentsPrefix + this.ID] = [];
            }
            if (!record[this._$embeddedComponentsPrefix + this.ID].contains(component.getID())) {
                record[this._$embeddedComponentsPrefix + this.ID].add(component.getID());
            }
        },
        _removeEmbeddedComponent : function (record, component) {
    
            var ids = record[this._$embeddedComponentsPrefix + this.ID];
            if (ids == null) return;
            if (ids.length == 0) {
                record[this._$embeddedComponentsPrefix + this.ID] = null;
                return;
            }
            ids.remove(component.getID());
            if (ids.length == 0) {
                record[this._$embeddedComponentsPrefix + this.ID] = null;
            }
        },
    
        _deleteEmbeddedComponents : function (record, value) {
            delete record[this._$embeddedComponentsPrefix + this.ID];
        },
    
    
        _$recordComponentsPrefix:"_recordComponents_",
        _hasRecordComponents : function (record) {
            return (record && record[this._$recordComponentsPrefix + this.ID] != null);
        },
        _getRecordComponents : function (record) {
            if (!record) return null;
    
            // Convert array of IDs into an array of component references
            var ids = record[this._$recordComponentsPrefix + this.ID],
                components = {}
            ;
            if (ids) {
                for (var key in ids) {
                    if (ids[key].isNullMarker) {
                        components[key] = ids[key];
                    } else {
                        components[key] = isc.Canvas.getById(ids[key]);
                    }
                }
            }
            return components;
        },
        _addRecordComponent : function (record, fieldName, component) {
            if(!record[this._$recordComponentsPrefix + this.ID]) {
                record[this._$recordComponentsPrefix + this.ID] = {};
            }
            if (component.isNullMarker) {
                record[this._$recordComponentsPrefix + this.ID][fieldName] = component;
            } else {
                record[this._$recordComponentsPrefix + this.ID][fieldName] = component.getID();
            }
        },
        _deleteRecordComponent : function (record, fieldName) {
            var ids = record[this._$recordComponentsPrefix + this.ID];
            if (ids == null) return;
            if (isc.isAn.emptyObject(ids)) {
                record[this._$recordComponentsPrefix + this.ID] = null;
                return;
            }
            // Not per-cell - just use the special "no field" fieldName
            if (fieldName == null) fieldName = this._$noFieldString;
    
            delete ids[fieldName];
            if (isc.isAn.emptyObject(ids)) {
                record[this._$recordComponentsPrefix + this.ID] = null;
            }
        },

    #2
    Hi Dave, we do always appreciate the patch suggestions, but as you know, we are reluctant to apply null checks that may mask a deeper problem..

    As you mentioned, you don't have time right now to prepare a test case. But can you give us a general idea of how these APIs are ending up with null records? Are you calling them directly with null records?

    If not, could you maybe share a couple of stack traces showing how they are called within the framework with a null record?

    We don't need a 100% proof that it's possible with valid usage to end up with a null record, but we do need at least a strong hint that this is a framework problem and not a usage issue - if we have that, then we would be happy to add null checks so you don't have to maintain your own patches.

    Comment


      #3
      Hi, thanks for the reply. We configure our application to automatically report any javascript errors back into our usage stats database and we get an alert. These types of errors are very rare and that is why I only report them to you once or twice a year. My assumption is they represent some edge case that is not worth tracking down. But, I want to avoid our users seeing any unexpected errors like this. I am sharing two recent stack traces that may help illuminate what happened. Hope this helps...

      Code:
      {"description":"null","stack":"TypeError: Cannot read properties of null (reading 'isExpansionComponent')
          at _3.isc_ListGrid__destroyEmbeddedComponentsForRebuild [as $810] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:2651:163)
          at _3.isc_ListGrid_updateBody [as updateBody] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:2644:57)
          at _3.isc_ListGrid_setFields [as setFields] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:1240:98)
          at _3.isc_c_Class_invokeSuper [as invokeSuper] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Core.js?isc_version=v12.1p_2021-04-12.js:319:93)
          at _3.isc_c_Class_Super [as Super] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Core.js?isc_version=v12.1p_2021-04-12.js:311:170","output":"null"," assetDashboardPopOutEnabled:"false"url":"https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Core.js?isc_version=v12.1p_2021-04-12.js","line"="331","col"="169"}
      
      {"description":"null","stack":"TypeError: Cannot read properties of null (reading '_embeddedComponents_fundAssetsGrid')
          at _3.isc_ListGrid__getEmbeddedComponents [as $917] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:926:800)
          at _3.isc_GridRenderer_getEmbeddedComponent [as getEmbeddedComponent] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:445:524)
          at _3.isc_ListGrid_getEmbeddedComponent [as getEmbeddedComponent] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:3060:11)
          at _3.isc_ListGrid_getRecordComponent [as getRecordComponent] (https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Grids.js?isc_version=v12.1p_2021-04-12.js:3060:151)
          at _3.startEditing (https://www.hidden.com/dpt.form:6243:32)
          at _3.recordClick (https://www.al","output":"null"," assetDashboardPopOutEnabled:"false"url":"https://www.hidden.com/isoversion/12.1.4.12.21/isomorphic/system/modules/ISC_Core.js?isc_version=v12.1p_2021-04-12.js","line"="2534","col"="160"}

      Comment


        #4
        Thanks Dave - it's true that some of the internal methods you listed don't do their own null-checks, but their callers ought to have been doing.

        So, we've audited this area and made a number of changes. There were indeed a few circumstances where it was possible for either record or component to evade being null-checked en route to the workhorse methods, and we've corrected those flows.

        We haven't null-guarded all of the methods you noted, but two in particular have been altered - _hasEmbeddedComponents() and _getEmbeddedComponents().

        These changes will be in builds of 12.1+ from November 26 and should prevent any future error-reports - but please do let us know if you get traces for any others.
        Last edited by Isomorphic; 25 Nov 2021, 04:41.

        Comment

        Working...
        X