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
16export class IndexedDBHelp {
17  private dbName: string = '';
18  private dbVersion: number = 1;
19  private db: IDBDatabase | undefined;
20
21  public open(dbName: string, dbVersion: number, storeOptions?: Array<StoreOptions>): Promise<IDBDatabase> {
22    this.dbName = dbName;
23    this.dbVersion = dbVersion;
24    return new Promise((resolve, reject) => {
25      if (this.db && this.db.name === dbName && this.db.version === dbVersion) {
26        resolve(this.db);
27        return;
28      }
29      const idbOpenDBRequest = indexedDB.open(dbName, dbVersion);
30      idbOpenDBRequest.onupgradeneeded = (): void => {
31        const database: IDBDatabase = idbOpenDBRequest.result;
32        this.db = database;
33        storeOptions?.forEach((option) => {
34          let optionName = option.name;
35          if (database.objectStoreNames.contains(optionName) === false) {
36            if (option.objectStoreParameters) {
37              let objectStore = database.createObjectStore(optionName, option.objectStoreParameters);
38              option.dataItems?.forEach((dataItem) => {
39                if (dataItem.indexParameters) {
40                  objectStore.createIndex(dataItem.name, dataItem.keypath, dataItem.indexParameters);
41                } else {
42                  objectStore.createIndex(dataItem.name, dataItem.keypath);
43                }
44              });
45            } else {
46              let objectStore = database.createObjectStore(optionName);
47              option.dataItems?.forEach((dataItem) => {
48                if (dataItem.indexParameters) {
49                  objectStore.createIndex(dataItem.name, dataItem.name, dataItem.indexParameters);
50                } else {
51                  objectStore.createIndex(dataItem.name, dataItem.name);
52                }
53              });
54            }
55          }
56        });
57        resolve(database);
58      };
59      idbOpenDBRequest.onsuccess = (event): void => {
60        const database: IDBDatabase = idbOpenDBRequest.result;
61        this.db = database;
62        resolve(database);
63      };
64      idbOpenDBRequest.onerror = (event): void => {
65        reject(event);
66      };
67    });
68  }
69
70  private async transaction(storeName: string): Promise<IDBTransaction> {
71    if (this.db === undefined) {
72      this.db = await this.open(this.dbName, this.dbVersion);
73    }
74    return this.db.transaction([storeName], 'readwrite');
75  }
76
77  public async getObjectStore(storeName: string): Promise<IDBObjectStore> {
78    let transaction = await this.transaction(storeName);
79    return transaction.objectStore(storeName);
80  }
81
82  public get(storeName: string, query: IDBValidKey | IDBKeyRange, queryIndex?: string): Promise<unknown> {
83    return new Promise((resolve, reject) => {
84      this.getObjectStore(storeName).then((objectStore: IDBObjectStore) => {
85        // @ts-ignore
86        let request: IDBRequest<unknown>;
87        if (queryIndex) {
88          const index = objectStore.index(queryIndex);
89          //@ts-ignore
90          request = index.getAll(query);
91        } else {
92          //@ts-ignore
93          request = objectStore.getAll(query);
94        }
95        request.onsuccess = function (event): void {
96          // @ts-ignore
97          resolve(event.target.result);
98        };
99        request.onerror = (event): void => {
100          reject(event);
101        };
102      });
103    });
104  }
105  // @ts-ignore
106  public add(storeName: string, value: unknown, key?: IDBValidKey): Promise<unknown> {
107    return new Promise((resolve, reject) => {
108      this.getObjectStore(storeName).then((objectStore: IDBObjectStore) => {
109        const request = objectStore.add(value, key);
110        request.onsuccess = function (event): void {
111          // @ts-ignore
112          resolve(event.target.result);
113        };
114        request.onerror = (event): void => {
115          reject(event);
116        };
117      });
118    });
119  }
120
121  public delete(storeName: string, query: IDBValidKey | IDBKeyRange): Promise<unknown> {
122    return new Promise((resolve, reject) => {
123      this.getObjectStore(storeName).then((objectStore: IDBObjectStore) => {
124        const request = objectStore.delete(query);
125        request.onsuccess = function (event): void {
126          // @ts-ignore
127          resolve(event.target.result);
128        };
129        request.onerror = (event): void => {
130          reject(event);
131        };
132      });
133    });
134  }
135  // @ts-ignore
136  public put(storeName: string, value: unknown, key?: IDBValidKey): Promise<unknown> {
137    return new Promise((resolve, reject) => {
138      this.getObjectStore(storeName).then((objectStore: IDBObjectStore) => {
139        const request = objectStore.put(value, key);
140        request.onsuccess = function (event): void {
141          // @ts-ignore
142          resolve(event.target.result);
143        };
144        request.onerror = (event): void => {
145          reject(event);
146        };
147      });
148    });
149  }
150}
151
152export class StoreOptions {
153  name: string = '';
154  objectStoreParameters?: IDBObjectStoreParameters;
155  dataItems?: Array<{ name: string; keypath: string[] | string; indexParameters?: IDBIndexParameters }>;
156}
157