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