unsavedChildren.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /**
  2. * Copyright (c) 2015-present, Parse, LLC.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. * @flow
  10. */
  11. import ParseFile from './ParseFile';
  12. import ParseObject from './ParseObject';
  13. import ParseRelation from './ParseRelation';
  14. /*:: type EncounterMap = {
  15. objects: { [identifier: string]: ParseObject | boolean; };
  16. files: Array<ParseFile>;
  17. };*/
  18. /**
  19. * Return an array of unsaved children, which are either Parse Objects or Files.
  20. * If it encounters any dirty Objects without Ids, it will throw an exception.
  21. */
  22. export default function unsavedChildren(obj
  23. /*: ParseObject*/
  24. , allowDeepUnsaved
  25. /*:: ?: boolean*/
  26. )
  27. /*: Array<ParseFile | ParseObject>*/
  28. {
  29. const encountered = {
  30. objects: {},
  31. files: []
  32. };
  33. const identifier = obj.className + ':' + obj._getId();
  34. encountered.objects[identifier] = obj.dirty() ? obj : true;
  35. const attributes = obj.attributes;
  36. for (const attr in attributes) {
  37. if (typeof attributes[attr] === 'object') {
  38. traverse(attributes[attr], encountered, false, !!allowDeepUnsaved);
  39. }
  40. }
  41. const unsaved = [];
  42. for (const id in encountered.objects) {
  43. if (id !== identifier && encountered.objects[id] !== true) {
  44. unsaved.push(encountered.objects[id]);
  45. }
  46. }
  47. return unsaved.concat(encountered.files);
  48. }
  49. function traverse(obj
  50. /*: ParseObject*/
  51. , encountered
  52. /*: EncounterMap*/
  53. , shouldThrow
  54. /*: boolean*/
  55. , allowDeepUnsaved
  56. /*: boolean*/
  57. ) {
  58. if (obj instanceof ParseObject) {
  59. if (!obj.id && shouldThrow) {
  60. throw new Error('Cannot create a pointer to an unsaved Object.');
  61. }
  62. const identifier = obj.className + ':' + obj._getId();
  63. if (!encountered.objects[identifier]) {
  64. encountered.objects[identifier] = obj.dirty() ? obj : true;
  65. const attributes = obj.attributes;
  66. for (const attr in attributes) {
  67. if (typeof attributes[attr] === 'object') {
  68. traverse(attributes[attr], encountered, !allowDeepUnsaved, allowDeepUnsaved);
  69. }
  70. }
  71. }
  72. return;
  73. }
  74. if (obj instanceof ParseFile) {
  75. if (!obj.url() && encountered.files.indexOf(obj) < 0) {
  76. encountered.files.push(obj);
  77. }
  78. return;
  79. }
  80. if (obj instanceof ParseRelation) {
  81. return;
  82. }
  83. if (Array.isArray(obj)) {
  84. obj.forEach(el => {
  85. if (typeof el === 'object') {
  86. traverse(el, encountered, shouldThrow, allowDeepUnsaved);
  87. }
  88. });
  89. }
  90. for (const k in obj) {
  91. if (typeof obj[k] === 'object') {
  92. traverse(obj[k], encountered, shouldThrow, allowDeepUnsaved);
  93. }
  94. }
  95. }