Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.mdns.eu/nextcloud/passwords-client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarius David Wieschollek <passwords.public@mdns.eu>2020-12-02 02:09:25 +0300
committerMarius David Wieschollek <passwords.public@mdns.eu>2020-12-02 02:09:25 +0300
commit277f5a1571a1a839d4f000a8ef65e35cba5b05ce (patch)
treefca78bd10526c8b1dc62d51252dd4ca0e26ac6e0 /src/Services
parent16ac1f3d221b49b09001bda1d5f1194c5a7cafdd (diff)
Add model service and support for detail levels
Signed-off-by: Marius David Wieschollek <passwords.public@mdns.eu>
Diffstat (limited to 'src/Services')
-rw-r--r--src/Services/ModelService.js255
1 files changed, 255 insertions, 0 deletions
diff --git a/src/Services/ModelService.js b/src/Services/ModelService.js
new file mode 100644
index 0000000..e06bcb9
--- /dev/null
+++ b/src/Services/ModelService.js
@@ -0,0 +1,255 @@
+export default class ModelService {
+
+ /**
+ *
+ * @param {BaseClassLoader} cl
+ */
+ constructor(cl) {
+ this._cl = cl;
+ this._cache = /** @type {Cache} **/ cl.getInstance('cache.cache');
+ }
+
+ hasModel(type, id) {
+ return this._cache.has(`${type}.${id}`);
+ }
+
+ getModel(type, id) {
+ if(this.hasModel(type, id)) {
+ return this._cache.get(`${type}.${id}`);
+ }
+ return null;
+ }
+
+ addModel(type, model) {
+ this._cache.set(`${type}.${model.getId()}`, model);
+ // @TODO update related models
+ }
+
+ /**
+ *
+ * @param type
+ * @param data
+ * @param detailLevel
+ * @returns {Promise<AbstractRevisionModel|Tag>|Promise<AbstractRevisionModel|Password>|Folder}
+ */
+ makeFromApiData(type, data, detailLevel = []) {
+ if(type === 'password') return this.makePasswordFromApiData(data, detailLevel)
+ if(type === 'folder') return this.makeFolderFromApiData(data, detailLevel)
+ if(type === 'tag') return this.makeTagFromApiData(data, detailLevel)
+ }
+
+ async makePasswordCollectionFromApiData(data, detailLevel = []) {
+ let passwords = [];
+ for(let passwordData of data) {
+ passwords.push(await this.makePasswordFromApiData(passwordData, detailLevel));
+ }
+
+ return this._cl.getClass('collection.password', passwords);
+ }
+
+ async makePasswordFromApiData(data, detailLevel = []) {
+ let converter = /** @type {PasswordConverter} **/ this._cl.getInstance('converter.password'),
+ newModel = await converter.fromEncryptedData(data);
+ newModel.setDetailLevel(detailLevel);
+
+ let revisionCacheKey = `password.${newModel.getId()}.${newModel.getRevision()}`;
+ if(!this._cache.has(revisionCacheKey)) {
+ let revisionModel = converter.fromModel(newModel);
+ this._cache.set(revisionCacheKey, revisionModel);
+ }
+ if(detailLevel.indexOf('folder') !== -1) {
+ newModel.setParent(await this.makeFolderFromApiData(data.folder, 'model'));
+ }
+ await this._makeCollectionFromData(newModel, 'password', 'tag', detailLevel, data);
+ await this._makeCollectionFromData(newModel, 'password', 'revision', detailLevel, data);
+
+ let cacheKey = `password.${newModel.getId()}`;
+ if(!this._cache.has(cacheKey)) {
+ this._cache.set(cacheKey, newModel);
+ return newModel;
+ } else {
+ return this._mergePasswordModel(this._cache.get(cacheKey), newModel);
+ }
+ }
+
+ async makeFolderCollectionFromApiData(data, detailLevel = []) {
+ let folders = [];
+ for(let folderData of data) {
+ folders.push(await this.makeFolderFromApiData(folderData, detailLevel));
+ }
+
+ return this._cl.getClass('collection.folder', folders);
+ }
+
+ /**
+ *
+ * @param data
+ * @param detailLevel
+ * @return {Folder}
+ */
+ async makeFolderFromApiData(data, detailLevel = []) {
+ let converter = /** @type {FolderConverter} **/ this._cl.getInstance('converter.folder'),
+ newModel = await converter.fromEncryptedData(data);
+ newModel.setDetailLevel(detailLevel);
+
+ let revisionCacheKey = `folder.${newModel.getId()}.${newModel.getRevision()}`;
+ if(!this._cache.has(revisionCacheKey)) {
+ let revisionModel = converter.fromModel(newModel);
+ this._cache.set(revisionCacheKey, revisionModel);
+ }
+ await this._makeCollectionFromData(newModel, 'folder', 'revision', detailLevel, data);
+ await this._makeCollectionFromData(newModel, 'folder', 'folder', detailLevel, data);
+ await this._makeCollectionFromData(newModel, 'folder', 'password', detailLevel, data);
+
+ if(detailLevel.indexOf('parent') !== -1) {
+ newModel.setParent(await this.makeFolderFromApiData(data.parent, 'model'));
+ }
+
+ let cacheKey = `folder.${newModel.getId()}`;
+ if(!this._cache.has(cacheKey)) {
+ this._cache.set(cacheKey, newModel);
+ return newModel;
+ } else {
+ return this._mergeFolderModel(this._cache.get(cacheKey), newModel);
+ }
+ }
+
+ async makeTagCollectionFromApiData(data, detailLevel = []) {
+ let tags = [];
+ for(let tagData of data) {
+ tags.push(await this.makeTagFromApiData(tagData, detailLevel));
+ }
+
+ return this._cl.getClass('collection.tag', tags);
+ }
+
+ async makeTagFromApiData(data, detailLevel = []) {
+ let converter = /** @type {TagConverter} **/ this._cl.getInstance('converter.tag'),
+ newModel = await converter.fromEncryptedData(data);
+ newModel.setDetailLevel(detailLevel);
+
+ let revisionCacheKey = `tag.${newModel.getId()}.${newModel.getRevision()}`;
+ if(!this._cache.has(revisionCacheKey)) {
+ let revisionModel = converter.fromModel(newModel);
+ this._cache.set(revisionCacheKey, revisionModel);
+ }
+ await this._makeCollectionFromData(newModel, 'tag', 'password', detailLevel, data);
+ await this._makeCollectionFromData(newModel, 'tag', 'revision', detailLevel, data);
+
+ let cacheKey = `tag.${newModel.getId()}`;
+ if(!this._cache.has(cacheKey)) {
+ this._cache.set(cacheKey, newModel);
+ return newModel;
+ } else {
+ return this._mergeTagModel(this._cache.get(cacheKey), newModel);
+ }
+ }
+
+ async _makeCollectionFromData(model, baseType, type, detailLevel, data) {
+ let property = `${type}s`;
+ if(detailLevel.indexOf(property) !== -1) {
+ if(!data.hasOwnProperty(property)) {
+ // @TODO data missing = potential bug?
+ }
+ let method = type === 'revision' ?
+ `_make${baseType[0].toUpperCase()}${baseType.substr(1)}Revision`:
+ `make${type[0].toUpperCase()}${type.substr(1)}FromApiData`,
+ models = [];
+ for(let modelData of data[property]) {
+ models.push(await this[method](modelData, 'model'));
+ }
+
+ let cacheKey = `${baseType}.${model.getId()}.${property}`;
+ if(this._cache.has(cacheKey)) {
+ let collection = /** @type {AbstractCollection} **/ this._cache.has(cacheKey);
+ collection.replaceAll(models);
+ model.setProperty(property, collection);
+ } else {
+ let collection = this._cl.getClass(type === 'revision' ? `collection.${baseType}`:`collection.${type}`, models);
+ this._cache.set(cacheKey, collection);
+ model.setProperty(property, collection);
+ }
+ }
+ }
+
+ /**
+ *
+ * @param {Password} model
+ * @param {Password} newModel
+ * @returns {Password}
+ * @private
+ */
+ _mergePasswordModel(model, newModel) {
+ if(newModel.getProperty('revisions') !== undefined) {
+ model.setProperty('revisions', newModel.getProperty('revisions'));
+ }
+ if(newModel.getProperty('tags') !== undefined) {
+ model.setProperty('tags', newModel.getProperty('tags'));
+ }
+ if(newModel.getProperty('folder') !== undefined) {
+ model.setProperty('folder', newModel.getProperty('folder'));
+ }
+ this._mergeModelDetailLevel(model, newModel.getDetailLevel());
+
+ return newModel;
+ }
+
+ /**
+ *
+ * @param {Folder} model
+ * @param {Folder} newModel
+ * @returns {Folder}
+ * @private
+ */
+ _mergeFolderModel(model, newModel) {
+ if(newModel.getProperty('revisions') !== undefined) {
+ model.setProperty('revisions', newModel.getProperty('revisions'));
+ }
+ if(newModel.getProperty('passwords') !== undefined) {
+ model.setProperty('passwords', newModel.getProperty('passwords'));
+ }
+ if(newModel.getProperty('folders') !== undefined) {
+ model.setProperty('folders', newModel.getProperty('folders'));
+ }
+ if(newModel.getProperty('parent') !== undefined) {
+ model.setProperty('parent', newModel.getProperty('parent'));
+ }
+ this._mergeModelDetailLevel(model, newModel.getDetailLevel());
+
+ return newModel;
+ }
+
+ /**
+ *
+ * @param {Tag} model
+ * @param {Tag} newModel
+ * @returns {Tag}
+ * @private
+ */
+ _mergeTagModel(model, newModel) {
+ if(newModel.getProperty('revisions') !== undefined) {
+ model.setProperty('revisions', newModel.getProperty('revisions'));
+ }
+ if(newModel.getProperty('passwords') !== undefined) {
+ model.setProperty('passwords', newModel.getProperty('passwords'));
+ }
+ this._mergeModelDetailLevel(model, newModel.getDetailLevel());
+
+ return newModel;
+ }
+
+ /**
+ * @param {AbstractRevisionModel} model
+ * @param {String[]} detailLevel
+ * @private
+ */
+ _mergeModelDetailLevel(model, detailLevel = []) {
+ let modelDetailLevel = model.getDetailLevel();
+ for(let level of detailLevel) {
+ if(modelDetailLevel.indexOf(level) === -1) {
+ modelDetailLevel.push(level);
+ }
+ }
+ model.setDetailLevel(modelDetailLevel);
+ }
+} \ No newline at end of file