parse-community / Parse-SDK-JS

The JavaScript SDK for Parse Platform
https://parseplatform.org
Apache License 2.0
1.33k stars 597 forks source link

Queries fail to parse results when asking for subfield of a null pointer or null array of pointers #1196

Closed henrikperrochon closed 4 years ago

henrikperrochon commented 4 years ago

Issue Description

When using select to specify fields and subfields, a query may fail to parse results if one of the field is null.

Steps to reproduce

1 - From the dashboard, select a collection (let say Sample) with a field typed as array (let say arrayfield). 2 - Set the field to be null. 3 - Call the following query : new Parse.Query('Sample').select(['arrayfield.subfield']).find().then(console.log, console.error);

Expected Results

An error occurs: Uncaught (in promise) code: undefined text: "Cannot read property 'subfield' of null"

Actual Outcome

The networking tools from Chrome shows that the server manage to answer, but the Parse JS SDK fails to parse the results. The following method fails when reading the null array. if (_obj !== undefined) { does not prevent null objects.

  function handleSelectResult(data, select) {
    var serverDataMask = {};
    (0, _forEach.default)(select).call(select, function (field) {
      var hasSubObjectSelect = (0, _indexOf.default)(field).call(field, ".") !== -1;

      if (!hasSubObjectSelect && !data.hasOwnProperty(field)) {
        // this field was selected, but is missing from the retrieved data
        data[field] = undefined;
      } else if (hasSubObjectSelect) {
        // this field references a sub-object,
        // so we need to walk down the path components
        var pathComponents = field.split(".");
        var _obj = data;
        var serverMask = serverDataMask;
        (0, _forEach.default)(pathComponents).call(pathComponents, function (component, index, arr) {
          // add keys if the expected data is missing
          if (_obj && !_obj.hasOwnProperty(component)) {
            _obj[component] = undefined;
          }

          if (_obj !== undefined) { <----------------------------------------------- the issue is triggered after this test
            _obj = _obj[component];
          } //add this path component to the server mask so we can fill it in later if needed

          if (index < arr.length - 1) {
            if (!serverMask[component]) {
              serverMask[component] = {};
            }

            serverMask = serverMask[component];
          }
        });
      }
    });

    if ((0, _keys2.default)(serverDataMask).length > 0) {
      // When selecting from sub-objects, we don't want to blow away the missing
      // information that we may have retrieved before. We've already added any
      // missing selected keys to sub-objects, but we still need to add in the
      // data for any previously retrieved sub-objects that were not selected.
      var serverData = _CoreManager.default.getObjectStateController().getServerData({
        id: data.objectId,
        className: data.className
      });

      copyMissingDataWithMask(serverData, data, serverDataMask, false);
    }
  }

Environment Setup

Logs/Trace

The actual request: curl 'https://dev.bruce.work/1/classes/Mission' \ -H 'authority: xxx' \ -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36' \ -H 'content-type: text/plain' \ -H 'accept: */*' \ -H 'origin: https://xxx' \ -H 'sec-fetch-site: same-site' \ -H 'sec-fetch-mode: cors' \ -H 'sec-fetch-dest: empty' \ -H 'referer: https://xxx/app/missions/6kYVPa22Oo' \ -H 'accept-language: en-US,en;q=0.9,fr;q=0.8' \ --data-binary '{"where":{"objectId":"6kYVPa22Oo"},"keys":"category,days,education,endingAt,equipment.name_fr,income,incomeIsNet,information,languages,level,organization.name,organization.presentation,place.postalCode,place.streetName,place.streetNumber,place.town,skills,skills.name_fr,solved,startingAt,timeDescription,title,toDo,wage,wagePerHour,wagePerMonth","limit":1,"_method":"GET","_ApplicationId":"XXX","_JavaScriptKey":"XXX","_ClientVersion":"js2.14.0","_InstallationId":"XXX","_SessionToken":"r:XXX"}' \ --compressed

The server's answer: {"results":[{"objectId":"6kYVPa22Oo","category":"CDD","organization":{"objectId":"4HCDkx2svH","createdAt":"2017-10-11T14:44:01.770Z","updatedAt":"2020-07-17T09:00:02.323Z","presentation":"","name":"La vida chula","ACL":{"gwVBuDTmy9":{"read":true,"write":true},"*":{"read":true},"role:Administrator":{"read":true,"write":true},"role:Master":{"read":true,"write":true}},"__type":"Object","className":"Organization"},"solved":false,"createdAt":"2020-06-17T12:56:19.453Z","updatedAt":"2020-07-18T12:42:27.633Z","days":4,"education":[{"degree":"BEP","domain":null}],"endingAt":{"__type":"Date","iso":"2020-06-30T00:00:00.000Z"},"equipment":[],"income":653.4,"information":"Aucune consigne spécifique pour cette mission, soyez à l'heure et donnez le meilleur de vous-même!","languages":[{"degree":"elementary","field":"en","language":{"className":"Language"}},{"degree":"elementary","field":"es","language":{"className":"Language"}},{"degree":"elementary","field":"da","language":{"className":"Language"}}],"startingAt":{"__type":"Date","iso":"2020-06-25T00:00:00.000Z"},"timeDescription":"Horaires : du lundi au mardi et du jeudi au vendredi de 10:00 à 19:00..","title":"Serveur / Serveuse petit déjeuner","toDo":"Dresser les tables","wage":15,"wagePerHour":null,"wagePerMonth":null,"level":[1,12],"place":{"objectId":"MuohKETlFl","postalCode":"75010","streetName":"Rue des Petites Écuries","streetNumber":"44","town":"Paris","createdAt":"2020-06-17T12:54:48.058Z","updatedAt":"2020-06-17T12:54:48.058Z","ACL":{"*":{"read":true},"gwVBuDTmy9":{"read":true},"role:Administrator":{"read":true,"write":true},"role:Master":{"read":true,"write":true}},"__type":"Object","className":"Place"},"skills":null}]}

The error: Uncaught (in promise) code: undefined text: "Cannot read property 'name_fr' of null"

dplewis commented 4 years ago

Thanks for reporting. We will look into it shortly.