-
Notifications
You must be signed in to change notification settings - Fork 0
/
IndexedDBStore.ts
146 lines (121 loc) · 4.09 KB
/
IndexedDBStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { DBSchema, IDBPDatabase, openDB } from "idb";
import { InternalLocationData } from "../types";
import {
DataStore,
DataStoreInvalidLocationError,
DataStoreNotInitializedError,
} from "./DataStore";
interface COVID19TimeSeriesDBSchema extends DBSchema {
settings: {
key: string;
value: unknown;
};
data: {
key: string;
value: InternalLocationData;
indexes: {
byCountryOrRegion: string;
byProvinceOrState: string;
};
};
}
/**
* A data store that saves to and loads from an IndexedDB database.
*
* For more information about its methods see {@link DataStore}.
*/
export class IndexedDBStore implements DataStore {
readonly savedAtKey = "DataSavedAt";
readonly sourceLastUpdatedAtKey = "DataSourceLastUpdatedAt";
readonly dbName = "COVID19APIDB";
readonly dbVersion = 1;
private _db: IDBPDatabase<COVID19TimeSeriesDBSchema> | undefined;
private get db(): IDBPDatabase<COVID19TimeSeriesDBSchema> {
if (this._db == null) {
throw new DataStoreNotInitializedError();
}
return this._db;
}
async init(): Promise<void> {
await this.setDB();
}
async clearData(): Promise<void> {
const tx = this.db.transaction(["data", "settings"], "readwrite");
await tx.objectStore("data").clear();
await tx.objectStore("settings").clear();
}
async getLocationData(locations: string[]): Promise<InternalLocationData[]> {
const dataStore = this.db.transaction("data").objectStore("data");
const data: InternalLocationData[] = [];
for (const location of locations) {
const locationData = await dataStore.get(location);
if (locationData == null) {
throw new DataStoreInvalidLocationError(location);
}
data.push(locationData);
}
return data;
}
async getStatesData(countryOrRegion: string): Promise<InternalLocationData[]> {
const countiesAndStates = await this.db.getAllFromIndex(
"data",
"byCountryOrRegion",
countryOrRegion
);
return countiesAndStates.filter((s) => s.county == null);
}
async getCountiesData(
countryOrRegion: string,
provinceOrState: string
): Promise<InternalLocationData[]> {
const countiesAndState = await this.db.getAllFromIndex(
"data",
"byProvinceOrState",
provinceOrState
);
return countiesAndState.filter(
(data) => data.countryOrRegion === countryOrRegion && data.county != null
);
}
async getLocationsList(): Promise<string[]> {
return await this.db.getAllKeys("data");
}
async getLocationCount(): Promise<number> {
return await this.db.count("data");
}
async getSavedAt(): Promise<Date | undefined> {
return (await this.db.get("settings", this.savedAtKey)) as Date | undefined;
}
async getSourceLastUpdatedAt(): Promise<Date | undefined> {
return (await this.db.get("settings", this.sourceLastUpdatedAtKey)) as Date | undefined;
}
async putLocationData(data: InternalLocationData[]): Promise<void> {
const tx = this.db.transaction(["data", "settings"], "readwrite");
const dataStore = tx.objectStore("data");
for (const locationData of data) {
await dataStore.put(locationData);
}
const settingsStore = tx.objectStore("settings");
await settingsStore.put(new Date(), this.savedAtKey);
}
async setSourceLastUpdatedAt(lastUpdatedAt: Date): Promise<void> {
await this.db.put("settings", lastUpdatedAt, this.sourceLastUpdatedAtKey);
}
/**
* Opens an IndexedDB connection.
*
* If the database is not yet created, it also creates it. If the database exists but is an older
* version, it upgrades it.
*/
private async setDB(): Promise<void> {
this._db = await openDB<COVID19TimeSeriesDBSchema>(this.dbName, this.dbVersion, {
upgrade(db, _oldVersion, _newVersion, transaction) {
db.createObjectStore("data", { keyPath: "location" });
db.createObjectStore("settings");
const dataStore = transaction.objectStore("data");
dataStore.createIndex("byCountryOrRegion", "countryOrRegion");
dataStore.createIndex("byProvinceOrState", "provinceOrState");
},
});
}
}