• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 fs = requireInternal('file.fs');
18const SESSION_ID = '__sessionId';
19const VERSION = '__version';
20const COMPLEX_TYPE = '[COMPLEX]';
21const STRING_TYPE = '[STRING]';
22const NULL_TYPE = '[NULL]';
23const ASSET_KEYS = ['status', 'name', 'uri', 'path', 'createTime', 'modifyTime', 'size'];
24const STATUS_INDEX = 0;
25const ASSET_KEY_SEPARATOR = '.';
26const JS_ERROR = 1;
27const SDK_VERSION_8 = 8;
28const SDK_VERSION_9 = 9;
29const SESSION_ID_REGEX = /^\w+$/;
30const SESSION_ID_MAX_LENGTH = 128;
31const ASSETS_MAX_NUMBER = 50;
32const HEAD_SIZE = 3;
33const END_SIZE = 3;
34const MIN_SIZE = HEAD_SIZE + END_SIZE + 3;
35const REPLACE_CHAIN = '***';
36const DEFAULT_ANONYMOUS = '******';
37
38class Distributed {
39  constructor(obj) {
40    constructorMethod(this, obj);
41  }
42
43  setSessionId(sessionId) {
44    if (sessionId == null || sessionId === '') {
45      leaveSession(this.__sdkVersion, this.__proxy);
46      return false;
47    }
48    if (this.__proxy[SESSION_ID] === sessionId) {
49      return true;
50    }
51    leaveSession(this.__sdkVersion, this.__proxy);
52    let object = joinSession(this.__sdkVersion, this.__proxy, this.__objectId, sessionId);
53    if (object != null) {
54      this.__proxy = object;
55      return true;
56    }
57    return false;
58  }
59
60  on(type, callback) {
61    onWatch(this.__sdkVersion, type, this.__proxy, callback);
62    distributedObject.recordCallback(this.__sdkVersion, type, this.__objectId, callback);
63  }
64
65  off(type, callback) {
66    offWatch(this.__sdkVersion, type, this.__proxy, callback);
67    if (callback !== undefined || callback != null) {
68      distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId, callback);
69    } else {
70      distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId);
71    }
72  }
73
74  save(deviceId, callback) {
75    if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') {
76      console.info('not join a session, can not do save');
77      return JS_ERROR;
78    }
79    return this.__proxy.save(deviceId, this[VERSION], callback);
80  }
81
82  revokeSave(callback) {
83    if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') {
84      console.info('not join a session, can not do revoke save');
85      return JS_ERROR;
86    }
87    return this.__proxy.revokeSave(callback);
88  }
89
90  __proxy;
91  __objectId;
92  __version;
93  __sdkVersion = SDK_VERSION_8;
94}
95
96function constructorMethod(result, obj) {
97  result.__proxy = obj;
98  Object.keys(obj).forEach(key => {
99    Object.defineProperty(result, key, {
100      enumerable: true,
101      configurable: true,
102      get: function () {
103        return result.__proxy[key];
104      },
105      set: function (newValue) {
106        result[VERSION]++;
107        result.__proxy[key] = newValue;
108      }
109    });
110  });
111  Object.defineProperty(result, SESSION_ID, {
112    enumerable: true,
113    configurable: true,
114    get: function () {
115      return result.__proxy[SESSION_ID];
116    },
117    set: function (newValue) {
118      result.__proxy[SESSION_ID] = newValue;
119    }
120  });
121  result.__objectId = randomNum();
122  result[VERSION] = 0;
123  console.info('constructor success ');
124}
125
126function randomNum() {
127  return distributedObject.sequenceNum();
128}
129
130function newDistributed(obj) {
131  console.info('start newDistributed');
132  if (obj == null) {
133    console.error('object is null');
134    return null;
135  }
136  return new Distributed(obj);
137}
138
139function getObjectValue(object, key) {
140  console.info('start get ' + key);
141  let result = object.get(key);
142  if (typeof result === 'string') {
143    if (result.startsWith(STRING_TYPE)) {
144      result = result.substr(STRING_TYPE.length);
145    } else if (result.startsWith(COMPLEX_TYPE)) {
146      result = JSON.parse(result.substr(COMPLEX_TYPE.length));
147    } else if (result.startsWith(NULL_TYPE)) {
148      result = null;
149    } else {
150      console.error('error type');
151    }
152  }
153  console.info('get success');
154  return result;
155}
156
157function setObjectValue(object, key, newValue) {
158  console.info('start set ' + key);
159  if (typeof newValue === 'object') {
160    let value = COMPLEX_TYPE + JSON.stringify(newValue);
161    object.put(key, value);
162  } else if (typeof newValue === 'string') {
163    let value = STRING_TYPE + newValue;
164    object.put(key, value);
165  } else if (newValue == null) {
166    let value = NULL_TYPE;
167    object.put(key, value);
168  } else {
169    object.put(key, newValue);
170  }
171}
172
173function isAsset(obj) {
174  if (Object.prototype.toString.call(obj) !== '[object Object]') {
175    return false;
176  }
177  let length = Object.prototype.hasOwnProperty.call(obj, ASSET_KEYS[STATUS_INDEX]) ? ASSET_KEYS.length : ASSET_KEYS.length - 1;
178  if (Object.keys(obj).length !== length) {
179    return false;
180  }
181  if (Object.prototype.hasOwnProperty.call(obj, ASSET_KEYS[STATUS_INDEX]) &&
182    typeof obj[ASSET_KEYS[STATUS_INDEX]] !== 'number' && typeof obj[ASSET_KEYS[STATUS_INDEX]] !== 'undefined') {
183    return false;
184  }
185  for (const key of ASSET_KEYS.slice(1)) {
186    if (!Object.prototype.hasOwnProperty.call(obj, key) || typeof obj[key] !== 'string') {
187      return false;
188    }
189  }
190  return true;
191}
192
193function defineAsset(object, key, data) {
194  Object.defineProperty(object, key, {
195    enumerable: true,
196    configurable: true,
197    get: function () {
198      return getAssetValue(object, key);
199    },
200    set: function (newValue) {
201      setAssetValue(object, key, newValue);
202    }
203  });
204  let asset = object[key];
205  Object.keys(data).forEach(subKey => {
206    if (data[subKey] !== '') {
207      asset[subKey] = data[subKey];
208    }
209  });
210}
211
212function getAssetValue(object, key) {
213  let asset = {};
214  ASSET_KEYS.forEach(subKey => {
215    Object.defineProperty(asset, subKey, {
216      enumerable: true,
217      configurable: true,
218      get: function () {
219        return getObjectValue(object, key + ASSET_KEY_SEPARATOR + subKey);
220      },
221      set: function (newValue) {
222        setObjectValue(object, key + ASSET_KEY_SEPARATOR + subKey, newValue);
223      }
224    });
225  });
226  return asset;
227}
228
229function setAssetValue(object, key, newValue) {
230  if (!isAsset(newValue)) {
231    throw {
232      code: 401,
233      message: 'cannot set ' + key + ' by non Asset type data'
234    };
235  }
236  Object.keys(newValue).forEach(subKey => {
237    setObjectValue(object, key + ASSET_KEY_SEPARATOR + subKey, newValue[subKey]);
238  });
239}
240
241function joinSession(version, obj, objectId, sessionId, context) {
242  if (obj == null || sessionId == null || sessionId === '') {
243    console.error('object is null');
244    return null;
245  }
246
247  let object = null;
248  if (context !== undefined || context != null) {
249    object = distributedObject.createObjectSync(version, sessionId, objectId, context);
250  } else {
251    object = distributedObject.createObjectSync(version, sessionId, objectId);
252  }
253
254  if (object == null) {
255    console.error('create fail');
256    return null;
257  }
258  Object.keys(obj).forEach(key => {
259    console.info('start define ' + key);
260    if (isAsset(obj[key])) {
261      defineAsset(object, key, obj[key]);
262    } else {
263      Object.defineProperty(object, key, {
264        enumerable: true,
265        configurable: true,
266        get: function () {
267          return getObjectValue(object, key);
268        },
269        set: function (newValue) {
270          setObjectValue(object, key, newValue);
271        }
272      });
273      if (obj[key] !== undefined) {
274        object[key] = obj[key];
275      }
276    }
277  });
278
279  Object.defineProperty(object, SESSION_ID, {
280    value: sessionId,
281    configurable: true,
282  });
283  return object;
284}
285
286function leaveSession(version, obj) {
287  console.info('start leaveSession');
288  if (obj == null || obj[SESSION_ID] == null || obj[SESSION_ID] === '') {
289    console.warn('object is null');
290    return;
291  }
292  Object.keys(obj).forEach(key => {
293    Object.defineProperty(obj, key, {
294      value: obj[key],
295      configurable: true,
296      writable: true,
297      enumerable: true,
298    });
299    if (isAsset(obj[key])) {
300      Object.keys(obj[key]).forEach(subKey => {
301        Object.defineProperty(obj[key], subKey, {
302          value: obj[key][subKey],
303          configurable: true,
304          writable: true,
305          enumerable: true,
306        });
307      });
308    }
309  });
310  // disconnect,delete object
311  distributedObject.destroyObjectSync(version, obj);
312  delete obj[SESSION_ID];
313}
314
315function toBeAnonymous(name) {
316  if (name == null || name === undefined || name === '') {
317    return '';
318  }
319  if (name.length <= HEAD_SIZE) {
320    return DEFAULT_ANONYMOUS;
321  }
322  if (name.length < MIN_SIZE) {
323    return name.substring(0, HEAD_SIZE) + REPLACE_CHAIN;
324  }
325  return name.substring(0, HEAD_SIZE) + REPLACE_CHAIN + name.substring(name.length - END_SIZE);
326}
327
328function onWatch(version, type, obj, callback) {
329  console.info('start on ' + toBeAnonymous(obj[SESSION_ID]));
330  if (obj[SESSION_ID] != null && obj[SESSION_ID] !== undefined && obj[SESSION_ID].length > 0) {
331    distributedObject.on(version, type, obj, callback);
332  }
333}
334
335function offWatch(version, type, obj, callback = undefined) {
336  console.info('start off ' + toBeAnonymous(obj[SESSION_ID]) + ' ' + callback);
337  if (obj[SESSION_ID] != null && obj[SESSION_ID] !== undefined && obj[SESSION_ID].length > 0) {
338    if (callback !== undefined || callback != null) {
339      distributedObject.off(version, type, obj, callback);
340    } else {
341      distributedObject.off(version, type, obj);
342    }
343  }
344}
345
346function newDistributedV9(context, obj) {
347  console.info('start newDistributed');
348  let checkparameter = function(parameter, type) {
349    throw {
350      code: 401,
351      message :"Parameter error. The type of '" + parameter + "' must be '" + type + "'."};
352  };
353  if (typeof context !== 'object') {
354    checkparameter('context', 'Context');
355  }
356  if (typeof obj !== 'object') {
357    checkparameter('source', 'object');
358  }
359  if (obj == null) {
360    console.error('object is null');
361    return null;
362  }
363  return new DistributedV9(obj, context);
364}
365
366function appendPropertyToObj(result, obj) {
367  result.__proxy = Object.assign(result.__proxy, obj);
368  Object.keys(obj).forEach(key => {
369    Object.defineProperty(result, key, {
370      enumerable: true,
371      configurable: true,
372      get: function () {
373        return result.__proxy[key];
374      },
375      set: function (newValue) {
376        result.__proxy[key] = newValue;
377      }
378    });
379  });
380}
381
382function getDefaultAsset(uri, distributedDir) {
383  if (uri == null) {
384    throw {
385      code: 15400002,
386      message: 'The asset uri to be set is null.'
387    };
388  }
389  const fileName = uri.substring(uri.lastIndexOf('/') + 1);
390  const filePath = distributedDir + '/' + fileName;
391  let stat;
392  try {
393    stat = fs.statSync(filePath);
394    return {
395      name: fileName,
396      uri: uri,
397      path: filePath,
398      createTime: stat.ctime.toString(),
399      modifyTime: stat.mtime.toString(),
400      size: stat.size.toString()
401    };
402  } catch (error) {
403    console.error(error);
404    return {
405      name: '',
406      uri: '',
407      path: '',
408      createTime: 0,
409      modifyTime: 0,
410      size: 0
411    };
412  }
413}
414
415class DistributedV9 {
416
417  constructor(obj, context) {
418    this.__context = context;
419    constructorMethod(this, obj);
420  }
421
422  setSessionId(sessionId, callback) {
423    if (typeof sessionId === 'function' || sessionId == null || sessionId === '') {
424      leaveSession(this.__sdkVersion, this.__proxy);
425      if (typeof sessionId === 'function') {
426        return sessionId(this.__proxy);
427      } else if (typeof callback === 'function') {
428        return callback(null, this.__proxy);
429      } else {
430        return Promise.resolve(null, this.__proxy);
431      }
432    }
433    if (this.__proxy[SESSION_ID] === sessionId) {
434      if (typeof callback === 'function') {
435        return callback(null, this.__proxy);
436      } else {
437        return Promise.resolve(null, this.__proxy);
438      }
439    }
440    leaveSession(this.__sdkVersion, this.__proxy);
441    if (sessionId.length > SESSION_ID_MAX_LENGTH || !SESSION_ID_REGEX.test(sessionId)) {
442      throw {
443        code: 401,
444        message: 'The sessionId allows only letters, digits, and underscores(_), and cannot exceed 128 in length.'
445      };
446    }
447    let object = joinSession(this.__sdkVersion, this.__proxy, this.__objectId, sessionId, this.__context);
448    if (object != null) {
449      this.__proxy = object;
450      if (typeof callback === 'function') {
451        return callback(null, this.__proxy);
452      } else {
453        return Promise.resolve(null, object);
454      }
455    } else {
456      if (typeof callback === 'function') {
457        return callback(null, null);
458      } else {
459        return Promise.reject(null, null);
460      }
461    }
462  }
463
464  on(type, callback) {
465    onWatch(this.__sdkVersion, type, this.__proxy, callback);
466    distributedObject.recordCallback(this.__sdkVersion, type, this.__objectId, callback);
467  }
468
469  off(type, callback) {
470    offWatch(this.__sdkVersion, type, this.__proxy, callback);
471    if (callback !== undefined || callback != null) {
472      distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId, callback);
473    } else {
474      distributedObject.deleteCallback(this.__sdkVersion, type, this.__objectId);
475    }
476  }
477
478  save(deviceId, callback) {
479    if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') {
480      console.info('not join a session, can not do save');
481      return JS_ERROR;
482    }
483    return this.__proxy.save(deviceId, this[VERSION], callback);
484  }
485
486  revokeSave(callback) {
487    if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') {
488      console.info('not join a session, can not do revoke save');
489      return JS_ERROR;
490    }
491    return this.__proxy.revokeSave(callback);
492  }
493
494  bindAssetStore(assetkey, bindInfo, callback) {
495    if (this.__proxy[SESSION_ID] == null || this.__proxy[SESSION_ID] === '') {
496      console.info('not join a session, can not do bindAssetStore');
497      return JS_ERROR;
498    }
499    return this.__proxy.bindAssetStore(assetkey, bindInfo, callback);
500  }
501
502  setAsset(assetKey, uri) {
503    if (this.__proxy[SESSION_ID] != null && this.__proxy[SESSION_ID] !== '') {
504      throw {
505        code: 15400003,
506        message: 'SessionId has been set, and asset cannot be set.'
507      };
508    }
509    if (!assetKey || !uri) {
510      throw {
511        code: 15400002,
512        message: 'The property or uri of the asset is invalid.'
513      };
514    }
515
516    let assetObj = {};
517    const distributedDir = this.__context.distributedFilesDir;
518    const asset = getDefaultAsset(uri, distributedDir);
519    assetObj[assetKey] = [asset];
520    assetObj[assetKey + '0'] = asset;
521    appendPropertyToObj(this, assetObj);
522    return Promise.resolve();
523  }
524
525  setAssets(assetsKey, uris) {
526    if (this.__proxy[SESSION_ID] != null && this.__proxy[SESSION_ID] !== '') {
527      throw {
528        code: 15400003,
529        message: 'SessionId has been set, and assets cannot be set.'
530      };
531    }
532    if (!assetsKey) {
533      throw {
534        code: 15400002,
535        message: 'The property of the assets is invalid.'
536      };
537    }
538    if (!Array.isArray(uris) || uris.length <= 0 || uris.length > ASSETS_MAX_NUMBER) {
539      throw {
540        code: 15400002,
541        message: 'The uri array of the set assets is not an array or the length is invalid.'
542      };
543    }
544    for (let index = 0; index < uris.length; index++) {
545      if (!uris[index]) {
546        throw {
547          code: 15400002,
548          message: 'Uri in assets array is invalid.'
549        };
550      }
551    }
552
553    let assetObj = {};
554    let assets = [];
555    const distributedDir = this.__context.distributedFilesDir;
556    for (let index = 0; index < uris.length; index++) {
557      const asset = getDefaultAsset(uris[index], distributedDir);
558      assets.push(asset);
559      assetObj[assetsKey + index] = asset;
560    }
561    assetObj[assetsKey] = assets;
562    appendPropertyToObj(this, assetObj);
563    return Promise.resolve();
564  }
565
566  __context;
567  __proxy;
568  __objectId;
569  __version;
570  __sdkVersion = SDK_VERSION_9;
571}
572
573export default {
574  createDistributedObject: newDistributed,
575  create: newDistributedV9,
576  genSessionId: randomNum
577};
578