1/* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16const distributedObject = requireInternal('data.distributedDataObject'); 17const SESSION_ID = '__sessionId'; 18const VERSION = '__version'; 19const COMPLEX_TYPE = '[COMPLEX]'; 20const STRING_TYPE = '[STRING]'; 21const NULL_TYPE = '[NULL]'; 22const ASSET_KEYS = ['status', 'name', 'uri', 'path', 'createTime', 'modifyTime', 'size']; 23const STATUS_INDEX = 0; 24const ASSET_KEY_SEPARATOR = '.'; 25const JS_ERROR = 1; 26const SDK_VERSION_8 = 8; 27const SDK_VERSION_9 = 9; 28const SESSION_ID_REGEX = /^\w+$/; 29const SESSION_ID_MAX_LENGTH = 128; 30 31class Distributed { 32 constructor(obj) { 33 constructorMethod(this, obj); 34 } 35 36 setSessionId(sessionId) { 37 if (sessionId == null || sessionId === '') { 38 leaveSession(this.__sdkVersion, this.__proxy); 39 return false; 40 } 41 if (this.__proxy[SESSION_ID] === sessionId) { 42 return true; 43 } 44 leaveSession(this.__sdkVersion, this.__proxy); 45 let object = joinSession(this.__sdkVersion, this.__proxy, this.__objectId, sessionId); 46 if (object != null) { 47 this.__proxy = object; 48 return true; 49 } 50 return false; 51 } 52 53 on(type, callback) { 54 onWatch(this.__sdkVersion, type, this.__proxy, callback); 55 distributedObject.recordCallback(this.__sdkVersion, type, this.__objectId, callback); 56 } 57 58 off(type, callback) { 59 offWatch(this.__sdkVersion, type, this.__proxy, callback); 60 if (callback !== undefined || callback != null) { 61 distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId, callback); 62 } else { 63 distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId); 64 } 65 } 66 67 save(deviceId, callback) { 68 if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') { 69 console.info('not join a session, can not do save'); 70 return JS_ERROR; 71 } 72 return this.__proxy.save(deviceId, this[VERSION], callback); 73 } 74 75 revokeSave(callback) { 76 if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') { 77 console.info('not join a session, can not do revoke save'); 78 return JS_ERROR; 79 } 80 return this.__proxy.revokeSave(callback); 81 } 82 83 __proxy; 84 __objectId; 85 __version; 86 __sdkVersion = SDK_VERSION_8; 87} 88 89function constructorMethod(result, obj) { 90 result.__proxy = obj; 91 Object.keys(obj).forEach(key => { 92 Object.defineProperty(result, key, { 93 enumerable: true, 94 configurable: true, 95 get: function () { 96 return result.__proxy[key]; 97 }, 98 set: function (newValue) { 99 result[VERSION]++; 100 result.__proxy[key] = newValue; 101 } 102 }); 103 }); 104 Object.defineProperty(result, SESSION_ID, { 105 enumerable: true, 106 configurable: true, 107 get: function () { 108 return result.__proxy[SESSION_ID]; 109 }, 110 set: function (newValue) { 111 result.__proxy[SESSION_ID] = newValue; 112 } 113 }); 114 result.__objectId = randomNum(); 115 result[VERSION] = 0; 116 console.info('constructor success '); 117} 118 119function randomNum() { 120 return distributedObject.sequenceNum(); 121} 122 123function newDistributed(obj) { 124 console.info('start newDistributed'); 125 if (obj == null) { 126 console.error('object is null'); 127 return null; 128 } 129 return new Distributed(obj); 130} 131 132function getObjectValue(object, key) { 133 console.info('start get ' + key); 134 let result = object.get(key); 135 if (typeof result === 'string') { 136 if (result.startsWith(STRING_TYPE)) { 137 result = result.substr(STRING_TYPE.length); 138 } else if (result.startsWith(COMPLEX_TYPE)) { 139 result = JSON.parse(result.substr(COMPLEX_TYPE.length)); 140 } else if (result.startsWith(NULL_TYPE)) { 141 result = null; 142 } else { 143 console.error('error type'); 144 } 145 } 146 console.info('get success'); 147 return result; 148} 149 150function setObjectValue(object, key, newValue) { 151 console.info('start set ' + key); 152 if (typeof newValue === 'object') { 153 let value = COMPLEX_TYPE + JSON.stringify(newValue); 154 object.put(key, value); 155 } else if (typeof newValue === 'string') { 156 let value = STRING_TYPE + newValue; 157 object.put(key, value); 158 } else if (newValue == null) { 159 let value = NULL_TYPE; 160 object.put(key, value); 161 } else { 162 object.put(key, newValue); 163 } 164} 165 166function isAsset(obj) { 167 if (Object.prototype.toString.call(obj) !== '[object Object]') { 168 return false; 169 } 170 let length = Object.prototype.hasOwnProperty.call(obj, ASSET_KEYS[STATUS_INDEX]) ? ASSET_KEYS.length : ASSET_KEYS.length - 1; 171 if (Object.keys(obj).length !== length) { 172 return false; 173 } 174 if (Object.prototype.hasOwnProperty.call(obj, ASSET_KEYS[STATUS_INDEX]) && 175 typeof obj[ASSET_KEYS[STATUS_INDEX]] !== 'number' && typeof obj[ASSET_KEYS[STATUS_INDEX]] !== 'undefined') { 176 return false; 177 } 178 for (const key of ASSET_KEYS.slice(1)) { 179 if (!Object.prototype.hasOwnProperty.call(obj, key) || typeof obj[key] !== 'string') { 180 return false; 181 } 182 } 183 return true; 184} 185 186function defineAsset(object, key, data) { 187 Object.defineProperty(object, key, { 188 enumerable: true, 189 configurable: true, 190 get: function () { 191 return getAssetValue(object, key); 192 }, 193 set: function (newValue) { 194 setAssetValue(object, key, newValue); 195 } 196 }); 197 let asset = object[key]; 198 Object.keys(data).forEach(subKey => { 199 if (data[subKey] !== '') { 200 asset[subKey] = data[subKey]; 201 } 202 }); 203} 204 205function getAssetValue(object, key) { 206 let asset = {}; 207 ASSET_KEYS.forEach(subKey => { 208 Object.defineProperty(asset, subKey, { 209 enumerable: true, 210 configurable: true, 211 get: function () { 212 return getObjectValue(object, key + ASSET_KEY_SEPARATOR + subKey); 213 }, 214 set: function (newValue) { 215 setObjectValue(object, key + ASSET_KEY_SEPARATOR + subKey, newValue); 216 } 217 }); 218 }); 219 return asset; 220} 221 222function setAssetValue(object, key, newValue) { 223 if (!isAsset(newValue)) { 224 throw { 225 code: 401, 226 message: 'cannot set ' + key + ' by non Asset type data' 227 }; 228 } 229 Object.keys(newValue).forEach(subKey => { 230 setObjectValue(object, key + ASSET_KEY_SEPARATOR + subKey, newValue[subKey]); 231 }); 232} 233 234function joinSession(version, obj, objectId, sessionId, context) { 235 if (obj == null || sessionId == null || sessionId === '') { 236 console.error('object is null'); 237 return null; 238 } 239 240 let object = null; 241 if (context !== undefined || context != null) { 242 object = distributedObject.createObjectSync(version, sessionId, objectId, context); 243 } else { 244 object = distributedObject.createObjectSync(version, sessionId, objectId); 245 } 246 247 if (object == null) { 248 console.error('create fail'); 249 return null; 250 } 251 Object.keys(obj).forEach(key => { 252 console.info('start define ' + key); 253 if (isAsset(obj[key])) { 254 defineAsset(object, key, obj[key]); 255 } else { 256 Object.defineProperty(object, key, { 257 enumerable: true, 258 configurable: true, 259 get: function () { 260 return getObjectValue(object, key); 261 }, 262 set: function (newValue) { 263 setObjectValue(object, key, newValue); 264 } 265 }); 266 if (obj[key] !== undefined) { 267 object[key] = obj[key]; 268 } 269 } 270 }); 271 272 Object.defineProperty(object, SESSION_ID, { 273 value: sessionId, 274 configurable: true, 275 }); 276 return object; 277} 278 279function leaveSession(version, obj) { 280 console.info('start leaveSession'); 281 if (obj == null || obj[SESSION_ID] == null || obj[SESSION_ID] === '') { 282 console.warn('object is null'); 283 return; 284 } 285 Object.keys(obj).forEach(key => { 286 Object.defineProperty(obj, key, { 287 value: obj[key], 288 configurable: true, 289 writable: true, 290 enumerable: true, 291 }); 292 if (isAsset(obj[key])) { 293 Object.keys(obj[key]).forEach(subKey => { 294 Object.defineProperty(obj[key], subKey, { 295 value: obj[key][subKey], 296 configurable: true, 297 writable: true, 298 enumerable: true, 299 }); 300 }); 301 } 302 }); 303 // disconnect,delete object 304 distributedObject.destroyObjectSync(version, obj); 305 delete obj[SESSION_ID]; 306} 307 308function onWatch(version, type, obj, callback) { 309 console.info('start on ' + obj[SESSION_ID]); 310 if (obj[SESSION_ID] != null && obj[SESSION_ID] !== undefined && obj[SESSION_ID].length > 0) { 311 distributedObject.on(version, type, obj, callback); 312 } 313} 314 315function offWatch(version, type, obj, callback = undefined) { 316 console.info('start off ' + obj[SESSION_ID] + ' ' + callback); 317 if (obj[SESSION_ID] != null && obj[SESSION_ID] !== undefined && obj[SESSION_ID].length > 0) { 318 if (callback !== undefined || callback != null) { 319 distributedObject.off(version, type, obj, callback); 320 } else { 321 distributedObject.off(version, type, obj); 322 } 323 } 324} 325 326function newDistributedV9(context, obj) { 327 console.info('start newDistributed'); 328 let checkparameter = function(parameter, type) { 329 throw { 330 code: 401, 331 message :"Parameter error. The type of '" + parameter + "' must be '" + type + "'."}; 332 }; 333 if (typeof context !== 'object') { 334 checkparameter('context', 'Context'); 335 } 336 if (typeof obj !== 'object') { 337 checkparameter('source', 'object'); 338 } 339 if (obj == null) { 340 console.error('object is null'); 341 return null; 342 } 343 return new DistributedV9(obj, context); 344} 345 346class DistributedV9 { 347 348 constructor(obj, context) { 349 this.__context = context; 350 constructorMethod(this, obj); 351 } 352 353 setSessionId(sessionId, callback) { 354 if (typeof sessionId === 'function' || sessionId == null || sessionId === '') { 355 leaveSession(this.__sdkVersion, this.__proxy); 356 if (typeof sessionId === 'function') { 357 return sessionId(this.__proxy); 358 } else if (typeof callback === 'function') { 359 return callback(null, this.__proxy); 360 } else { 361 return Promise.resolve(null, this.__proxy); 362 } 363 } 364 if (this.__proxy[SESSION_ID] === sessionId) { 365 if (typeof callback === 'function') { 366 return callback(null, this.__proxy); 367 } else { 368 return Promise.resolve(null, this.__proxy); 369 } 370 } 371 leaveSession(this.__sdkVersion, this.__proxy); 372 if (sessionId.length > SESSION_ID_MAX_LENGTH || !SESSION_ID_REGEX.test(sessionId)) { 373 throw { 374 code: 401, 375 message: 'The sessionId allows only letters, digits, and underscores(_), and cannot exceed 128 in length.' 376 }; 377 } 378 let object = joinSession(this.__sdkVersion, this.__proxy, this.__objectId, sessionId, this.__context); 379 if (object != null) { 380 this.__proxy = object; 381 if (typeof callback === 'function') { 382 return callback(null, this.__proxy); 383 } else { 384 return Promise.resolve(null, object); 385 } 386 } else { 387 if (typeof callback === 'function') { 388 return callback(null, null); 389 } else { 390 return Promise.reject(null, null); 391 } 392 } 393 } 394 395 on(type, callback) { 396 onWatch(this.__sdkVersion, type, this.__proxy, callback); 397 distributedObject.recordCallback(this.__sdkVersion, type, this.__objectId, callback); 398 } 399 400 off(type, callback) { 401 offWatch(this.__sdkVersion, type, this.__proxy, callback); 402 if (callback !== undefined || callback != null) { 403 distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId, callback); 404 } else { 405 distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId); 406 } 407 } 408 409 save(deviceId, callback) { 410 if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') { 411 console.info('not join a session, can not do save'); 412 return JS_ERROR; 413 } 414 return this.__proxy.save(deviceId, this[VERSION], callback); 415 } 416 417 revokeSave(callback) { 418 if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') { 419 console.info('not join a session, can not do revoke save'); 420 return JS_ERROR; 421 } 422 return this.__proxy.revokeSave(callback); 423 } 424 425 bindAssetStore(assetkey, bindInfo, callback) { 426 if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') { 427 console.info('not join a session, can not do bindAssetStore'); 428 return JS_ERROR; 429 } 430 return this.__proxy.bindAssetStore(assetkey, bindInfo, callback); 431 } 432 433 __context; 434 __proxy; 435 __objectId; 436 __version; 437 __sdkVersion = SDK_VERSION_9; 438} 439 440export default { 441 createDistributedObject: newDistributed, 442 create: newDistributedV9, 443 genSessionId: randomNum 444}; 445