1. 1 : /**
  2. 2 : * A class for simplying resource storage
  3. 3 : * @memberof Spyral.Util
  4. 4 : * @class
  5. 5 : */
  6. 6 : class Storage {
  7. 7 :
  8. 8 : constructor(config) {
  9. 9 : this.MAX_LENGTH = 950000; // keep it under 1 megabyte
  10. 10 : }
  11. 11 :
  12. 12 : /**
  13. 13 : * Store a resource
  14. 14 : * @name Spyral.Util.Storage.storeResource
  15. 15 : * @function
  16. 16 : * @static
  17. 17 : * @param {String} id
  18. 18 : * @param {*} data
  19. 19 : * @returns {Promise}
  20. 20 : */
  21. 21 : static storeResource(id, data) {
  22. 22 : var dataString = JSON.stringify(data);
  23. 23 :
  24. 24 : if (dataString.length > this.MAX_LENGTH) {
  25. 25 : // split into chunks
  26. 26 : var promise = new Promise(function(resolve, reject) {
  27. 27 : var numChunks = Math.ceil(dataString.length / this.MAX_LENGTH);
  28. 28 :
  29. 29 : var chunkIds = [];
  30. 30 : for (var i = 0; i < numChunks; i++) {
  31. 31 : chunkIds.push(id+'-chunk'+i);
  32. 32 : }
  33. 33 : this._doStore(id+'-hasChunks', JSON.stringify(chunkIds)).then(function() {
  34. 34 : var chunkCount = 0;
  35. 35 : var currIndex = 0;
  36. 36 : for (var i = 0; i < numChunks; i++) {
  37. 37 : var chunkString = dataString.substr(currIndex, this.MAX_LENGTH);
  38. 38 :
  39. 39 : this._doStore(chunkIds[i], chunkString).then(function() {
  40. 40 : chunkCount++;
  41. 41 : if (chunkCount == numChunks) {
  42. 42 : resolve();
  43. 43 : }
  44. 44 : }, function() {
  45. 45 : reject();
  46. 46 : });
  47. 47 :
  48. 48 : currIndex += this.MAX_LENGTH;
  49. 49 : }
  50. 50 : }.bind(this), function() {
  51. 51 : reject();
  52. 52 : });
  53. 53 : }.bind(this));
  54. 54 :
  55. 55 : return promise;
  56. 56 : } else {
  57. 57 : return this._doStore(id, dataString);
  58. 58 : }
  59. 59 : }
  60. 60 :
  61. 61 : /**
  62. 62 : * Get the URL for trombone
  63. 63 : * @name Spyral.Util.Storage.getTromboneUrl
  64. 64 : * @function
  65. 65 : * @static
  66. 66 : * @returns {String}
  67. 67 : */
  68. 68 : static getTromboneUrl() {
  69. 69 : return Voyant.application.getTromboneUrl();
  70. 70 : }
  71. 71 :
  72. 72 : static _doStore(id, dataString) {
  73. 73 : return fetch(this.getTromboneUrl(), {
  74. 74 : method: 'GET',
  75. 75 : body: {
  76. 76 : tool: 'resource.StoredResource',
  77. 77 : resourceId: id,
  78. 78 : storeResource: dataString
  79. 79 : }
  80. 80 : });
  81. 81 : }
  82. 82 :
  83. 83 : /**
  84. 84 : * Get a stored resource
  85. 85 : * @name Spyral.Util.Storage.getStoredResource
  86. 86 : * @function
  87. 87 : * @static
  88. 88 : * @param {String} id
  89. 89 : * @returns {Promise}
  90. 90 : */
  91. 91 : static getStoredResource(id) {
  92. 92 : return fetch(this.getTromboneUrl(), {
  93. 93 : method: 'GET',
  94. 94 : body: {
  95. 95 : tool: 'resource.StoredResource',
  96. 96 : verifyResourceId: id+'-hasChunks'
  97. 97 : }
  98. 98 : }).then(function(response) {
  99. 99 : if (response.ok) {
  100. 100 : response.json().then(json => {
  101. 101 : if (json && json.storedResource && json.storedResource.id && json.storedResource.id !== '') {
  102. 102 : // chunks
  103. 103 : this._doGetStored(json.storedResource.id, false).then(function(chunkIds) {
  104. 104 : var fullData = '';
  105. 105 : var dataChunks = {};
  106. 106 : for (var i = 0; i < chunkIds.length; i++) {
  107. 107 : this._doGetStored(chunkIds[i], true).then(function(response) {
  108. 108 : var chunkId = response[0];
  109. 109 : var value = response[1];
  110. 110 : dataChunks[chunkId] = value;
  111. 111 :
  112. 112 : var done = true;
  113. 113 : for (var j = chunkIds.length-1; j >= 0; j--) {
  114. 114 : if (dataChunks[chunkIds[j]] === undefined) {
  115. 115 : done = false;
  116. 116 : break;
  117. 117 : }
  118. 118 : }
  119. 119 :
  120. 120 : if (done) {
  121. 121 : for (var j = 0; j < chunkIds.length; j++) {
  122. 122 : fullData += dataChunks[chunkIds[j]];
  123. 123 : }
  124. 124 : return fullData;
  125. 125 : }
  126. 126 : }, function(err) {
  127. 127 : throw Error('Storage: '+err)
  128. 128 : });
  129. 129 : }
  130. 130 : }, function(err) {
  131. 131 : throw Error('Storage: '+err)
  132. 132 : });
  133. 133 : } else {
  134. 134 : // no chunks
  135. 135 : return this._doGetStored(id, false).then(function(value) {
  136. 136 : return value
  137. 137 : }, function(err) {
  138. 138 : throw Error('Storage: '+err)
  139. 139 : });
  140. 140 : }
  141. 141 : });
  142. 142 : } else {
  143. 143 :
  144. 144 : }
  145. 145 : });
  146. 146 : }
  147. 147 :
  148. 148 : static _doGetStored(id, isChunk) {
  149. 149 : return fetch(this.getTromboneUrl(), {
  150. 150 : method: 'GET',
  151. 151 : body: {
  152. 152 : tool: 'resource.StoredResource',
  153. 153 : retrieveResourceId: id,
  154. 154 : failQuietly: true
  155. 155 : }
  156. 156 : }).then(function(response) {
  157. 157 : if (response.ok) {
  158. 158 : response.json().then(json => {
  159. 159 : var id = json.storedResource.id;
  160. 160 : var value = json.storedResource.resource;
  161. 161 : if (value.length == 0) {
  162. 162 : throw Error('Storage: stored file is empty')
  163. 163 : } else {
  164. 164 : if (isChunk !== true) {
  165. 165 : // value = JSON.parse(value);
  166. 166 : return value;
  167. 167 : } else {
  168. 168 : return [id, value];
  169. 169 : }
  170. 170 : }
  171. 171 : })
  172. 172 : }
  173. 173 : });
  174. 174 : }
  175. 175 : }
  176. 176 :
  177. 177 : export default Storage