ParseObject.js 84 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _CoreManager = _interopRequireDefault(require("./CoreManager"));
  7. var _canBeSerialized = _interopRequireDefault(require("./canBeSerialized"));
  8. var _decode = _interopRequireDefault(require("./decode"));
  9. var _encode = _interopRequireDefault(require("./encode"));
  10. var _escape = _interopRequireDefault(require("./escape"));
  11. var _EventuallyQueue = _interopRequireDefault(require("./EventuallyQueue"));
  12. var _ParseACL = _interopRequireDefault(require("./ParseACL"));
  13. var _parseDate = _interopRequireDefault(require("./parseDate"));
  14. var _ParseError = _interopRequireDefault(require("./ParseError"));
  15. var _ParseFile = _interopRequireDefault(require("./ParseFile"));
  16. var _promiseUtils = require("./promiseUtils");
  17. var _LocalDatastoreUtils = require("./LocalDatastoreUtils");
  18. var _ParseOp = require("./ParseOp");
  19. var _ParseQuery = _interopRequireDefault(require("./ParseQuery"));
  20. var _ParseRelation = _interopRequireDefault(require("./ParseRelation"));
  21. var SingleInstanceStateController = _interopRequireWildcard(require("./SingleInstanceStateController"));
  22. var _unique = _interopRequireDefault(require("./unique"));
  23. var UniqueInstanceStateController = _interopRequireWildcard(require("./UniqueInstanceStateController"));
  24. var _unsavedChildren = _interopRequireDefault(require("./unsavedChildren"));
  25. function _getRequireWildcardCache(nodeInterop) {
  26. if (typeof WeakMap !== "function") return null;
  27. var cacheBabelInterop = new WeakMap();
  28. var cacheNodeInterop = new WeakMap();
  29. return (_getRequireWildcardCache = function (nodeInterop) {
  30. return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
  31. })(nodeInterop);
  32. }
  33. function _interopRequireWildcard(obj, nodeInterop) {
  34. if (!nodeInterop && obj && obj.__esModule) {
  35. return obj;
  36. }
  37. if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
  38. return {
  39. default: obj
  40. };
  41. }
  42. var cache = _getRequireWildcardCache(nodeInterop);
  43. if (cache && cache.has(obj)) {
  44. return cache.get(obj);
  45. }
  46. var newObj = {};
  47. var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
  48. for (var key in obj) {
  49. if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
  50. var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
  51. if (desc && (desc.get || desc.set)) {
  52. Object.defineProperty(newObj, key, desc);
  53. } else {
  54. newObj[key] = obj[key];
  55. }
  56. }
  57. }
  58. newObj.default = obj;
  59. if (cache) {
  60. cache.set(obj, newObj);
  61. }
  62. return newObj;
  63. }
  64. function _interopRequireDefault(obj) {
  65. return obj && obj.__esModule ? obj : {
  66. default: obj
  67. };
  68. }
  69. /**
  70. * @flow
  71. */
  72. /*:: import type { AttributeMap, OpsMap } from './ObjectStateMutations';*/
  73. /*:: import type { RequestOptions, FullOptions } from './RESTController';*/
  74. const uuidv4 = require('./uuid');
  75. /*:: export type Pointer = {
  76. __type: string,
  77. className: string,
  78. objectId: string,
  79. };*/
  80. /*:: type SaveParams = {
  81. method: string,
  82. path: string,
  83. body: AttributeMap,
  84. };*/
  85. /*:: export type SaveOptions = FullOptions & {
  86. cascadeSave?: boolean,
  87. context?: AttributeMap,
  88. };*/
  89. // Mapping of class names to constructors, so we can populate objects from the
  90. // server with appropriate subclasses of ParseObject
  91. const classMap = {};
  92. // Global counter for generating unique Ids for non-single-instance objects
  93. let objectCount = 0;
  94. // On web clients, objects are single-instance: any two objects with the same Id
  95. // will have the same attributes. However, this may be dangerous default
  96. // behavior in a server scenario
  97. let singleInstance = !_CoreManager.default.get('IS_NODE');
  98. if (singleInstance) {
  99. _CoreManager.default.setObjectStateController(SingleInstanceStateController);
  100. } else {
  101. _CoreManager.default.setObjectStateController(UniqueInstanceStateController);
  102. }
  103. function getServerUrlPath() {
  104. let serverUrl = _CoreManager.default.get('SERVER_URL');
  105. if (serverUrl[serverUrl.length - 1] !== '/') {
  106. serverUrl += '/';
  107. }
  108. const url = serverUrl.replace(/https?:\/\//, '');
  109. return url.substr(url.indexOf('/'));
  110. }
  111. /**
  112. * Creates a new model with defined attributes.
  113. *
  114. * <p>You won't normally call this method directly. It is recommended that
  115. * you use a subclass of <code>Parse.Object</code> instead, created by calling
  116. * <code>extend</code>.</p>
  117. *
  118. * <p>However, if you don't want to use a subclass, or aren't sure which
  119. * subclass is appropriate, you can use this form:<pre>
  120. * var object = new Parse.Object("ClassName");
  121. * </pre>
  122. * That is basically equivalent to:<pre>
  123. * var MyClass = Parse.Object.extend("ClassName");
  124. * var object = new MyClass();
  125. * </pre></p>
  126. *
  127. * @alias Parse.Object
  128. */
  129. class ParseObject {
  130. /**
  131. * @param {string} className The class name for the object
  132. * @param {object} attributes The initial set of data to store in the object.
  133. * @param {object} options The options for this object instance.
  134. */
  135. constructor(className /*: ?string | { className: string, [attr: string]: mixed }*/, attributes /*:: ?: { [attr: string]: mixed }*/, options /*:: ?: { ignoreValidation: boolean }*/) {
  136. // Enable legacy initializers
  137. if (typeof this.initialize === 'function') {
  138. this.initialize.apply(this, arguments);
  139. }
  140. let toSet = null;
  141. this._objCount = objectCount++;
  142. if (typeof className === 'string') {
  143. this.className = className;
  144. if (attributes && typeof attributes === 'object') {
  145. toSet = attributes;
  146. }
  147. } else if (className && typeof className === 'object') {
  148. this.className = className.className;
  149. toSet = {};
  150. for (const attr in className) {
  151. if (attr !== 'className') {
  152. toSet[attr] = className[attr];
  153. }
  154. }
  155. if (attributes && typeof attributes === 'object') {
  156. options = attributes;
  157. }
  158. }
  159. if (toSet && !this.set(toSet, options)) {
  160. throw new Error("Can't create an invalid Parse Object");
  161. }
  162. }
  163. /**
  164. * The ID of this object, unique within its class.
  165. *
  166. * @property {string} id
  167. */
  168. /*:: id: ?string;*/
  169. /*:: _localId: ?string;*/
  170. /*:: _objCount: number;*/
  171. /*:: className: string;*/
  172. /* Prototype getters / setters */
  173. get attributes() /*: AttributeMap*/{
  174. const stateController = _CoreManager.default.getObjectStateController();
  175. return Object.freeze(stateController.estimateAttributes(this._getStateIdentifier()));
  176. }
  177. /**
  178. * The first time this object was saved on the server.
  179. *
  180. * @property {Date} createdAt
  181. * @returns {Date}
  182. */
  183. get createdAt() /*: ?Date*/{
  184. return this._getServerData().createdAt;
  185. }
  186. /**
  187. * The last time this object was updated on the server.
  188. *
  189. * @property {Date} updatedAt
  190. * @returns {Date}
  191. */
  192. get updatedAt() /*: ?Date*/{
  193. return this._getServerData().updatedAt;
  194. }
  195. /* Private methods */
  196. /**
  197. * Returns a local or server Id used uniquely identify this object
  198. *
  199. * @returns {string}
  200. */
  201. _getId() /*: string*/{
  202. if (typeof this.id === 'string') {
  203. return this.id;
  204. }
  205. if (typeof this._localId === 'string') {
  206. return this._localId;
  207. }
  208. const localId = 'local' + uuidv4();
  209. this._localId = localId;
  210. return localId;
  211. }
  212. /**
  213. * Returns a unique identifier used to pull data from the State Controller.
  214. *
  215. * @returns {Parse.Object|object}
  216. */
  217. _getStateIdentifier() /*: ParseObject | { id: string, className: string }*/{
  218. if (singleInstance) {
  219. let id = this.id;
  220. if (!id) {
  221. id = this._getId();
  222. }
  223. return {
  224. id: id,
  225. className: this.className
  226. };
  227. } else {
  228. return this;
  229. }
  230. }
  231. _getServerData() /*: AttributeMap*/{
  232. const stateController = _CoreManager.default.getObjectStateController();
  233. return stateController.getServerData(this._getStateIdentifier());
  234. }
  235. _clearServerData() {
  236. const serverData = this._getServerData();
  237. const unset = {};
  238. for (const attr in serverData) {
  239. unset[attr] = undefined;
  240. }
  241. const stateController = _CoreManager.default.getObjectStateController();
  242. stateController.setServerData(this._getStateIdentifier(), unset);
  243. }
  244. _getPendingOps() /*: Array<OpsMap>*/{
  245. const stateController = _CoreManager.default.getObjectStateController();
  246. return stateController.getPendingOps(this._getStateIdentifier());
  247. }
  248. /**
  249. * @param {Array<string>} [keysToClear] - if specified, only ops matching
  250. * these fields will be cleared
  251. */
  252. _clearPendingOps(keysToClear /*:: ?: Array<string>*/) {
  253. const pending = this._getPendingOps();
  254. const latest = pending[pending.length - 1];
  255. const keys = keysToClear || Object.keys(latest);
  256. keys.forEach(key => {
  257. delete latest[key];
  258. });
  259. }
  260. _getDirtyObjectAttributes() /*: AttributeMap*/{
  261. const attributes = this.attributes;
  262. const stateController = _CoreManager.default.getObjectStateController();
  263. const objectCache = stateController.getObjectCache(this._getStateIdentifier());
  264. const dirty = {};
  265. for (const attr in attributes) {
  266. const val = attributes[attr];
  267. if (val && typeof val === 'object' && !(val instanceof ParseObject) && !(val instanceof _ParseFile.default) && !(val instanceof _ParseRelation.default)) {
  268. // Due to the way browsers construct maps, the key order will not change
  269. // unless the object is changed
  270. try {
  271. const json = (0, _encode.default)(val, false, true);
  272. const stringified = JSON.stringify(json);
  273. if (objectCache[attr] !== stringified) {
  274. dirty[attr] = val;
  275. }
  276. } catch (e) {
  277. // Error occurred, possibly by a nested unsaved pointer in a mutable container
  278. // No matter how it happened, it indicates a change in the attribute
  279. dirty[attr] = val;
  280. }
  281. }
  282. }
  283. return dirty;
  284. }
  285. _toFullJSON(seen /*:: ?: Array<any>*/, offline /*:: ?: boolean*/) /*: AttributeMap*/{
  286. const json /*: { [key: string]: mixed }*/ = this.toJSON(seen, offline);
  287. json.__type = 'Object';
  288. json.className = this.className;
  289. return json;
  290. }
  291. _getSaveJSON() /*: AttributeMap*/{
  292. const pending = this._getPendingOps();
  293. const dirtyObjects = this._getDirtyObjectAttributes();
  294. const json = {};
  295. for (var attr in dirtyObjects) {
  296. let isDotNotation = false;
  297. for (let i = 0; i < pending.length; i += 1) {
  298. for (const field in pending[i]) {
  299. // Dot notation operations are handled later
  300. if (field.includes('.')) {
  301. const fieldName = field.split('.')[0];
  302. if (fieldName === attr) {
  303. isDotNotation = true;
  304. break;
  305. }
  306. }
  307. }
  308. }
  309. if (!isDotNotation) {
  310. json[attr] = new _ParseOp.SetOp(dirtyObjects[attr]).toJSON();
  311. }
  312. }
  313. for (attr in pending[0]) {
  314. json[attr] = pending[0][attr].toJSON();
  315. }
  316. return json;
  317. }
  318. _getSaveParams() /*: SaveParams*/{
  319. let method = this.id ? 'PUT' : 'POST';
  320. const body = this._getSaveJSON();
  321. let path = 'classes/' + this.className;
  322. if (_CoreManager.default.get('ALLOW_CUSTOM_OBJECT_ID')) {
  323. if (!this.createdAt) {
  324. method = 'POST';
  325. body.objectId = this.id;
  326. } else {
  327. method = 'PUT';
  328. path += '/' + this.id;
  329. }
  330. } else if (this.id) {
  331. path += '/' + this.id;
  332. } else if (this.className === '_User') {
  333. path = 'users';
  334. }
  335. return {
  336. method,
  337. body,
  338. path
  339. };
  340. }
  341. _finishFetch(serverData /*: AttributeMap*/) {
  342. if (!this.id && serverData.objectId) {
  343. this.id = serverData.objectId;
  344. }
  345. const stateController = _CoreManager.default.getObjectStateController();
  346. stateController.initializeState(this._getStateIdentifier());
  347. const decoded = {};
  348. for (const attr in serverData) {
  349. if (attr === 'ACL') {
  350. decoded[attr] = new _ParseACL.default(serverData[attr]);
  351. } else if (attr !== 'objectId') {
  352. decoded[attr] = (0, _decode.default)(serverData[attr]);
  353. if (decoded[attr] instanceof _ParseRelation.default) {
  354. decoded[attr]._ensureParentAndKey(this, attr);
  355. }
  356. }
  357. }
  358. if (decoded.createdAt && typeof decoded.createdAt === 'string') {
  359. decoded.createdAt = (0, _parseDate.default)(decoded.createdAt);
  360. }
  361. if (decoded.updatedAt && typeof decoded.updatedAt === 'string') {
  362. decoded.updatedAt = (0, _parseDate.default)(decoded.updatedAt);
  363. }
  364. if (!decoded.updatedAt && decoded.createdAt) {
  365. decoded.updatedAt = decoded.createdAt;
  366. }
  367. stateController.commitServerChanges(this._getStateIdentifier(), decoded);
  368. }
  369. _setExisted(existed /*: boolean*/) {
  370. const stateController = _CoreManager.default.getObjectStateController();
  371. const state = stateController.getState(this._getStateIdentifier());
  372. if (state) {
  373. state.existed = existed;
  374. }
  375. }
  376. _migrateId(serverId /*: string*/) {
  377. if (this._localId && serverId) {
  378. if (singleInstance) {
  379. const stateController = _CoreManager.default.getObjectStateController();
  380. const oldState = stateController.removeState(this._getStateIdentifier());
  381. this.id = serverId;
  382. delete this._localId;
  383. if (oldState) {
  384. stateController.initializeState(this._getStateIdentifier(), oldState);
  385. }
  386. } else {
  387. this.id = serverId;
  388. delete this._localId;
  389. }
  390. }
  391. }
  392. _handleSaveResponse(response /*: AttributeMap*/, status /*: number*/) {
  393. const changes = {};
  394. const stateController = _CoreManager.default.getObjectStateController();
  395. const pending = stateController.popPendingState(this._getStateIdentifier());
  396. for (var attr in pending) {
  397. if (pending[attr] instanceof _ParseOp.RelationOp) {
  398. changes[attr] = pending[attr].applyTo(undefined, this, attr);
  399. } else if (!(attr in response)) {
  400. // Only SetOps and UnsetOps should not come back with results
  401. changes[attr] = pending[attr].applyTo(undefined);
  402. }
  403. }
  404. for (attr in response) {
  405. if ((attr === 'createdAt' || attr === 'updatedAt') && typeof response[attr] === 'string') {
  406. changes[attr] = (0, _parseDate.default)(response[attr]);
  407. } else if (attr === 'ACL') {
  408. changes[attr] = new _ParseACL.default(response[attr]);
  409. } else if (attr !== 'objectId') {
  410. const val = (0, _decode.default)(response[attr]);
  411. if (val && Object.getPrototypeOf(val) === Object.prototype) {
  412. changes[attr] = {
  413. ...this.attributes[attr],
  414. ...val
  415. };
  416. } else {
  417. changes[attr] = val;
  418. }
  419. if (changes[attr] instanceof _ParseOp.UnsetOp) {
  420. changes[attr] = undefined;
  421. }
  422. }
  423. }
  424. if (changes.createdAt && !changes.updatedAt) {
  425. changes.updatedAt = changes.createdAt;
  426. }
  427. this._migrateId(response.objectId);
  428. if (status !== 201) {
  429. this._setExisted(true);
  430. }
  431. stateController.commitServerChanges(this._getStateIdentifier(), changes);
  432. }
  433. _handleSaveError() {
  434. const stateController = _CoreManager.default.getObjectStateController();
  435. stateController.mergeFirstPendingState(this._getStateIdentifier());
  436. }
  437. static _getClassMap() {
  438. return classMap;
  439. }
  440. /* Public methods */
  441. initialize() {
  442. // NOOP
  443. }
  444. /**
  445. * Returns a JSON version of the object suitable for saving to Parse.
  446. *
  447. * @param seen
  448. * @param offline
  449. * @returns {object}
  450. */
  451. toJSON(seen /*: Array<any> | void*/, offline /*:: ?: boolean*/) /*: AttributeMap*/{
  452. const seenEntry = this.id ? this.className + ':' + this.id : this;
  453. seen = seen || [seenEntry];
  454. const json = {};
  455. const attrs = this.attributes;
  456. for (const attr in attrs) {
  457. if ((attr === 'createdAt' || attr === 'updatedAt') && attrs[attr].toJSON) {
  458. json[attr] = attrs[attr].toJSON();
  459. } else {
  460. json[attr] = (0, _encode.default)(attrs[attr], false, false, seen, offline);
  461. }
  462. }
  463. const pending = this._getPendingOps();
  464. for (const attr in pending[0]) {
  465. json[attr] = pending[0][attr].toJSON(offline);
  466. }
  467. if (this.id) {
  468. json.objectId = this.id;
  469. }
  470. return json;
  471. }
  472. /**
  473. * Determines whether this ParseObject is equal to another ParseObject
  474. *
  475. * @param {object} other - An other object ot compare
  476. * @returns {boolean}
  477. */
  478. equals(other /*: mixed*/) /*: boolean*/{
  479. if (this === other) {
  480. return true;
  481. }
  482. return other instanceof ParseObject && this.className === other.className && this.id === other.id && typeof this.id !== 'undefined';
  483. }
  484. /**
  485. * Returns true if this object has been modified since its last
  486. * save/refresh. If an attribute is specified, it returns true only if that
  487. * particular attribute has been modified since the last save/refresh.
  488. *
  489. * @param {string} attr An attribute name (optional).
  490. * @returns {boolean}
  491. */
  492. dirty(attr /*:: ?: string*/) /*: boolean*/{
  493. if (!this.id) {
  494. return true;
  495. }
  496. const pendingOps = this._getPendingOps();
  497. const dirtyObjects = this._getDirtyObjectAttributes();
  498. if (attr) {
  499. if (dirtyObjects.hasOwnProperty(attr)) {
  500. return true;
  501. }
  502. for (let i = 0; i < pendingOps.length; i++) {
  503. if (pendingOps[i].hasOwnProperty(attr)) {
  504. return true;
  505. }
  506. }
  507. return false;
  508. }
  509. if (Object.keys(pendingOps[0]).length !== 0) {
  510. return true;
  511. }
  512. if (Object.keys(dirtyObjects).length !== 0) {
  513. return true;
  514. }
  515. return false;
  516. }
  517. /**
  518. * Returns an array of keys that have been modified since last save/refresh
  519. *
  520. * @returns {string[]}
  521. */
  522. dirtyKeys() /*: Array<string>*/{
  523. const pendingOps = this._getPendingOps();
  524. const keys = {};
  525. for (let i = 0; i < pendingOps.length; i++) {
  526. for (const attr in pendingOps[i]) {
  527. keys[attr] = true;
  528. }
  529. }
  530. const dirtyObjects = this._getDirtyObjectAttributes();
  531. for (const attr in dirtyObjects) {
  532. keys[attr] = true;
  533. }
  534. return Object.keys(keys);
  535. }
  536. /**
  537. * Returns true if the object has been fetched.
  538. *
  539. * @returns {boolean}
  540. */
  541. isDataAvailable() /*: boolean*/{
  542. const serverData = this._getServerData();
  543. return !!Object.keys(serverData).length;
  544. }
  545. /**
  546. * Gets a Pointer referencing this Object.
  547. *
  548. * @returns {Pointer}
  549. */
  550. toPointer() /*: Pointer*/{
  551. if (!this.id) {
  552. throw new Error('Cannot create a pointer to an unsaved ParseObject');
  553. }
  554. return {
  555. __type: 'Pointer',
  556. className: this.className,
  557. objectId: this.id
  558. };
  559. }
  560. /**
  561. * Gets a Pointer referencing this Object.
  562. *
  563. * @returns {Pointer}
  564. */
  565. toOfflinePointer() /*: Pointer*/{
  566. if (!this._localId) {
  567. throw new Error('Cannot create a offline pointer to a saved ParseObject');
  568. }
  569. return {
  570. __type: 'Object',
  571. className: this.className,
  572. _localId: this._localId
  573. };
  574. }
  575. /**
  576. * Gets the value of an attribute.
  577. *
  578. * @param {string} attr The string name of an attribute.
  579. * @returns {*}
  580. */
  581. get(attr /*: string*/) /*: mixed*/{
  582. return this.attributes[attr];
  583. }
  584. /**
  585. * Gets a relation on the given class for the attribute.
  586. *
  587. * @param {string} attr The attribute to get the relation for.
  588. * @returns {Parse.Relation}
  589. */
  590. relation(attr /*: string*/) /*: ParseRelation*/{
  591. const value = this.get(attr);
  592. if (value) {
  593. if (!(value instanceof _ParseRelation.default)) {
  594. throw new Error('Called relation() on non-relation field ' + attr);
  595. }
  596. value._ensureParentAndKey(this, attr);
  597. return value;
  598. }
  599. return new _ParseRelation.default(this, attr);
  600. }
  601. /**
  602. * Gets the HTML-escaped value of an attribute.
  603. *
  604. * @param {string} attr The string name of an attribute.
  605. * @returns {string}
  606. */
  607. escape(attr /*: string*/) /*: string*/{
  608. let val = this.attributes[attr];
  609. if (val == null) {
  610. return '';
  611. }
  612. if (typeof val !== 'string') {
  613. if (typeof val.toString !== 'function') {
  614. return '';
  615. }
  616. val = val.toString();
  617. }
  618. return (0, _escape.default)(val);
  619. }
  620. /**
  621. * Returns <code>true</code> if the attribute contains a value that is not
  622. * null or undefined.
  623. *
  624. * @param {string} attr The string name of the attribute.
  625. * @returns {boolean}
  626. */
  627. has(attr /*: string*/) /*: boolean*/{
  628. const attributes = this.attributes;
  629. if (attributes.hasOwnProperty(attr)) {
  630. return attributes[attr] != null;
  631. }
  632. return false;
  633. }
  634. /**
  635. * Sets a hash of model attributes on the object.
  636. *
  637. * <p>You can call it with an object containing keys and values, with one
  638. * key and value, or dot notation. For example:<pre>
  639. * gameTurn.set({
  640. * player: player1,
  641. * diceRoll: 2
  642. * }, {
  643. * error: function(gameTurnAgain, error) {
  644. * // The set failed validation.
  645. * }
  646. * });
  647. *
  648. * game.set("currentPlayer", player2, {
  649. * error: function(gameTurnAgain, error) {
  650. * // The set failed validation.
  651. * }
  652. * });
  653. *
  654. * game.set("finished", true);</pre></p>
  655. *
  656. * game.set("player.score", 10);</pre></p>
  657. *
  658. * @param {(string|object)} key The key to set.
  659. * @param {(string|object)} value The value to give it.
  660. * @param {object} options A set of options for the set.
  661. * The only supported option is <code>error</code>.
  662. * @returns {(ParseObject|boolean)} true if the set succeeded.
  663. */
  664. set(key /*: mixed*/, value /*: mixed*/, options /*:: ?: mixed*/) /*: ParseObject | boolean*/{
  665. let changes = {};
  666. const newOps = {};
  667. if (key && typeof key === 'object') {
  668. changes = key;
  669. options = value;
  670. } else if (typeof key === 'string') {
  671. changes[key] = value;
  672. } else {
  673. return this;
  674. }
  675. options = options || {};
  676. let readonly = [];
  677. if (typeof this.constructor.readOnlyAttributes === 'function') {
  678. readonly = readonly.concat(this.constructor.readOnlyAttributes());
  679. }
  680. for (const k in changes) {
  681. if (k === 'createdAt' || k === 'updatedAt') {
  682. // This property is read-only, but for legacy reasons we silently
  683. // ignore it
  684. continue;
  685. }
  686. if (readonly.indexOf(k) > -1) {
  687. throw new Error('Cannot modify readonly attribute: ' + k);
  688. }
  689. if (options.unset) {
  690. newOps[k] = new _ParseOp.UnsetOp();
  691. } else if (changes[k] instanceof _ParseOp.Op) {
  692. newOps[k] = changes[k];
  693. } else if (changes[k] && typeof changes[k] === 'object' && typeof changes[k].__op === 'string') {
  694. newOps[k] = (0, _ParseOp.opFromJSON)(changes[k]);
  695. } else if (k === 'objectId' || k === 'id') {
  696. if (typeof changes[k] === 'string') {
  697. this.id = changes[k];
  698. }
  699. } else if (k === 'ACL' && typeof changes[k] === 'object' && !(changes[k] instanceof _ParseACL.default)) {
  700. newOps[k] = new _ParseOp.SetOp(new _ParseACL.default(changes[k]));
  701. } else if (changes[k] instanceof _ParseRelation.default) {
  702. const relation = new _ParseRelation.default(this, k);
  703. relation.targetClassName = changes[k].targetClassName;
  704. newOps[k] = new _ParseOp.SetOp(relation);
  705. } else {
  706. newOps[k] = new _ParseOp.SetOp(changes[k]);
  707. }
  708. }
  709. const currentAttributes = this.attributes;
  710. // Calculate new values
  711. const newValues = {};
  712. for (const attr in newOps) {
  713. if (newOps[attr] instanceof _ParseOp.RelationOp) {
  714. newValues[attr] = newOps[attr].applyTo(currentAttributes[attr], this, attr);
  715. } else if (!(newOps[attr] instanceof _ParseOp.UnsetOp)) {
  716. newValues[attr] = newOps[attr].applyTo(currentAttributes[attr]);
  717. }
  718. }
  719. // Validate changes
  720. if (!options.ignoreValidation) {
  721. const validation = this.validate(newValues);
  722. if (validation) {
  723. if (typeof options.error === 'function') {
  724. options.error(this, validation);
  725. }
  726. return false;
  727. }
  728. }
  729. // Consolidate Ops
  730. const pendingOps = this._getPendingOps();
  731. const last = pendingOps.length - 1;
  732. const stateController = _CoreManager.default.getObjectStateController();
  733. for (const attr in newOps) {
  734. const nextOp = newOps[attr].mergeWith(pendingOps[last][attr]);
  735. stateController.setPendingOp(this._getStateIdentifier(), attr, nextOp);
  736. }
  737. return this;
  738. }
  739. /**
  740. * Remove an attribute from the model. This is a noop if the attribute doesn't
  741. * exist.
  742. *
  743. * @param {string} attr The string name of an attribute.
  744. * @param options
  745. * @returns {(ParseObject | boolean)}
  746. */
  747. unset(attr /*: string*/, options /*:: ?: { [opt: string]: mixed }*/) /*: ParseObject | boolean*/{
  748. options = options || {};
  749. options.unset = true;
  750. return this.set(attr, null, options);
  751. }
  752. /**
  753. * Atomically increments the value of the given attribute the next time the
  754. * object is saved. If no amount is specified, 1 is used by default.
  755. *
  756. * @param attr {String} The key.
  757. * @param amount {Number} The amount to increment by (optional).
  758. * @returns {(ParseObject|boolean)}
  759. */
  760. increment(attr /*: string*/, amount /*:: ?: number*/) /*: ParseObject | boolean*/{
  761. if (typeof amount === 'undefined') {
  762. amount = 1;
  763. }
  764. if (typeof amount !== 'number') {
  765. throw new Error('Cannot increment by a non-numeric amount.');
  766. }
  767. return this.set(attr, new _ParseOp.IncrementOp(amount));
  768. }
  769. /**
  770. * Atomically decrements the value of the given attribute the next time the
  771. * object is saved. If no amount is specified, 1 is used by default.
  772. *
  773. * @param attr {String} The key.
  774. * @param amount {Number} The amount to decrement by (optional).
  775. * @returns {(ParseObject | boolean)}
  776. */
  777. decrement(attr /*: string*/, amount /*:: ?: number*/) /*: ParseObject | boolean*/{
  778. if (typeof amount === 'undefined') {
  779. amount = 1;
  780. }
  781. if (typeof amount !== 'number') {
  782. throw new Error('Cannot decrement by a non-numeric amount.');
  783. }
  784. return this.set(attr, new _ParseOp.IncrementOp(amount * -1));
  785. }
  786. /**
  787. * Atomically add an object to the end of the array associated with a given
  788. * key.
  789. *
  790. * @param attr {String} The key.
  791. * @param item {} The item to add.
  792. * @returns {(ParseObject | boolean)}
  793. */
  794. add(attr /*: string*/, item /*: mixed*/) /*: ParseObject | boolean*/{
  795. return this.set(attr, new _ParseOp.AddOp([item]));
  796. }
  797. /**
  798. * Atomically add the objects to the end of the array associated with a given
  799. * key.
  800. *
  801. * @param attr {String} The key.
  802. * @param items {Object[]} The items to add.
  803. * @returns {(ParseObject | boolean)}
  804. */
  805. addAll(attr /*: string*/, items /*: Array<mixed>*/) /*: ParseObject | boolean*/{
  806. return this.set(attr, new _ParseOp.AddOp(items));
  807. }
  808. /**
  809. * Atomically add an object to the array associated with a given key, only
  810. * if it is not already present in the array. The position of the insert is
  811. * not guaranteed.
  812. *
  813. * @param attr {String} The key.
  814. * @param item {} The object to add.
  815. * @returns {(ParseObject | boolean)}
  816. */
  817. addUnique(attr /*: string*/, item /*: mixed*/) /*: ParseObject | boolean*/{
  818. return this.set(attr, new _ParseOp.AddUniqueOp([item]));
  819. }
  820. /**
  821. * Atomically add the objects to the array associated with a given key, only
  822. * if it is not already present in the array. The position of the insert is
  823. * not guaranteed.
  824. *
  825. * @param attr {String} The key.
  826. * @param items {Object[]} The objects to add.
  827. * @returns {(ParseObject | boolean)}
  828. */
  829. addAllUnique(attr /*: string*/, items /*: Array<mixed>*/) /*: ParseObject | boolean*/{
  830. return this.set(attr, new _ParseOp.AddUniqueOp(items));
  831. }
  832. /**
  833. * Atomically remove all instances of an object from the array associated
  834. * with a given key.
  835. *
  836. * @param attr {String} The key.
  837. * @param item {} The object to remove.
  838. * @returns {(ParseObject | boolean)}
  839. */
  840. remove(attr /*: string*/, item /*: mixed*/) /*: ParseObject | boolean*/{
  841. return this.set(attr, new _ParseOp.RemoveOp([item]));
  842. }
  843. /**
  844. * Atomically remove all instances of the objects from the array associated
  845. * with a given key.
  846. *
  847. * @param attr {String} The key.
  848. * @param items {Object[]} The object to remove.
  849. * @returns {(ParseObject | boolean)}
  850. */
  851. removeAll(attr /*: string*/, items /*: Array<mixed>*/) /*: ParseObject | boolean*/{
  852. return this.set(attr, new _ParseOp.RemoveOp(items));
  853. }
  854. /**
  855. * Returns an instance of a subclass of Parse.Op describing what kind of
  856. * modification has been performed on this field since the last time it was
  857. * saved. For example, after calling object.increment("x"), calling
  858. * object.op("x") would return an instance of Parse.Op.Increment.
  859. *
  860. * @param attr {String} The key.
  861. * @returns {Parse.Op | undefined} The operation, or undefined if none.
  862. */
  863. op(attr /*: string*/) /*: ?Op*/{
  864. const pending = this._getPendingOps();
  865. for (let i = pending.length; i--;) {
  866. if (pending[i][attr]) {
  867. return pending[i][attr];
  868. }
  869. }
  870. }
  871. /**
  872. * Creates a new model with identical attributes to this one.
  873. *
  874. * @returns {Parse.Object}
  875. */
  876. clone() /*: any*/{
  877. const clone = new this.constructor(this.className);
  878. let attributes = this.attributes;
  879. if (typeof this.constructor.readOnlyAttributes === 'function') {
  880. const readonly = this.constructor.readOnlyAttributes() || [];
  881. // Attributes are frozen, so we have to rebuild an object,
  882. // rather than delete readonly keys
  883. const copy = {};
  884. for (const a in attributes) {
  885. if (readonly.indexOf(a) < 0) {
  886. copy[a] = attributes[a];
  887. }
  888. }
  889. attributes = copy;
  890. }
  891. if (clone.set) {
  892. clone.set(attributes);
  893. }
  894. return clone;
  895. }
  896. /**
  897. * Creates a new instance of this object. Not to be confused with clone()
  898. *
  899. * @returns {Parse.Object}
  900. */
  901. newInstance() /*: any*/{
  902. const clone = new this.constructor(this.className);
  903. clone.id = this.id;
  904. if (singleInstance) {
  905. // Just return an object with the right id
  906. return clone;
  907. }
  908. const stateController = _CoreManager.default.getObjectStateController();
  909. if (stateController) {
  910. stateController.duplicateState(this._getStateIdentifier(), clone._getStateIdentifier());
  911. }
  912. return clone;
  913. }
  914. /**
  915. * Returns true if this object has never been saved to Parse.
  916. *
  917. * @returns {boolean}
  918. */
  919. isNew() /*: boolean*/{
  920. return !this.id;
  921. }
  922. /**
  923. * Returns true if this object was created by the Parse server when the
  924. * object might have already been there (e.g. in the case of a Facebook
  925. * login)
  926. *
  927. * @returns {boolean}
  928. */
  929. existed() /*: boolean*/{
  930. if (!this.id) {
  931. return false;
  932. }
  933. const stateController = _CoreManager.default.getObjectStateController();
  934. const state = stateController.getState(this._getStateIdentifier());
  935. if (state) {
  936. return state.existed;
  937. }
  938. return false;
  939. }
  940. /**
  941. * Returns true if this object exists on the Server
  942. *
  943. * @param {object} options
  944. * Valid options are:<ul>
  945. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  946. * be used for this request.
  947. * <li>sessionToken: A valid session token, used for making a request on
  948. * behalf of a specific user.
  949. * </ul>
  950. * @returns {Promise<boolean>} A boolean promise that is fulfilled if object exists.
  951. */
  952. async exists(options /*:: ?: RequestOptions*/) /*: Promise<boolean>*/{
  953. if (!this.id) {
  954. return false;
  955. }
  956. try {
  957. const query = new _ParseQuery.default(this.className);
  958. await query.get(this.id, options);
  959. return true;
  960. } catch (e) {
  961. if (e.code === _ParseError.default.OBJECT_NOT_FOUND) {
  962. return false;
  963. }
  964. throw e;
  965. }
  966. }
  967. /**
  968. * Checks if the model is currently in a valid state.
  969. *
  970. * @returns {boolean}
  971. */
  972. isValid() /*: boolean*/{
  973. return !this.validate(this.attributes);
  974. }
  975. /**
  976. * You should not call this function directly unless you subclass
  977. * <code>Parse.Object</code>, in which case you can override this method
  978. * to provide additional validation on <code>set</code> and
  979. * <code>save</code>. Your implementation should return
  980. *
  981. * @param {object} attrs The current data to validate.
  982. * @returns {Parse.Error|boolean} False if the data is valid. An error object otherwise.
  983. * @see Parse.Object#set
  984. */
  985. validate(attrs /*: AttributeMap*/) /*: ParseError | boolean*/{
  986. if (attrs.hasOwnProperty('ACL') && !(attrs.ACL instanceof _ParseACL.default)) {
  987. return new _ParseError.default(_ParseError.default.OTHER_CAUSE, 'ACL must be a Parse ACL.');
  988. }
  989. for (const key in attrs) {
  990. if (!/^[A-Za-z][0-9A-Za-z_.]*$/.test(key)) {
  991. return new _ParseError.default(_ParseError.default.INVALID_KEY_NAME);
  992. }
  993. }
  994. return false;
  995. }
  996. /**
  997. * Returns the ACL for this object.
  998. *
  999. * @returns {Parse.ACL} An instance of Parse.ACL.
  1000. * @see Parse.Object#get
  1001. */
  1002. getACL() /*: ?ParseACL*/{
  1003. const acl = this.get('ACL');
  1004. if (acl instanceof _ParseACL.default) {
  1005. return acl;
  1006. }
  1007. return null;
  1008. }
  1009. /**
  1010. * Sets the ACL to be used for this object.
  1011. *
  1012. * @param {Parse.ACL} acl An instance of Parse.ACL.
  1013. * @param {object} options
  1014. * @returns {(ParseObject | boolean)} Whether the set passed validation.
  1015. * @see Parse.Object#set
  1016. */
  1017. setACL(acl /*: ParseACL*/, options /*:: ?: mixed*/) /*: ParseObject | boolean*/{
  1018. return this.set('ACL', acl, options);
  1019. }
  1020. /**
  1021. * Clears any (or specific) changes to this object made since the last call to save()
  1022. *
  1023. * @param {string} [keys] - specify which fields to revert
  1024. */
  1025. revert(...keys /*: Array<string>*/) /*: void*/{
  1026. let keysToRevert;
  1027. if (keys.length) {
  1028. keysToRevert = [];
  1029. for (const key of keys) {
  1030. if (typeof key === 'string') {
  1031. keysToRevert.push(key);
  1032. } else {
  1033. throw new Error('Parse.Object#revert expects either no, or a list of string, arguments.');
  1034. }
  1035. }
  1036. }
  1037. this._clearPendingOps(keysToRevert);
  1038. }
  1039. /**
  1040. * Clears all attributes on a model
  1041. *
  1042. * @returns {(ParseObject | boolean)}
  1043. */
  1044. clear() /*: ParseObject | boolean*/{
  1045. const attributes = this.attributes;
  1046. const erasable = {};
  1047. let readonly = ['createdAt', 'updatedAt'];
  1048. if (typeof this.constructor.readOnlyAttributes === 'function') {
  1049. readonly = readonly.concat(this.constructor.readOnlyAttributes());
  1050. }
  1051. for (const attr in attributes) {
  1052. if (readonly.indexOf(attr) < 0) {
  1053. erasable[attr] = true;
  1054. }
  1055. }
  1056. return this.set(erasable, {
  1057. unset: true
  1058. });
  1059. }
  1060. /**
  1061. * Fetch the model from the server. If the server's representation of the
  1062. * model differs from its current attributes, they will be overriden.
  1063. *
  1064. * @param {object} options
  1065. * Valid options are:<ul>
  1066. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1067. * be used for this request.
  1068. * <li>sessionToken: A valid session token, used for making a request on
  1069. * behalf of a specific user.
  1070. * <li>include: The name(s) of the key(s) to include. Can be a string, an array of strings,
  1071. * or an array of array of strings.
  1072. * <li>context: A dictionary that is accessible in Cloud Code `beforeFind` trigger.
  1073. * </ul>
  1074. * @returns {Promise} A promise that is fulfilled when the fetch
  1075. * completes.
  1076. */
  1077. fetch(options /*: RequestOptions*/) /*: Promise*/{
  1078. options = options || {};
  1079. const fetchOptions = {};
  1080. if (options.hasOwnProperty('useMasterKey')) {
  1081. fetchOptions.useMasterKey = options.useMasterKey;
  1082. }
  1083. if (options.hasOwnProperty('sessionToken')) {
  1084. fetchOptions.sessionToken = options.sessionToken;
  1085. }
  1086. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1087. fetchOptions.context = options.context;
  1088. }
  1089. if (options.hasOwnProperty('include')) {
  1090. fetchOptions.include = [];
  1091. if (Array.isArray(options.include)) {
  1092. options.include.forEach(key => {
  1093. if (Array.isArray(key)) {
  1094. fetchOptions.include = fetchOptions.include.concat(key);
  1095. } else {
  1096. fetchOptions.include.push(key);
  1097. }
  1098. });
  1099. } else {
  1100. fetchOptions.include.push(options.include);
  1101. }
  1102. }
  1103. const controller = _CoreManager.default.getObjectController();
  1104. return controller.fetch(this, true, fetchOptions);
  1105. }
  1106. /**
  1107. * Fetch the model from the server. If the server's representation of the
  1108. * model differs from its current attributes, they will be overriden.
  1109. *
  1110. * Includes nested Parse.Objects for the provided key. You can use dot
  1111. * notation to specify which fields in the included object are also fetched.
  1112. *
  1113. * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.
  1114. * @param {object} options
  1115. * Valid options are:<ul>
  1116. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1117. * be used for this request.
  1118. * <li>sessionToken: A valid session token, used for making a request on
  1119. * behalf of a specific user.
  1120. * </ul>
  1121. * @returns {Promise} A promise that is fulfilled when the fetch
  1122. * completes.
  1123. */
  1124. fetchWithInclude(keys /*: String | Array<string | Array<string>>*/, options /*: RequestOptions*/) /*: Promise*/{
  1125. options = options || {};
  1126. options.include = keys;
  1127. return this.fetch(options);
  1128. }
  1129. /**
  1130. * Saves this object to the server at some unspecified time in the future,
  1131. * even if Parse is currently inaccessible.
  1132. *
  1133. * Use this when you may not have a solid network connection, and don't need to know when the save completes.
  1134. * If there is some problem with the object such that it can't be saved, it will be silently discarded.
  1135. *
  1136. * Objects saved with this method will be stored locally in an on-disk cache until they can be delivered to Parse.
  1137. * They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection is
  1138. * available. Objects saved this way will persist even after the app is closed, in which case they will be sent the
  1139. * next time the app is opened.
  1140. *
  1141. * @param {object} [options]
  1142. * Used to pass option parameters to method if arg1 and arg2 were both passed as strings.
  1143. * Valid options are:
  1144. * <ul>
  1145. * <li>sessionToken: A valid session token, used for making a request on
  1146. * behalf of a specific user.
  1147. * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).
  1148. * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.
  1149. * </ul>
  1150. * @returns {Promise} A promise that is fulfilled when the save
  1151. * completes.
  1152. */
  1153. async saveEventually(options /*: SaveOptions*/) /*: Promise*/{
  1154. try {
  1155. await this.save(null, options);
  1156. } catch (e) {
  1157. if (e.message === 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {
  1158. await _EventuallyQueue.default.save(this, options);
  1159. _EventuallyQueue.default.poll();
  1160. }
  1161. }
  1162. return this;
  1163. }
  1164. /**
  1165. * Set a hash of model attributes, and save the model to the server.
  1166. * updatedAt will be updated when the request returns.
  1167. * You can either call it as:<pre>
  1168. * object.save();</pre>
  1169. * or<pre>
  1170. * object.save(attrs);</pre>
  1171. * or<pre>
  1172. * object.save(null, options);</pre>
  1173. * or<pre>
  1174. * object.save(attrs, options);</pre>
  1175. * or<pre>
  1176. * object.save(key, value);</pre>
  1177. * or<pre>
  1178. * object.save(key, value, options);</pre>
  1179. *
  1180. * Example 1: <pre>
  1181. * gameTurn.save({
  1182. * player: "Jake Cutter",
  1183. * diceRoll: 2
  1184. * }).then(function(gameTurnAgain) {
  1185. * // The save was successful.
  1186. * }, function(error) {
  1187. * // The save failed. Error is an instance of Parse.Error.
  1188. * });</pre>
  1189. *
  1190. * Example 2: <pre>
  1191. * gameTurn.save("player", "Jake Cutter");</pre>
  1192. *
  1193. * @param {string | object | null} [arg1]
  1194. * Valid options are:<ul>
  1195. * <li>`Object` - Key/value pairs to update on the object.</li>
  1196. * <li>`String` Key - Key of attribute to update (requires arg2 to also be string)</li>
  1197. * <li>`null` - Passing null for arg1 allows you to save the object with options passed in arg2.</li>
  1198. * </ul>
  1199. * @param {string | object} [arg2]
  1200. * <ul>
  1201. * <li>`String` Value - If arg1 was passed as a key, arg2 is the value that should be set on that key.</li>
  1202. * <li>`Object` Options - Valid options are:
  1203. * <ul>
  1204. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1205. * be used for this request.
  1206. * <li>sessionToken: A valid session token, used for making a request on
  1207. * behalf of a specific user.
  1208. * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).
  1209. * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.
  1210. * </ul>
  1211. * </li>
  1212. * </ul>
  1213. * @param {object} [arg3]
  1214. * Used to pass option parameters to method if arg1 and arg2 were both passed as strings.
  1215. * Valid options are:
  1216. * <ul>
  1217. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1218. * be used for this request.
  1219. * <li>sessionToken: A valid session token, used for making a request on
  1220. * behalf of a specific user.
  1221. * <li>cascadeSave: If `false`, nested objects will not be saved (default is `true`).
  1222. * <li>context: A dictionary that is accessible in Cloud Code `beforeSave` and `afterSave` triggers.
  1223. * </ul>
  1224. * @returns {Promise} A promise that is fulfilled when the save
  1225. * completes.
  1226. */
  1227. save(arg1 /*: ?string | { [attr: string]: mixed }*/, arg2 /*: SaveOptions | mixed*/, arg3 /*:: ?: SaveOptions*/) /*: Promise*/{
  1228. let attrs;
  1229. let options;
  1230. if (typeof arg1 === 'object' || typeof arg1 === 'undefined') {
  1231. attrs = arg1;
  1232. if (typeof arg2 === 'object') {
  1233. options = arg2;
  1234. }
  1235. } else {
  1236. attrs = {};
  1237. attrs[arg1] = arg2;
  1238. options = arg3;
  1239. }
  1240. if (attrs) {
  1241. const validation = this.validate(attrs);
  1242. if (validation) {
  1243. return Promise.reject(validation);
  1244. }
  1245. this.set(attrs, options);
  1246. }
  1247. options = options || {};
  1248. const saveOptions = {};
  1249. if (options.hasOwnProperty('useMasterKey')) {
  1250. saveOptions.useMasterKey = !!options.useMasterKey;
  1251. }
  1252. if (options.hasOwnProperty('sessionToken') && typeof options.sessionToken === 'string') {
  1253. saveOptions.sessionToken = options.sessionToken;
  1254. }
  1255. if (options.hasOwnProperty('installationId') && typeof options.installationId === 'string') {
  1256. saveOptions.installationId = options.installationId;
  1257. }
  1258. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1259. saveOptions.context = options.context;
  1260. }
  1261. const controller = _CoreManager.default.getObjectController();
  1262. const unsaved = options.cascadeSave !== false ? (0, _unsavedChildren.default)(this) : null;
  1263. return controller.save(unsaved, saveOptions).then(() => {
  1264. return controller.save(this, saveOptions);
  1265. });
  1266. }
  1267. /**
  1268. * Deletes this object from the server at some unspecified time in the future,
  1269. * even if Parse is currently inaccessible.
  1270. *
  1271. * Use this when you may not have a solid network connection,
  1272. * and don't need to know when the delete completes. If there is some problem with the object
  1273. * such that it can't be deleted, the request will be silently discarded.
  1274. *
  1275. * Delete instructions made with this method will be stored locally in an on-disk cache until they can be transmitted
  1276. * to Parse. They will be sent immediately if possible. Otherwise, they will be sent the next time a network connection
  1277. * is available. Delete requests will persist even after the app is closed, in which case they will be sent the
  1278. * next time the app is opened.
  1279. *
  1280. * @param {object} [options]
  1281. * Valid options are:<ul>
  1282. * <li>sessionToken: A valid session token, used for making a request on
  1283. * behalf of a specific user.
  1284. * <li>context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers.
  1285. * </ul>
  1286. * @returns {Promise} A promise that is fulfilled when the destroy
  1287. * completes.
  1288. */
  1289. async destroyEventually(options /*: RequestOptions*/) /*: Promise*/{
  1290. try {
  1291. await this.destroy(options);
  1292. } catch (e) {
  1293. if (e.message === 'XMLHttpRequest failed: "Unable to connect to the Parse API"') {
  1294. await _EventuallyQueue.default.destroy(this, options);
  1295. _EventuallyQueue.default.poll();
  1296. }
  1297. }
  1298. return this;
  1299. }
  1300. /**
  1301. * Destroy this model on the server if it was already persisted.
  1302. *
  1303. * @param {object} options
  1304. * Valid options are:<ul>
  1305. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1306. * be used for this request.
  1307. * <li>sessionToken: A valid session token, used for making a request on
  1308. * behalf of a specific user.
  1309. * <li>context: A dictionary that is accessible in Cloud Code `beforeDelete` and `afterDelete` triggers.
  1310. * </ul>
  1311. * @returns {Promise} A promise that is fulfilled when the destroy
  1312. * completes.
  1313. */
  1314. destroy(options /*: RequestOptions*/) /*: Promise*/{
  1315. options = options || {};
  1316. const destroyOptions = {};
  1317. if (options.hasOwnProperty('useMasterKey')) {
  1318. destroyOptions.useMasterKey = options.useMasterKey;
  1319. }
  1320. if (options.hasOwnProperty('sessionToken')) {
  1321. destroyOptions.sessionToken = options.sessionToken;
  1322. }
  1323. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1324. destroyOptions.context = options.context;
  1325. }
  1326. if (!this.id) {
  1327. return Promise.resolve();
  1328. }
  1329. return _CoreManager.default.getObjectController().destroy(this, destroyOptions);
  1330. }
  1331. /**
  1332. * Asynchronously stores the object and every object it points to in the local datastore,
  1333. * recursively, using a default pin name: _default.
  1334. *
  1335. * If those other objects have not been fetched from Parse, they will not be stored.
  1336. * However, if they have changed data, all the changes will be retained.
  1337. *
  1338. * <pre>
  1339. * await object.pin();
  1340. * </pre>
  1341. *
  1342. * To retrieve object:
  1343. * <code>query.fromLocalDatastore()</code> or <code>query.fromPin()</code>
  1344. *
  1345. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1346. */
  1347. pin() /*: Promise<void>*/{
  1348. return ParseObject.pinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, [this]);
  1349. }
  1350. /**
  1351. * Asynchronously removes the object and every object it points to in the local datastore,
  1352. * recursively, using a default pin name: _default.
  1353. *
  1354. * <pre>
  1355. * await object.unPin();
  1356. * </pre>
  1357. *
  1358. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  1359. */
  1360. unPin() /*: Promise<void>*/{
  1361. return ParseObject.unPinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, [this]);
  1362. }
  1363. /**
  1364. * Asynchronously returns if the object is pinned
  1365. *
  1366. * <pre>
  1367. * const isPinned = await object.isPinned();
  1368. * </pre>
  1369. *
  1370. * @returns {Promise<boolean>} A boolean promise that is fulfilled if object is pinned.
  1371. */
  1372. async isPinned() /*: Promise<boolean>*/{
  1373. const localDatastore = _CoreManager.default.getLocalDatastore();
  1374. if (!localDatastore.isEnabled) {
  1375. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  1376. }
  1377. const objectKey = localDatastore.getKeyForObject(this);
  1378. const pin = await localDatastore.fromPinWithName(objectKey);
  1379. return pin.length > 0;
  1380. }
  1381. /**
  1382. * Asynchronously stores the objects and every object they point to in the local datastore, recursively.
  1383. *
  1384. * If those other objects have not been fetched from Parse, they will not be stored.
  1385. * However, if they have changed data, all the changes will be retained.
  1386. *
  1387. * <pre>
  1388. * await object.pinWithName(name);
  1389. * </pre>
  1390. *
  1391. * To retrieve object:
  1392. * <code>query.fromLocalDatastore()</code> or <code>query.fromPinWithName(name)</code>
  1393. *
  1394. * @param {string} name Name of Pin.
  1395. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1396. */
  1397. pinWithName(name /*: string*/) /*: Promise<void>*/{
  1398. return ParseObject.pinAllWithName(name, [this]);
  1399. }
  1400. /**
  1401. * Asynchronously removes the object and every object it points to in the local datastore, recursively.
  1402. *
  1403. * <pre>
  1404. * await object.unPinWithName(name);
  1405. * </pre>
  1406. *
  1407. * @param {string} name Name of Pin.
  1408. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  1409. */
  1410. unPinWithName(name /*: string*/) /*: Promise<void>*/{
  1411. return ParseObject.unPinAllWithName(name, [this]);
  1412. }
  1413. /**
  1414. * Asynchronously loads data from the local datastore into this object.
  1415. *
  1416. * <pre>
  1417. * await object.fetchFromLocalDatastore();
  1418. * </pre>
  1419. *
  1420. * You can create an unfetched pointer with <code>Parse.Object.createWithoutData()</code>
  1421. * and then call <code>fetchFromLocalDatastore()</code> on it.
  1422. *
  1423. * @returns {Promise} A promise that is fulfilled when the fetch completes.
  1424. */
  1425. async fetchFromLocalDatastore() /*: Promise<ParseObject>*/{
  1426. const localDatastore = _CoreManager.default.getLocalDatastore();
  1427. if (!localDatastore.isEnabled) {
  1428. throw new Error('Parse.enableLocalDatastore() must be called first');
  1429. }
  1430. const objectKey = localDatastore.getKeyForObject(this);
  1431. const pinned = await localDatastore._serializeObject(objectKey);
  1432. if (!pinned) {
  1433. throw new Error('Cannot fetch an unsaved ParseObject');
  1434. }
  1435. const result = ParseObject.fromJSON(pinned);
  1436. this._finishFetch(result.toJSON());
  1437. return this;
  1438. }
  1439. /* Static methods */
  1440. static _clearAllState() {
  1441. const stateController = _CoreManager.default.getObjectStateController();
  1442. stateController.clearAllState();
  1443. }
  1444. /**
  1445. * Fetches the given list of Parse.Object.
  1446. * If any error is encountered, stops and calls the error handler.
  1447. *
  1448. * <pre>
  1449. * Parse.Object.fetchAll([object1, object2, ...])
  1450. * .then((list) => {
  1451. * // All the objects were fetched.
  1452. * }, (error) => {
  1453. * // An error occurred while fetching one of the objects.
  1454. * });
  1455. * </pre>
  1456. *
  1457. * @param {Array} list A list of <code>Parse.Object</code>.
  1458. * @param {object} options
  1459. * Valid options are:<ul>
  1460. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1461. * be used for this request.
  1462. * <li>sessionToken: A valid session token, used for making a request on
  1463. * behalf of a specific user.
  1464. * <li>include: The name(s) of the key(s) to include. Can be a string, an array of strings,
  1465. * or an array of array of strings.
  1466. * </ul>
  1467. * @static
  1468. * @returns {Parse.Object[]}
  1469. */
  1470. static fetchAll(list /*: Array<ParseObject>*/, options /*: RequestOptions*/ = {}) {
  1471. const queryOptions = {};
  1472. if (options.hasOwnProperty('useMasterKey')) {
  1473. queryOptions.useMasterKey = options.useMasterKey;
  1474. }
  1475. if (options.hasOwnProperty('sessionToken')) {
  1476. queryOptions.sessionToken = options.sessionToken;
  1477. }
  1478. if (options.hasOwnProperty('include')) {
  1479. queryOptions.include = ParseObject.handleIncludeOptions(options);
  1480. }
  1481. return _CoreManager.default.getObjectController().fetch(list, true, queryOptions);
  1482. }
  1483. /**
  1484. * Fetches the given list of Parse.Object.
  1485. *
  1486. * Includes nested Parse.Objects for the provided key. You can use dot
  1487. * notation to specify which fields in the included object are also fetched.
  1488. *
  1489. * If any error is encountered, stops and calls the error handler.
  1490. *
  1491. * <pre>
  1492. * Parse.Object.fetchAllWithInclude([object1, object2, ...], [pointer1, pointer2, ...])
  1493. * .then((list) => {
  1494. * // All the objects were fetched.
  1495. * }, (error) => {
  1496. * // An error occurred while fetching one of the objects.
  1497. * });
  1498. * </pre>
  1499. *
  1500. * @param {Array} list A list of <code>Parse.Object</code>.
  1501. * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.
  1502. * @param {object} options
  1503. * Valid options are:<ul>
  1504. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1505. * be used for this request.
  1506. * <li>sessionToken: A valid session token, used for making a request on
  1507. * behalf of a specific user.
  1508. * </ul>
  1509. * @static
  1510. * @returns {Parse.Object[]}
  1511. */
  1512. static fetchAllWithInclude(list /*: Array<ParseObject>*/, keys /*: String | Array<string | Array<string>>*/, options /*: RequestOptions*/) {
  1513. options = options || {};
  1514. options.include = keys;
  1515. return ParseObject.fetchAll(list, options);
  1516. }
  1517. /**
  1518. * Fetches the given list of Parse.Object if needed.
  1519. * If any error is encountered, stops and calls the error handler.
  1520. *
  1521. * Includes nested Parse.Objects for the provided key. You can use dot
  1522. * notation to specify which fields in the included object are also fetched.
  1523. *
  1524. * If any error is encountered, stops and calls the error handler.
  1525. *
  1526. * <pre>
  1527. * Parse.Object.fetchAllIfNeededWithInclude([object1, object2, ...], [pointer1, pointer2, ...])
  1528. * .then((list) => {
  1529. * // All the objects were fetched.
  1530. * }, (error) => {
  1531. * // An error occurred while fetching one of the objects.
  1532. * });
  1533. * </pre>
  1534. *
  1535. * @param {Array} list A list of <code>Parse.Object</code>.
  1536. * @param {string | Array<string | Array<string>>} keys The name(s) of the key(s) to include.
  1537. * @param {object} options
  1538. * Valid options are:<ul>
  1539. * <li>useMasterKey: In Cloud Code and Node only, causes the Master Key to
  1540. * be used for this request.
  1541. * <li>sessionToken: A valid session token, used for making a request on
  1542. * behalf of a specific user.
  1543. * </ul>
  1544. * @static
  1545. * @returns {Parse.Object[]}
  1546. */
  1547. static fetchAllIfNeededWithInclude(list /*: Array<ParseObject>*/, keys /*: String | Array<string | Array<string>>*/, options /*: RequestOptions*/) {
  1548. options = options || {};
  1549. options.include = keys;
  1550. return ParseObject.fetchAllIfNeeded(list, options);
  1551. }
  1552. /**
  1553. * Fetches the given list of Parse.Object if needed.
  1554. * If any error is encountered, stops and calls the error handler.
  1555. *
  1556. * <pre>
  1557. * Parse.Object.fetchAllIfNeeded([object1, ...])
  1558. * .then((list) => {
  1559. * // Objects were fetched and updated.
  1560. * }, (error) => {
  1561. * // An error occurred while fetching one of the objects.
  1562. * });
  1563. * </pre>
  1564. *
  1565. * @param {Array} list A list of <code>Parse.Object</code>.
  1566. * @param {object} options
  1567. * @static
  1568. * @returns {Parse.Object[]}
  1569. */
  1570. static fetchAllIfNeeded(list /*: Array<ParseObject>*/, options) {
  1571. options = options || {};
  1572. const queryOptions = {};
  1573. if (options.hasOwnProperty('useMasterKey')) {
  1574. queryOptions.useMasterKey = options.useMasterKey;
  1575. }
  1576. if (options.hasOwnProperty('sessionToken')) {
  1577. queryOptions.sessionToken = options.sessionToken;
  1578. }
  1579. if (options.hasOwnProperty('include')) {
  1580. queryOptions.include = ParseObject.handleIncludeOptions(options);
  1581. }
  1582. return _CoreManager.default.getObjectController().fetch(list, false, queryOptions);
  1583. }
  1584. static handleIncludeOptions(options) {
  1585. let include = [];
  1586. if (Array.isArray(options.include)) {
  1587. options.include.forEach(key => {
  1588. if (Array.isArray(key)) {
  1589. include = include.concat(key);
  1590. } else {
  1591. include.push(key);
  1592. }
  1593. });
  1594. } else {
  1595. include.push(options.include);
  1596. }
  1597. return include;
  1598. }
  1599. /**
  1600. * Destroy the given list of models on the server if it was already persisted.
  1601. *
  1602. * <p>Unlike saveAll, if an error occurs while deleting an individual model,
  1603. * this method will continue trying to delete the rest of the models if
  1604. * possible, except in the case of a fatal error like a connection error.
  1605. *
  1606. * <p>In particular, the Parse.Error object returned in the case of error may
  1607. * be one of two types:
  1608. *
  1609. * <ul>
  1610. * <li>A Parse.Error.AGGREGATE_ERROR. This object's "errors" property is an
  1611. * array of other Parse.Error objects. Each error object in this array
  1612. * has an "object" property that references the object that could not be
  1613. * deleted (for instance, because that object could not be found).</li>
  1614. * <li>A non-aggregate Parse.Error. This indicates a serious error that
  1615. * caused the delete operation to be aborted partway through (for
  1616. * instance, a connection failure in the middle of the delete).</li>
  1617. * </ul>
  1618. *
  1619. * <pre>
  1620. * Parse.Object.destroyAll([object1, object2, ...])
  1621. * .then((list) => {
  1622. * // All the objects were deleted.
  1623. * }, (error) => {
  1624. * // An error occurred while deleting one or more of the objects.
  1625. * // If this is an aggregate error, then we can inspect each error
  1626. * // object individually to determine the reason why a particular
  1627. * // object was not deleted.
  1628. * if (error.code === Parse.Error.AGGREGATE_ERROR) {
  1629. * for (var i = 0; i < error.errors.length; i++) {
  1630. * console.log("Couldn't delete " + error.errors[i].object.id +
  1631. * "due to " + error.errors[i].message);
  1632. * }
  1633. * } else {
  1634. * console.log("Delete aborted because of " + error.message);
  1635. * }
  1636. * });
  1637. * </pre>
  1638. *
  1639. * @param {Array} list A list of <code>Parse.Object</code>.
  1640. * @param {object} options
  1641. * @static
  1642. * @returns {Promise} A promise that is fulfilled when the destroyAll
  1643. * completes.
  1644. */
  1645. static destroyAll(list /*: Array<ParseObject>*/, options = {}) {
  1646. const destroyOptions = {};
  1647. if (options.hasOwnProperty('useMasterKey')) {
  1648. destroyOptions.useMasterKey = options.useMasterKey;
  1649. }
  1650. if (options.hasOwnProperty('sessionToken')) {
  1651. destroyOptions.sessionToken = options.sessionToken;
  1652. }
  1653. if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') {
  1654. destroyOptions.batchSize = options.batchSize;
  1655. }
  1656. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1657. destroyOptions.context = options.context;
  1658. }
  1659. return _CoreManager.default.getObjectController().destroy(list, destroyOptions);
  1660. }
  1661. /**
  1662. * Saves the given list of Parse.Object.
  1663. * If any error is encountered, stops and calls the error handler.
  1664. *
  1665. * <pre>
  1666. * Parse.Object.saveAll([object1, object2, ...])
  1667. * .then((list) => {
  1668. * // All the objects were saved.
  1669. * }, (error) => {
  1670. * // An error occurred while saving one of the objects.
  1671. * });
  1672. * </pre>
  1673. *
  1674. * @param {Array} list A list of <code>Parse.Object</code>.
  1675. * @param {object} options
  1676. * @static
  1677. * @returns {Parse.Object[]}
  1678. */
  1679. static saveAll(list /*: Array<ParseObject>*/, options /*: RequestOptions*/ = {}) {
  1680. const saveOptions = {};
  1681. if (options.hasOwnProperty('useMasterKey')) {
  1682. saveOptions.useMasterKey = options.useMasterKey;
  1683. }
  1684. if (options.hasOwnProperty('sessionToken')) {
  1685. saveOptions.sessionToken = options.sessionToken;
  1686. }
  1687. if (options.hasOwnProperty('batchSize') && typeof options.batchSize === 'number') {
  1688. saveOptions.batchSize = options.batchSize;
  1689. }
  1690. if (options.hasOwnProperty('context') && typeof options.context === 'object') {
  1691. saveOptions.context = options.context;
  1692. }
  1693. return _CoreManager.default.getObjectController().save(list, saveOptions);
  1694. }
  1695. /**
  1696. * Creates a reference to a subclass of Parse.Object with the given id. This
  1697. * does not exist on Parse.Object, only on subclasses.
  1698. *
  1699. * <p>A shortcut for: <pre>
  1700. * var Foo = Parse.Object.extend("Foo");
  1701. * var pointerToFoo = new Foo();
  1702. * pointerToFoo.id = "myObjectId";
  1703. * </pre>
  1704. *
  1705. * @param {string} id The ID of the object to create a reference to.
  1706. * @static
  1707. * @returns {Parse.Object} A Parse.Object reference.
  1708. */
  1709. static createWithoutData(id /*: string*/) {
  1710. const obj = new this();
  1711. obj.id = id;
  1712. return obj;
  1713. }
  1714. /**
  1715. * Creates a new instance of a Parse Object from a JSON representation.
  1716. *
  1717. * @param {object} json The JSON map of the Object's data
  1718. * @param {boolean} override In single instance mode, all old server data
  1719. * is overwritten if this is set to true
  1720. * @param {boolean} dirty Whether the Parse.Object should set JSON keys to dirty
  1721. * @static
  1722. * @returns {Parse.Object} A Parse.Object reference
  1723. */
  1724. static fromJSON(json /*: any*/, override /*:: ?: boolean*/, dirty /*:: ?: boolean*/) {
  1725. if (!json.className) {
  1726. throw new Error('Cannot create an object without a className');
  1727. }
  1728. const constructor = classMap[json.className];
  1729. const o = constructor ? new constructor(json.className) : new ParseObject(json.className);
  1730. const otherAttributes = {};
  1731. for (const attr in json) {
  1732. if (attr !== 'className' && attr !== '__type') {
  1733. otherAttributes[attr] = json[attr];
  1734. if (dirty) {
  1735. o.set(attr, json[attr]);
  1736. }
  1737. }
  1738. }
  1739. if (override) {
  1740. // id needs to be set before clearServerData can work
  1741. if (otherAttributes.objectId) {
  1742. o.id = otherAttributes.objectId;
  1743. }
  1744. let preserved = null;
  1745. if (typeof o._preserveFieldsOnFetch === 'function') {
  1746. preserved = o._preserveFieldsOnFetch();
  1747. }
  1748. o._clearServerData();
  1749. if (preserved) {
  1750. o._finishFetch(preserved);
  1751. }
  1752. }
  1753. o._finishFetch(otherAttributes);
  1754. if (json.objectId) {
  1755. o._setExisted(true);
  1756. }
  1757. return o;
  1758. }
  1759. /**
  1760. * Registers a subclass of Parse.Object with a specific class name.
  1761. * When objects of that class are retrieved from a query, they will be
  1762. * instantiated with this subclass.
  1763. * This is only necessary when using ES6 subclassing.
  1764. *
  1765. * @param {string} className The class name of the subclass
  1766. * @param {Function} constructor The subclass
  1767. */
  1768. static registerSubclass(className /*: string*/, constructor /*: any*/) {
  1769. if (typeof className !== 'string') {
  1770. throw new TypeError('The first argument must be a valid class name.');
  1771. }
  1772. if (typeof constructor === 'undefined') {
  1773. throw new TypeError('You must supply a subclass constructor.');
  1774. }
  1775. if (typeof constructor !== 'function') {
  1776. throw new TypeError('You must register the subclass constructor. ' + 'Did you attempt to register an instance of the subclass?');
  1777. }
  1778. classMap[className] = constructor;
  1779. if (!constructor.className) {
  1780. constructor.className = className;
  1781. }
  1782. }
  1783. /**
  1784. * Unegisters a subclass of Parse.Object with a specific class name.
  1785. *
  1786. * @param {string} className The class name of the subclass
  1787. */
  1788. static unregisterSubclass(className /*: string*/) {
  1789. if (typeof className !== 'string') {
  1790. throw new TypeError('The first argument must be a valid class name.');
  1791. }
  1792. delete classMap[className];
  1793. }
  1794. /**
  1795. * Creates a new subclass of Parse.Object for the given Parse class name.
  1796. *
  1797. * <p>Every extension of a Parse class will inherit from the most recent
  1798. * previous extension of that class. When a Parse.Object is automatically
  1799. * created by parsing JSON, it will use the most recent extension of that
  1800. * class.</p>
  1801. *
  1802. * <p>You should call either:<pre>
  1803. * var MyClass = Parse.Object.extend("MyClass", {
  1804. * <i>Instance methods</i>,
  1805. * initialize: function(attrs, options) {
  1806. * this.someInstanceProperty = [],
  1807. * <i>Other instance properties</i>
  1808. * }
  1809. * }, {
  1810. * <i>Class properties</i>
  1811. * });</pre>
  1812. * or, for Backbone compatibility:<pre>
  1813. * var MyClass = Parse.Object.extend({
  1814. * className: "MyClass",
  1815. * <i>Instance methods</i>,
  1816. * initialize: function(attrs, options) {
  1817. * this.someInstanceProperty = [],
  1818. * <i>Other instance properties</i>
  1819. * }
  1820. * }, {
  1821. * <i>Class properties</i>
  1822. * });</pre></p>
  1823. *
  1824. * @param {string} className The name of the Parse class backing this model.
  1825. * @param {object} protoProps Instance properties to add to instances of the
  1826. * class returned from this method.
  1827. * @param {object} classProps Class properties to add the class returned from
  1828. * this method.
  1829. * @returns {Parse.Object} A new subclass of Parse.Object.
  1830. */
  1831. static extend(className /*: any*/, protoProps /*: any*/, classProps /*: any*/) {
  1832. if (typeof className !== 'string') {
  1833. if (className && typeof className.className === 'string') {
  1834. return ParseObject.extend(className.className, className, protoProps);
  1835. } else {
  1836. throw new Error("Parse.Object.extend's first argument should be the className.");
  1837. }
  1838. }
  1839. let adjustedClassName = className;
  1840. if (adjustedClassName === 'User' && _CoreManager.default.get('PERFORM_USER_REWRITE')) {
  1841. adjustedClassName = '_User';
  1842. }
  1843. let parentProto = ParseObject.prototype;
  1844. if (this.hasOwnProperty('__super__') && this.__super__) {
  1845. parentProto = this.prototype;
  1846. }
  1847. let ParseObjectSubclass = function (attributes, options) {
  1848. this.className = adjustedClassName;
  1849. this._objCount = objectCount++;
  1850. // Enable legacy initializers
  1851. if (typeof this.initialize === 'function') {
  1852. this.initialize.apply(this, arguments);
  1853. }
  1854. if (this._initializers) {
  1855. for (const initializer of this._initializers) {
  1856. initializer.apply(this, arguments);
  1857. }
  1858. }
  1859. if (attributes && typeof attributes === 'object') {
  1860. if (!this.set(attributes || {}, options)) {
  1861. throw new Error("Can't create an invalid Parse Object");
  1862. }
  1863. }
  1864. };
  1865. if (classMap[adjustedClassName]) {
  1866. ParseObjectSubclass = classMap[adjustedClassName];
  1867. } else {
  1868. ParseObjectSubclass.extend = function (name, protoProps, classProps) {
  1869. if (typeof name === 'string') {
  1870. return ParseObject.extend.call(ParseObjectSubclass, name, protoProps, classProps);
  1871. }
  1872. return ParseObject.extend.call(ParseObjectSubclass, adjustedClassName, name, protoProps);
  1873. };
  1874. ParseObjectSubclass.createWithoutData = ParseObject.createWithoutData;
  1875. ParseObjectSubclass.className = adjustedClassName;
  1876. ParseObjectSubclass.__super__ = parentProto;
  1877. ParseObjectSubclass.prototype = Object.create(parentProto, {
  1878. constructor: {
  1879. value: ParseObjectSubclass,
  1880. enumerable: false,
  1881. writable: true,
  1882. configurable: true
  1883. }
  1884. });
  1885. }
  1886. if (protoProps) {
  1887. for (const prop in protoProps) {
  1888. if (prop === 'initialize') {
  1889. Object.defineProperty(ParseObjectSubclass.prototype, '_initializers', {
  1890. value: [...(ParseObjectSubclass.prototype._initializers || []), protoProps[prop]],
  1891. enumerable: false,
  1892. writable: true,
  1893. configurable: true
  1894. });
  1895. continue;
  1896. }
  1897. if (prop !== 'className') {
  1898. Object.defineProperty(ParseObjectSubclass.prototype, prop, {
  1899. value: protoProps[prop],
  1900. enumerable: false,
  1901. writable: true,
  1902. configurable: true
  1903. });
  1904. }
  1905. }
  1906. }
  1907. if (classProps) {
  1908. for (const prop in classProps) {
  1909. if (prop !== 'className') {
  1910. Object.defineProperty(ParseObjectSubclass, prop, {
  1911. value: classProps[prop],
  1912. enumerable: false,
  1913. writable: true,
  1914. configurable: true
  1915. });
  1916. }
  1917. }
  1918. }
  1919. classMap[adjustedClassName] = ParseObjectSubclass;
  1920. return ParseObjectSubclass;
  1921. }
  1922. /**
  1923. * Enable single instance objects, where any local objects with the same Id
  1924. * share the same attributes, and stay synchronized with each other.
  1925. * This is disabled by default in server environments, since it can lead to
  1926. * security issues.
  1927. *
  1928. * @static
  1929. */
  1930. static enableSingleInstance() {
  1931. singleInstance = true;
  1932. _CoreManager.default.setObjectStateController(SingleInstanceStateController);
  1933. }
  1934. /**
  1935. * Disable single instance objects, where any local objects with the same Id
  1936. * share the same attributes, and stay synchronized with each other.
  1937. * When disabled, you can have two instances of the same object in memory
  1938. * without them sharing attributes.
  1939. *
  1940. * @static
  1941. */
  1942. static disableSingleInstance() {
  1943. singleInstance = false;
  1944. _CoreManager.default.setObjectStateController(UniqueInstanceStateController);
  1945. }
  1946. /**
  1947. * Asynchronously stores the objects and every object they point to in the local datastore,
  1948. * recursively, using a default pin name: _default.
  1949. *
  1950. * If those other objects have not been fetched from Parse, they will not be stored.
  1951. * However, if they have changed data, all the changes will be retained.
  1952. *
  1953. * <pre>
  1954. * await Parse.Object.pinAll([...]);
  1955. * </pre>
  1956. *
  1957. * To retrieve object:
  1958. * <code>query.fromLocalDatastore()</code> or <code>query.fromPin()</code>
  1959. *
  1960. * @param {Array} objects A list of <code>Parse.Object</code>.
  1961. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1962. * @static
  1963. */
  1964. static pinAll(objects /*: Array<ParseObject>*/) /*: Promise<void>*/{
  1965. const localDatastore = _CoreManager.default.getLocalDatastore();
  1966. if (!localDatastore.isEnabled) {
  1967. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  1968. }
  1969. return ParseObject.pinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, objects);
  1970. }
  1971. /**
  1972. * Asynchronously stores the objects and every object they point to in the local datastore, recursively.
  1973. *
  1974. * If those other objects have not been fetched from Parse, they will not be stored.
  1975. * However, if they have changed data, all the changes will be retained.
  1976. *
  1977. * <pre>
  1978. * await Parse.Object.pinAllWithName(name, [obj1, obj2, ...]);
  1979. * </pre>
  1980. *
  1981. * To retrieve object:
  1982. * <code>query.fromLocalDatastore()</code> or <code>query.fromPinWithName(name)</code>
  1983. *
  1984. * @param {string} name Name of Pin.
  1985. * @param {Array} objects A list of <code>Parse.Object</code>.
  1986. * @returns {Promise} A promise that is fulfilled when the pin completes.
  1987. * @static
  1988. */
  1989. static pinAllWithName(name /*: string*/, objects /*: Array<ParseObject>*/) /*: Promise<void>*/{
  1990. const localDatastore = _CoreManager.default.getLocalDatastore();
  1991. if (!localDatastore.isEnabled) {
  1992. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  1993. }
  1994. return localDatastore._handlePinAllWithName(name, objects);
  1995. }
  1996. /**
  1997. * Asynchronously removes the objects and every object they point to in the local datastore,
  1998. * recursively, using a default pin name: _default.
  1999. *
  2000. * <pre>
  2001. * await Parse.Object.unPinAll([...]);
  2002. * </pre>
  2003. *
  2004. * @param {Array} objects A list of <code>Parse.Object</code>.
  2005. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2006. * @static
  2007. */
  2008. static unPinAll(objects /*: Array<ParseObject>*/) /*: Promise<void>*/{
  2009. const localDatastore = _CoreManager.default.getLocalDatastore();
  2010. if (!localDatastore.isEnabled) {
  2011. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  2012. }
  2013. return ParseObject.unPinAllWithName(_LocalDatastoreUtils.DEFAULT_PIN, objects);
  2014. }
  2015. /**
  2016. * Asynchronously removes the objects and every object they point to in the local datastore, recursively.
  2017. *
  2018. * <pre>
  2019. * await Parse.Object.unPinAllWithName(name, [obj1, obj2, ...]);
  2020. * </pre>
  2021. *
  2022. * @param {string} name Name of Pin.
  2023. * @param {Array} objects A list of <code>Parse.Object</code>.
  2024. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2025. * @static
  2026. */
  2027. static unPinAllWithName(name /*: string*/, objects /*: Array<ParseObject>*/) /*: Promise<void>*/{
  2028. const localDatastore = _CoreManager.default.getLocalDatastore();
  2029. if (!localDatastore.isEnabled) {
  2030. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  2031. }
  2032. return localDatastore._handleUnPinAllWithName(name, objects);
  2033. }
  2034. /**
  2035. * Asynchronously removes all objects in the local datastore using a default pin name: _default.
  2036. *
  2037. * <pre>
  2038. * await Parse.Object.unPinAllObjects();
  2039. * </pre>
  2040. *
  2041. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2042. * @static
  2043. */
  2044. static unPinAllObjects() /*: Promise<void>*/{
  2045. const localDatastore = _CoreManager.default.getLocalDatastore();
  2046. if (!localDatastore.isEnabled) {
  2047. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  2048. }
  2049. return localDatastore.unPinWithName(_LocalDatastoreUtils.DEFAULT_PIN);
  2050. }
  2051. /**
  2052. * Asynchronously removes all objects with the specified pin name.
  2053. * Deletes the pin name also.
  2054. *
  2055. * <pre>
  2056. * await Parse.Object.unPinAllObjectsWithName(name);
  2057. * </pre>
  2058. *
  2059. * @param {string} name Name of Pin.
  2060. * @returns {Promise} A promise that is fulfilled when the unPin completes.
  2061. * @static
  2062. */
  2063. static unPinAllObjectsWithName(name /*: string*/) /*: Promise<void>*/{
  2064. const localDatastore = _CoreManager.default.getLocalDatastore();
  2065. if (!localDatastore.isEnabled) {
  2066. return Promise.reject('Parse.enableLocalDatastore() must be called first');
  2067. }
  2068. return localDatastore.unPinWithName(_LocalDatastoreUtils.PIN_PREFIX + name);
  2069. }
  2070. }
  2071. const DefaultController = {
  2072. fetch(target /*: ParseObject | Array<ParseObject>*/, forceFetch /*: boolean*/, options /*: RequestOptions*/) /*: Promise<Array<void> | ParseObject>*/{
  2073. const localDatastore = _CoreManager.default.getLocalDatastore();
  2074. if (Array.isArray(target)) {
  2075. if (target.length < 1) {
  2076. return Promise.resolve([]);
  2077. }
  2078. const objs = [];
  2079. const ids = [];
  2080. let className = null;
  2081. const results = [];
  2082. let error = null;
  2083. target.forEach(el => {
  2084. if (error) {
  2085. return;
  2086. }
  2087. if (!className) {
  2088. className = el.className;
  2089. }
  2090. if (className !== el.className) {
  2091. error = new _ParseError.default(_ParseError.default.INVALID_CLASS_NAME, 'All objects should be of the same class');
  2092. }
  2093. if (!el.id) {
  2094. error = new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'All objects must have an ID');
  2095. }
  2096. if (forceFetch || !el.isDataAvailable()) {
  2097. ids.push(el.id);
  2098. objs.push(el);
  2099. }
  2100. results.push(el);
  2101. });
  2102. if (error) {
  2103. return Promise.reject(error);
  2104. }
  2105. const query = new _ParseQuery.default(className);
  2106. query.containedIn('objectId', ids);
  2107. if (options && options.include) {
  2108. query.include(options.include);
  2109. }
  2110. query._limit = ids.length;
  2111. return query.find(options).then(async objects => {
  2112. const idMap = {};
  2113. objects.forEach(o => {
  2114. idMap[o.id] = o;
  2115. });
  2116. for (let i = 0; i < objs.length; i++) {
  2117. const obj = objs[i];
  2118. if (!obj || !obj.id || !idMap[obj.id]) {
  2119. if (forceFetch) {
  2120. return Promise.reject(new _ParseError.default(_ParseError.default.OBJECT_NOT_FOUND, 'All objects must exist on the server.'));
  2121. }
  2122. }
  2123. }
  2124. if (!singleInstance) {
  2125. // If single instance objects are disabled, we need to replace the
  2126. for (let i = 0; i < results.length; i++) {
  2127. const obj = results[i];
  2128. if (obj && obj.id && idMap[obj.id]) {
  2129. const id = obj.id;
  2130. obj._finishFetch(idMap[id].toJSON());
  2131. results[i] = idMap[id];
  2132. }
  2133. }
  2134. }
  2135. for (const object of results) {
  2136. await localDatastore._updateObjectIfPinned(object);
  2137. }
  2138. return Promise.resolve(results);
  2139. });
  2140. } else if (target instanceof ParseObject) {
  2141. if (!target.id) {
  2142. return Promise.reject(new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'Object does not have an ID'));
  2143. }
  2144. const RESTController = _CoreManager.default.getRESTController();
  2145. const params = {};
  2146. if (options && options.include) {
  2147. params.include = options.include.join();
  2148. }
  2149. return RESTController.request('GET', 'classes/' + target.className + '/' + target._getId(), params, options).then(async response => {
  2150. target._clearPendingOps();
  2151. target._clearServerData();
  2152. target._finishFetch(response);
  2153. await localDatastore._updateObjectIfPinned(target);
  2154. return target;
  2155. });
  2156. }
  2157. return Promise.resolve();
  2158. },
  2159. async destroy(target /*: ParseObject | Array<ParseObject>*/, options /*: RequestOptions*/) /*: Promise<Array<void> | ParseObject>*/{
  2160. const batchSize = options && options.batchSize ? options.batchSize : _CoreManager.default.get('REQUEST_BATCH_SIZE');
  2161. const localDatastore = _CoreManager.default.getLocalDatastore();
  2162. const RESTController = _CoreManager.default.getRESTController();
  2163. if (Array.isArray(target)) {
  2164. if (target.length < 1) {
  2165. return Promise.resolve([]);
  2166. }
  2167. const batches = [[]];
  2168. target.forEach(obj => {
  2169. if (!obj.id) {
  2170. return;
  2171. }
  2172. batches[batches.length - 1].push(obj);
  2173. if (batches[batches.length - 1].length >= batchSize) {
  2174. batches.push([]);
  2175. }
  2176. });
  2177. if (batches[batches.length - 1].length === 0) {
  2178. // If the last batch is empty, remove it
  2179. batches.pop();
  2180. }
  2181. let deleteCompleted = Promise.resolve();
  2182. const errors = [];
  2183. batches.forEach(batch => {
  2184. deleteCompleted = deleteCompleted.then(() => {
  2185. return RESTController.request('POST', 'batch', {
  2186. requests: batch.map(obj => {
  2187. return {
  2188. method: 'DELETE',
  2189. path: getServerUrlPath() + 'classes/' + obj.className + '/' + obj._getId(),
  2190. body: {}
  2191. };
  2192. })
  2193. }, options).then(results => {
  2194. for (let i = 0; i < results.length; i++) {
  2195. if (results[i] && results[i].hasOwnProperty('error')) {
  2196. const err = new _ParseError.default(results[i].error.code, results[i].error.error);
  2197. err.object = batch[i];
  2198. errors.push(err);
  2199. }
  2200. }
  2201. });
  2202. });
  2203. });
  2204. return deleteCompleted.then(async () => {
  2205. if (errors.length) {
  2206. const aggregate = new _ParseError.default(_ParseError.default.AGGREGATE_ERROR);
  2207. aggregate.errors = errors;
  2208. return Promise.reject(aggregate);
  2209. }
  2210. for (const object of target) {
  2211. await localDatastore._destroyObjectIfPinned(object);
  2212. }
  2213. return Promise.resolve(target);
  2214. });
  2215. } else if (target instanceof ParseObject) {
  2216. return RESTController.request('DELETE', 'classes/' + target.className + '/' + target._getId(), {}, options).then(async () => {
  2217. await localDatastore._destroyObjectIfPinned(target);
  2218. return Promise.resolve(target);
  2219. });
  2220. }
  2221. return Promise.resolve(target);
  2222. },
  2223. save(target /*: ParseObject | Array<ParseObject | ParseFile>*/, options /*: RequestOptions*/) {
  2224. const batchSize = options && options.batchSize ? options.batchSize : _CoreManager.default.get('REQUEST_BATCH_SIZE');
  2225. const localDatastore = _CoreManager.default.getLocalDatastore();
  2226. const mapIdForPin = {};
  2227. const RESTController = _CoreManager.default.getRESTController();
  2228. const stateController = _CoreManager.default.getObjectStateController();
  2229. const allowCustomObjectId = _CoreManager.default.get('ALLOW_CUSTOM_OBJECT_ID');
  2230. options = options || {};
  2231. options.returnStatus = options.returnStatus || true;
  2232. if (Array.isArray(target)) {
  2233. if (target.length < 1) {
  2234. return Promise.resolve([]);
  2235. }
  2236. let unsaved = target.concat();
  2237. for (let i = 0; i < target.length; i++) {
  2238. if (target[i] instanceof ParseObject) {
  2239. unsaved = unsaved.concat((0, _unsavedChildren.default)(target[i], true));
  2240. }
  2241. }
  2242. unsaved = (0, _unique.default)(unsaved);
  2243. const filesSaved /*: Array<ParseFile>*/ = [];
  2244. let pending /*: Array<ParseObject>*/ = [];
  2245. unsaved.forEach(el => {
  2246. if (el instanceof _ParseFile.default) {
  2247. filesSaved.push(el.save(options));
  2248. } else if (el instanceof ParseObject) {
  2249. pending.push(el);
  2250. }
  2251. });
  2252. return Promise.all(filesSaved).then(() => {
  2253. let objectError = null;
  2254. return (0, _promiseUtils.continueWhile)(() => {
  2255. return pending.length > 0;
  2256. }, () => {
  2257. const batch = [];
  2258. const nextPending = [];
  2259. pending.forEach(el => {
  2260. if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(el, 'id') && !el.id) {
  2261. throw new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'objectId must not be empty or null');
  2262. }
  2263. if (batch.length < batchSize && (0, _canBeSerialized.default)(el)) {
  2264. batch.push(el);
  2265. } else {
  2266. nextPending.push(el);
  2267. }
  2268. });
  2269. pending = nextPending;
  2270. if (batch.length < 1) {
  2271. return Promise.reject(new _ParseError.default(_ParseError.default.OTHER_CAUSE, 'Tried to save a batch with a cycle.'));
  2272. }
  2273. // Queue up tasks for each object in the batch.
  2274. // When every task is ready, the API request will execute
  2275. const batchReturned = new _promiseUtils.resolvingPromise();
  2276. const batchReady = [];
  2277. const batchTasks = [];
  2278. batch.forEach((obj, index) => {
  2279. const ready = new _promiseUtils.resolvingPromise();
  2280. batchReady.push(ready);
  2281. stateController.pushPendingState(obj._getStateIdentifier());
  2282. batchTasks.push(stateController.enqueueTask(obj._getStateIdentifier(), function () {
  2283. ready.resolve();
  2284. return batchReturned.then(responses => {
  2285. if (responses[index].hasOwnProperty('success')) {
  2286. const objectId = responses[index].success.objectId;
  2287. const status = responses[index]._status;
  2288. delete responses[index]._status;
  2289. mapIdForPin[objectId] = obj._localId;
  2290. obj._handleSaveResponse(responses[index].success, status);
  2291. } else {
  2292. if (!objectError && responses[index].hasOwnProperty('error')) {
  2293. const serverError = responses[index].error;
  2294. objectError = new _ParseError.default(serverError.code, serverError.error);
  2295. // Cancel the rest of the save
  2296. pending = [];
  2297. }
  2298. obj._handleSaveError();
  2299. }
  2300. });
  2301. }));
  2302. });
  2303. (0, _promiseUtils.when)(batchReady).then(() => {
  2304. // Kick off the batch request
  2305. return RESTController.request('POST', 'batch', {
  2306. requests: batch.map(obj => {
  2307. const params = obj._getSaveParams();
  2308. params.path = getServerUrlPath() + params.path;
  2309. return params;
  2310. })
  2311. }, options);
  2312. }).then(batchReturned.resolve, error => {
  2313. batchReturned.reject(new _ParseError.default(_ParseError.default.INCORRECT_TYPE, error.message));
  2314. });
  2315. return (0, _promiseUtils.when)(batchTasks);
  2316. }).then(async () => {
  2317. if (objectError) {
  2318. return Promise.reject(objectError);
  2319. }
  2320. for (const object of target) {
  2321. // Make sure that it is a ParseObject before updating it into the localDataStore
  2322. if (object instanceof ParseObject) {
  2323. await localDatastore._updateLocalIdForObject(mapIdForPin[object.id], object);
  2324. await localDatastore._updateObjectIfPinned(object);
  2325. }
  2326. }
  2327. return Promise.resolve(target);
  2328. });
  2329. });
  2330. } else if (target instanceof ParseObject) {
  2331. if (allowCustomObjectId && Object.prototype.hasOwnProperty.call(target, 'id') && !target.id) {
  2332. throw new _ParseError.default(_ParseError.default.MISSING_OBJECT_ID, 'objectId must not be empty or null');
  2333. }
  2334. // generate _localId in case if cascadeSave=false
  2335. target._getId();
  2336. const localId = target._localId;
  2337. // copying target lets Flow guarantee the pointer isn't modified elsewhere
  2338. const targetCopy = target;
  2339. const task = function () {
  2340. const params = targetCopy._getSaveParams();
  2341. return RESTController.request(params.method, params.path, params.body, options).then(response => {
  2342. const status = response._status;
  2343. delete response._status;
  2344. targetCopy._handleSaveResponse(response, status);
  2345. }, error => {
  2346. targetCopy._handleSaveError();
  2347. return Promise.reject(error);
  2348. });
  2349. };
  2350. stateController.pushPendingState(target._getStateIdentifier());
  2351. return stateController.enqueueTask(target._getStateIdentifier(), task).then(async () => {
  2352. await localDatastore._updateLocalIdForObject(localId, target);
  2353. await localDatastore._updateObjectIfPinned(target);
  2354. return target;
  2355. }, error => {
  2356. return Promise.reject(error);
  2357. });
  2358. }
  2359. return Promise.resolve();
  2360. }
  2361. };
  2362. _CoreManager.default.setObjectController(DefaultController);
  2363. var _default = ParseObject;
  2364. exports.default = _default;