ParseObject.js 81 KB

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